Skip to content

Commit 88f51f3

Browse files
committed
feat: StatusManager tracks seenTxs
- use a composite key of `txHash+chainId` to track unique `EventFeed` submissions
1 parent 024fd1d commit 88f51f3

File tree

3 files changed

+39
-4
lines changed

3 files changed

+39
-4
lines changed

packages/fast-usdc/src/exos/status-manager.js

+32-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ import { CctpTxEvidenceShape, PendingTxShape } from '../typeGuards.js';
66
import { TxStatus } from '../constants.js';
77

88
/**
9-
* @import {MapStore} from '@agoric/store';
9+
* @import {MapStore, SetStore} from '@agoric/store';
1010
* @import {Zone} from '@agoric/zone';
11-
* @import {CctpTxEvidence, NobleAddress, PendingTxKey, PendingTx} from '../types.js';
11+
* @import {CctpTxEvidence, NobleAddress, SeenTxKey, PendingTxKey, PendingTx} from '../types.js';
1212
*/
1313

1414
/**
@@ -29,6 +29,15 @@ const getPendingTxKey = evidence => {
2929
return toPendingTxKey(forwardingAddress, amount);
3030
};
3131

32+
/**
33+
* Get the key for the seenTxs SetStore
34+
* @param {CctpTxEvidence} evidence
35+
*/
36+
const getSeenKey = evidence => {
37+
const { txHash, chainId } = evidence;
38+
return /** @type {SeenTxKey} */ (JSON.stringify([txHash, chainId]));
39+
};
40+
3241
/**
3342
* The `StatusManager` keeps track of Pending and Seen Transactions
3443
* via {@link TxStatus} states, aiding in coordination between the `Advancer`
@@ -45,6 +54,25 @@ export const prepareStatusManager = zone => {
4554
valueShape: M.arrayOf(PendingTxShape),
4655
});
4756

57+
/** @type {SetStore<SeenTxKey>} */
58+
const seenTxs = zone.setStore('SeenTxs', {
59+
keyShape: M.string(),
60+
});
61+
62+
/**
63+
* Ensures that `txHash+chainId` has not been processed
64+
* and adds `txHash+chainId` to `seenTxs` set.
65+
*
66+
* @param {CctpTxEvidence} evidence
67+
*/
68+
const assertNotSeen = evidence => {
69+
const seenKey = getSeenKey(evidence);
70+
if (seenTxs.has(seenKey)) {
71+
throw makeError(`Transaction already seen: ${q(seenKey)}`);
72+
}
73+
seenTxs.add(seenKey);
74+
};
75+
4876
return zone.exo(
4977
'Fast USDC Status Manager',
5078
M.interface('StatusManagerI', {
@@ -60,6 +88,7 @@ export const prepareStatusManager = zone => {
6088
* @param {CctpTxEvidence} evidence
6189
*/
6290
advance(evidence) {
91+
assertNotSeen(evidence);
6392
const key = getPendingTxKey(evidence);
6493
const entry = { ...evidence, status: TxStatus.Advanced };
6594

@@ -76,6 +105,7 @@ export const prepareStatusManager = zone => {
76105
* @param {CctpTxEvidence} evidence
77106
*/
78107
observe(evidence) {
108+
assertNotSeen(evidence);
79109
const key = getPendingTxKey(evidence);
80110
const entry = { ...evidence, status: TxStatus.Observed };
81111

packages/fast-usdc/src/types.ts

+3
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,7 @@ export interface PendingTx extends CctpTxEvidence {
3232
/** composite of NobleAddress and transaction amount value */
3333
export type PendingTxKey = `"["${NobleAddress}",${bigint}]"`;
3434

35+
/** composite of EvmHash and chainId */
36+
export type SeenTxKey = `"["${EvmHash}",${number}]"`;
37+
3538
export type * from './constants.js';

packages/fast-usdc/test/exos/status-manager.test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ test('cannot process same tx twice', t => {
4343
statusManager.advance(evidence);
4444

4545
t.throws(() => statusManager.advance(evidence), {
46-
message: `Transaction already seen: "0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702+1"`,
46+
message:
47+
'Transaction already seen: "[[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]]"',
4748
});
4849

4950
t.throws(() => statusManager.observe(evidence), {
50-
message: `Transaction already seen: "0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702+1"`,
51+
message:
52+
'Transaction already seen: "[[\\"0xc81bc6105b60a234c7c50ac17816ebcd5561d366df8bf3be59ff387552761702\\",1]]"',
5153
});
5254

5355
// new txHash should not throw

0 commit comments

Comments
 (0)