8

Below is from a truffle test cases in javascript, where I was trying to add the gas cost to an account balance to confirm the sum of transaction, where the sum should be equal to the previous balance.

But I couldn't get it to work!

The rcpt.cumulativeGasUsed, web3.eth.gasPrice, getBalance(accounts[1]) are all correct, but the arithmetic is not, some fromWei, toWei kung-fu needed?

What am I doing wrong here?

            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)));

});

After a lot of experiments, it looks like the below method works fine, but couldn't explain the logic of transaction cost! Any idea is most welcome.

Anyways unit test is passing now!!!

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");
          });
        });
2
  • 1
    The correct formula for the ether paid by a transaction should be gasUsed * gasPrice + value. If the contracts make a transfer to the sender that will change the final balance.
    – Ismael
    Mar 7, 2018 at 6:20
  • yup, that is what my understanding also. You can see that in my original question, where I did rcpt.cumulativeGasUsed * web3.eth.gasPrice to get the transaction cost. But it didn't work! If you have a console open, you can quickly verify the same with the Metacoin in a truffle test case. Looks like some truffle test magic??? Mar 8, 2018 at 22:08

2 Answers 2

12

Note: This answer is only valid for Truffle v4.


I've tried this test with the MetaCoin example from truffle (ie run truffle unbox metacoin in an empty directory)

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");
  });
});

The correspoding output is:

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

  1 passing (217ms)

One difference is that I'm reading the gasPrice from the transaction (getTransaction(hash).gasPrice) instead of the network eth.gasPrice().

3
  • 1
    ok, that explains, getTransaction(hash).gasPrice is 100000000000 and eth.gasPrice() is 20000000000 in my tests. Mar 9, 2018 at 5:31
  • For me tx.gasPrice is not BN (string), tx.gasPrice is int -- both require: new BN( n ); Jul 8, 2019 at 11:31
  • 1
    @TheBurgerKing The answer from last year was for Truffle v4, that uses web3 v0.20 which uses a fork of BigNumber.js. Likely you are using the new Truffle v5, which uses web3 v1.0 which uses BN.js in turn. I'd suggest to create a new answer with the solution for Truffle v5. I'd edit my answer to indicate it was tested with Truffle v4.
    – Ismael
    Jul 8, 2019 at 15:03
4

The following code is the same code as Ismael's code, with the changes to work with Truffle v5, as getBalance's return type, beside gasUsed and gasPrice values are not BN by default:

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");
  });
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.