@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.1.1-upgrade-19-dev-c605745.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.
Files changed (40) hide show
  1. package/README.md +43 -0
  2. package/package.json +29 -25
  3. package/src/add-operators.core.js +63 -0
  4. package/src/cli/bridge-action.js +40 -0
  5. package/src/cli/cli.js +46 -153
  6. package/src/cli/config-commands.js +108 -0
  7. package/src/cli/config.js +15 -9
  8. package/src/cli/lp-commands.js +160 -0
  9. package/src/cli/operator-commands.js +146 -0
  10. package/src/cli/transfer.js +63 -13
  11. package/src/{util → cli/util}/agoric.js +1 -1
  12. package/src/cli/util/bank.js +12 -0
  13. package/src/{util → cli/util}/cctp.js +1 -1
  14. package/src/{util → cli/util}/file.js +1 -1
  15. package/src/clientSupport.js +98 -0
  16. package/src/constants.js +25 -2
  17. package/src/distribute-fees.core.js +93 -0
  18. package/src/exos/advancer.js +220 -106
  19. package/src/exos/liquidity-pool.js +130 -81
  20. package/src/exos/operator-kit.js +16 -12
  21. package/src/exos/settler.js +360 -64
  22. package/src/exos/status-manager.js +316 -65
  23. package/src/exos/transaction-feed.js +121 -42
  24. package/src/fast-usdc-policy.core.js +65 -0
  25. package/src/fast-usdc.contract.js +165 -84
  26. package/src/fast-usdc.flows.js +10 -0
  27. package/src/main.js +1 -0
  28. package/src/pool-share-math.js +55 -9
  29. package/src/{fast-usdc.start.js → start-fast-usdc.core.js} +48 -86
  30. package/src/type-guards.js +75 -24
  31. package/src/types.ts +89 -14
  32. package/src/utils/chain-policies.js +140 -0
  33. package/src/utils/core-eval.js +73 -0
  34. package/src/utils/deploy-config.js +127 -0
  35. package/src/utils/fees.js +3 -4
  36. package/tools/cli-tools.ts +9 -0
  37. package/tools/mock-io.ts +14 -0
  38. package/src/exos/README.md +0 -26
  39. package/src/utils/address.js +0 -71
  40. /package/src/{util → cli/util}/noble.js +0 -0
@@ -1,13 +1,15 @@
1
- import { AmountMath, AmountShape } from '@agoric/ertp';
1
+ import { AmountMath, AmountShape, RatioShape } from '@agoric/ertp';
2
2
  import {
3
3
  makeRecorderTopic,
4
+ RecorderKitShape,
4
5
  TopicsRecordShape,
5
- } from '@agoric/zoe/src/contractSupport/topics.js';
6
+ } from '@agoric/zoe/src/contractSupport/index.js';
6
7
  import { SeatShape } from '@agoric/zoe/src/typeGuards.js';
7
8
  import { M } from '@endo/patterns';
8
9
  import { Fail, q } from '@endo/errors';
