@agoric/fast-usdc 0.1.1-dev-9835fb0.0 → 0.1.1-dev-bfca51a.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/fast-usdc",
3
- "version": "0.1.1-dev-9835fb0.0+9835fb0",
3
+ "version": "0.1.1-dev-bfca51a.0+bfca51a",
4
4
  "description": "CLI and library for Fast USDC product",
5
5
  "type": "module",
6
6
  "files": [
@@ -22,9 +22,9 @@
22
22
  "lint:eslint": "eslint ."
23
23
  },
24
24
  "devDependencies": {
25
- "@agoric/swingset-liveslots": "0.10.3-dev-9835fb0.0+9835fb0",
26
- "@agoric/vats": "0.15.2-dev-9835fb0.0+9835fb0",
27
- "@agoric/zone": "0.2.3-dev-9835fb0.0+9835fb0",
25
+ "@agoric/swingset-liveslots": "0.10.3-dev-bfca51a.0+bfca51a",
26
+ "@agoric/vats": "0.15.2-dev-bfca51a.0+bfca51a",
27
+ "@agoric/zone": "0.2.3-dev-bfca51a.0+bfca51a",
28
28
  "@fast-check/ava": "^2.0.1",
29
29
  "ava": "^5.3.0",
30
30
  "c8": "^10.1.2",
@@ -32,16 +32,16 @@
32
32
  "ts-blank-space": "^0.4.4"
33
33
  },
34
34
  "dependencies": {
35
- "@agoric/client-utils": "0.1.1-dev-9835fb0.0+9835fb0",
36
- "@agoric/cosmic-proto": "0.4.1-dev-9835fb0.0+9835fb0",
37
- "@agoric/ertp": "0.16.3-dev-9835fb0.0+9835fb0",
38
- "@agoric/internal": "0.3.3-dev-9835fb0.0+9835fb0",
39
- "@agoric/notifier": "0.6.3-dev-9835fb0.0+9835fb0",
40
- "@agoric/orchestration": "0.1.1-dev-9835fb0.0+9835fb0",
41
- "@agoric/store": "0.9.3-dev-9835fb0.0+9835fb0",
42
- "@agoric/vat-data": "0.5.3-dev-9835fb0.0+9835fb0",
43
- "@agoric/vow": "0.1.1-dev-9835fb0.0+9835fb0",
44
- "@agoric/zoe": "0.26.3-dev-9835fb0.0+9835fb0",
35
+ "@agoric/client-utils": "0.1.1-dev-bfca51a.0+bfca51a",
36
+ "@agoric/cosmic-proto": "0.4.1-dev-bfca51a.0+bfca51a",
37
+ "@agoric/ertp": "0.16.3-dev-bfca51a.0+bfca51a",
38
+ "@agoric/internal": "0.3.3-dev-bfca51a.0+bfca51a",
39
+ "@agoric/notifier": "0.6.3-dev-bfca51a.0+bfca51a",
40
+ "@agoric/orchestration": "0.1.1-dev-bfca51a.0+bfca51a",
41
+ "@agoric/store": "0.9.3-dev-bfca51a.0+bfca51a",
42
+ "@agoric/vat-data": "0.5.3-dev-bfca51a.0+bfca51a",
43
+ "@agoric/vow": "0.1.1-dev-bfca51a.0+bfca51a",
44
+ "@agoric/zoe": "0.26.3-dev-bfca51a.0+bfca51a",
45
45
  "@cosmjs/proto-signing": "^0.32.4",
46
46
  "@cosmjs/stargate": "^0.32.4",
47
47
  "@endo/base64": "^1.0.9",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "9835fb0e5e8322e9e3eb5af21d3c69cbde4247c8"
84
+ "gitHead": "bfca51a6114a29205a8ec05f793edee1249f8953"
85
85
  }
