1

I have a simple contract that helps make a payment to another account. Let's say I start the contract with value V, contract gas costs of G and value translated into the other account X. I assume the following will be true:

V = G + X.

However it isn't.

Following a simple truffle init step, I am working with the following contract.

Pay.sol

pragma solidity ^0.4.17;

/// @title Pay - Facilitates payments.
contract Pay {
    event Payment(
        address _from,
        address _to,
        uint amount
    );

    /// @dev Makes a payment.
    /// @param _to Address to pay to.
    function pay(address _to) public payable {
        require(msg.value > 0);
        // Does this transfer the right amount of ether (msg.value measured in wei)?
        _to.transfer(msg.value);
        Payment(msg.sender, _to, msg.value);
    }
}

And the following test file.

pay.js

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

contract('Pay', function(accounts) {
    it("should put money in the first account", function() {
        return Pay.deployed().then(function(instance) {
            pay = instance;
            return web3.eth.getBalance(accounts[1]);
        }).then(function(balance){
            startingBalance = balance.toNumber();
            gasPrice = 200000;
            payAmount = 2500000;
            return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
        }).then(function() {
            return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
        }).then(function(gasCost) {
            gasSpent = gasCost * gasPrice;
            console.log(gasCost);
            console.log(gasPrice);
            console.log(gasSpent);
            return web3.eth.getBalance(accounts[1]);
        }).then(function(balance) {
            endingBalance = balance.toNumber();
            assert.equal(endingBalance - startingBalance, payAmount);
        })
    });
});

The output is the following:

  Contract: Pay
32001
200000
6400200000
    1) should put money in the first account

    Events emitted during test:
    ---------------------------

    Payment(_from: 0x627306090abab3a6e1400e9345bc60c78a8bef57, _to: 0xf17f52151ebef6c7334fad080c5704d77216b732, amount: 2500000)

    ---------------------------


  0 passing (476ms)
  1 failing

  1) Contract: Pay should put money in the first account:
     AssertionError: expected 2506752 to equal 2500000
      at test/pay.js:23:20
      at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:118:7)

In particular, as you see from the output:

V-X=-6752 (can it even be negative?) G=6400200000

UPDATE: Correct answer for reference.

pay.js

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

contract('Pay', function(accounts) {
    it("should put money in the first account", function() {
        return Pay.deployed().then(function(instance) {
            pay = instance;
            return web3.eth.getBalance(accounts[1]);
        }).then(function(balance){
            startingBalance = balance;
            gasPrice = 200000;
            payAmount = 2500000;
            return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
        }).then(function(result) {
            gasUsed = result.receipt.gasUsed;
            return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
        }).then(function(gasCost) {
            gasSpent = gasCost * gasPrice;
            return web3.eth.getBalance(accounts[1]);
        }).then(function(balance) {
            endingBalance = balance;
            assert.equal(endingBalance.sub(startingBalance).toNumber(), payAmount);
        })
    });

    it("should pay gas costs from the second account", function() {
        return Pay.deployed().then(function(instance) {
            pay = instance;
            return web3.eth.getBalance(accounts[0]);
        }).then(function(balance){
            startingBalance = balance;
            gasPrice = 200000;
            payAmount = 2500000;
            return pay.pay(accounts[1], {from: accounts[0], value: payAmount, gasPrice: gasPrice });
        }).then(function(result) {
            gasUsed = result.receipt.gasUsed;
            return pay.pay.estimateGas(accounts[1], {from: accounts[0], value: payAmount });
        }).then(function(gasCost) {
            gasSpent = gasCost * gasPrice;
            return web3.eth.getBalance(accounts[0]);
        }).then(function(balance) {
            endingBalance = balance;
            assert.equal(startingBalance.sub(endingBalance).toNumber(), payAmount + gasSpent);
        })
    });
});
2

You are taking the difference using javascript's numbers

startingBalance = balance.toNumber();
...
endingBalance = balance.toNumber();
assert.equal(endingBalance - startingBalance, payAmount);

But javascript numbers do not have enough precision to handle large values.

You should store the balance as BigNums and make the conversion to number as the last operation.

startingBalance = balance;
...
endingBalance = balance;
assert.equal(endingBalance.sub(startingBalance).toNumber(), payAmount);

Also you are paying the operations with account[0] you have to use balances from that account to measure gas spent.


You are using estimateGas to determine use gas used you should use the gasUsed field from the transaction's receipt.

From eth_estimategas documentation

Note that the estimate may be significantly more than the amount of gas actually used by the transaction, for a variety of reasons including EVM mechanics and node performance.

There are operation that has a negative cost, for example deleting storage will return gas, but it has to succeed first.

  • Thanks for this, but when running result.receipt I get a gas used of 32001 which corresponds to the estimate above. However, I have to multiply that with gasPrice of 200,000 that I have set. The resulting overall gas fee of 6,400,200,000 does not match the incremental amount paid from the contract. – Peteris Mar 15 '18 at 23:17
  • @Peteris I've updated my answer, you were using javascript's numbers in your test but it doesn't have enough precision to get the correct result. – Ismael Mar 16 '18 at 4:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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