Add replay for failed txs to get revert reason

This commit is contained in:
Eric 2023-09-13 16:09:15 +10:00
parent 9536e5e5bf
commit a4fd152c8c
No known key found for this signature in database
5 changed files with 42 additions and 1 deletions

View File

@ -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,

View File

@ -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):

View File

@ -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.} =

View File

@ -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

View File

@ -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