Skip to content

Commit 67b7ece

Browse files
committed
race cond. fix
1 parent 5c32813 commit 67b7ece

4 files changed

Lines changed: 41 additions & 33 deletions

File tree

src/hooks/transactions/transactionStore.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,8 @@ describe('transactionStore', () => {
5656
setTimeout(() => {
5757
onReplaced!({
5858
reason: 'repriced',
59-
transaction: {
60-
hash: newHash,
61-
},
62-
} as any)
59+
transactionHash: newHash,
60+
})
6361
}, 0)
6462

6563
return {

src/hooks/transactions/transactionStore.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -399,21 +399,15 @@ export function createTransactionStore(config_: ConfigWithEns) {
399399
const requestPromise = waitForTransaction(config, {
400400
confirmations: 1,
401401
hash: hash as `0x${string}`,
402-
onReplaced: (replacedTransaction) => {
403-
if (replacedTransaction.reason === 'repriced') {
404-
setTransactionStatus(
405-
account,
406-
chainId,
407-
hash,
408-
'repriced',
409-
replacedTransaction.transaction.hash,
410-
)
402+
onReplaced: (response) => {
403+
if (response.reason === 'repriced') {
404+
setTransactionStatus(account, chainId, hash, 'repriced', response.transactionHash)
411405
addTransaction(account, chainId, {
412406
...transaction,
413407
isSafeTx: false,
414-
hash: replacedTransaction.transaction.hash,
408+
hash: response.transactionHash,
415409
})
416-
transactionRequestCache.set(replacedTransaction.transaction.hash, requestPromise)
410+
transactionRequestCache.set(response.transactionHash, requestPromise)
417411
transactionRequestCache.delete(hash)
418412
}
419413
},

src/hooks/transactions/waitForTransaction.test.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { type PartialMockedFunction } from '@app/test-utils'
2-
3-
import { PartiallyMockedFunction } from '@vitest/spy'
4-
import { WaitForTransactionReceiptReturnType } from 'viem'
1+
import { type MockedFunctionDeep } from '@vitest/spy'
2+
import { WaitForTransactionReceiptReturnType, type Transaction } from 'viem'
53
import { waitForTransactionReceipt } from 'viem/actions'
6-
import { describe, expect, it, vi } from 'vitest'
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
75

86
import { ClientWithEns, ConfigWithEns } from '@app/types'
97
import { fetchTxFromSafeTxHash } from '@app/utils/safe'
@@ -14,11 +12,10 @@ vi.mock('viem/actions')
1412

1513
vi.mock('@app/utils/safe')
1614

17-
const mockWaitForTransactionReceipt = waitForTransactionReceipt as unknown as PartialMockedFunction<
15+
const mockWaitForTransactionReceipt = waitForTransactionReceipt as MockedFunctionDeep<
1816
typeof waitForTransactionReceipt
1917
>
20-
21-
const mockFetchTxFromSafeTxHash = fetchTxFromSafeTxHash as unknown as PartiallyMockedFunction<
18+
const mockFetchTxFromSafeTxHash = fetchTxFromSafeTxHash as MockedFunctionDeep<
2219
typeof fetchTxFromSafeTxHash
2320
>
2421

@@ -50,6 +47,10 @@ const mockTransactionReceiptData: WaitForTransactionReceiptReturnType = {
5047
type: 'legacy',
5148
}
5249

50+
beforeEach(() => {
51+
vi.clearAllMocks()
52+
})
53+
5354
describe('waitForTransaction', () => {
5455
it('should wait for standard transaction', async () => {
5556
// @ts-ignore vi.fn is messing with types
@@ -62,7 +63,6 @@ describe('waitForTransaction', () => {
6263
expect(result).toStrictEqual(mockTransactionReceiptData)
6364
})
6465
it('should pass onReplaced tx to waitForTransactionReceipt', async () => {
65-
// @ts-ignore vi.fn is messing with types
6666
mockWaitForTransactionReceipt.mockResolvedValueOnce(mockTransactionReceiptData)
6767

6868
const onReplaced = vi.fn()
@@ -72,12 +72,22 @@ describe('waitForTransaction', () => {
7272
onReplaced,
7373
})
7474

75-
expect(mockWaitForTransactionReceipt).toHaveBeenCalledWith(
76-
mockClient,
77-
expect.objectContaining({
78-
onReplaced,
79-
}),
80-
)
75+
expect(mockWaitForTransactionReceipt).toHaveBeenCalled()
76+
77+
const onReplacedInternalFn = mockWaitForTransactionReceipt.mock.calls[0][1].onReplaced
78+
onReplacedInternalFn!({
79+
reason: 'replaced',
80+
transaction: {
81+
hash: '0xnewhash',
82+
} as unknown as Transaction,
83+
replacedTransaction: {} as unknown as Transaction,
84+
transactionReceipt: {} as unknown as WaitForTransactionReceiptReturnType,
85+
})
86+
87+
expect(onReplaced).toHaveBeenCalledWith({
88+
reason: 'replaced',
89+
transactionHash: '0xnewhash',
90+
})
8191
})
8292
})
8393

src/hooks/transactions/waitForTransaction.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {
44
EIP1193RequestFn,
55
Hash,
66
PublicRpcSchema,
7-
WaitForTransactionReceiptParameters,
7+
ReplacementReason,
88
WaitForTransactionReceiptReturnType,
99
} from 'viem'
1010
import { hexToString } from 'viem'
@@ -24,7 +24,7 @@ export type WaitForTransactionArgs = {
2424
/** Transaction hash to monitor */
2525
hash: Hash
2626
/** Callback to invoke when the transaction has been replaced (sped up). */
27-
onReplaced?: WaitForTransactionReceiptParameters['onReplaced']
27+
onReplaced?: (response: { reason: ReplacementReason; transactionHash: Hash }) => void
2828
/*
2929
* Maximum amount of time to wait before timing out in milliseconds
3030
* @default 0
@@ -81,7 +81,8 @@ export async function waitForTransaction(
8181
const receipt = await waitForTransactionReceipt(client, {
8282
hash,
8383
confirmations,
84-
onReplaced,
84+
onReplaced: ({ reason, transaction }) =>
85+
onReplaced?.({ reason, transactionHash: transaction.hash }),
8586
timeout,
8687
})
8788
if (receipt.status === 'reverted') {
@@ -96,6 +97,11 @@ export async function waitForTransaction(
9697
} as CallParameters)) as unknown as string
9798
const reason = hexToString(`0x${code.substring(138)}`)
9899
throw new Error(reason)
100+
} else if (isSafeTx) {
101+
onReplaced?.({
102+
reason: 'repriced',
103+
transactionHash: receipt.transactionHash,
104+
})
99105
}
100106
return receipt
101107
}

0 commit comments

Comments
 (0)