Los costos de gas no se suman en un contrato de transacción simple

Tengo un contrato simple que ayuda a hacer un pago a otra cuenta. Digamos que comienzo el contrato con el valor V, los costos de gas del contrato Gy el valor se traduce a la otra cuenta X. Supongo que lo siguiente será cierto:

V = G + X.

Sin embargo no lo es.

Siguiendo un simple truffle initpaso, estoy trabajando con el siguiente contrato.

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

Y el siguiente archivo de prueba.

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

La salida es la siguiente:

  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)

En particular, como se ve en la salida:

V-X=-6752(¿puede incluso ser negativo?)G=6400200000

ACTUALIZACIÓN : respuesta correcta para referencia.

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

Respuestas (1)

Estás tomando la diferencia usando los números de javascript

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

Pero los números de javascript no tienen suficiente precisión para manejar valores grandes.

Debe almacenar el saldo como BigNums y realizar la conversión a número como la última operación.

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

También estás pagando las operaciones con las account[0]que tienes que usar saldos de esa cuenta para medir el gas gastado.


Está utilizando estimateGaspara determinar el uso de gas utilizado, debe utilizar el gasUsedcampo del recibo de la transacción .

De la documentación de eth_estimategas

Tenga en cuenta que la estimación puede ser significativamente mayor que la cantidad de gas realmente utilizada por la transacción, por una variedad de razones que incluyen la mecánica de EVM y el rendimiento del nodo.

Hay operaciones que tienen un costo negativo, por ejemplo, eliminar el almacenamiento devolverá el gas, pero primero debe tener éxito.

Gracias por esto, pero cuando estoy funcionando result.receiptobtengo un gas usado de 32001 que corresponde a la estimación anterior. Sin embargo, tengo que multiplicar eso con gasPrice de 200,000 que he establecido. La tarifa de gas total resultante de 6.400.200.000 no coincide con el monto incremental pagado por el contrato.
@Peteris Actualicé mi respuesta, estaba usando los números de javascript en su prueba pero no tiene suficiente precisión para obtener el resultado correcto.