¿Cómo se calcula la dirección de un contrato de Ethereum?

¿Cómo se calcula la dirección de un contrato de Ethereum? ¿Qué casos de uso existen para conocer la dirección de un contrato por adelantado?

Además de la respuesta @eth , debe tenerse en cuenta que toma los últimos 20 bytes.

Respuestas (7)

EDIT Abril 2019 : CREATE2información añadida.

EDITAR enero de 2022 : sintaxis de Solidity actualizada a ^ 0.8.0.

La dirección de un contrato de Ethereum se calcula de forma determinista a partir de la dirección de su creador ( sender) y cuántas transacciones ha enviado el creador ( nonce). Los sendery nonceestán codificados con RLP y luego se codifican con Keccak-256 .

Del pieterio:

def mk_contract_address(sender, nonce):
    return sha3(rlp.encode([normalize_address(sender), nonce]))[12:]

En Solidez:

nonce0= address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x80))))));
nonce1= address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xd6), bytes1(0x94), _origin, bytes1(0x01))))));

Ejemplo con alguna discusión:

Para el remitente 0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0, las direcciones de contrato que creará son las siguientes:

nonce0= "0xcd234a471b72ba2f1ccf0a70fcaba648a5eecd8d"
nonce1= "0x343c43a37d37dff08ae8c4a11544c718abb4fcf8"
nonce2= "0xf778b86fa74e846c4f0a1fbd1335fe81c00a0c91"
nonce3= "0xfffd933a0bc612844eaf0c6fe3e5b8e9b6c1d19c"

En Java con Web3j:

private String calculateContractAddress(String address, long nonce){
    byte[] addressAsBytes = Numeric.hexStringToByteArray(address);

    byte[] calculatedAddressAsBytes =
            Hash.sha3(RlpEncoder.encode(
                    new RlpList(
                            RlpString.create(addressAsBytes),
                            RlpString.create((nonce)))));

    calculatedAddressAsBytes = Arrays.copyOfRange(calculatedAddressAsBytes,
            12, calculatedAddressAsBytes.length);
    String calculatedAddressAsHex = Numeric.toHexString(calculatedAddressAsBytes);
    return calculatedAddressAsHex;
}

Nota : Según EIP 161 A , las cuentas de contrato de especificación se inician con nonce = 1 (en la red principal). Por lo tanto, la primera dirección del contrato, creada por otro contrato, se calculará con nonce distinto de cero.


CREATE2

Se agregó un nuevo código de operación CREATE2en EIP-1014 que es otra forma en que se puede crear un contrato.

Por contrato creado por CREATE2su dirección será:

keccak256( 0xff ++ senderAddress ++ salt ++ keccak256(init_code))[12:]

Se agregará más información aquí y, mientras tanto, consulte EIP-1014 .

Para la parte del caso de uso, podría mencionar algo sobre contratos de prefinanciación
¿Qué pasa si el creador es un contrato en sí mismo? ¿La dirección también es un nonce? ¿Se incrementa con cada llamada que hace o solo con la creación de nuevos contactos? ¿O es relevante la dirección y el nonce de tx.origin?
@TjadenHess Gracias, voté a favor en lugar de explicar más sobre la financiación previa.
@mKoeppelmann: Mismo cálculo si el creador es un contrato; nonce aumenta cada transacción individual que realiza una cuenta (el nuevo contrato/cuenta comenzaría en nonce 0), y la dirección y el nonce de los remitentes anteriores (como tx.origin) no afectan la dirección del nuevo contrato.
@eth: "nonce aumenta cada transacción individual" esto es lo que me confunde, porque en la definición en el papel amarillo solo las cuentas controladas por humanos pueden realizar transacciones. Una transacción es algo que se firma con una clave privada. Los contratos solo pueden realizar llamadas activadas por transacciones. Así que me pregunto qué "llamadas" aumentan el nonce de un contrato. ¿Todas las llamadas o solo las llamadas que crean un nuevo contrato?
Creo que la pregunta que estoy haciendo tiene un poco de tema, así que creé una nueva pregunta aquí: ethereum.stackexchange.com/questions/764/…
¿Qué sucede en el caso de un contrato que crea un contrato? ¿Un contrato también tiene un número de secuencia?
@StevenRoose Sí, los contratos tienen nonces. Un nonce de un contrato solo se incrementa cuando ese contrato crea otro contrato. Está en una pregunta separada que Martin hizo ethereum.stackexchange.com/questions/764/… :)
0xd6, 0x94, 0x80 y 0x01, ¿cuáles son esos números mágicos?
Son parte de la codificación RLP
Aquí está la calculadora de dirección de contrato inteligente en línea con soporte CREATE2 completo: toolkit.abdk.consulting/ethereum#contract-address
@MikhailVladimirov Gracias, si tiene tiempo para publicar una respuesta separada, explicando los diferentes campos/cómo usar su calculadora, actualizaré mi respuesta CREATE2 para señalar la suya.
Para Solidity ^0.6.0, tuve que usar address(uint160(uint256(keccak256(abi.encodePacked(byte(0xd6), byte(0x94), _origin, byte(0x80))))))(for nonce0).

