Only cancels transactions if nonce has been incremented since the last estimateGas failure

This commit is contained in:
Eric 2023-10-02 07:34:58 +03:00
parent 9ffc1763e4
commit 1862c9eea6
No known key found for this signature in database
5 changed files with 52 additions and 5 deletions

View File

@ -8,6 +8,7 @@ import ./provider
import ./signer
import ./events
import ./fields
import ./exceptions
export basics
export provider
@ -116,6 +117,38 @@ proc call(contract: Contract,
let response = await contract.provider.call(transaction, overrides)
return decodeResponse(ReturnType, returnMultiple, response)
proc populateTransaction(
contract: Contract,
tx: Transaction
): Future[Transaction] {.async.} =
if signer =? contract.signer:
try:
return await signer.populateTransaction(tx)
except EstimateGasError as e:
if nonce =? e.transaction.nonce:
if lastSeenNonce =? signer.lastSeenNonce and
lastSeenNonce > nonce:
discard await signer.cancelTransaction(e.transaction)
let revertReason = if not e.parent.isNil: e.parent.msg
else: "unknown"
info "A cancellation transaction has been sent to prevent stuck " &
"transactions",
nonce = e.transaction.nonce,
revertReason
# nonce wasn't incremented by another transaction, so force update the
# lastSeenNonce
else:
signer.updateNonce(nonce - 1, force = true)
trace "nonce decremented -- estimateGas failed but no further " &
"nonces were generated. Prevents stuck txs.",
failedNonce = nonce,
newNonce = nonce - 1
raiseContractError e.msgStack
proc send(contract: Contract,
function: string,
parameters: tuple,
@ -123,7 +156,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, cancelOnEstimateGasError = true)
let populated = await contract.populateTransaction(transaction)
let txResp = await signer.sendTransaction(populated)
return txResp.some
else:

7
ethers/exceptions.nim Normal file
View File

@ -0,0 +1,7 @@
import ./basics
func msgStack*(error: ref EthersError): string =
var msg = error.msg
if not error.parent.isNil:
msg &= " -- Parent exception: " & error.parent.msg
return msg

View File

@ -66,7 +66,9 @@ method estimateGas*(signer: Signer,
method getChainId*(signer: Signer): Future[UInt256] {.base, gcsafe.} =
signer.provider.getChainId()
method getNonce(signer: Signer): Future[UInt256] {.base, gcsafe, async.} =
func lastSeenNonce*(signer: Signer): ?UInt256 = signer.lastSeenNonce
method getNonce*(signer: Signer): Future[UInt256] {.base, gcsafe, async.} =
var nonce = await signer.getTransactionCount(BlockTag.pending)
if lastSeen =? signer.lastSeenNonce and lastSeen >= nonce:
@ -84,7 +86,7 @@ method updateNonce*(
signer.lastSeenNonce = some nonce
return
if nonce > lastSeen:
if force or nonce > lastSeen:
signer.lastSeenNonce = some nonce
method decreaseNonce*(signer: Signer) {.base, gcsafe.} =
@ -92,8 +94,7 @@ method decreaseNonce*(signer: Signer) {.base, gcsafe.} =
signer.lastSeenNonce = some lastSeen - 1
method populateTransaction*(signer: Signer,
transaction: Transaction,
cancelOnEstimateGasError = false):
transaction: Transaction):
Future[Transaction] {.base, async.} =
if sender =? transaction.sender and sender != await signer.getAddress():

View File

@ -16,6 +16,7 @@ type
method mint(token: TestToken, holder: Address, amount: UInt256): ?TransactionResponse {.base, contract.}
method myBalance(token: TestToken): UInt256 {.base, contract, view.}
method doRevert(token: TestToken, reason: string): ?TransactionResponse {.base, contract.}
for url in ["ws://localhost:8545", "http://localhost:8545"]:

View File

@ -21,4 +21,9 @@ contract TestToken is ERC20 {
function myBalance() public view returns (uint256) {
return balanceOf(msg.sender);
}
function doRevert(string memory reason) public {
// Revert every tx with given reason
require(false, reason);
}
}