[Ethereum] How to get the transaction cost in a Truffle unit test

gasgas-pricesoliditytestingtruffle

How can I get the actual gas consumed by a sendTransaction inside a truffle test?

In the following test, buyer sends an amount of 50 ETH to a contract:

const EmptyContract = artifacts.require("EmptyContract");
const BigNumber = web3.BigNumber;

contract('Test', function(accounts) {   
    it("should work", async () => {
        let contract = await EmptyContract.new({ from: accounts[1] });
        let amount = web3.toWei(50, 'ether');
        let buyer = accounts[2];

        // BALANCE BEFORE TX
        let balanceBefore = web3.eth.getBalance(buyer);

        // SEND TX
        let hash = await contract.buy.sendTransaction({from: buyer, value: amount});

        // BALANCE AFTER TX
        const balanceAfter = web3.eth.getBalance(buyer);

        let tx = await web3.eth.getTransaction(hash);
        const gasUsed = tx.gas;
        const gasCost = tx.gasPrice.mul(gasUsed);
        console.log("BEFORE", balanceBefore.toNumber());
        console.log("amount sent", amount);
        console.log("gas price", tx.gasPrice.toNumber());
        console.log("gas used", gasUsed);
        console.log("gas cost", gasCost.toNumber());
        console.log("AFTER", balanceAfter.toNumber());
        console.log("CHECKSUM", balanceAfter.add(gasCost).add(amount).toNumber());

        assert.equal(balanceBefore.toNumber(), balanceAfter.add(gasCost).add(amount).toNumber());
    });
});

Then buyer's balance is checked. It should be

[balance before tx] = [balance after tx] + [amount sent] + [gas cost]

But it fails, printing:

BEFORE 100000000000000000000
amount sent 50000000000000000000
gas price 100000000000
gas used 6721975
gas cost 672197500000000000
AFTER 49997862200000000000
CHECKSUM 100670059700000000000       
   :    
AssertionError: expected 100000000000000000000 to equal 100670059700000000000

The contract is very basic:

pragma solidity ^0.4.18;

contract EmptyContract {
    function buy() public payable  { }
}

It seems that the transaction cost 0.6700597 ETH less than the value returned by tx.

I followed Transaction gas cost in truffle test case but it didn't help.

Any idea?

Best Answer

You have to multiply the gasUsed for the gasPrice. The gasUsed is contained into the transaction receipt. While the gasPrice is into the transaction itself. Indeed, in your case, you're trying to multiply the gasPrice for the gas, which is the one provided by the sender (which can be higher that the gasUsed). ref: getTransaction, getTransactionReceipt

So, this is how your code should look like to be correct

const hash = await contract.buy.sendTransaction({from: buyer, value: amount});

// BALANCE AFTER TX
const balanceAfter = web3.eth.getBalance(buyer);
const tx = await web3.eth.getTransaction(hash);
const receipt = await web3.eth.getTransactionReceipt(hash);
const gasCost = tx.gasPrice.mul(receipt.gasUsed);

or you can also actually save the getTransactionReceipt call in case you call your method directly (in this case should give you a result that contains also the transaction receipt ref)

const txInfo = await contract.buy({from: buyer, value: amount});

// BALANCE AFTER TX
const balanceAfter = web3.eth.getBalance(buyer);
const tx = await web3.eth.getTransaction(txInfo.tx);
const gasCost = tx.gasPrice.mul(txInfo.receipt.gasUsed);