Gracias a la respuesta de eth , ayudó mucho a resolver el problema de $2000.

Acabo de resolver el problema con los fondos, que se enviaron en la red principal de Ethereum a la dirección del contrato inteligente, implementado para probar la red Ethereum. Usamos la misma billetera para implementar diferentes contratos inteligentes en la red principal de Ethereum varias veces hasta que el campo de transacción nonce alcanzó el mismo valor 13, que se usó para implementar en la red de prueba . Llamamos al método especial de contrato inteligente recién implementado para recuperar fondos. Entonces, se implementó un contrato inteligente después de que realmente se financió: https://etherscan.io/address/0x9c86825280b1d6c7dB043D4CC86E1549990149f9

Acabo de terminar un artículo sobre este problema: https://medium.com/@k06a/how-we-sent-eth-to-the-wrong-address-and-successfully-recovered-them-2fc18e09d8f6

ingrese la descripción de la imagen aquí

Gracias ... pero ¿qué pasa si el propietario del contrato de la red principal tiene un nonce actual más alto que el que se usó para crear el contrato en la red de prueba? Tengo la misma situación que la tuya... pero el único problema es que mi monedero de propietario neto principal tiene el último nonce 236 y el contrato de testnet se creó a las 13 nonce... ¿hay alguna forma de anular e implementar el contrato con un nonce anterior?
Con las tarifas de gas actuales, pagaría una fortuna para implementar el contrato 12 veces, antes de llegar a 13. ¿Por qué no estableció el nonce en 13 de inmediato?
@Qwerty, ¿puede establecer un nonce usted mismo para la implementación?
@Sky Sí, en metamask al confirmar la transacción. Lo acabo de probar con REMIX. Si no lo ve en Metamask, habilite las opciones avanzadas.

Aquí hay un script de node.js que calcula de forma determinista una dirección de contrato de Ethereum dada la dirección pública del creador del contrato y el valor de nonce.

Avíseme si alguien tiene preguntas sobre entradas, etc.

// node version: v9.10.0
// module versions:
// rlp@2.0.0
// keccak@1.4.0

const rlp = require("rlp");
const keccak = require("keccak");

var nonce = 0x00; //The nonce must be a hex literal!
var sender = "0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"; //Requires a hex string as input!

var input_arr = [sender, nonce];
var rlp_encoded = rlp.encode(input_arr);

var contract_address_long = keccak("keccak256")
  .update(rlp_encoded)
  .digest("hex");

var contract_address = contract_address_long.substring(24); //Trim the first 24 characters.
console.log("contract_address: " + contract_address);

Tenga en cuenta que el nonce se puede incrementar normalmente, solo recuerde que es un valor hexadecimal.

Salida (nonce = 0x00):

contract_address: cd234a471b72ba2f1ccf0a70fcaba648a5eecd8d

Salida (nonce = 0x01):

contract_address: 343c43a37d37dff08ae8c4a11544c718abb4fcf8
¿Qué es rlp ?
Aquí hay una buena explicación: ethereum.stackexchange.com/questions/19092/…

Aquí está la versión actualizada de Python para las bibliotecas modernas de Ethereum ( eth-utils):

import rlp
from eth_utils import keccak, to_checksum_address, to_bytes