@@ -99,6 +99,7 @@ export const addOperatorCommands = (
99
99
  .requiredOption('--chainId <string>', 'chain id', Number)
100
100
  .requiredOption('--amount <number>', 'number', parseNat)
101
101
  .requiredOption('--forwardingAddress <string>', 'bech32 address', String)
102
+ .requiredOption('--sender <string>', 'Ethereum address initiating', String)
102
103
  .requiredOption('--txHash <0xhexo>', 'hex hash', parseHex)
103
104
  .option('--offerId <string>', 'Offer id', String, `operatorAttest-${now()}`)
104
105
  .action(async opts => {
@@ -109,12 +110,13 @@ export const addOperatorCommands = (
109
110
  recipientAddress,
110
111
  amount,
111
112
  forwardingAddress,
113
+ sender,
112
114
  ...flat
113
115
  } = opts;
114
116
 
115
117
  const evidence = harden({
116
118
  aux: { forwardingChannel, recipientAddress },
117
- tx: { amount, forwardingAddress },
119
+ tx: { amount, forwardingAddress, sender },
118
120
  ...flat,
119
121
  });
120
122
  mustMatch(evidence, CctpTxEvidenceShape);
package/src/constants.js CHANGED
@@ -21,7 +21,12 @@ export const TxStatus = /** @type {const} */ ({
21
21
  });
22
22
  harden(TxStatus);
23
23
 
24
- // TODO: define valid state transitions
24
+ // According to the state diagram
25
+ export const TerminalTxStatus = {
26
+ [TxStatus.Forwarded]: true,
27
+ [TxStatus.ForwardFailed]: true,
28
+ [TxStatus.Disbursed]: true,
29
+ };
25
30
 
26
31
  /**
27
32
  * Status values for the StatusManager.
@@ -143,7 +143,7 @@ export const prepareSettler = (
143
143
  );
144
144
 
145
145
  // given the sourceChannel check, we can be certain of this cast
146
- const sender = /** @type {NobleAddress} */ (tx.sender);
146
+ const nfa = /** @type {NobleAddress} */ (tx.sender);
147
147
 
148
148
  if (tx.denom !== remoteDenom) {
149
149
  const { denom: actual } = tx;
@@ -170,23 +170,23 @@ export const prepareSettler = (
170
170
  const amount = BigInt(tx.amount); // TODO: what if this throws?
171
171
 
172
172
  const { self } = this.facets;
173
- const found = statusManager.dequeueStatus(sender, amount);
174
- log('dequeued', found, 'for', sender, amount);
173
+ const found = statusManager.dequeueStatus(nfa, amount);
174
+ log('dequeued', found, 'for', nfa, amount);
175
175
  switch (found?.status) {
176
176
  case PendingTxStatus.Advanced:
177
- return self.disburse(found.txHash, sender, amount);
177
+ return self.disburse(found.txHash, nfa, amount);
178
178
 
179
179
  case PendingTxStatus.Advancing:
180
- this.state.mintedEarly.add(makeMintedEarlyKey(sender, amount));
180
+ this.state.mintedEarly.add(makeMintedEarlyKey(nfa, amount));
181
181
  return;
182
182
 
183
183
  case PendingTxStatus.Observed:
184
184
  case PendingTxStatus.AdvanceFailed:
185
- return self.forward(found.txHash, sender, amount, EUD);
185
+ return self.forward(found.txHash, nfa, amount, EUD);
186
186
 
187
187
  case undefined:
188
188
  default:
189
- log('⚠️ tap: no status for ', sender, amount);
189
+ log('⚠️ tap: no status for ', nfa, amount);
190
190
  }
191
191
  },
192
192
  },
@@ -231,10 +231,10 @@ export const prepareSettler = (
231
231
  self: {
232
232
  /**
233
233
  * @param {EvmHash} txHash
234
- * @param {NobleAddress} sender
234
+ * @param {NobleAddress} nfa
235
235
  * @param {NatValue} fullValue
236
236
  */
237
- async disburse(txHash, sender, fullValue) {
237
+ async disburse(txHash, nfa, fullValue) {
238
238
  const { repayer, settlementAccount } = this.state;
239
239
  const received = AmountMath.make(USDC, fullValue);
240
240
  const { zcfSeat: settlingSeat } = zcf.makeEmptySeatKit();
@@ -264,11 +264,11 @@ export const prepareSettler = (
264
264
  },
265
265
  /**
266
266
  * @param {EvmHash} txHash
267
- * @param {NobleAddress} sender
267
+ * @param {NobleAddress} nfa
268
268
  * @param {NatValue} fullValue
269
269
  * @param {string} EUD
270
270
  */
271
- forward(txHash, sender, fullValue, EUD) {
271
+ forward(txHash, nfa, fullValue, EUD) {
272
272
  const { settlementAccount, intermediateRecipient } = this.state;
273
273
 
274
274
  const dest = chainHub.makeChainAddress(EUD);
@@ -281,7 +281,7 @@ export const prepareSettler = (
281
281
  );
282
282
  void vowTools.watch(txfrV, this.facets.transferHandler, {
283
283
  txHash,
284
- sender,
284
+ nfa,
285
285
  fullValue,
286
286
  });
287
287
  },
@@ -293,13 +293,13 @@ export const prepareSettler = (
293
293
  *
294
294
  * @typedef {{
295
295
  * txHash: EvmHash;
296
- * sender: NobleAddress;
296
+ * nfa: NobleAddress;
297
297
  * fullValue: NatValue;
298
298
  * }} SettlerTransferCtx
299
299
  */
300
300
  onFulfilled(_result, ctx) {
301
- const { txHash, sender, fullValue } = ctx;
302
- statusManager.forwarded(txHash, sender, fullValue);
301
+ const { txHash, nfa, fullValue } = ctx;
302
+ statusManager.forwarded(txHash, nfa, fullValue);
303
303
  },
304
304
  /**
305
305
  * @param {unknown} reason
@@ -307,8 +307,8 @@ export const prepareSettler = (
307
307
  */
308
308
  onRejected(reason, ctx) {
309
309
  log('⚠️ transfer rejected!', reason, ctx);
310
- // const { txHash, sender, amount } = ctx;
311
- // TODO(#10510): statusManager.forwardFailed(txHash, sender, amount);
310
+ // const { txHash, nfa, amount } = ctx;
311
+ // TODO(#10510): statusManager.forwardFailed(txHash, nfa, amount);
312
312
  },
313
313
  },
314
314
  },
@@ -2,26 +2,23 @@ import { M } from '@endo/patterns';
2
2
  import { Fail, makeError, q } from '@endo/errors';
3
3
  import { appendToStoredArray } from '@agoric/store/src/stores/store-utils.js';
4
4
  import { E } from '@endo/eventual-send';
5
- import { makeTracer } from '@agoric/internal';
5
+ import { makeTracer, pureDataMarshaller } from '@agoric/internal';
6
6
  import {
7
7
  CctpTxEvidenceShape,
8
8
  EvmHashShape,
9
9
  PendingTxShape,
10
10
  } from '../type-guards.js';
11
- import { PendingTxStatus, TxStatus } from '../constants.js';
11
+ import { PendingTxStatus, TerminalTxStatus, TxStatus } from '../constants.js';
12
12
 
13
13
  /**
14
14
  * @import {MapStore, SetStore} from '@agoric/store';
15
15
  * @import {Zone} from '@agoric/zone';
16
- * @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn} from '../types.js';
16
+ * @import {CctpTxEvidence, NobleAddress, PendingTx, EvmHash, LogFn, TransactionRecord} from '../types.js';
17
17
  */
18
18
 
19
19
  /**
20
20
  * @typedef {`pendingTx:${bigint}:${NobleAddress}`} PendingTxKey
21
21
  * The string template is for developer visibility but not meant to ever be parsed.
22
- *
23
- * @typedef {`seenTx:${string}:${EvmHash}`} SeenTxKey
24
- * The string template is for developer visibility but not meant to ever be parsed.
25
22
  */
26
23
 
27
24
  /**
@@ -29,13 +26,13 @@ import { PendingTxStatus, TxStatus } from '../constants.js';
29
26
  *
30
27
  * The key is a composite but not meant to be parsable.
31
28
  *
32
- * @param {NobleAddress} addr
29
+ * @param {NobleAddress} nfa Noble Forwarding Account (implies EUD)
33
30
  * @param {bigint} amount
34
31
  * @returns {PendingTxKey}
35
32
  */
36
- const makePendingTxKey = (addr, amount) =>
33
+ const makePendingTxKey = (nfa, amount) =>
37
34
  // amount can't contain colon
38
- `pendingTx:${amount}:${addr}`;
35
+ `pendingTx:${amount}:${nfa}`;
39
36
 
40
37
  /**
41
38
  * Get the key for the pendingTxs MapStore.
@@ -48,20 +45,6 @@ const pendingTxKeyOf = evidence => {
48
45
  return makePendingTxKey(forwardingAddress, amount);
49
46
  };
50
47
 
51
- /**
52
- * Get the key for the seenTxs SetStore.
53
- *
54
- * The key is a composite but not meant to be parsable.
55
- *
56
- * @param {CctpTxEvidence} evidence
57
- * @returns {SeenTxKey}
58
- */
59
- const seenTxKeyOf = evidence => {
60
- const { txHash, chainId } = evidence;
61
- // chainId can't contain colon
62
- return `seenTx:${chainId}:${txHash}`;
63
- };
64
-
65
48
  /**
66
49
  * @typedef {{
67
50
  * log?: LogFn;
@@ -76,35 +59,85 @@ const seenTxKeyOf = evidence => {
76
59
  * XXX consider separate facets for `Advancing` and `Settling` capabilities.
77
60
  *
78
61
  * @param {Zone} zone
79
- * @param {ERef<StorageNode>} transactionsNode
62
+ * @param {ERef<StorageNode>} txnsNode
80
63
  * @param {StatusManagerPowers} caps
81
64
  */
82
65
  export const prepareStatusManager = (
83
66
  zone,
84
- transactionsNode,
67
+ txnsNode,
85
68
  {
86
69
  log = makeTracer('Advancer', true),
87
70
  } = /** @type {StatusManagerPowers} */ ({}),
88
71
  ) => {
89
- /** @type {MapStore<PendingTxKey, PendingTx[]>} */
72
+ /**
73
+ * Keyed by a tuple of the Noble Forwarding Account and amount.
74
+ * @type {MapStore<PendingTxKey, PendingTx[]>}
75
+ */
90
76
  const pendingTxs = zone.mapStore('PendingTxs', {
91
77
  keyShape: M.string(),
92
78
  valueShape: M.arrayOf(PendingTxShape),
93
79
  });
94
80
 
95
- /** @type {SetStore<SeenTxKey>} */
81
+ /**
82
+ * Transactions seen *ever* by the contract.
83
+ *
84
+ * Note that like all durable stores, this SetStore is stored in IAVL. It
85
+ * grows without bound (though the amount of growth per incoming message to
86
+ * the contract is bounded). At some point in the future we may want to prune.
87
+ * @type {SetStore<EvmHash>}
88
+ */
96
89
  const seenTxs = zone.setStore('SeenTxs', {
97
90
  keyShape: M.string(),
98
91
  });
99
92
 
93
+ /**
94
+ * Transactions that have completed, but are still in vstorage.
95
+ *
96
+ * @type {SetStore<EvmHash>}
97
+ */
98
+ const storedCompletedTxs = zone.setStore('StoredCompletedTxs', {
99
+ keyShape: M.string(),
100
+ });
101
+
102
+ /**
103
+ * @param {EvmHash} txId
104
+ * @param {TransactionRecord} record
105
+ */
106
+ const publishTxnRecord = (txId, record) => {
107
+ const txNode = E(txnsNode).makeChildNode(txId, {
108
+ sequence: true, // avoid overwriting other output in the block
109
+ });
110
+ void E(txNode).setValue(
111
+ JSON.stringify(pureDataMarshaller.toCapData(record)),
112
+ );
113
+ };
114
+
115
+ /**
116
+ * @param {CctpTxEvidence['txHash']} hash
117
+ * @param {CctpTxEvidence} evidence
118
+ */
119
+ const publishEvidence = (hash, evidence) => {
120
+ // Don't await, just writing to vstorage.
121
+ void publishTxnRecord(
122
+ hash,
123
+ harden({ evidence, status: TxStatus.Observed }),
124
+ );
125
+ };
126
+
100
127
  /**
101
128
  * @param {CctpTxEvidence['txHash']} hash
102
129
  * @param {TxStatus} status
103
130
  */
104
131
  const publishStatus = (hash, status) => {
105
- const txnNodeP = E(transactionsNode).makeChildNode(hash);
106
132
  // Don't await, just writing to vstorage.
107
- void E(txnNodeP).setValue(status);
133
+ void publishTxnRecord(hash, harden({ status }));
134
+ if (TerminalTxStatus[status]) {
135
+ // UNTIL https://github.com/Agoric/agoric-sdk/issues/7405
136
+ // Queue it for deletion later because if we deleted it now the earlier
137
+ // writes in this block would be wiped. For now we keep track of what to
138
+ // delete when we know it'll be another block.
139
+ storedCompletedTxs.add(hash);
140
+ }
108
141
  };
109
142
 
110
143
  /**
@@ -117,32 +150,36 @@ export const prepareStatusManager = (
117
150
  * @param {PendingTxStatus} status
118
151
  */
119
152
  const initPendingTx = (evidence, status) => {
120
- const seenKey = seenTxKeyOf(evidence);
121
- if (seenTxs.has(seenKey)) {
122
- throw makeError(`Transaction already seen: ${q(seenKey)}`);
153
+ const { txHash } = evidence;
154
+ if (seenTxs.has(txHash)) {
155
+ throw makeError(`Transaction already seen: ${q(txHash)}`);
123
156
  }
124
- seenTxs.add(seenKey);
157
+ seenTxs.add(txHash);
125
158
 
126
159
  appendToStoredArray(
127
160
  pendingTxs,
128
161
  pendingTxKeyOf(evidence),
129
162
  harden({ ...evidence, status }),
130
163
  );
131
- publishStatus(evidence.txHash, status);
164
+ publishEvidence(txHash, evidence);
165
+ if (status !== PendingTxStatus.Observed) {
166
+ // publishEvidence publishes Observed
167
+ publishStatus(txHash, status);
168
+ }
132
169
  };
133
170
 
134
171
  /**
135
172
  * Update the pending transaction status.
136
173
  *
137
- * @param {{sender: NobleAddress, amount: bigint}} keyParts
174
+ * @param {{nfa: NobleAddress, amount: bigint}} keyParts
138
175
  * @param {PendingTxStatus} status
139
176
  */
140
- function setPendingTxStatus({ sender, amount }, status) {
141
- const key = makePendingTxKey(sender, amount);
142
- pendingTxs.has(key) || Fail`no advancing tx with ${{ sender, amount }}`;
177
+ function setPendingTxStatus({ nfa, amount }, status) {
178
+ const key = makePendingTxKey(nfa, amount);
179
+ pendingTxs.has(key) || Fail`no advancing tx with ${{ nfa, amount }}`;
143
180
  const pending = pendingTxs.get(key);
144
181
  const ix = pending.findIndex(tx => tx.status === PendingTxStatus.Advancing);
145
- ix >= 0 || Fail`no advancing tx with ${{ sender, amount }}`;
182
+ ix >= 0 || Fail`no advancing tx with ${{ nfa, amount }}`;
146
183
  const [prefix, tx, suffix] = [
147
184
  pending.slice(0, ix),
148
185
  pending[ix],
@@ -161,6 +198,7 @@ export const prepareStatusManager = (
161
198
  advanceOutcome: M.call(M.string(), M.nat(), M.boolean()).returns(),
162
199
  observe: M.call(CctpTxEvidenceShape).returns(M.undefined()),
163
200
  hasBeenObserved: M.call(CctpTxEvidenceShape).returns(M.boolean()),
201
+ deleteCompletedTxs: M.call().returns(M.undefined()),
164
202
  dequeueStatus: M.call(M.string(), M.bigint()).returns(
165
203
  M.or(
166
204
  {
@@ -197,14 +235,14 @@ export const prepareStatusManager = (
197
235
  /**
198
236
  * Record result of ADVANCING
199
237
  *
200
- * @param {NobleAddress} sender
238
+ * @param {NobleAddress} nfa Noble Forwarding Account
201
239
  * @param {import('@agoric/ertp').NatValue} amount
202
240
  * @param {boolean} success - Advanced vs. AdvanceFailed
203
241
  * @throws {Error} if nothing to advance
204
242
  */
205
- advanceOutcome(sender, amount, success) {
243
+ advanceOutcome(nfa, amount, success) {
206
244
  setPendingTxStatus(
207
- { sender, amount },
245
+ { nfa, amount },
208
246
  success ? PendingTxStatus.Advanced : PendingTxStatus.AdvanceFailed,
209
247
  );
210
248
  },
@@ -223,20 +261,32 @@ export const prepareStatusManager = (
223
261
  * @param {CctpTxEvidence} evidence
224
262
  */
225
263
  hasBeenObserved(evidence) {
226
- const seenKey = seenTxKeyOf(evidence);
227
- return seenTxs.has(seenKey);
264
+ return seenTxs.has(evidence.txHash);
265
+ },
266
+
267
+ // UNTIL https://github.com/Agoric/agoric-sdk/issues/7405
268
+ deleteCompletedTxs() {
269
+ for (const txHash of storedCompletedTxs.values()) {
270
+ // As of now, setValue('') on a non-sequence node will delete it
271
+ const txNode = E(txnsNode).makeChildNode(txHash, {
272
+ sequence: false,
273
+ });
274
+ void E(txNode)
275
+ .setValue('')
276
+ .then(() => storedCompletedTxs.delete(txHash));
277
+ }
228
278
  },
229
279
 
230
280
  /**
231
281
  * Remove and return an `ADVANCED` or `OBSERVED` tx waiting to be `SETTLED`.
232
282
  *
233
- * @param {NobleAddress} address
283
+ * @param {NobleAddress} nfa
234
284
  * @param {bigint} amount
235
285
  * @returns {Pick<PendingTx, 'status' | 'txHash'> | undefined} undefined if nothing
236
286
  * with this address and amount has been marked pending.
237
287
  */
238
- dequeueStatus(address, amount) {
239
- const key = makePendingTxKey(address, amount);
288
+ dequeueStatus(nfa, amount) {
289
+ const key = makePendingTxKey(nfa, amount);
240
290
  if (!pendingTxs.has(key)) return undefined;
241
291
  const pending = pendingTxs.get(key);
242
292
 
@@ -272,16 +322,16 @@ export const prepareStatusManager = (
272
322
  * Mark a transaction as `FORWARDED`
273
323
  *
274
324
  * @param {EvmHash | undefined} txHash - undefined in case mint before observed
275
- * @param {NobleAddress} address
325
+ * @param {NobleAddress} nfa
276
326
  * @param {bigint} amount
277
327
  */
278
- forwarded(txHash, address, amount) {
328
+ forwarded(txHash, nfa, amount) {
279
329
  if (txHash) {
280
330
  publishStatus(txHash, TxStatus.Forwarded);
281
331
  } else {
282
332
  // TODO store (early) `Minted` transactions to check against incoming evidence
283
333
  log(
284
- `⚠️ Forwarded minted amount ${amount} from account ${address} before it was observed.`,
334
+ `⚠️ Forwarded minted amount ${amount} from account ${nfa} before it was observed.`,
285
335
  );
286
336
  }
287
337
  },
@@ -291,12 +341,12 @@ export const prepareStatusManager = (
291
341
  *
292
342
  * XXX only used in tests. should we remove?
293
343
  *
294
- * @param {NobleAddress} address
344
+ * @param {NobleAddress} nfa
295
345
  * @param {bigint} amount
296
346
  * @returns {PendingTx[]}
297
347
  */
298
- lookupPending(address, amount) {
299
- const key = makePendingTxKey(address, amount);
348
+ lookupPending(nfa, amount) {
349
+ const key = makePendingTxKey(nfa, amount);
300
350
  if (!pendingTxs.has(key)) {
301
351
  return harden([]);
302
352
  }
@@ -26,7 +26,7 @@ import { defineInertInvitation } from './utils/zoe.js';
26
26
 
27
27
  const trace = makeTracer('FastUsdc');
28
28
 
29
- const STATUS_NODE = 'status';
29
+ const TXNS_NODE = 'txns';
30
30
  const FEE_NODE = 'feeConfig';
31
31
  const ADDRESSES_BAGGAGE_KEY = 'addresses';
32
32
 
@@ -39,7 +39,6 @@ const ADDRESSES_BAGGAGE_KEY = 'addresses';
39
39
  * @import {Zone} from '@agoric/zone';
40
40
  * @import {OperatorKit} from './exos/operator-kit.js';
41
41
  * @import {CctpTxEvidence, FeeConfig} from './types.js';
42
- * @import {RepayAmountKWR, RepayPaymentKWR} from './exos/liquidity-pool.js';
43
42
  */
44
43
 
45
44
  /**
@@ -110,8 +109,10 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
110
109
  marshaller,
111
110
  );
112
111
 
113
- const statusNode = E(storageNode).makeChildNode(STATUS_NODE);
114
- const statusManager = prepareStatusManager(zone, statusNode);
112
+ const statusManager = prepareStatusManager(
113
+ zone,
114
+ E(storageNode).makeChildNode(TXNS_NODE),
115
+ );
115
116
 
116
117
  const { USDC } = terms.brands;
117
118
  const { withdrawToSeat } = tools.zoeTools;
@@ -6,7 +6,7 @@ import { PendingTxStatus } from './constants.js';
6
6
  * @import {TypedPattern} from '@agoric/internal';
7
7
  * @import {FastUsdcTerms} from './fast-usdc.contract.js';
8
8
  * @import {USDCProposalShapes} from './pool-share-math.js';
9
- * @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook} from './types.js';
9
+ * @import {CctpTxEvidence, FeeConfig, PendingTx, PoolMetrics, ChainPolicy, FeedPolicy, AddressHook, EvmAddress, EvmHash} from './types.js';
10
10
  */
11
11
 
12
12
  /**
@@ -36,7 +36,14 @@ export const FastUSDCTermsShape = harden({
36
36
  usdcDenom: M.string(),
37
37
  });
38
38
 
39
- /** @type {TypedPattern<string>} */
39
+ /** @type {TypedPattern<EvmAddress>} */
40
+ export const EvmAddressShape = M.string({
41
+ // 0x + 40 hex digits
42
+ stringLengthLimit: 42,
43
+ });
44
+ harden(EvmAddressShape);
45
+
46
+ /** @type {TypedPattern<EvmHash>} */
40
47
  export const EvmHashShape = M.string({
41
48
  stringLengthLimit: 66,
42
49
  });
@@ -54,6 +61,7 @@ export const CctpTxEvidenceShape = {
54
61
  tx: {
55
62
  amount: M.nat(),
56
63
  forwardingAddress: M.string(),
64
+ sender: EvmAddressShape,
57
65
  },
58
66
  txHash: EvmHashShape,
59
67
  };
package/src/types.ts CHANGED
@@ -7,10 +7,11 @@ import type {
7
7
  import type { IBCChannelID } from '@agoric/vats';
8
8
  import type { Amount } from '@agoric/ertp';
9
9
  import type { CopyRecord, Passable } from '@endo/pass-style';
10
- import type { PendingTxStatus } from './constants.js';
10
+ import type { PendingTxStatus, TxStatus } from './constants.js';
11
11
  import type { FastUsdcTerms } from './fast-usdc.contract.js';
12
12
 
13
13
  export type EvmHash = `0x${string}`;
14
+ export type EvmAddress = `0x${string & { length: 40 }}`;
14
15
  export type NobleAddress = `noble1${string}`;
15
16
  export type EvmChainID = number;
16
17
  export type EvmChainName = string;
@@ -28,10 +29,20 @@ export interface CctpTxEvidence {
28
29
  tx: {
29
30
  amount: bigint;
30
31
  forwardingAddress: NobleAddress;
32
+ sender: EvmAddress;
31
33
  };
32
34
  txHash: EvmHash;
33
35
  }
34
36
 
37
+ /**
38
+ * 'evidence' only available when it's first observed and not in subsequent
39
+ * updates.
40
+ */
41
+ export interface TransactionRecord extends CopyRecord {
42
+ evidence?: CctpTxEvidence;
43
+ status: TxStatus;
44
+ }
45
+
35
46
  export type LogFn = (...args: unknown[]) => void;
36
47
 
37
48
  export interface PendingTx extends CctpTxEvidence {