5

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?

2 Answers 2

9

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);
2
  • Thanks! I don't get why web3.eth.getTransaction(hash).gas and web3.eth.getTransactionReceipt(hash).gasUsed are different. What's the meaning of the former?
    – Robert
    Commented Mar 19, 2018 at 8:55
  • As I've explained in the post web3.eth.getTransaction(hash).gas is the value provided by the user. While web3.eth.getTransactionReceipt(hash).gasUsed is the gas used by the transaction. So gas must to be >= gasUsed in order to execute the transaction. Of course you couldn't know before how much gas your transaction will use, that's why user must to increase the gas in order to be sure that the transaction would be executed. gasUsed is often <= gas and the difference (not used gas) is refunded immediately.
    – qbsp
    Commented Mar 19, 2018 at 9:01
0

Alternative: you can use truffle-cost. It allows you to log transaction costs instead of test duration, with a syntax like this:

result = await truffleCost.log(
  yourContract.yourFunction()
);

Please see link for installation (or should I explain in here?).

Disclaimer: I'm truffle-cost's developer.

Your Answer

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

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