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 G
y 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 init
paso, 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);
})
});
});
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 estimateGas
para determinar el uso de gas utilizado, debe utilizar el gasUsed
campo 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.
Pedro es
result.receipt
obtengo 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.ismael