-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(bridge-ui): pending transactions custom store with better error h…
…andling (#13581) Co-authored-by: jeff <113397187+cyberhorsey@users.noreply.github.com>
- Loading branch information
1 parent
01e8690
commit 394a9d1
Showing
8 changed files
with
203 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { get } from 'svelte/store'; | ||
import type { Signer, Transaction, ethers } from 'ethers'; | ||
import { pendingTransactions } from './transactions'; | ||
|
||
jest.mock('../constants/envVars'); | ||
|
||
// Transaction we're going to add to the store | ||
const tx = { hash: '0x789' } as Transaction; | ||
|
||
// These are the pending transactions we'll have initially in the store | ||
const initialTxs = [{ hash: '0x123' }, { hash: '0x456' }] as Transaction[]; | ||
|
||
const mockSigner = (receipt: ethers.providers.TransactionReceipt) => { | ||
const waitForTransaction = jest | ||
.fn() | ||
.mockImplementation(() => Promise.resolve(receipt)); | ||
|
||
return { | ||
provider: { waitForTransaction }, | ||
} as unknown as Signer; | ||
}; | ||
|
||
describe('transaction stores', () => { | ||
beforeEach(() => { | ||
pendingTransactions.set(initialTxs); | ||
}); | ||
|
||
it('tests a successful pendingTransactions', () => { | ||
const txTeceipt = { status: 1 } as ethers.providers.TransactionReceipt; | ||
const signer = mockSigner(txTeceipt); | ||
|
||
pendingTransactions | ||
.add(tx, signer) | ||
.then((receipt) => { | ||
// The transaction should have been removed from the store | ||
expect(get(pendingTransactions)).toStrictEqual(initialTxs); | ||
|
||
expect(receipt).toEqual(txTeceipt); | ||
}) | ||
.catch(() => { | ||
throw new Error('Should not have thrown'); | ||
}); | ||
|
||
// The transaction should have added to the store | ||
expect(get(pendingTransactions)).toStrictEqual([...initialTxs, tx]); | ||
}); | ||
|
||
it('tests a failed pendingTransactions custom store', () => { | ||
const txTeceipt = { status: 0 } as ethers.providers.TransactionReceipt; | ||
const signer = mockSigner(txTeceipt); | ||
|
||
pendingTransactions | ||
.add(tx, signer) | ||
.then(() => { | ||
throw new Error('Should have thrown'); | ||
}) | ||
.catch((receipt) => { | ||
// The transaction should have been removed from the store | ||
expect(get(pendingTransactions)).toStrictEqual(initialTxs); | ||
|
||
expect(receipt).toEqual(txTeceipt); | ||
}); | ||
|
||
// The transaction should have added to the store | ||
expect(get(pendingTransactions)).toStrictEqual([...initialTxs, tx]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,69 @@ | ||
import { writable } from 'svelte/store'; | ||
|
||
import type { Transaction } from 'ethers'; | ||
import type { Signer, Transaction, ethers } from 'ethers'; | ||
import type { BridgeTransaction, Transactioner } from '../domain/transactions'; | ||
import { Deferred } from '../utils/Deferred'; | ||
|
||
export const transactions = writable<BridgeTransaction[]>([]); | ||
export const transactioner = writable<Transactioner>(); | ||
|
||
// Custom store: pendingTransactions | ||
const { subscribe, set, update } = writable<Transaction[]>([]); | ||
export const pendingTransactions = { | ||
/** | ||
* We're creating here a custom store, which is a writable store. | ||
* We must stick to the store contract, which is: | ||
*/ | ||
set, | ||
subscribe, | ||
// update, // this method is optional. | ||
|
||
/** | ||
* Custom method, which will help us add a new transaction to the store | ||
* and get it removed onces the transaction is mined. | ||
*/ | ||
add: (tx: Transaction, signer: Signer) => { | ||
const deferred = new Deferred<ethers.providers.TransactionReceipt>(); | ||
|
||
update((txs: Transaction[]) => { | ||
// New array with the new transaction appended | ||
const newPendingTransactions = [...txs, tx]; | ||
|
||
// Save the index of the new transaction to later on remove it | ||
// from the list of pending transactions. | ||
const idxAppendedTransaction = newPendingTransactions.length - 1; | ||
|
||
// Next step is to wait for the transaction to be mined | ||
// before removing it from the store. | ||
|
||
/** | ||
* Returns a Promise which will not resolve until transactionHash is mined. | ||
* If confirms is 0, this method is non-blocking and if the transaction | ||
* has not been mined returns null. Otherwise, this method will block until | ||
* the transaction has confirms blocks mined on top of the block in which | ||
* is was mined. | ||
* See https://docs.ethers.org/v5/api/providers/provider/#Provider-waitForTransaction | ||
*/ | ||
signer.provider.waitForTransaction(tx.hash, 1).then((receipt) => { | ||
// The transaction has been mined. | ||
|
||
// Removes the transaction from the store | ||
update((txs: Transaction[]) => { | ||
const copyPendingTransactions = [...txs]; | ||
copyPendingTransactions.splice(idxAppendedTransaction, 1); | ||
return copyPendingTransactions; | ||
}); | ||
|
||
// Resolves or rejects the promise depending on the transaction status. | ||
if (receipt.status === 1) { | ||
deferred.resolve(receipt); | ||
} else { | ||
deferred.reject(receipt); | ||
} | ||
}); | ||
|
||
const pendingTransactions = writable<Transaction[]>([]); | ||
const transactions = writable<BridgeTransaction[]>([]); | ||
const transactioner = writable<Transactioner>(); | ||
return newPendingTransactions; | ||
}); | ||
|
||
export { pendingTransactions, transactions, transactioner }; | ||
return deferred.promise; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { Deferred } from './Deferred'; | ||
|
||
describe('Deferred', () => { | ||
it('should resolve', async () => { | ||
const deferred = new Deferred(); | ||
deferred.resolve('test'); | ||
|
||
try { | ||
await expect(deferred.promise).resolves.toBe('test'); | ||
} catch (e) { | ||
throw Error('This should never happen'); | ||
} | ||
}); | ||
|
||
it('should reject', async () => { | ||
const deferred = new Deferred(); | ||
deferred.reject('test'); | ||
|
||
try { | ||
await deferred.promise; | ||
} catch (err) { | ||
expect(err).toBe('test'); | ||
} | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
export class Deferred<T> { | ||
public promise: Promise<T>; | ||
public resolve: (value?: T) => void; | ||
public reject: (reason?: T) => void; | ||
|
||
constructor() { | ||
this.promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
} | ||
} |