Costo del gas de transacción en el caso de prueba de la trufa

A continuación se muestra un caso de prueba de trufas en javascript, donde estaba tratando de agregar el costo del gas al saldo de una cuenta para confirmar la suma de la transacción, donde la suma debería ser igual al saldo anterior.

¡Pero no pude hacerlo funcionar!

El rcpt.cumulativeGasUsed, web3.eth.gasPrice, getBalance(accounts[1]) son todos correctos, pero la aritmética no lo es, ¿se necesita algo de Wei, a Wei kung-fu?

¿Qué estoy haciendo mal aquí?

            return instance.bailOut({ from: accounts[1] }).then(function (resp) {
            var rcpt = web3.eth.getTransactionReceipt(resp.tx);
            console.log("Sum: " + ((rcpt.cumulativeGasUsed * web3.eth.gasPrice) + web3.eth.getBalance(accounts[1]).toString(10)));

});

Después de muchos experimentos, parece que el siguiente método funciona bien, ¡pero no pudo explicar la lógica del costo de transacción! Cualquier idea es más que bienvenida.

¡De todos modos, la prueba unitaria está pasando ahora!

return contractInstance.withdraw({ value: web3.toWei(1, "ether"), gas: 1000000 }).then(function () {
          var contractAddressBalance = web3.fromWei(web3.eth.getBalance(contractAddress).toString(10));
          console.log("contractAddress balance after withdraw: " + contractAddressBalance);
          return contractInstance.bailOut({ from: accounts[1] }).then(function (resp) {
            var rcpt = web3.eth.getTransactionReceipt(resp.tx);

            console.log("cumulativeGasUsed: " + rcpt.cumulativeGasUsed);
            console.log("gasPrice: " + web3.eth.gasPrice);

            var transactionCost = (rcpt.cumulativeGasUsed / 10000000); // How come this works???

            console.log("transactionCost: " + transactionCost);
            console.log("Account[1] balance after withdraw: " + web3.eth.getBalance(accounts[1]));

            contractWalletAfter = web3.fromWei(web3.eth.getBalance(accounts[1]).add(web3.fromWei(rcpt.cumulativeGasUsed)));
            assert.equal(((contractWalletAfter.minus(contractWalletBefore)).add(transactionCost)).valueOf(), 6, "6 wasn't in the contract wallet");
          });
        });
La fórmula correcta para el éter pagado por una transacción debería ser gasUsed * gasPrice + value. Si los contratos hacen una transferencia al remitente que cambiará el saldo final.
sí, eso es lo que yo entiendo también. Puede ver eso en mi pregunta original, donde hice rcpt.cumulativeGasUsed * web3.eth.gasPricepara obtener el costo de transacción. ¡Pero no funcionó! Si tiene una consola abierta, puede verificar rápidamente lo mismo con Metacoin en un caso de prueba de trufa. ¿Parece alguna prueba mágica de trufas?

Respuestas (2)

Nota: Esta respuesta solo es válida para Truffle v4.


Probé esta prueba con el ejemplo MetaCoin de truffle (es decir, ejecutar truffle unbox metacoinen un directorio vacío)

var MetaCoin = artifacts.require("./MetaCoin.sol");

contract('MetaCoin', function(accounts) {
  it("Test gas", async () => {
    const meta = await MetaCoin.deployed();

    // Initial balance of the second account
    const initial = await web3.eth.getBalance(accounts[1]);
    console.log(`Initial: ${initial.toString()}`);

    // Obtain gas used from the receipt
    const receipt = await meta.sendCoin(accounts[2], 1, { from: accounts[1] });
    const gasUsed = receipt.receipt.gasUsed;
    console.log(`GasUsed: ${receipt.receipt.gasUsed}`);

    // Obtain gasPrice from the transaction
    const tx = await web3.eth.getTransaction(receipt.tx);
    const gasPrice = tx.gasPrice;
    console.log(`GasPrice: ${tx.gasPrice}`);

    // Final balance
    const final = await web3.eth.getBalance(accounts[1]);
    console.log(`Final: ${final.toString()}`);
    assert.equal(final.add(gasPrice.mul(gasUsed)).toString(), initial.toString(), "Must be equal");
  });
});

La salida correspondiente es:

  Contract: MetaCoin
Initial: 99971803600000000000
GasUsed: 23497
GasPrice: 100000000000
Final: 99969453900000000000Test gas (199ms)

  1 passing (217ms)

Una diferencia es que estoy leyendo el precio del gas de la transacción ( getTransaction(hash).gasPrice) en lugar de la red eth.gasPrice().

ok, eso explica, getTransaction(hash).gasPricees 100000000000 y eth.gasPrice()es 20000000000 en mis pruebas.
Para mí, tx.gasPrice no es BN (cadena), tx.gasPrice es int; ambos requieren: new BN (n);
@TheBurgerKing La respuesta del año pasado fue para Truffle v4, que usa web3 v0.20 que usa una bifurcación de BigNumber.js. Es probable que esté usando el nuevo Truffle v5, que usa web3 v1.0, que a su vez usa BN.js. Sugeriría crear una nueva respuesta con la solución para Truffle v5. Editaría mi respuesta para indicar que se probó con Truffle v4.

El siguiente código es el mismo código que el código de Ismael , con los cambios para trabajar con Truffle v5, como getBalancetipo de retorno, al lado gasUsedy gasPricelos valores no son BN por defecto:

var MetaCoin = artifacts.require("./MetaCoin.sol");

const { toBN } = web3.utils;

contract('MetaCoin', function(accounts) {
  it("Test gas", async () => {
    const meta = await MetaCoin.deployed();

    // Initial balance of the second account
    const initial = toBN(await web3.eth.getBalance(accounts[1]));
    console.log(`Initial: ${initial.toString()}`);

    // Obtain gas used from the receipt
    const receipt = await meta.sendCoin(accounts[2], 1, { from: accounts[1] });
    const gasUsed = toBN(receipt.receipt.gasUsed);
    console.log(`GasUsed: ${receipt.receipt.gasUsed}`);

    // Obtain gasPrice from the transaction
    const tx = await web3.eth.getTransaction(receipt.tx);
    const gasPrice = toBN(tx.gasPrice);
    console.log(`GasPrice: ${tx.gasPrice}`);

    // Final balance
    const final = toBN(await web3.eth.getBalance(accounts[1]));
    console.log(`Final: ${final.toString()}`);
    assert.equal(final.add(gasPrice.mul(gasUsed)).toString(), initial.toString(), "Must be equal");
  });
});