@agoric/fast-usdc 0.1.1-dev-9423fce.0 → 0.1.1-dev-7efdf47.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 +15 -15
- package/src/exos/advancer.js +9 -6
- package/src/exos/status-manager.js +12 -18
- package/src/exos/transaction-feed.js +56 -39
- package/src/fast-usdc.contract.js +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/fast-usdc",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-7efdf47.0+7efdf47",
|
|
4
4
|
"description": "CLI and library for Fast USDC product",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"lint:eslint": "eslint ."
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
27
|
-
"@agoric/vats": "0.15.2-dev-
|
|
28
|
-
"@agoric/zone": "0.2.3-dev-
|
|
26
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-7efdf47.0+7efdf47",
|
|
27
|
+
"@agoric/vats": "0.15.2-dev-7efdf47.0+7efdf47",
|
|
28
|
+
"@agoric/zone": "0.2.3-dev-7efdf47.0+7efdf47",
|
|
29
29
|
"@fast-check/ava": "^2.0.1",
|
|
30
30
|
"ava": "^5.3.0",
|
|
31
31
|
"c8": "^10.1.2",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
"ts-blank-space": "^0.4.4"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@agoric/client-utils": "0.1.1-dev-
|
|
37
|
-
"@agoric/cosmic-proto": "0.4.1-dev-
|
|
38
|
-
"@agoric/ertp": "0.16.3-dev-
|
|
39
|
-
"@agoric/internal": "0.3.3-dev-
|
|
40
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
41
|
-
"@agoric/orchestration": "0.1.1-dev-
|
|
42
|
-
"@agoric/store": "0.9.3-dev-
|
|
43
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
44
|
-
"@agoric/vow": "0.1.1-dev-
|
|
45
|
-
"@agoric/zoe": "0.26.3-dev-
|
|
36
|
+
"@agoric/client-utils": "0.1.1-dev-7efdf47.0+7efdf47",
|
|
37
|
+
"@agoric/cosmic-proto": "0.4.1-dev-7efdf47.0+7efdf47",
|
|
38
|
+
"@agoric/ertp": "0.16.3-dev-7efdf47.0+7efdf47",
|
|
39
|
+
"@agoric/internal": "0.3.3-dev-7efdf47.0+7efdf47",
|
|
40
|
+
"@agoric/notifier": "0.6.3-dev-7efdf47.0+7efdf47",
|
|
41
|
+
"@agoric/orchestration": "0.1.1-dev-7efdf47.0+7efdf47",
|
|
42
|
+
"@agoric/store": "0.9.3-dev-7efdf47.0+7efdf47",
|
|
43
|
+
"@agoric/vat-data": "0.5.3-dev-7efdf47.0+7efdf47",
|
|
44
|
+
"@agoric/vow": "0.1.1-dev-7efdf47.0+7efdf47",
|
|
45
|
+
"@agoric/zoe": "0.26.3-dev-7efdf47.0+7efdf47",
|
|
46
46
|
"@cosmjs/proto-signing": "^0.32.4",
|
|
47
47
|
"@cosmjs/stargate": "^0.32.4",
|
|
48
48
|
"@endo/base64": "^1.0.9",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"publishConfig": {
|
|
83
83
|
"access": "public"
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "7efdf47d7749b9a0c93dca09d51e6939f36aa260"
|
|
86
86
|
}
|
package/src/exos/advancer.js
CHANGED
|
@@ -211,6 +211,8 @@ export const prepareAdvancerKit = (
|
|
|
211
211
|
poolAccount,
|
|
212
212
|
harden({ USDC: advanceAmount }),
|
|
213
213
|
);
|
|
214
|
+
// WARNING: this must never reject, see handler @throws {never} below
|
|
215
|
+
// void not enforced by linter until #10627 no-floating-vows
|
|
214
216
|
void watch(depositV, this.facets.depositHandler, {
|
|
215
217
|
advanceAmount,
|
|
216
218
|
destination,
|
|
@@ -233,6 +235,7 @@ export const prepareAdvancerKit = (
|
|
|
233
235
|
/**
|
|
234
236
|
* @param {undefined} result
|
|
235
237
|
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
238
|
+
* @throws {never} WARNING: this function must not throw, because user funds are at risk
|
|
236
239
|
*/
|
|
237
240
|
onFulfilled(result, ctx) {
|
|
238
241
|
const { poolAccount, intermediateRecipient, settlementAddress } =
|
|
@@ -265,6 +268,7 @@ export const prepareAdvancerKit = (
|
|
|
265
268
|
*
|
|
266
269
|
* @param {Error} error
|
|
267
270
|
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
271
|
+
* @throws {never} WARNING: this function must not throw, because user funds are at risk
|
|
268
272
|
*/
|
|
269
273
|
onRejected(error, { tmpSeat, advanceAmount, ...restCtx }) {
|
|
270
274
|
log(
|
|
@@ -285,17 +289,12 @@ export const prepareAdvancerKit = (
|
|
|
285
289
|
/**
|
|
286
290
|
* @param {undefined} result
|
|
287
291
|
* @param {AdvancerVowCtx} ctx
|
|
292
|
+
* @throws {never} WARNING: this function must not throw, because user funds are at risk
|
|
288
293
|
*/
|
|
289
294
|
onFulfilled(result, ctx) {
|
|
290
295
|
const { notifier } = this.state;
|
|
291
296
|
const { advanceAmount, destination, ...detail } = ctx;
|
|
292
297
|
log('Advance succeeded', { advanceAmount, destination });
|
|
293
|
-
// During development, due to a bug, this call threw.
|
|
294
|
-
// The failure was silent (no diagnostics) due to:
|
|
295
|
-
// - #10576 Vows do not report unhandled rejections
|
|
296
|
-
// For now, the advancer kit relies on consistency between
|
|
297
|
-
// notify, statusManager, and callers of handleTransactionEvent().
|
|
298
|
-
// TODO: revisit #10576 during #10510
|
|
299
298
|
notifier.notifyAdvancingResult({ destination, ...detail }, true);
|
|
300
299
|
},
|
|
301
300
|
/**
|
|
@@ -314,6 +313,8 @@ export const prepareAdvancerKit = (
|
|
|
314
313
|
tmpReturnSeat,
|
|
315
314
|
harden({ USDC: advanceAmount }),
|
|
316
315
|
);
|
|
316
|
+
// WARNING: this must never reject, see handler @throws {never} below
|
|
317
|
+
// void not enforced by linter until #10627 no-floating-vows
|
|
317
318
|
void watch(withdrawV, this.facets.withdrawHandler, {
|
|
318
319
|
advanceAmount,
|
|
319
320
|
tmpReturnSeat,
|
|
@@ -325,6 +326,7 @@ export const prepareAdvancerKit = (
|
|
|
325
326
|
*
|
|
326
327
|
* @param {undefined} result
|
|
327
328
|
* @param {{ advanceAmount: Amount<'nat'>; tmpReturnSeat: ZCFSeat; }} ctx
|
|
329
|
+
* @throws {never} WARNING: this function must not throw, because user funds are at risk
|
|
328
330
|
*/
|
|
329
331
|
onFulfilled(result, { advanceAmount, tmpReturnSeat }) {
|
|
330
332
|
const { borrower } = this.state;
|
|
@@ -342,6 +344,7 @@ export const prepareAdvancerKit = (
|
|
|
342
344
|
/**
|
|
343
345
|
* @param {Error} error
|
|
344
346
|
* @param {{ advanceAmount: Amount<'nat'>; tmpReturnSeat: ZCFSeat; }} ctx
|
|
347
|
+
* @throws {never} WARNING: this function must not throw, because user funds are at risk
|
|
345
348
|
*/
|
|
346
349
|
onRejected(error, { advanceAmount, tmpReturnSeat }) {
|
|
347
350
|
log(
|
|
@@ -117,9 +117,8 @@ export const prepareStatusManager = (
|
|
|
117
117
|
/**
|
|
118
118
|
* @param {EvmHash} txId
|
|
119
119
|
* @param {TransactionRecord} record
|
|
120
|
-
* @returns {Promise<void>}
|
|
121
120
|
*/
|
|
122
|
-
const publishTxnRecord =
|
|
121
|
+
const publishTxnRecord = (txId, record) => {
|
|
123
122
|
const txNode = E(txnsNode).makeChildNode(txId, {
|
|
124
123
|
sequence: true, // avoid overwriting other output in the block
|
|
125
124
|
});
|
|
@@ -133,9 +132,10 @@ export const prepareStatusManager = (
|
|
|
133
132
|
storedCompletedTxs.add(txId);
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
// Don't await, just writing to vstorage.
|
|
136
|
+
void E.when(E(marshaller).toCapData(record), capData =>
|
|
137
|
+
E(txNode).setValue(JSON.stringify(capData)),
|
|
138
|
+
);
|
|
139
139
|
};
|
|
140
140
|
|
|
141
141
|
/**
|
|
@@ -144,10 +144,7 @@ export const prepareStatusManager = (
|
|
|
144
144
|
*/
|
|
145
145
|
const publishEvidence = (hash, evidence) => {
|
|
146
146
|
// Don't await, just writing to vstorage.
|
|
147
|
-
|
|
148
|
-
hash,
|
|
149
|
-
harden({ evidence, status: TxStatus.Observed }),
|
|
150
|
-
);
|
|
147
|
+
publishTxnRecord(hash, harden({ evidence, status: TxStatus.Observed }));
|
|
151
148
|
};
|
|
152
149
|
|
|
153
150
|
/**
|
|
@@ -174,10 +171,10 @@ export const prepareStatusManager = (
|
|
|
174
171
|
);
|
|
175
172
|
publishEvidence(txHash, evidence);
|
|
176
173
|
if (status === PendingTxStatus.AdvanceSkipped) {
|
|
177
|
-
|
|
174
|
+
publishTxnRecord(txHash, harden({ status, risksIdentified }));
|
|
178
175
|
} else if (status !== PendingTxStatus.Observed) {
|
|
179
176
|
// publishEvidence publishes Observed
|
|
180
|
-
|
|
177
|
+
publishTxnRecord(txHash, harden({ status }));
|
|
181
178
|
}
|
|
182
179
|
};
|
|
183
180
|
|
|
@@ -200,7 +197,7 @@ export const prepareStatusManager = (
|
|
|
200
197
|
];
|
|
201
198
|
const txpost = { ...tx, status };
|
|
202
199
|
pendingSettleTxs.set(key, harden([...prefix, txpost, ...suffix]));
|
|
203
|
-
|
|
200
|
+
publishTxnRecord(tx.txHash, harden({ status }));
|
|
204
201
|
}
|
|
205
202
|
|
|
206
203
|
return zone.exo(
|
|
@@ -288,7 +285,7 @@ export const prepareStatusManager = (
|
|
|
288
285
|
* @param {boolean} success whether the Transfer succeeded
|
|
289
286
|
*/
|
|
290
287
|
advanceOutcomeForMintedEarly(txHash, success) {
|
|
291
|
-
|
|
288
|
+
publishTxnRecord(
|
|
292
289
|
txHash,
|
|
293
290
|
harden({
|
|
294
291
|
status: success
|
|
@@ -381,10 +378,7 @@ export const prepareStatusManager = (
|
|
|
381
378
|
* @param {import('./liquidity-pool.js').RepayAmountKWR} split
|
|
382
379
|
*/
|
|
383
380
|
disbursed(txHash, split) {
|
|
384
|
-
|
|
385
|
-
txHash,
|
|
386
|
-
harden({ split, status: TxStatus.Disbursed }),
|
|
387
|
-
);
|
|
381
|
+
publishTxnRecord(txHash, harden({ split, status: TxStatus.Disbursed }));
|
|
388
382
|
},
|
|
389
383
|
|
|
390
384
|
/**
|
|
@@ -394,7 +388,7 @@ export const prepareStatusManager = (
|
|
|
394
388
|
* @param {boolean} success
|
|
395
389
|
*/
|
|
396
390
|
forwarded(txHash, success) {
|
|
397
|
-
|
|
391
|
+
publishTxnRecord(
|
|
398
392
|
txHash,
|
|
399
393
|
harden({
|
|
400
394
|
status: success ? TxStatus.Forwarded : TxStatus.ForwardFailed,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
/** @file Exo for @see {prepareTransactionFeedKit} */
|
|
1
2
|
import { makeTracer } from '@agoric/internal';
|
|
2
3
|
import { prepareDurablePublishKit } from '@agoric/notifier';
|
|
3
|
-
import { keyEQ, M } from '@endo/patterns';
|
|
4
4
|
import { Fail, quote } from '@endo/errors';
|
|
5
|
+
import { keyEQ, M } from '@endo/patterns';
|
|
5
6
|
import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js';
|
|
6
7
|
import { defineInertInvitation } from '../utils/zoe.js';
|
|
7
8
|
import { prepareOperatorKit } from './operator-kit.js';
|
|
@@ -64,8 +65,14 @@ export const stateShape = {
|
|
|
64
65
|
pending: M.remotable(),
|
|
65
66
|
risks: M.remotable(),
|
|
66
67
|
};
|
|
68
|
+
harden(stateShape);
|
|
67
69
|
|
|
68
70
|
/**
|
|
71
|
+
* A TransactionFeed is responsible for finding quorum among oracles.
|
|
72
|
+
*
|
|
73
|
+
* It receives attestations, records their evidence, and when enough oracles
|
|
74
|
+
* agree, publishes the results for the advancer to act on.
|
|
75
|
+
*
|
|
69
76
|
* @param {Zone} zone
|
|
70
77
|
* @param {ZCF} zcf
|
|
71
78
|
*/
|
|
@@ -148,18 +155,20 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
148
155
|
|
|
149
156
|
/** @param {string} operatorId */
|
|
150
157
|
removeOperator(operatorId) {
|
|
151
|
-
const { operators } = this.state;
|
|
158
|
+
const { operators, pending, risks } = this.state;
|
|
152
159
|
trace('removeOperator', operatorId);
|
|
153
160
|
const operatorKit = operators.get(operatorId);
|
|
154
161
|
operatorKit.admin.disable();
|
|
155
162
|
operators.delete(operatorId);
|
|
163
|
+
pending.delete(operatorId);
|
|
164
|
+
risks.delete(operatorId);
|
|
156
165
|
},
|
|
157
166
|
},
|
|
158
167
|
operatorPowers: {
|
|
159
168
|
/**
|
|
160
169
|
* Add evidence from an operator.
|
|
161
170
|
*
|
|
162
|
-
* NB: the operatorKit is responsible for
|
|
171
|
+
* NB: the operatorKit is responsible for revoking access.
|
|
163
172
|
*
|
|
164
173
|
* @param {CctpTxEvidence} evidence
|
|
165
174
|
* @param {RiskAssessment} riskAssessment
|
|
@@ -169,10 +178,6 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
169
178
|
const { operators, pending, risks } = this.state;
|
|
170
179
|
trace('attest', operatorId, evidence);
|
|
171
180
|
|
|
172
|
-
// TODO https://github.com/Agoric/agoric-sdk/pull/10720
|
|
173
|
-
// TODO validate that it's a valid for Fast USDC before accepting
|
|
174
|
-
// E.g. that the `recipientAddress` is the FU settlement account and that
|
|
175
|
-
// the EUD is a chain supported by FU.
|
|
176
181
|
const { txHash } = evidence;
|
|
177
182
|
|
|
178
183
|
// accept the evidence
|
|
@@ -192,6 +197,29 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
192
197
|
const found = [...pending.values()].filter(store =>
|
|
193
198
|
store.has(txHash),
|
|
194
199
|
);
|
|
200
|
+
|
|
201
|
+
{
|
|
202
|
+
let lastEvidence;
|
|
203
|
+
for (const store of found) {
|
|
204
|
+
const next = store.get(txHash);
|
|
205
|
+
if (lastEvidence && !keyEQ(lastEvidence, next)) {
|
|
206
|
+
// Ignore conflicting evidence, but treat it as an error
|
|
207
|
+
// because it should never happen and needs to be prevented
|
|
208
|
+
// from happening again.
|
|
209
|
+
trace(
|
|
210
|
+
'🚨 conflicting evidence for',
|
|
211
|
+
txHash,
|
|
212
|
+
':',
|
|
213
|
+
lastEvidence,
|
|
214
|
+
'!=',
|
|
215
|
+
next,
|
|
216
|
+
);
|
|
217
|
+
Fail`conflicting evidence for ${quote(txHash)}`;
|
|
218
|
+
}
|
|
219
|
+
lastEvidence = next;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
195
223
|
const minAttestations = Math.ceil(operators.getSize() / 2);
|
|
196
224
|
trace(
|
|
197
225
|
'transaction',
|
|
@@ -202,48 +230,37 @@ export const prepareTransactionFeedKit = (zone, zcf) => {
|
|
|
202
230
|
minAttestations,
|
|
203
231
|
'necessary attestations',
|
|
204
232
|
);
|
|
233
|
+
|
|
205
234
|
if (found.length < minAttestations) {
|
|
235
|
+
// wait for more
|
|
206
236
|
return;
|
|
207
237
|
}
|
|
208
238
|
|
|
209
|
-
let lastEvidence;
|
|
210
|
-
for (const store of found) {
|
|
211
|
-
const next = store.get(txHash);
|
|
212
|
-
if (lastEvidence && !keyEQ(lastEvidence, next)) {
|
|
213
|
-
// Ignore conflicting evidence, but treat it as an error
|
|
214
|
-
// because it should never happen and needs to be prevented
|
|
215
|
-
// from happening again.
|
|
216
|
-
trace(
|
|
217
|
-
'🚨 conflicting evidence for',
|
|
218
|
-
txHash,
|
|
219
|
-
':',
|
|
220
|
-
lastEvidence,
|
|
221
|
-
'!=',
|
|
222
|
-
next,
|
|
223
|
-
);
|
|
224
|
-
Fail`conflicting evidence for ${quote(txHash)}`;
|
|
225
|
-
}
|
|
226
|
-
lastEvidence = next;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
239
|
const riskStores = [...risks.values()].filter(store =>
|
|
230
240
|
store.has(txHash),
|
|
231
241
|
);
|
|
232
|
-
// take the union of risks identified from all operators
|
|
233
|
-
const risksIdentified = allRisksIdentified(riskStores, txHash);
|
|
234
242
|
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
243
|
+
// Publish at the threshold of agreement
|
|
244
|
+
if (found.length === minAttestations) {
|
|
245
|
+
// take the union of risks identified from all operators
|
|
246
|
+
const risksIdentified = allRisksIdentified(riskStores, txHash);
|
|
247
|
+
trace('publishing evidence', evidence, risksIdentified);
|
|
248
|
+
publisher.publish({
|
|
249
|
+
evidence,
|
|
250
|
+
risk: { risksIdentified },
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
238
253
|
}
|
|
239
|
-
|
|
240
|
-
|
|
254
|
+
|
|
255
|
+
if (found.length === pending.getSize()) {
|
|
256
|
+
// all have reported so clean up
|
|
257
|
+
for (const store of found) {
|
|
258
|
+
store.delete(txHash);
|
|
259
|
+
}
|
|
260
|
+
for (const store of riskStores) {
|
|
261
|
+
store.delete(txHash);
|
|
262
|
+
}
|
|
241
263
|
}
|
|
242
|
-
trace('publishing evidence', evidence, risksIdentified);
|
|
243
|
-
publisher.publish({
|
|
244
|
-
evidence,
|
|
245
|
-
risk: { risksIdentified },
|
|
246
|
-
});
|
|
247
264
|
},
|
|
248
265
|
},
|
|
249
266
|
public: {
|
|
@@ -67,10 +67,11 @@ harden(meta);
|
|
|
67
67
|
* @param {ERef<Marshaller>} marshaller
|
|
68
68
|
* @param {FeeConfig} feeConfig
|
|
69
69
|
*/
|
|
70
|
-
const publishFeeConfig =
|
|
70
|
+
const publishFeeConfig = (node, marshaller, feeConfig) => {
|
|
71
71
|
const feeNode = E(node).makeChildNode(FEE_NODE);
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
void E.when(E(marshaller).toCapData(feeConfig), value =>
|
|
73
|
+
E(feeNode).setValue(JSON.stringify(value)),
|
|
74
|
+
);
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
/**
|
|
@@ -247,7 +248,7 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
247
248
|
// So we use zone.exoClassKit above to define the liquidity pool kind
|
|
248
249
|
// and pass the shareMint into the maker / init function.
|
|
249
250
|
|
|
250
|
-
|
|
251
|
+
publishFeeConfig(storageNode, marshaller, feeConfig);
|
|
251
252
|
|
|
252
253
|
const shareMint = await provideSingleton(
|
|
253
254
|
zone.mapStore('mint'),
|