Guardar programáticamente la dirección del contrato de un contrato enviado

Entiendo cómo funciona el siguiente bloque de código web3js, adaptado de aquí:

https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethcontract

Pero, ¿cómo se guarda la dirección del contrato mediante programación? Uno podría, supongo, abrir un archivo y conservarlo dentro de la devolución de llamada, pero no eso. =:)

Y no creo que el identificador myContractReturned nos ayude; dada la naturaleza asíncrona de JavaScript (sin mencionar el retraso de la minería). ¿Cómo se usa eso?

Es una especie de pregunta académica (porque no hay garantía de que se genere una dirección), pero es curioso saber qué han hecho (o pensado) los amigos de la comunidad. ¡Gracias!

--

var myContractReturned = MyContract.new(param1,param2, {
   from:mySenderAddress,
   data:bytecode,
   gas:gasEstimate},
   function(error, myContract){
     if(!error) {
        if(!myContract.address) {
            // Step-1: Runs on contract submission/deployment.
            console.log(myContract.transactionHash)
        } 
        else {
            // Step-2: Runs after contract is deployed.
            console.log(myContract.address)
        }

     }
  });
Gracias "@Mikko Ohtamaa" y "@Xavier Leprêtre B9lab" por sus respectivas respuestas. ¡Ambas son respuestas bastante buenas y animo a los lectores a ver ambas! La respuesta de Xavier nos recuerda que, al calcularla previamente, podemos conocer la dirección del contrato incluso antes de que se envíe.

Respuestas (2)

La dirección del contrato se calcula a partir de la dirección del implementador y la transacción nonce. No necesita esperar a que se extraiga el contrato para obtenerlo.

En NodeJs, algo así funcionará:

var ethUtil = require('ethereumjs-util');

var currentNonce = web3.eth.getTransactionCount(myAccount);
var futureAddress = ethUtil.bufferToHex(ethUtil.generateAddress(myAccount, currentNonce));
// direcciónfuturo es la dirección del contrato que implementa a continuación

var MiContrato = web3.eth.contract(abiArray);
var contractInstance = MyContract.new([contructorParam1] [, contructorParam2], {data: '0x12345...', from: myAccount, gas: 1000000});

// Aquí puede confirmar que su dirección es de hecho la que calculó anteriormente.

No olvide mejorar este código de demostración utilizando llamadas asincrónicas.

Lástima que las personas que lleguen a la respuesta aceptada creerán que no puedes tener la dirección antes de que se haya extraído.
@"Xavier Leprêtre B9lab" Cierto, y es por eso que actualicé mi respuesta aceptada, para que la gente pueda verla. La respuesta aceptada se cambió desde su comentario anterior :)

El constructor del contrato devolverá un hash de transacción donde se está implementando el contrato. La dirección del contrato final se puede determinar de manera determinista a partir de la dirección del implementador y la dirección del implementador nonce (ver otra respuesta). Esta información también está disponible web3.eth.getTransactionReceiptdespués de que se haya extraído la transacción de implementación.

Tenga en cuenta que no sabe si la implementación del contrato tendrá éxito o fallará antes de que se haya extraído la transacción. No puede interactuar con el contrato antes de que se haya extraído la transacción.

Aquí hay una secuencia de comandos de implementación de muestra para Node 7 ( lea el tutorial completo ):

// Copyright 2017 https://tokenmarket.net - MIT licensed
//
// Run with Node 7.x as:
//
// node --harmony-async-await  deploy.js
//
// DO NOT RUN IN GETH CONSOLE

let fs = require("fs");
let Web3 = require('web3'); // https://www.npmjs.com/package/web3

// Create a web3 connection to a running geth node over JSON-RPC running at
// http://localhost:8545
// For geth VPS server + SSH tunneling see
// https://gist.github.com/miohtama/ce612b35415e74268ff243af645048f4
let web3 = new Web3();
web3.setProvider(new web3.providers.HttpProvider('http://localhost:8545'));

// Read the compiled contract code
// Compile with
// solc SampleContract.sol --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,devdoc,interface,opcodes,srcmap,srcmap-runtime,userdoc > contracts.json
let source = fs.readFileSync("contracts.json");
let contracts = JSON.parse(source)["contracts"];

// ABI description as JSON structure
let abi = JSON.parse(contracts.SampleContract.abi);

// Smart contract EVM bytecode as hex
let code = contracts.SampleContract.bin;

// Create Contract proxy class
let SampleContract = web3.eth.contract(abi);

// Unlock the coinbase account to make transactions out of it
console.log("Unlocking coinbase account");
var password = "";
try {
  web3.personal.unlockAccount(web3.eth.coinbase, password);
} catch(e) {
  console.log(e);
  return;
}

console.log("Deploying the contract");
let contract = SampleContract.new({from: web3.eth.coinbase, gas: 1000000, data: code});

// Transaction has entered to geth memory pool
console.log("Your contract is being deployed in transaction at http://testnet.etherscan.io/tx/" + contract.transactionHash);

// http://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// We need to wait until any miner has included the transaction
// in a block to get the address of the contract
async function waitBlock() {
  while (true) {
    let receipt = web3.eth.getTransactionReceipt(contract.transactionHash);
    if (receipt && receipt.contractAddress) {
      console.log("Your contract has been deployed at http://testnet.etherscan.io/address/" + receipt.contractAddress);
      console.log("Note that it might take 30 - 90 sceonds for the block to propagate befor it's visible in etherscan.io");
      break;
    }
    console.log("Waiting a mined block to include your contract... currently in block " + web3.eth.blockNumber);
    await sleep(4000);
  }
}

waitBlock();
Gracias por esta respuesta "@Mikko Ohtamaa". Apreciado. A continuación, "@Xavier Leprêtre B9lab" proporciona un enfoque alternativo, y los lectores deben aprovechar ambas respuestas (apropiadas para su caso de uso). :)
Por favor, aclare que el código provisto no es compatible con la consola JavaScript de geth (el código usa funciones de ES6 como funciones de flecha, mientras que geth solo es compatible con ES5). Yo mismo cometí el error y parece que no soy el primero: ethereum.stackexchange.com/q/33978/36845