9
10
  import {
10
11
  borrowCalc,
12
+ checkPoolBalance,
11
13
  depositCalc,
12
14
  makeParity,
13
15
  repayCalc,
@@ -20,6 +22,7 @@ import {
20
22
  } from '../type-guards.js';
21
23
 
22
24
  /**
25
+ * @import {Amount, Brand, Payment} from '@agoric/ertp';
23
26
  * @import {Zone} from '@agoric/zone';
24
27
  * @import {Remote} from '@agoric/internal'
25
28
  * @import {StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
@@ -28,32 +31,7 @@ import {
28
31
  * @import {PoolStats} from '../types.js';
29
32
  */
30
33
 
31
- const { add, isEqual, makeEmpty } = AmountMath;
32
-
33
- /** @param {Brand} brand */
34
- const makeDust = brand => AmountMath.make(brand, 1n);
35
-
36
- /**
37
- * Verifies that the total pool balance (unencumbered + encumbered) matches the
38
- * shareWorth numerator. The total pool balance consists of:
39
- * 1. unencumbered balance - USDC available in the pool for borrowing
40
- * 2. encumbered balance - USDC currently lent out
41
- *
42
- * A negligible `dust` amount is used to initialize shareWorth with a non-zero
43
- * denominator. It must remain in the pool at all times.
44
- *
45
- * @param {ZCFSeat} poolSeat
46
- * @param {ShareWorth} shareWorth
47
- * @param {Brand} USDC
48
- * @param {Amount<'nat'>} encumberedBalance
49
- */
50
- const checkPoolBalance = (poolSeat, shareWorth, USDC, encumberedBalance) => {
51
- const unencumberedBalance = poolSeat.getAmountAllocated('USDC', USDC);
52
- const dust = makeDust(USDC);
53
- const grossBalance = add(add(unencumberedBalance, dust), encumberedBalance);
54
- isEqual(grossBalance, shareWorth.numerator) ||
55
- Fail`🚨 pool balance ${q(unencumberedBalance)} and encumbered balance ${q(encumberedBalance)} inconsistent with shareWorth ${q(shareWorth)}`;
56
- };
34
+ const { add, isGTE, makeEmpty } = AmountMath;
57
35
 
58
36
  /**
59
37
  * @typedef {{
@@ -71,6 +49,22 @@ const checkPoolBalance = (poolSeat, shareWorth, USDC, encumberedBalance) => {
71
49
  * }} RepayPaymentKWR
72
50
  */
73
51
 
52
+ export const stateShape = harden({
53
+ encumberedBalance: AmountShape,
54
+ feeSeat: M.remotable(),
55
+ poolStats: M.record(),
56
+ poolMetricsRecorderKit: RecorderKitShape,
57
+ poolSeat: M.remotable(),
58
+ PoolShares: M.remotable(),
59
+ proposalShapes: {
60
+ deposit: M.pattern(),
61
+ withdraw: M.pattern(),
62
+ withdrawFees: M.pattern(),
63
+ },
64
+ shareMint: M.remotable(),
65
+ shareWorth: RatioShape,
66
+ });
67
+
74
68
  /**
75
69
  * @param {Zone} zone
76
70
  * @param {ZCF} zcf
@@ -84,11 +78,8 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
84
78
  'Liquidity Pool',
85
79
  {
86
80
  borrower: M.interface('borrower', {
87
- getBalance: M.call().returns(AmountShape),
88
- borrow: M.call(
89
- SeatShape,
90
- harden({ USDC: makeNatAmountShape(USDC, 1n) }),
91
- ).returns(),
81
+ borrow: M.call(SeatShape, makeNatAmountShape(USDC, 1n)).returns(),
82
+ returnToPool: M.call(SeatShape, makeNatAmountShape(USDC, 1n)).returns(),
92
83
  }),
93
84
  repayer: M.interface('repayer', {
94
85
  repay: M.call(
@@ -109,11 +100,18 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
109
100
  withdrawHandler: M.interface('withdrawHandler', {
110
101
  handle: M.call(SeatShape, M.any()).returns(M.promise()),
111
102
  }),
103
+ withdrawFeesHandler: M.interface('withdrawFeesHandler', {
104
+ handle: M.call(SeatShape, M.any()).returns(M.promise()),
105
+ }),
112
106
  public: M.interface('public', {
113
107
  makeDepositInvitation: M.call().returns(M.promise()),
114
108
  makeWithdrawInvitation: M.call().returns(M.promise()),
115
109
  getPublicTopics: M.call().returns(TopicsRecordShape),
116
110
  }),
111
+ feeRecipient: M.interface('feeRecipient', {
112
+ getContractFeeBalance: M.call().returns(AmountShape),
113
+ makeWithdrawFeesInvitation: M.call().returns(M.promise()),
114
+ }),
117
115
  },
118
116
  /**
119
117
  * @param {ZCFMint<'nat'>} shareMint
@@ -122,7 +120,7 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
122
120
  (shareMint, node) => {
123
121
  const { brand: PoolShares } = shareMint.getIssuerRecord();
124
122
  const proposalShapes = makeProposalShapes({ USDC, PoolShares });
125
- const shareWorth = makeParity(makeDust(USDC), PoolShares);
123
+ const shareWorth = makeParity(USDC, PoolShares);
126
124
  const { zcfSeat: poolSeat } = zcf.makeEmptySeatKit();
127
125
  const { zcfSeat: feeSeat } = zcf.makeEmptySeatKit();
128
126
  const poolMetricsRecorderKit = tools.makeRecorderKit(
@@ -152,38 +150,52 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
152
150
  },
153
151
  {
154
152
  borrower: {
155
- getBalance() {
156
- const { poolSeat } = this.state;
157
- return poolSeat.getAmountAllocated('USDC', USDC);
158
- },
159
153
  /**
160
154
  * @param {ZCFSeat} toSeat
161
- * @param {{ USDC: Amount<'nat'>}} amountKWR
155
+ * @param {Amount<'nat'>} amount
162
156
  */
163
- borrow(toSeat, amountKWR) {
157
+ borrow(toSeat, amount) {
164
158
  const { encumberedBalance, poolSeat, poolStats } = this.state;
165
159
 
166
160
  // Validate amount is available in pool
167
161
  const post = borrowCalc(
168
- amountKWR.USDC,
162
+ amount,
169
163
  poolSeat.getAmountAllocated('USDC', USDC),
170
164
  encumberedBalance,
171
165
  poolStats,
172
166
  );
173
167
 
174
168
  // COMMIT POINT
175
- try {
176
- zcf.atomicRearrange(harden([[poolSeat, toSeat, amountKWR]]));
177
- } catch (cause) {
178
- const reason = Error('🚨 cannot commit borrow', { cause });
179
- console.error(reason.message, cause);
180
- zcf.shutdownWithFailure(reason);
181
- }
169
+ // UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
170
+ zcf.atomicRearrange(harden([[poolSeat, toSeat, { USDC: amount }]]));
182
171
 
183
172
  Object.assign(this.state, post);
184
173
  this.facets.external.publishPoolMetrics();
185
174
  },
186
- // TODO method to repay failed `LOA.deposit()`
175
+ /**
176
+ * If something fails during advance, return funds to the pool.
177
+ *
178
+ * @param {ZCFSeat} borrowSeat
179
+ * @param {Amount<'nat'>} amount
180
+ */
181
+ returnToPool(borrowSeat, amount) {
182
+ const { zcfSeat: repaySeat } = zcf.makeEmptySeatKit();
183
+ const returnAmounts = harden({
184
+ Principal: amount,
185
+ PoolFee: makeEmpty(USDC),
186
+ ContractFee: makeEmpty(USDC),
187
+ });
188
+ const borrowSeatAllocation = borrowSeat.getCurrentAllocation();
189
+ isGTE(borrowSeatAllocation.USDC, amount) ||
190
+ Fail`⚠️ borrowSeatAllocation ${q(borrowSeatAllocation)} less than amountKWR ${q(amount)}`;
191
+ // arrange payments in a format repay is expecting
192
+ zcf.atomicRearrange(
193
+ harden([[borrowSeat, repaySeat, { USDC: amount }, returnAmounts]]),
194
+ );
195
+ this.facets.repayer.repay(repaySeat, returnAmounts);
196
+ borrowSeat.exit();
197
+ repaySeat.exit();
198
+ },
187
199
  },
188
200
  repayer: {
189
201
  /**
@@ -198,7 +210,11 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
198
210
  poolStats,
199
211
  shareWorth,
200
212
  } = this.state;
201
- checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
213
+ checkPoolBalance(
214
+ poolSeat.getCurrentAllocation(),
215
+ shareWorth,
216
+ encumberedBalance,
217
+ );
202
218
 
203
219
  const fromSeatAllocation = fromSeat.getCurrentAllocation();
204
220
  // Validate allocation equals amounts and Principal <= encumberedBalance
@@ -213,23 +229,18 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
213
229
  const { ContractFee, ...rest } = amounts;
214
230
 
215
231
  // COMMIT POINT
216
- try {
217
- zcf.atomicRearrange(
218
- harden([
219
- [
220
- fromSeat,
221
- poolSeat,
222
- rest,
223
- { USDC: add(amounts.PoolFee, amounts.Principal) },
224
- ],
225
- [fromSeat, feeSeat, { ContractFee }, { USDC: ContractFee }],
226
- ]),
227
- );
228
- } catch (cause) {
229
- const reason = Error('🚨 cannot commit repay', { cause });
230
- console.error(reason.message, cause);
231
- zcf.shutdownWithFailure(reason);
232
- }
232
+ // UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
233
+ zcf.atomicRearrange(
234
+ harden([
235
+ [
236
+ fromSeat,
237
+ poolSeat,
238
+ rest,
239
+ { USDC: add(amounts.PoolFee, amounts.Principal) },
240
+ ],
241
+ [fromSeat, feeSeat, { ContractFee }, { USDC: ContractFee }],
242
+ ]),
243
+ );
233
244
 
234
245
  Object.assign(this.state, post);
235
246
  this.facets.external.publishPoolMetrics();
@@ -260,13 +271,16 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
260
271
  /** @type {USDCProposalShapes['deposit']} */
261
272
  // @ts-expect-error ensured by proposalShape
262
273
  const proposal = lp.getProposal();
263
- checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
274
+ checkPoolBalance(
275
+ poolSeat.getCurrentAllocation(),
276
+ shareWorth,
277
+ encumberedBalance,
278
+ );
264
279
  const post = depositCalc(shareWorth, proposal);
265
280
 
266
281
  // COMMIT POINT
267
-
282
+ const mint = shareMint.mintGains(post.payouts);
268
283
  try {
269
- const mint = shareMint.mintGains(post.payouts);
270
284
  this.state.shareWorth = post.shareWorth;
271
285
  zcf.atomicRearrange(
272
286
  harden([
@@ -276,12 +290,12 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
276
290
  [mint, lp, post.payouts],
277
291
  ]),
278
292
  );
293
+ } catch (cause) {
294
+ // UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
295
+ throw new Error('🚨 cannot commit deposit', { cause });
296
+ } finally {
279
297
  lp.exit();
280
298
  mint.exit();
281
- } catch (cause) {
282
- const reason = Error('🚨 cannot commit deposit', { cause });
283
- console.error(reason.message, cause);
284
- zcf.shutdownWithFailure(reason);
285
299
  }
286
300
  external.publishPoolMetrics();
287
301
  },
@@ -297,11 +311,14 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
297
311
  // @ts-expect-error ensured by proposalShape
298
312
  const proposal = lp.getProposal();
299
313
  const { zcfSeat: burn } = zcf.makeEmptySeatKit();
300
- checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
301
- const post = withdrawCalc(shareWorth, proposal);
314
+ const post = withdrawCalc(
315
+ shareWorth,
316
+ proposal,
317
+ poolSeat.getCurrentAllocation(),
318
+ encumberedBalance,
319
+ );
302
320
 
303
321
  // COMMIT POINT
304
-
305
322
  try {
306
323
  this.state.shareWorth = post.shareWorth;
307
324
  zcf.atomicRearrange(
@@ -313,16 +330,31 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
313
330
  ]),
314
331
  );
