@@ -6,9 +6,9 @@ import { CctpTxEvidenceShape, PendingTxShape } from '../typeGuards.js';
6
6
import { PendingTxStatus } from '../constants.js' ;
7
7
8
8
/**
9
- * @import {MapStore} from '@agoric/store';
9
+ * @import {MapStore, SetStore } from '@agoric/store';
10
10
* @import {Zone} from '@agoric/zone';
11
- * @import {CctpTxEvidence, NobleAddress, PendingTxKey, PendingTx} from '../types.js';
11
+ * @import {CctpTxEvidence, NobleAddress, SeenTxKey, PendingTxKey, PendingTx} from '../types.js';
12
12
*/
13
13
14
14
/**
@@ -29,6 +29,15 @@ const getPendingTxKey = evidence => {
29
29
return toPendingTxKey ( forwardingAddress , amount ) ;
30
30
} ;
31
31
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
+
32
41
/**
33
42
* The `StatusManager` keeps track of Pending and Seen Transactions
34
43
* via {@link PendingTxStatus} states, aiding in coordination between the `Advancer`
@@ -45,11 +54,27 @@ export const prepareStatusManager = zone => {
45
54
valueShape : M . arrayOf ( PendingTxShape ) ,
46
55
} ) ;
47
56
57
+ /** @type {SetStore<SeenTxKey> } */
58
+ const seenTxs = zone . setStore ( 'SeenTxs' , {
59
+ keyShape : M . string ( ) ,
60
+ } ) ;
61
+
48
62
/**
63
+ * Ensures that `txHash+chainId` has not been processed
64
+ * and adds entry to `seenTxs` set.
65
+ *
66
+ * Also records the CctpTxEvidence and status in `pendingTxs`.
67
+ *
49
68
* @param {CctpTxEvidence } evidence
50
69
* @param {PendingTxStatus } status
51
70
*/
52
71
const recordPendingTx = ( evidence , status ) => {
72
+ const seenKey = getSeenKey ( evidence ) ;
73
+ if ( seenTxs . has ( seenKey ) ) {
74
+ throw makeError ( `Transaction already seen: ${ q ( seenKey ) } ` ) ;
75
+ }
76
+ seenTxs . add ( seenKey ) ;
77
+
53
78
appendToStoredArray (
54
79
pendingTxs ,
55
80
getPendingTxKey ( evidence ) ,
0 commit comments