cancel transaction after estimateGas failure

This commit is contained in:
Eric 2023-09-27 17:44:16 +10:00
parent 620b402a7d
commit f7984ef384
No known key found for this signature in database
3 changed files with 47 additions and 3 deletions

View File

@ -123,7 +123,7 @@ proc send(contract: Contract,
Future[?TransactionResponse] {.async.} =
if signer =? contract.signer:
let transaction = createTransaction(contract, function, parameters, overrides)
let populated = await signer.populateTransaction(transaction)
let populated = await signer.populateTransaction(transaction, cancelOnEstimateGasError = true)
let txResp = await signer.sendTransaction(populated)
return txResp.some
else:

View File

@ -1,7 +1,12 @@
import ./basics
import ./provider
import pkg/chronicles
export basics
export chronicles
logScope:
topics = "ethers signer"
type
Signer* = ref object of RootObj
@ -64,8 +69,37 @@ method updateNonce*(signer: Signer, nonce: ?UInt256) {.base, gcsafe.} =
if nonce > lastSeen:
signer.lastSeenNonce = some nonce
method cancelTransaction(
signer: Signer,
tx: Transaction
): Future[TransactionResponse] {.async, base.} =
# cancels a transaction by sending with a 0-valued transaction to ourselves
# with the failed tx's nonce
without sender =? tx.sender:
raiseSignerError "transaction must have sender"
if sender != await signer.getAddress():
raiseSignerError "can only cancel a tx this signer has sent"
without nonce =? tx.nonce:
raiseSignerError "transaction must have nonce"
var cancelTx = tx
cancelTx.to = sender
cancelTx.value = 0.u256
cancelTx.nonce = some nonce
try:
cancelTx.gasLimit = some(await signer.estimateGas(cancelTx))
except ProviderError:
warn "failed to estimate gas for cancellation tx, sending anyway",
tx = $cancelTx
discard
trace "cancelling transaction to prevent stuck transactions", nonce
return await signer.sendTransaction(cancelTx)
method populateTransaction*(signer: Signer,
transaction: Transaction):
transaction: Transaction,
cancelOnEstimateGasError = false):
Future[Transaction] {.base, async.} =
if sender =? transaction.sender and sender != await signer.getAddress():
@ -84,6 +118,13 @@ method populateTransaction*(signer: Signer,
if transaction.gasPrice.isNone and (transaction.maxFee.isNone or transaction.maxPriorityFee.isNone):
populated.gasPrice = some(await signer.getGasPrice())
if transaction.gasLimit.isNone:
populated.gasLimit = some(await signer.estimateGas(populated))
try:
populated.gasLimit = some(await signer.estimateGas(populated))
except ProviderError as e:
# send a 0-valued transaction with the errored nonce to prevent stuck txs
discard await signer.cancelTransaction(populated)
raiseSignerError "estimateGas failed. *A cancellation transaction " &
"(0-valued tx to ourselves with the estimateGas nonce) has been sent " &
"to prevent stuck transactions.* Error: " & e.msg
return populated

View File

@ -1,4 +1,6 @@
import std/json
import std/options
import std/strutils
import pkg/asynctest
import pkg/questionable
import pkg/stint
@ -7,6 +9,7 @@ import pkg/ethers/erc20
import ./hardhat
import ./miner
import ./mocks
import ./examples
type
TestToken = ref object of Erc20Token