315
332
  shareMint.burnLosses(proposal.give, burn);
333
+ } catch (cause) {
334
+ // UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
335
+ throw new Error('🚨 cannot commit withdraw', { cause });
336
+ } finally {
316
337
  lp.exit();
317
338
  burn.exit();
318
- } catch (cause) {
319
- const reason = Error('🚨 cannot commit withdraw', { cause });
320
- console.error(reason.message, cause);
321
- zcf.shutdownWithFailure(reason);
322
339
  }
323
340
  external.publishPoolMetrics();
324
341
  },
325
342
  },
343
+ withdrawFeesHandler: {
344
+ /** @param {ZCFSeat} seat */
345
+ async handle(seat) {
346
+ const { feeSeat } = this.state;
347
+
348
+ const { want } = seat.getProposal();
349
+ const available = feeSeat.getAmountAllocated('USDC', want.USDC.brand);
350
+ isGTE(available, want.USDC) ||
351
+ Fail`cannot withdraw ${want.USDC}; only ${available} available`;
352
+
353
+ // COMMIT POINT
354
+ zcf.atomicRearrange(harden([[feeSeat, seat, want]]));
355
+ seat.exit();
356
+ },
357
+ },
326
358
  public: {
327
359
  makeDepositInvitation() {
328
360
  return zcf.makeInvitation(
@@ -350,11 +382,28 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
350
382
  };
351
383
  },
352
384
  },