def mk_contract_address(sender: str, nonce: int) -> str:
    """Create a contract address using eth-utils.

    # https://ethereum.stackexchange.com/a/761/620
    """
    sender_bytes = to_bytes(hexstr=sender)
    raw = rlp.encode([sender_bytes, nonce])
    h = keccak(raw)
    address_bytes = h[12:]
    return to_checksum_address(address_bytes)


print(to_checksum_address(mk_contract_address(to_checksum_address("0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"), 1)))
print("0x343c43a37d37dff08ae8c4a11544c718abb4fcf8")
assert mk_contract_address(to_checksum_address("0x6ac7ea33f8831ea9dcc53393aaa88b25a785dbf0"), 1) == \
    to_checksum_address("0x343c43a37d37dff08ae8c4a11544c718abb4fcf8")

RLP hecho en solidez (aunque no probé esto, ¡cuidado! solo para entender):

    function addressFrom(address _origin, uint _nonce) public pure returns (address) {
        if(_nonce == 0x00)     return address(keccak256(byte(0xd6), byte(0x94), _origin, byte(0x80)));
        if(_nonce <= 0x7f)     return address(keccak256(byte(0xd6), byte(0x94), _origin, byte(_nonce)));
        if(_nonce <= 0xff)     return address(keccak256(byte(0xd7), byte(0x94), _origin, byte(0x81), uint8(_nonce)));
        if(_nonce <= 0xffff)   return address(keccak256(byte(0xd8), byte(0x94), _origin, byte(0x82), uint16(_nonce)));
        if(_nonce <= 0xffffff) return address(keccak256(byte(0xd9), byte(0x94), _origin, byte(0x83), uint24(_nonce)));
        return address(keccak256(byte(0xda), byte(0x94), _origin, byte(0x84), uint32(_nonce))); // more than 2^32 nonces not realistic
    }
gracias por compartir, me pregunto de dónde sacaste esos números mágicos: 0x7f, 0xd6, 0x94
El nonce nunca puede ser 0, por lo que se puede eliminar la primera prueba.
Nonce comienza con cero, vea las transacciones en etherscan.

Aquí hay una implementación pura de ethers.js en TypeScript, que devuelve una dirección con suma de verificación. se espera que nonce sea un regular number.

(ethers.js también tiene una función llamada getContractAddress, pero no se puede usar para ningún momento)

import { ethers } from 'hardhat';

static getContractAddress(address: string, nonce: number): string {
    const rlp_encoded = ethers.utils.RLP.encode(
        [address, ethers.BigNumber.from(nonce.toString()).toHexString()]
    );
    const contract_address_long = ethers.utils.keccak256(rlp_encoded);
    const contract_address = '0x'.concat(contract_address_long.substring(26));
    return ethers.utils.getAddress(contract_address);
}

Esta implementación funciona en caso de que el nonce sea mayor que 0. Para nonce = 0, el resultado no es correcto (ver ejemplo y comparar con el resultado de la respuesta principal). Una sola línea simple que funciona correctamente en ethers.js esconst contractAddress = ethers.utils.getContractAddress({from, nonce});
@GongFu sí, es mejor usar la función incorporada. En ese momento, creo que estaba usando una versión obsoleta de Ethers que no tenía esta función.

La dirección del contrato suele ser un hash de la dirección del remitente y la billetera del remitente nonce. El código de contrato real no hace ninguna diferencia: el hash es el mismo independientemente del código.

Arriba dije típicamente porque hay otras formas de implementar contratos. Si un contrato existente implementa un contrato con un código de operación especial CREATE2, la dirección del contrato se calcula de manera un poco diferente.

Puede consultar los detalles, por ejemplo, aquí: https://medium.com/coinmonks/smart-contract-address-creation-method-difference- between -smart-contract-address-and-wallet-97b421506455

Se votó a favor pero se fusionó con la pregunta anterior para reunir las respuestas.
Ya comencé a preguntarme si respondí accidentalmente una pregunta con respuestas anteriores válidas.
¡No hay problema! A veces es útil fusionarse, a veces no. Gracias por todo tu trabajo. (He votado a favor de muchos, pero muchos otros no parecen votar mucho).