From a4fd152c8c892de0beffe060830770db338ea031 Mon Sep 17 00:00:00 2001 From: Eric <5089238+emizzle@users.noreply.github.com> Date: Wed, 13 Sep 2023 16:09:15 +1000 Subject: [PATCH] Add replay for failed txs to get revert reason --- ethers/contract.nim | 19 ++++++++++++++++++- ethers/provider.nim | 15 +++++++++++++++ ethers/providers/jsonrpc.nim | 7 +++++++ ethers/providers/jsonrpc/signatures.nim | 1 + testmodule/testContracts.nim | 1 + 5 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ethers/contract.nim b/ethers/contract.nim index c590313..963932a 100644 --- a/ethers/contract.nim +++ b/ethers/contract.nim @@ -252,7 +252,24 @@ proc confirm*(tx: Future[?TransactionResponse], "Transaction hash required. Possibly was a call instead of a send?" ) - return await response.confirm(confirmations, timeout) + let receipt = await response.confirm(confirmations, timeout) + + echo "[ether/contract.confirm] receipt.status: ", receipt.status + if receipt.status != TransactionStatus.Success: + without blockNumber =? receipt.blockNumber: + raiseContractError "Transaction reverted with unknown reason" + + let provider = response.provider + without transaction =? await provider.getTransaction(receipt.transactionHash): + raiseContractError "Transaction reverted with unknown reason" + + try: + await provider.replay(transaction, blockNumber) + except ProviderError as e: + # should contain the revert reason + raiseContractError e.msg + + return receipt proc queryFilter[E: Event](contract: Contract, _: type E, diff --git a/ethers/provider.nim b/ethers/provider.nim index 97f6fee..a6e0b1c 100644 --- a/ethers/provider.nim +++ b/ethers/provider.nim @@ -79,6 +79,11 @@ method getTransactionCount*(provider: Provider, Future[UInt256] {.base, gcsafe.} = doAssert false, "not implemented" +method getTransaction*(provider: Provider, + txHash: TransactionHash): + Future[?Transaction] {.base, gcsafe.} = + doAssert false, "not implemented" + method getTransactionReceipt*(provider: Provider, txHash: TransactionHash): Future[?TransactionReceipt] {.base, gcsafe.} = @@ -114,6 +119,16 @@ method subscribe*(provider: Provider, method unsubscribe*(subscription: Subscription) {.base, async.} = doAssert false, "not implemented" +proc replay*(provider: Provider, tx: Transaction, blockNumber: UInt256) {.async.} = + # Replay transaction at block. Useful for fetching revert reasons, which will + # be present in the raised error message. The replayed block number should + # include the state of the chain in the block previous to the block in which + # the transaction was mined. This means that transactions that were mined in + # the same block BEFORE this transaction will not have their state transitions + # included in the replay. + # More information: https://snakecharmers.ethereum.org/web3py-revert-reason-parsing/ + discard await provider.call(tx, BlockTag.init(blockNumber - 1)) + proc confirm*(tx: TransactionResponse, confirmations = EthersDefaultConfirmations, timeout = EthersReceiptTimeoutBlks): diff --git a/ethers/providers/jsonrpc.nim b/ethers/providers/jsonrpc.nim index 1a1af49..b1441b2 100644 --- a/ethers/providers/jsonrpc.nim +++ b/ethers/providers/jsonrpc.nim @@ -152,6 +152,13 @@ method getTransactionCount*(provider: JsonRpcProvider, let client = await provider.client return await client.eth_getTransactionCount(address, blockTag) +method getTransaction*(provider: JsonRpcProvider, + txHash: TransactionHash): + Future[?Transaction] {.async.} = + convertError: + let client = await provider.client + return await client.eth_getTransactionByHash(txHash) + method getTransactionReceipt*(provider: JsonRpcProvider, txHash: TransactionHash): Future[?TransactionReceipt] {.async.} = diff --git a/ethers/providers/jsonrpc/signatures.nim b/ethers/providers/jsonrpc/signatures.nim index 63bc395..66abb4a 100644 --- a/ethers/providers/jsonrpc/signatures.nim +++ b/ethers/providers/jsonrpc/signatures.nim @@ -5,6 +5,7 @@ proc eth_call(transaction: Transaction, blockTag: BlockTag): seq[byte] proc eth_gasPrice(): UInt256 proc eth_getBlockByNumber(blockTag: BlockTag, includeTransactions: bool): ?Block proc eth_getLogs(filter: EventFilter | Filter | FilterByBlockHash): JsonNode +proc eth_getTransactionByHash(hash: BlockHash): ?Transaction proc eth_getBlockByHash(hash: BlockHash, includeTransactions: bool): ?Block proc eth_getTransactionCount(address: Address, blockTag: BlockTag): UInt256 proc eth_estimateGas(transaction: Transaction): UInt256 diff --git a/testmodule/testContracts.nim b/testmodule/testContracts.nim index 2c85216..c17709a 100644 --- a/testmodule/testContracts.nim +++ b/testmodule/testContracts.nim @@ -5,6 +5,7 @@ import pkg/questionable import pkg/stint import pkg/ethers import pkg/ethers/erc20 +import pkg/ethers/testing import ./hardhat import ./helpers import ./miner