385
+ feeRecipient: {
386
+ getContractFeeBalance() {
387
+ const { feeSeat } = this.state;
388
+ /** @type {Amount<'nat'>} */
389
+ const balance = feeSeat.getCurrentAllocation().USDC;
390
+ return balance;
391
+ },
392
+ makeWithdrawFeesInvitation() {
393
+ return zcf.makeInvitation(
394
+ this.facets.withdrawFeesHandler,
395
+ 'Withdraw Fees',
396
+ undefined,
397
+ this.state.proposalShapes.withdrawFees,
398
+ );
399
+ },
400
+ },
353
401
  },
354
402
  {
355
403
  finish: ({ facets: { external } }) => {
356
404
  void external.publishPoolMetrics();
357
405
  },
406
+ stateShape,
358
407
  },
359
408
  );
360
409
  };
@@ -1,18 +1,18 @@
1
1
  import { makeTracer } from '@agoric/internal';
2
2
  import { Fail } from '@endo/errors';
3
3
  import { M } from '@endo/patterns';
4
- import { CctpTxEvidenceShape } from '../type-guards.js';
4
+ import { CctpTxEvidenceShape, RiskAssessmentShape } from '../type-guards.js';
5
5
 
6
6
  const trace = makeTracer('TxOperator');
7
7
 
