Implementación correcta de contratos con web3 1.0 y testrpc (error de código de operación no válido)

Seguí el siguiente tutorial: https://medium.com/@mvmurthy/full-stack-hello-world-voting-ethereum-dapp-tutorial-part-1-40d2d0d807c2

Ahora, este tutorial no usa la versión reciente de web3, pero traté de resolver esto con la documentación de web3.

Esto es lo lejos que llegué:

>byteCode = compiledCode.contracts[':Voting'].bytecode       
>deployedContract = VotingContract.new(['Rama','Nick','Jose'],{data:   byteCode, from: web3.eth.accounts[0], gas: 4700000})
>deployedContract.address
>contractInstance = VotingContract.at(deployedContract.address)

Compilé con éxito el byteCode e implementé el contrato de la siguiente manera:

> VotingContract.deploy({ data: byteCode, arguments: [[web3.utils.asciiToHex("name1"),web3.utils.asciiToHex("name2"),web3.utils.asciiToHex("name3")]] }).send({ from: acc1, gas: 4700000, gasPrice: '3000000' }).on('confirmation', function(confirmationNumber, receipt){console.log(confirmationNumber + ' ' + receipt) }).then(function(instance) {contractInstance = instance});

Ahora debería tener una instancia de contrato, ¿no? O se trata de otra cosa? El autor continúa haciendo lo siguiente:

>contractInstance = VotingContract.at(deployedContract.address)

Hice esto en su lugar:

var contract3 = new web3.eth.Contract(byteCode, contractInstance.options.address, {from: acc1, gasPrice:'3000000'});

Ahora, si trato de llamar a métodos en este contrato como:

contract3.methods.totalVotesFor(web3.utils.toHex('name1')).call(acc1).then(function(returned, err) {console.log(returned + ' ' + err)});

Recibo la siguiente respuesta:

Promise {
  _bitField: 0,
  _fulfillmentHandler0: undefined,
  _rejectionHandler0: undefined,
  _promise0: undefined,
  _receiver0: undefined }
> Unhandled rejection Error: Returned error: Error: VM Exception while executing eth_call: invalid opcode
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:59368:17
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:69306:5
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:11335:9
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:7895:16
    at replenish (C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:8415:25)
    at iterateeCallback (C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:8405:17)
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:8380:16
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:11332:13
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:69302:9
    at C:\Path\node_modules\ethereumjs-testrpc\build\cli.node.js:63982:7
    at Object.ErrorResponse (C:\Path\node_modules\web3-core-helpers\src\errors.js:29:16)
    at C:\Path\node_modules\web3-core-requestmanager\src\index.js:137:36
    at XMLHttpRequest.request.onreadystatechange (C:\Path\node_modules\web3-providers-http\src\index.js:64:13)
    at XMLHttpRequestEventTarget.dispatchEvent (C:\Path\node_modules\xhr2\lib\xhr2.js:64:18)
    at XMLHttpRequest._setReadyState (C:\Path\node_modules\xhr2\lib\xhr2.js:354:12)

¿Qué podría estar haciendo mal?

¡Gracias por tu ayuda!

Por favor revise esta pregunta . Podría ayudar.
Según la respuesta a continuación, creo que el problema podría estar en el código del contrato. ¿Podrías compartir eso?

Respuestas (3)

He usado estos scripts para implementar un contrato usando web3 v1.0-beta24

const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

const ownerAddress = "0x....";
const contractAbi = [{"constant":false.... }];
const contractCode = "0x606060...";

const MyContract = new web3.eth.Contract(contractAbi);

MyContract.deploy({
  data: contractCode,
})
.send({
  from: ownerAddress,
  gas: 4000000,
  gasPrice: '30000000000000',
})
.then((instance) => {
  console.log(`Address: ${instance.options.address}`);
});

Y este otro script para enviar un mensaje y hacer una consulta

const Web3 = require('web3');
const web3 = new Web3('http://127.0.0.1:8545');

const ownerAddress = "0x....";
const contractAbi = [{"constant":false.... }];
const contractCode = "0x606060...";
const contractAddress = "0x....";  // <----- previous script output

const myContract = new web3.eth.Contract(contractAbi, contractAddress);

const options = {
  from: ownerAddress,
  gas: 4000000,
  gasPrice: '30000000000000',
};

const message = web3.utils.asciiToHex('hola');

myContract.methods.sendMessage(message)
.send(options, (err, hash) => {
  if (err) {
    console.log(err);
  }
  console.log(`TxHash: ${hash}`);
})
.then((result) => {
  myContract.methods.getMessage().call()
  .then((result) => {
    console.log(web3.utils.hexToAscii(result));
  });
});

El contrato de muestra que he usado es muy simple.

pragma solidity ^0.4.18;

contract Echo {
    bytes32 public message;

    function sendMessage(bytes32 _message) public {
        message = _message;
    }

    function getMessage() view public returns (bytes32) {
        return message;
    }
}
Eso no funcionó para mí, de hecho, estaba obteniendo métodos vacíos{}, por lo que web3js me arroja un error que no puede obtener una longitud indefinida. estoy usando beta 10
@siva Estaba usando beta24 cuando se realizó la prueba, debería probar con una versión más reciente. Cuando el tiempo lo permita, comprobaré si el ejemplo funciona con la última versión.

La dirección del contrato se devuelve en receipto confirmationevento. Aquí está el código completo que vota y obtiene el resultado.

const fs = require('fs');
const solc = require('solc');
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:7545"));

async function deploy() {
    let accounts = await web3.eth.getAccounts();

    let code = fs.readFileSync('Voting.sol').toString();
    let compiledCode = solc.compile(code);
    let abi = JSON.parse(compiledCode.contracts[':Voting'].interface);
    let bytecode = compiledCode.contracts[':Voting'].bytecode;
    let votingContract = new web3.eth.Contract(abi, {from: accounts[0], gas: 47000, data: bytecode});

    let rama = web3.utils.asciiToHex('Rama');
    let nick = web3.utils.asciiToHex('Nick');
    let jose = web3.utils.asciiToHex('Jose');

    let contractInstance = await votingContract.deploy({
        arguments: [[rama, nick, jose]]
    })
    .send({
        from: accounts[0],
        gas: 1500000
    }, (err, txHash) => {
        console.log('send:', err, txHash);
    })
    .on('error', (err) => {
        console.log('error:', err);
    })
    .on('transactionHash', (err) => {
        console.log('transactionHash:', err);
    })
    .on('receipt', (receipt) => {
        console.log('receipt:', receipt);
        votingContract.options.address = receipt.contractAddress;
    });

    console.log('contractInstance.options:', contractInstance.options);

    let result = await votingContract.methods.totalVotesFor(rama).call({from: accounts[0]});
    console.log('result:', result); // 0

    let receipt = await votingContract.methods.voteForCandidate(rama).send({from: accounts[0]});
    console.log('voteForCandidate receipt:', receipt);

    result = await votingContract.methods.totalVotesFor(rama).call({from: accounts[0]});
    console.log('new result:', result); // 1
}

deploy()
.then(() => console.log('Success'))
.catch(err => console.log('Script failed:', err));

Tuve problemas similares. Creo que hay un error en web3.0 cuando se trata de argumentos de constructor. Los valores del constructor deben agregarse como hexadecimal al final del código de bytes, cuando crea el tx para implementar el contrato.

Puede usar txDataByCompiled de eth-crypto y luego enviar la transacción manualmente.