@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.2.0-u18.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/README.md +43 -0
- package/package.json +19 -16
- package/src/cli/bridge-action.js +40 -0
- package/src/cli/cli.js +45 -152
- package/src/cli/config-commands.js +108 -0
- package/src/cli/config.js +15 -9
- package/src/cli/lp-commands.js +178 -0
- package/src/cli/operator-commands.js +145 -0
- package/src/cli/transfer.js +61 -11
- package/src/constants.js +25 -2
- package/src/exos/advancer.js +149 -103
- package/src/exos/liquidity-pool.js +52 -50
- package/src/exos/operator-kit.js +16 -10
- package/src/exos/settler.js +295 -58
- package/src/exos/status-manager.js +270 -57
- package/src/exos/transaction-feed.js +93 -35
- package/src/fast-usdc-policy.core.js +75 -0
- package/src/fast-usdc.contract.js +152 -63
- package/src/fast-usdc.flows.js +10 -0
- package/src/fast-usdc.start.js +44 -14
- package/src/type-guards.js +42 -20
- package/src/types.ts +58 -12
- package/src/util/agoric.js +1 -1
- package/src/util/bank.js +12 -0
- package/src/util/cctp.js +1 -1
- package/src/util/file.js +1 -1
- package/src/utils/deploy-config.js +166 -0
- package/tools/cli-tools.ts +9 -0
- package/tools/mock-io.ts +14 -0
- package/src/exos/README.md +0 -26
- package/src/utils/address.js +0 -71
package/src/exos/advancer.js
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { decodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
|
|
2
|
+
import { AmountMath } from '@agoric/ertp';
|
|
2
3
|
import { assertAllDefined, makeTracer } from '@agoric/internal';
|
|
3
|
-
import { ChainAddressShape } from '@agoric/orchestration';
|
|
4
|
+
import { AnyNatAmountShape, ChainAddressShape } from '@agoric/orchestration';
|
|
4
5
|
import { pickFacet } from '@agoric/vat-data';
|
|
5
6
|
import { VowShape } from '@agoric/vow';
|
|
6
|
-
import { q } from '@endo/errors';
|
|
7
7
|
import { E } from '@endo/far';
|
|
8
|
-
import { M } from '@endo/patterns';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { M, mustMatch } from '@endo/patterns';
|
|
9
|
+
import { Fail, q } from '@endo/errors';
|
|
10
|
+
import {
|
|
11
|
+
AddressHookShape,
|
|
12
|
+
EvmHashShape,
|
|
13
|
+
EvidenceWithRiskShape,
|
|
14
|
+
} from '../type-guards.js';
|
|
11
15
|
import { makeFeeTools } from '../utils/fees.js';
|
|
12
16
|
|
|
13
|
-
const { isGTE } = AmountMath;
|
|
14
|
-
|
|
15
17
|
/**
|
|
16
18
|
* @import {HostInterface} from '@agoric/async-flow';
|
|
19
|
+
* @import {TypedPattern} from '@agoric/internal'
|
|
17
20
|
* @import {NatAmount} from '@agoric/ertp';
|
|
18
21
|
* @import {ChainAddress, ChainHub, Denom, OrchestrationAccount} from '@agoric/orchestration';
|
|
19
22
|
* @import {ZoeTools} from '@agoric/orchestration/src/utils/zoe-tools.js';
|
|
20
23
|
* @import {VowTools} from '@agoric/vow';
|
|
21
24
|
* @import {Zone} from '@agoric/zone';
|
|
22
|
-
* @import {CctpTxEvidence, FeeConfig, LogFn} from '../types.js';
|
|
25
|
+
* @import {CctpTxEvidence, AddressHook, EvmHash, FeeConfig, LogFn, NobleAddress, EvidenceWithRisk} from '../types.js';
|
|
23
26
|
* @import {StatusManager} from './status-manager.js';
|
|
24
27
|
* @import {LiquidityPoolKit} from './liquidity-pool.js';
|
|
25
28
|
*/
|
|
@@ -37,36 +40,47 @@ const { isGTE } = AmountMath;
|
|
|
37
40
|
* }} AdvancerKitPowers
|
|
38
41
|
*/
|
|
39
42
|
|
|
43
|
+
/** @type {TypedPattern<AdvancerVowCtx>} */
|
|
44
|
+
const AdvancerVowCtxShape = M.splitRecord(
|
|
45
|
+
{
|
|
46
|
+
fullAmount: AnyNatAmountShape,
|
|
47
|
+
advanceAmount: AnyNatAmountShape,
|
|
48
|
+
destination: ChainAddressShape,
|
|
49
|
+
forwardingAddress: M.string(),
|
|
50
|
+
txHash: EvmHashShape,
|
|
51
|
+
},
|
|
52
|
+
{ tmpSeat: M.remotable() },
|
|
53
|
+
);
|
|
54
|
+
|
|
40
55
|
/** type guards internal to the AdvancerKit */
|
|
41
56
|
const AdvancerKitI = harden({
|
|
42
57
|
advancer: M.interface('AdvancerI', {
|
|
43
|
-
handleTransactionEvent: M.callWhen(
|
|
58
|
+
handleTransactionEvent: M.callWhen(EvidenceWithRiskShape).returns(),
|
|
59
|
+
setIntermediateRecipient: M.call(ChainAddressShape).returns(),
|
|
44
60
|
}),
|
|
45
61
|
depositHandler: M.interface('DepositHandlerI', {
|
|
46
|
-
onFulfilled: M.call(M.undefined(),
|
|
47
|
-
|
|
48
|
-
destination: ChainAddressShape,
|
|
49
|
-
tmpSeat: M.remotable(),
|
|
50
|
-
}).returns(VowShape),
|
|
51
|
-
onRejected: M.call(M.error(), {
|
|
52
|
-
amount: AmountShape,
|
|
53
|
-
destination: ChainAddressShape,
|
|
54
|
-
tmpSeat: M.remotable(),
|
|
55
|
-
}).returns(),
|
|
62
|
+
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(VowShape),
|
|
63
|
+
onRejected: M.call(M.error(), AdvancerVowCtxShape).returns(),
|
|
56
64
|
}),
|
|
57
65
|
transferHandler: M.interface('TransferHandlerI', {
|
|
58
66
|
// TODO confirm undefined, and not bigint (sequence)
|
|
59
|
-
onFulfilled: M.call(M.undefined(),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
onRejected: M.call(M.error(), {
|
|
64
|
-
amount: AmountShape,
|
|
65
|
-
destination: ChainAddressShape,
|
|
66
|
-
}).returns(M.undefined()),
|
|
67
|
+
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(
|
|
68
|
+
M.undefined(),
|
|
69
|
+
),
|
|
70
|
+
onRejected: M.call(M.error(), AdvancerVowCtxShape).returns(M.undefined()),
|
|
67
71
|
}),
|
|
68
72
|
});
|
|
69
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {{
|
|
76
|
+
* fullAmount: NatAmount;
|
|
77
|
+
* advanceAmount: NatAmount;
|
|
78
|
+
* destination: ChainAddress;
|
|
79
|
+
* forwardingAddress: NobleAddress;
|
|
80
|
+
* txHash: EvmHash;
|
|
81
|
+
* }} AdvancerVowCtx
|
|
82
|
+
*/
|
|
83
|
+
|
|
70
84
|
/**
|
|
71
85
|
* @param {Zone} zone
|
|
72
86
|
* @param {AdvancerKitPowers} caps
|
|
@@ -82,7 +96,7 @@ export const prepareAdvancerKit = (
|
|
|
82
96
|
usdc,
|
|
83
97
|
vowTools: { watch, when },
|
|
84
98
|
zcf,
|
|
85
|
-
}
|
|
99
|
+
},
|
|
86
100
|
) => {
|
|
87
101
|
assertAllDefined({
|
|
88
102
|
chainHub,
|
|
@@ -100,11 +114,19 @@ export const prepareAdvancerKit = (
|
|
|
100
114
|
AdvancerKitI,
|
|
101
115
|
/**
|
|
102
116
|
* @param {{
|
|
117
|
+
* notifyFacet: import('./settler.js').SettlerKit['notify'];
|
|
103
118
|
* borrowerFacet: LiquidityPoolKit['borrower'];
|
|
104
119
|
* poolAccount: HostInterface<OrchestrationAccount<{chainId: 'agoric'}>>;
|
|
120
|
+
* settlementAddress: ChainAddress;
|
|
121
|
+
* intermediateRecipient?: ChainAddress;
|
|
105
122
|
* }} config
|
|
106
123
|
*/
|
|
107
|
-
config =>
|
|
124
|
+
config =>
|
|
125
|
+
harden({
|
|
126
|
+
...config,
|
|
127
|
+
// make sure the state record has this property, perhaps with an undefined value
|
|
128
|
+
intermediateRecipient: config.intermediateRecipient,
|
|
129
|
+
}),
|
|
108
130
|
{
|
|
109
131
|
advancer: {
|
|
110
132
|
/**
|
|
@@ -115,129 +137,153 @@ export const prepareAdvancerKit = (
|
|
|
115
137
|
* `StatusManager` - so we don't need to concern ourselves with
|
|
116
138
|
* preserving the vow chain for callers.
|
|
117
139
|
*
|
|
118
|
-
* @param {
|
|
140
|
+
* @param {EvidenceWithRisk} evidenceWithRisk
|
|
119
141
|
*/
|
|
120
|
-
async handleTransactionEvent(evidence) {
|
|
142
|
+
async handleTransactionEvent({ evidence, risk }) {
|
|
121
143
|
await null;
|
|
122
144
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const { EUD } = addressTools.getQueryParams(
|
|
126
|
-
recipientAddress,
|
|
127
|
-
EudParamShape,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// this will throw if the bech32 prefix is not found, but is handled by the catch
|
|
131
|
-
const destination = chainHub.makeChainAddress(EUD);
|
|
132
|
-
const requestedAmount = toAmount(evidence.tx.amount);
|
|
133
|
-
const advanceAmount = feeTools.calculateAdvance(requestedAmount);
|
|
134
|
-
|
|
135
|
-
// TODO: consider skipping and using `borrow()`s internal balance check
|
|
136
|
-
const poolBalance = borrowerFacet.getBalance();
|
|
137
|
-
if (!isGTE(poolBalance, requestedAmount)) {
|
|
138
|
-
log(
|
|
139
|
-
`Insufficient pool funds`,
|
|
140
|
-
`Requested ${q(advanceAmount)} but only have ${q(poolBalance)}`,
|
|
141
|
-
);
|
|
142
|
-
statusManager.observe(evidence);
|
|
145
|
+
if (statusManager.hasBeenObserved(evidence)) {
|
|
146
|
+
log('txHash already seen:', evidence.txHash);
|
|
143
147
|
return;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
statusManager.advance(evidence);
|
|
150
|
-
} catch (e) {
|
|
151
|
-
// Only anticipated error is `assertNotSeen`, so intercept the
|
|
152
|
-
// catch so we don't call .skip which also performs this check
|
|
153
|
-
log('Advancer error:', q(e).toString());
|
|
150
|
+
if (risk.risksIdentified?.length) {
|
|
151
|
+
log('risks identified, skipping advance');
|
|
152
|
+
statusManager.skipAdvance(evidence, risk.risksIdentified);
|
|
154
153
|
return;
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
const {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// We catch to report outside of the normal error flow since this is
|
|
165
|
-
// not expected.
|
|
166
|
-
log('🚨 advance borrow failed', q(e).toString());
|
|
156
|
+
const { borrowerFacet, poolAccount, settlementAddress } =
|
|
157
|
+
this.state;
|
|
158
|
+
const { recipientAddress } = evidence.aux;
|
|
159
|
+
const decoded = decodeAddressHook(recipientAddress);
|
|
160
|
+
mustMatch(decoded, AddressHookShape);
|
|
161
|
+
if (decoded.baseAddress !== settlementAddress.value) {
|
|
162
|
+
throw Fail`⚠️ baseAddress of address hook ${q(decoded.baseAddress)} does not match the expected address ${q(settlementAddress.value)}`;
|
|
167
163
|
}
|
|
164
|
+
const { EUD } = /** @type {AddressHook['query']} */ (decoded.query);
|
|
165
|
+
log(`decoded EUD: ${EUD}`);
|
|
166
|
+
// throws if the bech32 prefix is not found
|
|
167
|
+
const destination = chainHub.makeChainAddress(EUD);
|
|
168
|
+
|
|
169
|
+
const fullAmount = toAmount(evidence.tx.amount);
|
|
170
|
+
// throws if requested does not exceed fees
|
|
171
|
+
const advanceAmount = feeTools.calculateAdvance(fullAmount);
|
|
172
|
+
|
|
173
|
+
const { zcfSeat: tmpSeat } = zcf.makeEmptySeatKit();
|
|
174
|
+
// throws if the pool has insufficient funds
|
|
175
|
+
borrowerFacet.borrow(tmpSeat, advanceAmount);
|
|
176
|
+
|
|
177
|
+
// this cannot throw since `.isSeen()` is called in the same turn
|
|
178
|
+
statusManager.advance(evidence);
|
|
168
179
|
|
|
169
180
|
const depositV = localTransfer(
|
|
170
181
|
tmpSeat,
|
|
171
182
|
// @ts-expect-error LocalAccountMethods vs OrchestrationAccount
|
|
172
183
|
poolAccount,
|
|
173
|
-
|
|
184
|
+
harden({ USDC: advanceAmount }),
|
|
174
185
|
);
|
|
175
186
|
void watch(depositV, this.facets.depositHandler, {
|
|
176
|
-
|
|
187
|
+
advanceAmount,
|
|
177
188
|
destination,
|
|
189
|
+
forwardingAddress: evidence.tx.forwardingAddress,
|
|
190
|
+
fullAmount,
|
|
178
191
|
tmpSeat,
|
|
192
|
+
txHash: evidence.txHash,
|
|
179
193
|
});
|
|
180
|
-
} catch (
|
|
181
|
-
log('Advancer error:',
|
|
194
|
+
} catch (error) {
|
|
195
|
+
log('Advancer error:', error);
|
|
182
196
|
statusManager.observe(evidence);
|
|
183
197
|
}
|
|
184
198
|
},
|
|
199
|
+
/** @param {ChainAddress} intermediateRecipient */
|
|
200
|
+
setIntermediateRecipient(intermediateRecipient) {
|
|
201
|
+
this.state.intermediateRecipient = intermediateRecipient;
|
|
202
|
+
},
|
|
185
203
|
},
|
|
186
204
|
depositHandler: {
|
|
187
205
|
/**
|
|
188
206
|
* @param {undefined} result
|
|
189
|
-
* @param {
|
|
207
|
+
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
190
208
|
*/
|
|
191
|
-
onFulfilled(result,
|
|
192
|
-
const { poolAccount } = this.state;
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
209
|
+
onFulfilled(result, ctx) {
|
|
210
|
+
const { poolAccount, intermediateRecipient } = this.state;
|
|
211
|
+
const { destination, advanceAmount, ...detail } = ctx;
|
|
212
|
+
const transferV = E(poolAccount).transfer(
|
|
213
|
+
destination,
|
|
214
|
+
{ denom: usdc.denom, value: advanceAmount.value },
|
|
215
|
+
{ forwardOpts: { intermediateRecipient } },
|
|
216
|
+
);
|
|
197
217
|
return watch(transferV, this.facets.transferHandler, {
|
|
198
218
|
destination,
|
|
199
|
-
|
|
219
|
+
advanceAmount,
|
|
220
|
+
...detail,
|
|
200
221
|
});
|
|
201
222
|
},
|
|
202
223
|
/**
|
|
224
|
+
* We do not expect this to be a common failure. it should only occur
|
|
225
|
+
* if USDC is not registered in vbank or the tmpSeat has less than
|
|
226
|
+
* `advanceAmount`.
|
|
227
|
+
*
|
|
228
|
+
* If we do hit this path, we return funds to the Liquidity Pool and
|
|
229
|
+
* notify of Advancing failure.
|
|
230
|
+
*
|
|
203
231
|
* @param {Error} error
|
|
204
|
-
* @param {
|
|
232
|
+
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
205
233
|
*/
|
|
206
|
-
onRejected(error, { tmpSeat }) {
|
|
207
|
-
// TODO return seat allocation from ctx to LP?
|
|
208
|
-
log('🚨 advance deposit failed', q(error).toString());
|
|
209
|
-
// TODO #10510 (comprehensive error testing) determine
|
|
210
|
-
// course of action here
|
|
234
|
+
onRejected(error, { tmpSeat, advanceAmount, ...restCtx }) {
|
|
211
235
|
log(
|
|
212
|
-
'
|
|
213
|
-
|
|
236
|
+
'⚠️ deposit to localOrchAccount failed, attempting to return payment to LP',
|
|
237
|
+
error,
|
|
214
238
|
);
|
|
239
|
+
try {
|
|
240
|
+
const { borrowerFacet, notifyFacet } = this.state;
|
|
241
|
+
notifyFacet.notifyAdvancingResult(restCtx, false);
|
|
242
|
+
borrowerFacet.returnToPool(tmpSeat, advanceAmount);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
log('🚨 deposit to localOrchAccount failure recovery failed', e);
|
|
245
|
+
}
|
|
215
246
|
},
|
|
216
247
|
},
|
|
217
248
|
transferHandler: {
|
|
218
249
|
/**
|
|
219
|
-
* @param {
|
|
220
|
-
* @param {
|
|
250
|
+
* @param {unknown} result TODO confirm this is not a bigint (sequence)
|
|
251
|
+
* @param {AdvancerVowCtx} ctx
|
|
221
252
|
*/
|
|
222
|
-
onFulfilled(result,
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
log(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
253
|
+
onFulfilled(result, ctx) {
|
|
254
|
+
const { notifyFacet } = this.state;
|
|
255
|
+
const { advanceAmount, destination, ...detail } = ctx;
|
|
256
|
+
log('Advance transfer fulfilled', {
|
|
257
|
+
advanceAmount,
|
|
258
|
+
destination,
|
|
259
|
+
result,
|
|
260
|
+
});
|
|
261
|
+
// During development, due to a bug, this call threw.
|
|
262
|
+
// The failure was silent (no diagnostics) due to:
|
|
263
|
+
// - #10576 Vows do not report unhandled rejections
|
|
264
|
+
// For now, the advancer kit relies on consistency between
|
|
265
|
+
// notifyFacet, statusManager, and callers of handleTransactionEvent().
|
|
266
|
+
// TODO: revisit #10576 during #10510
|
|
267
|
+
notifyFacet.notifyAdvancingResult({ destination, ...detail }, true);
|
|
229
268
|
},
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
/**
|
|
270
|
+
* @param {Error} error
|
|
271
|
+
* @param {AdvancerVowCtx} ctx
|
|
272
|
+
*/
|
|
273
|
+
onRejected(error, ctx) {
|
|
274
|
+
const { notifyFacet } = this.state;
|
|
275
|
+
log('Advance transfer rejected', error);
|
|
276
|
+
notifyFacet.notifyAdvancingResult(ctx, false);
|
|
234
277
|
},
|
|
235
278
|
},
|
|
236
279
|
},
|
|
237
280
|
{
|
|
238
281
|
stateShape: harden({
|
|
282
|
+
notifyFacet: M.remotable(),
|
|
239
283
|
borrowerFacet: M.remotable(),
|
|
240
284
|
poolAccount: M.remotable(),
|
|
285
|
+
intermediateRecipient: M.opt(ChainAddressShape),
|
|
286
|
+
settlementAddress: M.opt(ChainAddressShape),
|
|
241
287
|
}),
|
|
242
288
|
},
|
|
243
289
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AmountMath
|
|
1
|
+
import { AmountMath } from '@agoric/ertp';
|
|
2
2
|
import {
|
|
3
3
|
makeRecorderTopic,
|
|
4
4
|
TopicsRecordShape,
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
* @import {PoolStats} from '../types.js';
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
const { add, isEqual, makeEmpty } = AmountMath;
|
|
31
|
+
const { add, isEqual, isGTE, makeEmpty } = AmountMath;
|
|
32
32
|
|
|
33
33
|
/** @param {Brand} brand */
|
|
34
34
|
const makeDust = brand => AmountMath.make(brand, 1n);
|
|
@@ -84,11 +84,8 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
84
84
|
'Liquidity Pool',
|
|
85
85
|
{
|
|
86
86
|
borrower: M.interface('borrower', {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
SeatShape,
|
|
90
|
-
harden({ USDC: makeNatAmountShape(USDC, 1n) }),
|
|
91
|
-
).returns(),
|
|
87
|
+
borrow: M.call(SeatShape, makeNatAmountShape(USDC, 1n)).returns(),
|
|
88
|
+
returnToPool: M.call(SeatShape, makeNatAmountShape(USDC, 1n)).returns(),
|
|
92
89
|
}),
|
|
93
90
|
repayer: M.interface('repayer', {
|
|
94
91
|
repay: M.call(
|
|
@@ -152,38 +149,50 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
152
149
|
},
|
|
153
150
|
{
|
|
154
151
|
borrower: {
|
|
155
|
-
getBalance() {
|
|
156
|
-
const { poolSeat } = this.state;
|
|
157
|
-
return poolSeat.getAmountAllocated('USDC', USDC);
|
|
158
|
-
},
|
|
159
152
|
/**
|
|
160
153
|
* @param {ZCFSeat} toSeat
|
|
161
|
-
* @param {
|
|
154
|
+
* @param {Amount<'nat'>} amount
|
|
162
155
|
*/
|
|
163
|
-
borrow(toSeat,
|
|
156
|
+
borrow(toSeat, amount) {
|
|
164
157
|
const { encumberedBalance, poolSeat, poolStats } = this.state;
|
|
165
158
|
|
|
166
159
|
// Validate amount is available in pool
|
|
167
160
|
const post = borrowCalc(
|
|
168
|
-
|
|
161
|
+
amount,
|
|
169
162
|
poolSeat.getAmountAllocated('USDC', USDC),
|
|
170
163
|
encumberedBalance,
|
|
171
164
|
poolStats,
|
|
172
165
|
);
|
|
173
166
|
|
|
174
167
|
// COMMIT POINT
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} catch (cause) {
|
|
178
|
-
const reason = Error('🚨 cannot commit borrow', { cause });
|
|
179
|
-
console.error(reason.message, cause);
|
|
180
|
-
zcf.shutdownWithFailure(reason);
|
|
181
|
-
}
|
|
168
|
+
// UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
|
|
169
|
+
zcf.atomicRearrange(harden([[poolSeat, toSeat, { USDC: amount }]]));
|
|
182
170
|
|
|
183
171
|
Object.assign(this.state, post);
|
|
184
172
|
this.facets.external.publishPoolMetrics();
|
|
185
173
|
},
|
|
186
|
-
|
|
174
|
+
/**
|
|
175
|
+
* If something fails during advance, return funds to the pool.
|
|
176
|
+
*
|
|
177
|
+
* @param {ZCFSeat} borrowSeat
|
|
178
|
+
* @param {Amount<'nat'>} amount
|
|
179
|
+
*/
|
|
180
|
+
returnToPool(borrowSeat, amount) {
|
|
181
|
+
const { zcfSeat: repaySeat } = zcf.makeEmptySeatKit();
|
|
182
|
+
const returnAmounts = harden({
|
|
183
|
+
Principal: amount,
|
|
184
|
+
PoolFee: makeEmpty(USDC),
|
|
185
|
+
ContractFee: makeEmpty(USDC),
|
|
186
|
+
});
|
|
187
|
+
const borrowSeatAllocation = borrowSeat.getCurrentAllocation();
|
|
188
|
+
isGTE(borrowSeatAllocation.USDC, amount) ||
|
|
189
|
+
Fail`⚠️ borrowSeatAllocation ${q(borrowSeatAllocation)} less than amountKWR ${q(amount)}`;
|
|
190
|
+
// arrange payments in a format repay is expecting
|
|
191
|
+
zcf.atomicRearrange(
|
|
192
|
+
harden([[borrowSeat, repaySeat, { USDC: amount }, returnAmounts]]),
|
|
193
|
+
);
|
|
194
|
+
return this.facets.repayer.repay(repaySeat, returnAmounts);
|
|
195
|
+
},
|
|
187
196
|
},
|
|
188
197
|
repayer: {
|
|
189
198
|
/**
|
|
@@ -213,23 +222,18 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
213
222
|
const { ContractFee, ...rest } = amounts;
|
|
214
223
|
|
|
215
224
|
// COMMIT POINT
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
} catch (cause) {
|
|
229
|
-
const reason = Error('🚨 cannot commit repay', { cause });
|
|
230
|
-
console.error(reason.message, cause);
|
|
231
|
-
zcf.shutdownWithFailure(reason);
|
|
232
|
-
}
|
|
225
|
+
// UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
|
|
226
|
+
zcf.atomicRearrange(
|
|
227
|
+
harden([
|
|
228
|
+
[
|
|
229
|
+
fromSeat,
|
|
230
|
+
poolSeat,
|
|
231
|
+
rest,
|
|
232
|
+
{ USDC: add(amounts.PoolFee, amounts.Principal) },
|
|
233
|
+
],
|
|
234
|
+
[fromSeat, feeSeat, { ContractFee }, { USDC: ContractFee }],
|
|
235
|
+
]),
|
|
236
|
+
);
|
|
233
237
|
|
|
234
238
|
Object.assign(this.state, post);
|
|
235
239
|
this.facets.external.publishPoolMetrics();
|
|
@@ -264,9 +268,8 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
264
268
|
const post = depositCalc(shareWorth, proposal);
|
|
265
269
|
|
|
266
270
|
// COMMIT POINT
|
|
267
|
-
|
|
271
|
+
const mint = shareMint.mintGains(post.payouts);
|
|
268
272
|
try {
|
|
269
|
-
const mint = shareMint.mintGains(post.payouts);
|
|
270
273
|
this.state.shareWorth = post.shareWorth;
|
|
271
274
|
zcf.atomicRearrange(
|
|
272
275
|
harden([
|
|
@@ -276,12 +279,12 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
276
279
|
[mint, lp, post.payouts],
|
|
277
280
|
]),
|
|
278
281
|
);
|
|
282
|
+
} catch (cause) {
|
|
283
|
+
// UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
|
|
284
|
+
throw new Error('🚨 cannot commit deposit', { cause });
|
|
285
|
+
} finally {
|
|
279
286
|
lp.exit();
|
|
280
287
|
mint.exit();
|
|
281
|
-
} catch (cause) {
|
|
282
|
-
const reason = Error('🚨 cannot commit deposit', { cause });
|
|
283
|
-
console.error(reason.message, cause);
|
|
284
|
-
zcf.shutdownWithFailure(reason);
|
|
285
288
|
}
|
|
286
289
|
external.publishPoolMetrics();
|
|
287
290
|
},
|
|
@@ -301,7 +304,6 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
301
304
|
const post = withdrawCalc(shareWorth, proposal);
|
|
302
305
|
|
|
303
306
|
// COMMIT POINT
|
|
304
|
-
|
|
305
307
|
try {
|
|
306
308
|
this.state.shareWorth = post.shareWorth;
|
|
307
309
|
zcf.atomicRearrange(
|
|
@@ -313,12 +315,12 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
313
315
|
]),
|
|
314
316
|
);
|
|
315
317
|
shareMint.burnLosses(proposal.give, burn);
|
|
318
|
+
} catch (cause) {
|
|
319
|
+
// UNTIL #10684: ability to terminate an incarnation w/o terminating the contract
|
|
320
|
+
throw new Error('🚨 cannot commit withdraw', { cause });
|
|
321
|
+
} finally {
|
|
316
322
|
lp.exit();
|
|
317
323
|
burn.exit();
|
|
318
|
-
} catch (cause) {
|
|
319
|
-
const reason = Error('🚨 cannot commit withdraw', { cause });
|
|
320
|
-
console.error(reason.message, cause);
|
|
321
|
-
zcf.shutdownWithFailure(reason);
|
|
322
324
|
}
|
|
323
325
|
external.publishPoolMetrics();
|
|
324
326
|
},
|
package/src/exos/operator-kit.js
CHANGED
|
@@ -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,
|
|
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)
|
|
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)
|
|
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,14 @@ 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
93
|
// TODO(bootstrap integration): cause this call to throw and confirm that it
|
|
89
94
|
// shows up in the the smart-wallet UpdateRecord `error` property
|
|
90
|
-
|
|
95
|
+
operator.submitEvidence(evidence, riskAssessment);
|
|
91
96
|
return staticPowers.makeInertInvitation(
|
|
92
97
|
'evidence was pushed in the invitation maker call',
|
|
93
98
|
);
|
|
@@ -98,12 +103,13 @@ export const prepareOperatorKit = (zone, staticPowers) =>
|
|
|
98
103
|
* submit evidence from this operator
|
|
99
104
|
*
|
|
100
105
|
* @param {CctpTxEvidence} evidence
|
|
106
|
+
* @param {RiskAssessment} [riskAssessment]
|
|
107
|
+
* @returns {void}
|
|
101
108
|
*/
|
|
102
|
-
|
|
109
|
+
submitEvidence(evidence, riskAssessment = {}) {
|
|
103
110
|
const { state } = this;
|
|
104
111
|
!state.disabled || Fail`submitEvidence for disabled operator`;
|
|
105
|
-
|
|
106
|
-
return result;
|
|
112
|
+
state.powers.attest(evidence, riskAssessment, state.operatorId);
|
|
107
113
|
},
|
|
108
114
|
/** @returns {OperatorStatus} */
|
|
109
115
|
getStatus() {
|