diff --git a/ethers/contract.nim b/ethers/contract.nim index fdb5744..4c047f6 100644 --- a/ethers/contract.nim +++ b/ethers/contract.nim @@ -250,31 +250,13 @@ proc confirm*(tx: Future[?TransactionResponse], ## `await token.connect(signer0) ## .mint(accounts[1], 100.u256) ## .confirm(3)` - ## Will raise ContractError with revert reason if TransactionReceipt.Status - ## is Failed without response =? (await tx): raise newException( EthersError, "Transaction hash required. Possibly was a call instead of a send?" ) - let receipt = await response.confirm(confirmations, timeout) - - # TODO: handle TransactionStatus.Invalid? - if receipt.status == TransactionStatus.Failure: - logScope: - transactionHash = receipt.transactionHash.to0xHex - - trace "transaction failed, replaying transaction to get revert reason" - - if revertReason =? await response.provider.getRevertReason(receipt): - trace "transaction revert reason obtained", revertReason - raiseContractError(revertReason) - else: - trace "transaction replay completed, no revert reason obtained" - raiseContractError("Transaction reverted with unknown reason") - - return receipt + return await response.confirm(confirmations, timeout) proc queryFilter[E: Event](contract: Contract, _: type E, diff --git a/ethers/provider.nim b/ethers/provider.nim index 46bec4b..a9b3215 100644 --- a/ethers/provider.nim +++ b/ethers/provider.nim @@ -81,6 +81,9 @@ const EthersReceiptTimeoutBlks* {.intdefine.} = 50 # in blocks logScope: topics = "ethers provider" +template raiseProviderError(msg: string) = + raise newException(ProviderError, msg) + func toTransaction*(past: PastTransaction): Transaction = Transaction( sender: some past.sender, @@ -194,11 +197,33 @@ method getRevertReason*( return await provider.getRevertReason(receipt.transactionHash, blockNumber - 1) +proc ensureSuccess( + provider: Provider, + receipt: TransactionReceipt +) {.async, upraises: [ProviderError].} = + ## If the receipt.status is Failed, the tx is replayed to obtain a revert + ## reason, after which a ProviderError with the revert reason is raised. + ## If no revert reason was obtained + + # TODO: handle TransactionStatus.Invalid? + if receipt.status == TransactionStatus.Failure: + logScope: + transactionHash = receipt.transactionHash.to0xHex + + trace "transaction failed, replaying transaction to get revert reason" + + if revertReason =? await provider.getRevertReason(receipt): + trace "transaction revert reason obtained", revertReason + raiseProviderError(revertReason) + else: + trace "transaction replay completed, no revert reason obtained" + raiseProviderError("Transaction reverted with unknown reason") + proc confirm*(tx: TransactionResponse, confirmations = EthersDefaultConfirmations, timeout = EthersReceiptTimeoutBlks): Future[TransactionReceipt] - {.async, upraises: [EthersError].} = + {.async, upraises: [ProviderError, EthersError].} = ## Waits for a transaction to be mined and for the specified number of blocks ## to pass since it was mined (confirmations). ## A timeout, in blocks, can be specified that will raise an error if too many @@ -237,6 +262,7 @@ proc confirm*(tx: TransactionResponse, if txBlockNumber + confirmations.u256 <= blockNumber + 1: await subscription.unsubscribe() + await tx.provider.ensureSuccess(receipt) return receipt proc confirm*(tx: Future[TransactionResponse],