8
8
  /**
9
9
  * @import {Zone} from '@agoric/zone';
10
- * @import {CctpTxEvidence} from '../types.js';
10
+ * @import {CctpTxEvidence, RiskAssessment} from '../types.js';
11
11
  */
12
12
 
13
13
  /**
14
14
  * @typedef {object} OperatorPowers
15
- * @property {(evidence: CctpTxEvidence, operatorKit: OperatorKit) => void} submitEvidence
15
+ * @property {(evidence: CctpTxEvidence, riskAssessment: RiskAssessment, operatorId: string) => void} attest
16
16
  */
17
17
 
18
18
  /**
@@ -31,11 +31,15 @@ const OperatorKitI = {
31
31
  }),
32
32
 
33
33
  invitationMakers: M.interface('InvitationMakers', {
34
- SubmitEvidence: M.call(CctpTxEvidenceShape).returns(M.promise()),
34
+ SubmitEvidence: M.call(CctpTxEvidenceShape)
35
+ .optional(RiskAssessmentShape)
36
+ .returns(M.promise()),
35
37
  }),
36
38
 
37
39
  operator: M.interface('Operator', {
38
- submitEvidence: M.call(CctpTxEvidenceShape).returns(M.promise()),
40
+ submitEvidence: M.call(CctpTxEvidenceShape)
41
+ .optional(RiskAssessmentShape)
42
+ .returns(),
39
43
  getStatus: M.call().returns(M.record()),
40
44
  }),
41
45
  };
@@ -81,13 +85,12 @@ export const prepareOperatorKit = (zone, staticPowers) =>
81
85
  * fluxAggregator contract used for price oracles.
82
86
  *
83
87
  * @param {CctpTxEvidence} evidence
88
+ * @param {RiskAssessment} [riskAssessment]
84
89
  * @returns {Promise<Invitation>}
85
90
  */
86
- async SubmitEvidence(evidence) {
91
+ async SubmitEvidence(evidence, riskAssessment) {
87
92
  const { operator } = this.facets;
88
- // TODO(bootstrap integration): cause this call to throw and confirm that it
89
- // shows up in the the smart-wallet UpdateRecord `error` property
90
- await operator.submitEvidence(evidence);
93
+ operator.submitEvidence(evidence, riskAssessment);
91
94
  return staticPowers.makeInertInvitation(
92
95
  'evidence was pushed in the invitation maker call',
93
96
  );
@@ -98,12 +101,13 @@ export const prepareOperatorKit = (zone, staticPowers) =>
98
101
  * submit evidence from this operator
99
102
  *
100
103
  * @param {CctpTxEvidence} evidence
104
+ * @param {RiskAssessment} [riskAssessment]
105
+ * @returns {void}
101
106
  */
102
- async submitEvidence(evidence) {
107
+ submitEvidence(evidence, riskAssessment = {}) {
103
108
  const { state } = this;
104
109
  !state.disabled || Fail`submitEvidence for disabled operator`;
105
- const result = state.powers.submitEvidence(evidence, this.facets);
106
- return result;
110
+ state.powers.attest(evidence, riskAssessment, state.operatorId);
107
111
  },
108
112
  /** @returns {OperatorStatus} */
109
113
  getStatus() {