Solidity
簡單交易合約中的 Gas 成本不加起來
我有一份簡單的契約,可以幫助向另一個帳戶付款。假設我以價值開始契約
V
,契約氣體成本G
和價值轉換到另一個帳戶X
。我假設以下情況為真:
V = G + X
.然而事實並非如此。
按照一個簡單的
truffle init
步驟,我正在處理以下契約。
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); } }
以及下面的測試文件。
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); }) }); });
輸出如下:
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)
特別是,正如您從輸出中看到的:
V-X=-6752
(它甚至可以是負面的嗎?)G=6400200000
更新:正確答案供參考。
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); }) }); });
您正在使用 javascript 的數字來獲取差異
startingBalance = balance.toNumber(); ... endingBalance = balance.toNumber(); assert.equal(endingBalance - startingBalance, payAmount);
但是 javascript 數字沒有足夠的精度來處理大值。
您應該將餘額儲存為 BigNums,並將轉換為數字作為最後的操作。
startingBalance = balance; ... endingBalance = balance; assert.equal(endingBalance.sub(startingBalance).toNumber(), payAmount);
此外,您正在支付操作費用,
account[0]
您必須使用該帳戶中的餘額來衡量所花費的氣體。您正在使用
estimateGas
來確定使用的氣體,您應該使用交易收據gasUsed
中的欄位。來自eth_estimategas文件
請注意,出於各種原因,包括 EVM 機制和節點性能,估計值可能遠遠超過交易實際使用的 gas 量。
有些操作是負成本的,例如刪除儲存會返回gas,但必須先成功。