Fix: ensure that gas estimations are done using the "pending" block

This commit is contained in:
Mark Spanbroek 2024-02-26 16:35:10 +01:00 committed by markspanbroek
parent d46f5a10d3
commit af5a0f5fb4
5 changed files with 73 additions and 3 deletions

View File

@ -88,7 +88,7 @@ method estimateGas*(
transaction.sender = some(address) transaction.sender = some(address)
try: try:
return await signer.provider.estimateGas(transaction) return await signer.provider.estimateGas(transaction, blockTag)
except ProviderError as e: except ProviderError as e:
raiseEstimateGasError transaction, e raiseEstimateGasError transaction, e
@ -161,7 +161,7 @@ method populateTransaction*(
# stuck transactions # stuck transactions
populated.nonce = some(await signer.getNonce()) populated.nonce = some(await signer.getNonce())
try: try:
populated.gasLimit = some(await signer.estimateGas(populated)) populated.gasLimit = some(await signer.estimateGas(populated, BlockTag.pending))
except EstimateGasError as e: except EstimateGasError as e:
signer.decreaseNonce() signer.decreaseNonce()
raise e raise e
@ -173,7 +173,7 @@ method populateTransaction*(
if transaction.nonce.isNone: if transaction.nonce.isNone:
populated.nonce = some(await signer.getNonce()) populated.nonce = some(await signer.getNonce())
if transaction.gasLimit.isNone: if transaction.gasLimit.isNone:
populated.gasLimit = some(await signer.estimateGas(populated)) populated.gasLimit = some(await signer.estimateGas(populated, BlockTag.pending))
finally: finally:
signer.populateLock.release() signer.populateLock.release()

View File

@ -6,5 +6,6 @@ import ./testEvents
import ./testWallet import ./testWallet
import ./testTesting import ./testTesting
import ./testErc20 import ./testErc20
import ./testGasEstimation
{.warning[UnusedImport]:off.} {.warning[UnusedImport]:off.}

View File

@ -0,0 +1,41 @@
import pkg/asynctest
import pkg/ethers
import pkg/serde
import ./hardhat
type
TestGasEstimation = ref object of Contract
proc getTime(contract: TestGasEstimation): UInt256 {.contract, view.}
proc checkTimeEquals(contract: TestGasEstimation, expected: UInt256) {.contract.}
suite "gas estimation":
var contract: TestGasEstimation
var provider: JsonRpcProvider
var snapshot: JsonNode
setup:
provider = JsonRpcProvider.new()
snapshot = await provider.send("evm_snapshot")
let deployment = readDeployment()
let signer = provider.getSigner()
contract = TestGasEstimation.new(!deployment.address(TestGasEstimation), signer)
teardown:
discard await provider.send("evm_revert", @[snapshot])
await provider.close()
test "uses pending block for gas estimations":
let latest = CallOverrides(blockTag: some BlockTag.latest)
let pending = CallOverrides(blockTag: some BlockTag.pending)
# retrieve time of pending block
let time = await contract.getTime(overrides=pending)
# ensure that time of latest block and pending block differ
check (await contract.getTime(overrides=latest)) != time
# fails with "Transaction ran out of gas" when gas estimation
# is not done using the pending block
await contract.checkTimeEquals(time)

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TestGasEstimation {
uint lastCheckedTime;
// this function returns a different value depending on whether
// it is called on the latest block, or on the pending block
function getTime() public view returns (uint) {
return block.timestamp;
}
// this function is designed to require a different amount of
// gas, depending on whether the parameter matches the block
// timestamp
function checkTimeEquals(uint expected) public {
if (expected == block.timestamp) {
lastCheckedTime = block.timestamp;
}
}
}

View File

@ -0,0 +1,6 @@
module.exports = async ({ deployments, getNamedAccounts }) => {
const { deployer } = await getNamedAccounts();
await deployments.deploy("TestGasEstimation", { from: deployer });
};
module.exports.tags = ["TestGasEstimation"];