From af5a0f5fb4fe4840b5c4c398cdb2480c669433a6 Mon Sep 17 00:00:00 2001 From: Mark Spanbroek Date: Mon, 26 Feb 2024 16:35:10 +0100 Subject: [PATCH] Fix: ensure that gas estimations are done using the "pending" block --- ethers/signer.nim | 6 ++-- testmodule/test.nim | 1 + testmodule/testGasEstimation.nim | 41 ++++++++++++++++++++++++ testnode/contracts/TestGasEstimation.sol | 22 +++++++++++++ testnode/deploy/testgasestimation.js | 6 ++++ 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 testmodule/testGasEstimation.nim create mode 100644 testnode/contracts/TestGasEstimation.sol create mode 100644 testnode/deploy/testgasestimation.js diff --git a/ethers/signer.nim b/ethers/signer.nim index cba120c..c8400f8 100644 --- a/ethers/signer.nim +++ b/ethers/signer.nim @@ -88,7 +88,7 @@ method estimateGas*( transaction.sender = some(address) try: - return await signer.provider.estimateGas(transaction) + return await signer.provider.estimateGas(transaction, blockTag) except ProviderError as e: raiseEstimateGasError transaction, e @@ -161,7 +161,7 @@ method populateTransaction*( # stuck transactions populated.nonce = some(await signer.getNonce()) try: - populated.gasLimit = some(await signer.estimateGas(populated)) + populated.gasLimit = some(await signer.estimateGas(populated, BlockTag.pending)) except EstimateGasError as e: signer.decreaseNonce() raise e @@ -173,7 +173,7 @@ method populateTransaction*( if transaction.nonce.isNone: populated.nonce = some(await signer.getNonce()) if transaction.gasLimit.isNone: - populated.gasLimit = some(await signer.estimateGas(populated)) + populated.gasLimit = some(await signer.estimateGas(populated, BlockTag.pending)) finally: signer.populateLock.release() diff --git a/testmodule/test.nim b/testmodule/test.nim index 4b10495..eb8b23a 100644 --- a/testmodule/test.nim +++ b/testmodule/test.nim @@ -6,5 +6,6 @@ import ./testEvents import ./testWallet import ./testTesting import ./testErc20 +import ./testGasEstimation {.warning[UnusedImport]:off.} diff --git a/testmodule/testGasEstimation.nim b/testmodule/testGasEstimation.nim new file mode 100644 index 0000000..5ee69c3 --- /dev/null +++ b/testmodule/testGasEstimation.nim @@ -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) diff --git a/testnode/contracts/TestGasEstimation.sol b/testnode/contracts/TestGasEstimation.sol new file mode 100644 index 0000000..089c025 --- /dev/null +++ b/testnode/contracts/TestGasEstimation.sol @@ -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; + } + } +} diff --git a/testnode/deploy/testgasestimation.js b/testnode/deploy/testgasestimation.js new file mode 100644 index 0000000..89c92cc --- /dev/null +++ b/testnode/deploy/testgasestimation.js @@ -0,0 +1,6 @@ +module.exports = async ({ deployments, getNamedAccounts }) => { + const { deployer } = await getNamedAccounts(); + await deployments.deploy("TestGasEstimation", { from: deployer }); +}; + +module.exports.tags = ["TestGasEstimation"];