Skip to content

Commit

Permalink
↔️ Handle dropped and replaced transactions (#355)
Browse files Browse the repository at this point in the history
* Handle dropped and replaced transactions

Previously if a user speeded up a transaction the old transaction would report as failed and the new one would not exist in useDapp state. 

This change add handling around failed transaction to check if it was replaced and repriced and should therefore continue on as a new pending transaction.
  • Loading branch information
quagliero authored Nov 15, 2021
1 parent be1875b commit bf3b543
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/four-snails-change.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@usedapp/core': patch
---

Handle dropped and replaced transactions
5 changes: 5 additions & 0 deletions docs/source/core.rst
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ Each can be one of the following:
{
type: 'transactionSucceed'
transaction: TransactionResponse
originalTransaction?: TransactionResponse
receipt: TransactionReceipt
transactionName?: string
}
Expand All @@ -376,6 +377,7 @@ Each can be one of the following:
{
type: 'transactionFailed'
transaction: TransactionResponse
originalTransaction?: TransactionResponse
receipt: TransactionReceipt
transactionName?: string
}
Expand Down Expand Up @@ -457,6 +459,7 @@ Each transaction has following type:
receipt?: TransactionReceipt
lastCheckedBlockNumber?: number
transactionName?: string
originalTransaction?: TransactionResponse
}
Link to: `Transaction Response <https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse>`_.
Expand Down Expand Up @@ -640,6 +643,8 @@ Fields:
- ``transaction?: TransactionResponse`` - optional field. See `Transaction Response <https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse>`_.
- ``originalTransaction?: TransactionResponse`` - optional field that contains the original transaction if it has been dropped and replaced. See `Transaction Response <https://docs.ethers.io/v5/api/providers/types/#providers-TransactionResponse>`_.
- ``receipt?: TransactionReceipt`` - optional field. See `Transaction Receipt <https://docs.ethers.io/v5/api/providers/types/#providers-TransactionReceipt>`_.
- ``chainId?: ChainId`` - optional field. See `chainId`_.
Expand Down
40 changes: 37 additions & 3 deletions packages/core/src/hooks/usePromiseTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { TransactionResponse } from '@ethersproject/abstract-provider'
import { useCallback, useState } from 'react'
import { useTransactionsContext } from '../providers'
import { useNotificationsContext, useTransactionsContext } from '../providers'
import { TransactionStatus, TransactionOptions } from '../../src'
import { TransactionState } from '../model'
import { errors } from 'ethers'

const isDroppedAndReplaced = (e: any) =>
e?.code === errors.TRANSACTION_REPLACED && e?.replacement && (e?.reason === 'repriced' || e?.cancelled === false)

export function usePromiseTransaction(chainId: number | undefined, options?: TransactionOptions) {
const [state, setState] = useState<TransactionStatus>({ status: 'None' })
const { addTransaction } = useTransactionsContext()
const { addNotification } = useNotificationsContext()

const promiseTransaction = useCallback(
async (transactionPromise: Promise<TransactionResponse>) => {
Expand All @@ -26,10 +32,38 @@ export function usePromiseTransaction(chainId: number | undefined, options?: Tra
const receipt = await transaction.wait()
setState({ receipt, transaction, status: 'Success', chainId })
return receipt
} catch (e) {
} catch (e: any) {
const errorMessage = e.error?.message ?? e.reason ?? e.data?.message ?? e.message
if (transaction) {
setState({ status: 'Fail', transaction, receipt: e.receipt, errorMessage, chainId })
const droppedAndReplaced = isDroppedAndReplaced(e)

if (droppedAndReplaced) {
const status: TransactionState = e.receipt.status === 0 ? 'Fail' : 'Success'
const type = status === 'Fail' ? 'transactionFailed' : 'transactionSucceed'

addNotification({
notification: {
type,
submittedAt: Date.now(),
transaction: e.replacement,
receipt: e.receipt,
transactionName: e.replacement?.transactionName,
originalTransaction: transaction,
},
chainId,
})

setState({
status,
transaction: e.replacement,
originalTransaction: transaction,
receipt: e.receipt,
errorMessage,
chainId,
})
} else {
setState({ status: 'Fail', transaction, receipt: e.receipt, errorMessage, chainId })
}
} else {
setState({ status: 'Exception', errorMessage, chainId })
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/model/TransactionStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface TransactionStatus {
receipt?: TransactionReceipt
chainId?: ChainId
errorMessage?: string
originalTransaction?: TransactionResponse
}

export function transactionErrored(transaction: TransactionStatus) {
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/providers/notifications/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ type NotificationPayload = { submittedAt: number } & (
transaction: TransactionResponse
receipt: TransactionReceipt
transactionName?: string
originalTransaction?: TransactionResponse
}
| {
type: 'transactionFailed'
transaction: TransactionResponse
receipt: TransactionReceipt
transactionName?: string
originalTransaction?: TransactionResponse
}
| { type: 'walletConnected'; address: string }
)
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/providers/transactions/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface StoredTransaction {
receipt?: TransactionReceipt
lastCheckedBlockNumber?: number
transactionName?: string
originalTransaction?: TransactionResponse
}

export function getStoredTransactionState(transaction: StoredTransaction) {
Expand Down

0 comments on commit bf3b543

Please sign in to comment.