@agoric/fast-usdc 0.2.0-upgrade-19-dev-0754752.0 → 0.2.0-upgrade-18a-dev-4ee0508.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 +25 -26
- package/src/cli/cli.js +1 -1
- package/src/cli/config-commands.js +1 -1
- package/src/cli/config.js +1 -1
- package/src/cli/lp-commands.js +39 -21
- package/src/cli/operator-commands.js +0 -1
- package/src/cli/transfer.js +5 -5
- package/src/exos/advancer.js +41 -109
- package/src/exos/liquidity-pool.js +34 -81
- package/src/exos/operator-kit.js +2 -0
- package/src/exos/settler.js +95 -154
- package/src/exos/status-manager.js +59 -97
- package/src/exos/transaction-feed.js +9 -30
- package/src/fast-usdc-policy.core.js +12 -2
- package/src/fast-usdc.contract.js +31 -23
- package/src/{start-fast-usdc.core.js → fast-usdc.start.js} +81 -13
- package/src/pool-share-math.js +9 -55
- package/src/type-guards.js +8 -37
- package/src/types.ts +5 -34
- package/src/utils/deploy-config.js +75 -36
- package/src/utils/fees.js +4 -3
- package/src/add-operators.core.js +0 -63
- package/src/clientSupport.js +0 -98
- package/src/distribute-fees.core.js +0 -93
- package/src/main.js +0 -1
- package/src/utils/chain-policies.js +0 -140
- package/src/utils/core-eval.js +0 -73
- /package/src/{cli/util → util}/agoric.js +0 -0
- /package/src/{cli/util → util}/bank.js +0 -0
- /package/src/{cli/util → util}/cctp.js +0 -0
- /package/src/{cli/util → util}/file.js +0 -0
- /package/src/{cli/util → util}/noble.js +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { AmountMath
|
|
1
|
+
import { AmountMath } from '@agoric/ertp';
|
|
2
2
|
import {
|
|
3
3
|
makeRecorderTopic,
|
|
4
|
-
RecorderKitShape,
|
|
5
4
|
TopicsRecordShape,
|
|
6
|
-
} from '@agoric/zoe/src/contractSupport/
|
|
5
|
+
} from '@agoric/zoe/src/contractSupport/topics.js';
|
|
7
6
|
import { SeatShape } from '@agoric/zoe/src/typeGuards.js';
|
|
8
7
|
import { M } from '@endo/patterns';
|
|
9
8
|
import { Fail, q } from '@endo/errors';
|
|
10
9
|
import {
|
|
11
10
|
borrowCalc,
|
|
12
|
-
checkPoolBalance,
|
|
13
11
|
depositCalc,
|
|
14
12
|
makeParity,
|
|
15
13
|
repayCalc,
|
|
@@ -22,7 +20,6 @@ import {
|
|
|
22
20
|
} from '../type-guards.js';
|
|
23
21
|
|
|
24
22
|
/**
|
|
25
|
-
* @import {Amount, Brand, Payment} from '@agoric/ertp';
|
|
26
23
|
* @import {Zone} from '@agoric/zone';
|
|
27
24
|
* @import {Remote} from '@agoric/internal'
|
|
28
25
|
* @import {StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
|
|
@@ -31,7 +28,32 @@ import {
|
|
|
31
28
|
* @import {PoolStats} from '../types.js';
|
|
32
29
|
*/
|
|
33
30
|
|
|
34
|
-
const { add, isGTE, makeEmpty } = AmountMath;
|
|
31
|
+
const { add, isEqual, isGTE, 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
|
+
};
|
|
35
57
|
|
|
36
58
|
/**
|
|
37
59
|
* @typedef {{
|
|
@@ -49,22 +71,6 @@ const { add, isGTE, makeEmpty } = AmountMath;
|
|
|
49
71
|
* }} RepayPaymentKWR
|
|
50
72
|
*/
|
|
51
73
|
|
|
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
|
-
|
|
68
74
|
/**
|
|
69
75
|
* @param {Zone} zone
|
|
70
76
|
* @param {ZCF} zcf
|
|
@@ -100,18 +106,11 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
100
106
|
withdrawHandler: M.interface('withdrawHandler', {
|
|
101
107
|
handle: M.call(SeatShape, M.any()).returns(M.promise()),
|
|
102
108
|
}),
|
|
103
|
-
withdrawFeesHandler: M.interface('withdrawFeesHandler', {
|
|
104
|
-
handle: M.call(SeatShape, M.any()).returns(M.promise()),
|
|
105
|
-
}),
|
|
106
109
|
public: M.interface('public', {
|
|
107
110
|
makeDepositInvitation: M.call().returns(M.promise()),
|
|
108
111
|
makeWithdrawInvitation: M.call().returns(M.promise()),
|
|
109
112
|
getPublicTopics: M.call().returns(TopicsRecordShape),
|
|
110
113
|
}),
|
|
111
|
-
feeRecipient: M.interface('feeRecipient', {
|
|
112
|
-
getContractFeeBalance: M.call().returns(AmountShape),
|
|
113
|
-
makeWithdrawFeesInvitation: M.call().returns(M.promise()),
|
|
114
|
-
}),
|
|
115
114
|
},
|
|
116
115
|
/**
|
|
117
116
|
* @param {ZCFMint<'nat'>} shareMint
|
|
@@ -120,7 +119,7 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
120
119
|
(shareMint, node) => {
|
|
121
120
|
const { brand: PoolShares } = shareMint.getIssuerRecord();
|
|
122
121
|
const proposalShapes = makeProposalShapes({ USDC, PoolShares });
|
|
123
|
-
const shareWorth = makeParity(USDC, PoolShares);
|
|
122
|
+
const shareWorth = makeParity(makeDust(USDC), PoolShares);
|
|
124
123
|
const { zcfSeat: poolSeat } = zcf.makeEmptySeatKit();
|
|
125
124
|
const { zcfSeat: feeSeat } = zcf.makeEmptySeatKit();
|
|
126
125
|
const poolMetricsRecorderKit = tools.makeRecorderKit(
|
|
@@ -192,9 +191,7 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
192
191
|
zcf.atomicRearrange(
|
|
193
192
|
harden([[borrowSeat, repaySeat, { USDC: amount }, returnAmounts]]),
|
|
194
193
|
);
|
|
195
|
-
this.facets.repayer.repay(repaySeat, returnAmounts);
|
|
196
|
-
borrowSeat.exit();
|
|
197
|
-
repaySeat.exit();
|
|
194
|
+
return this.facets.repayer.repay(repaySeat, returnAmounts);
|
|
198
195
|
},
|
|
199
196
|
},
|
|
200
197
|
repayer: {
|
|
@@ -210,11 +207,7 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
210
207
|
poolStats,
|
|
211
208
|
shareWorth,
|
|
212
209
|
} = this.state;
|
|
213
|
-
checkPoolBalance(
|
|
214
|
-
poolSeat.getCurrentAllocation(),
|
|
215
|
-
shareWorth,
|
|
216
|
-
encumberedBalance,
|
|
217
|
-
);
|
|
210
|
+
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
218
211
|
|
|
219
212
|
const fromSeatAllocation = fromSeat.getCurrentAllocation();
|
|
220
213
|
// Validate allocation equals amounts and Principal <= encumberedBalance
|
|
@@ -271,11 +264,7 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
271
264
|
/** @type {USDCProposalShapes['deposit']} */
|
|
272
265
|
// @ts-expect-error ensured by proposalShape
|
|
273
266
|
const proposal = lp.getProposal();
|
|
274
|
-
checkPoolBalance(
|
|
275
|
-
poolSeat.getCurrentAllocation(),
|
|
276
|
-
shareWorth,
|
|
277
|
-
encumberedBalance,
|
|
278
|
-
);
|
|
267
|
+
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
279
268
|
const post = depositCalc(shareWorth, proposal);
|
|
280
269
|
|
|
281
270
|
// COMMIT POINT
|
|
@@ -311,12 +300,8 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
311
300
|
// @ts-expect-error ensured by proposalShape
|
|
312
301
|
const proposal = lp.getProposal();
|
|
313
302
|
const { zcfSeat: burn } = zcf.makeEmptySeatKit();
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
proposal,
|
|
317
|
-
poolSeat.getCurrentAllocation(),
|
|
318
|
-
encumberedBalance,
|
|
319
|
-
);
|
|
303
|
+
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
304
|
+
const post = withdrawCalc(shareWorth, proposal);
|
|
320
305
|
|
|
321
306
|
// COMMIT POINT
|
|
322
307
|
try {
|
|
@@ -340,21 +325,6 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
340
325
|
external.publishPoolMetrics();
|
|
341
326
|
},
|
|
342
327
|
},
|
|
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
|
-
},
|
|
358
328
|
public: {
|
|
359
329
|
makeDepositInvitation() {
|
|
360
330
|
return zcf.makeInvitation(
|
|
@@ -382,28 +352,11 @@ export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
|
382
352
|
};
|
|
383
353
|
},
|
|
384
354
|
},
|
|
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
|
-
},
|
|
401
355
|
},
|
|
402
356
|
{
|
|
403
357
|
finish: ({ facets: { external } }) => {
|
|
404
358
|
void external.publishPoolMetrics();
|
|
405
359
|
},
|
|
406
|
-
stateShape,
|
|
407
360
|
},
|
|
408
361
|
);
|
|
409
362
|
};
|
package/src/exos/operator-kit.js
CHANGED
|
@@ -90,6 +90,8 @@ export const prepareOperatorKit = (zone, staticPowers) =>
|
|
|
90
90
|
*/
|
|
91
91
|
async SubmitEvidence(evidence, riskAssessment) {
|
|
92
92
|
const { operator } = this.facets;
|
|
93
|
+
// TODO(bootstrap integration): cause this call to throw and confirm that it
|
|
94
|
+
// shows up in the the smart-wallet UpdateRecord `error` property
|
|
93
95
|
operator.submitEvidence(evidence, riskAssessment);
|
|
94
96
|
return staticPowers.makeInertInvitation(
|
|
95
97
|
'evidence was pushed in the invitation maker call',
|
package/src/exos/settler.js
CHANGED
|
@@ -8,72 +8,20 @@ import { M } from '@endo/patterns';
|
|
|
8
8
|
import { decodeAddressHook } from '@agoric/cosmic-proto/address-hooks.js';
|
|
9
9
|
import { PendingTxStatus } from '../constants.js';
|
|
10
10
|
import { makeFeeTools } from '../utils/fees.js';
|
|
11
|
-
import {
|
|
12
|
-
CctpTxEvidenceShape,
|
|
13
|
-
EvmHashShape,
|
|
14
|
-
makeNatAmountShape,
|
|
15
|
-
} from '../type-guards.js';
|
|
11
|
+
import { EvmHashShape } from '../type-guards.js';
|
|
16
12
|
|
|
17
13
|
/**
|
|
18
14
|
* @import {FungibleTokenPacketData} from '@agoric/cosmic-proto/ibc/applications/transfer/v2/packet.js';
|
|
19
|
-
* @import {Amount, Brand, NatValue, Payment} from '@agoric/ertp';
|
|
20
15
|
* @import {Denom, OrchestrationAccount, ChainHub, ChainAddress} from '@agoric/orchestration';
|
|
21
16
|
* @import {WithdrawToSeat} from '@agoric/orchestration/src/utils/zoe-tools'
|
|
22
|
-
* @import {IBCChannelID,
|
|
17
|
+
* @import {IBCChannelID, VTransferIBCEvent} from '@agoric/vats';
|
|
23
18
|
* @import {Zone} from '@agoric/zone';
|
|
24
19
|
* @import {HostOf, HostInterface} from '@agoric/async-flow';
|
|
25
20
|
* @import {TargetRegistration} from '@agoric/vats/src/bridge-target.js';
|
|
26
|
-
* @import {NobleAddress, LiquidityPoolKit, FeeConfig, EvmHash, LogFn
|
|
21
|
+
* @import {NobleAddress, LiquidityPoolKit, FeeConfig, EvmHash, LogFn} from '../types.js';
|
|
27
22
|
* @import {StatusManager} from './status-manager.js';
|
|
28
23
|
*/
|
|
29
24
|
|
|
30
|
-
/**
|
|
31
|
-
* @param {IBCPacket} data
|
|
32
|
-
* @param {string} remoteDenom
|
|
33
|
-
* @returns {{ nfa: NobleAddress, amount: bigint, EUD: string } | {error: object[]}}
|
|
34
|
-
*/
|
|
35
|
-
const decodeEventPacket = ({ data }, remoteDenom) => {
|
|
36
|
-
// NB: may not be a FungibleTokenPacketData or even JSON
|
|
37
|
-
/** @type {FungibleTokenPacketData} */
|
|
38
|
-
let tx;
|
|
39
|
-
try {
|
|
40
|
-
tx = JSON.parse(atob(data));
|
|
41
|
-
} catch (e) {
|
|
42
|
-
return { error: ['could not parse packet data', data] };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// given the sourceChannel check, we can be certain of this cast
|
|
46
|
-
const nfa = /** @type {NobleAddress} */ (tx.sender);
|
|
47
|
-
|
|
48
|
-
if (tx.denom !== remoteDenom) {
|
|
49
|
-
const { denom: actual } = tx;
|
|
50
|
-
return { error: ['unexpected denom', { actual, expected: remoteDenom }] };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let EUD;
|
|
54
|
-
try {
|
|
55
|
-
({ EUD } = decodeAddressHook(tx.receiver).query);
|
|
56
|
-
if (!EUD) {
|
|
57
|
-
return { error: ['no EUD parameter', tx.receiver] };
|
|
58
|
-
}
|
|
59
|
-
if (typeof EUD !== 'string') {
|
|
60
|
-
return { error: ['EUD is not a string', EUD] };
|
|
61
|
-
}
|
|
62
|
-
} catch (e) {
|
|
63
|
-
return { error: ['no query params', tx.receiver] };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let amount;
|
|
67
|
-
try {
|
|
68
|
-
amount = BigInt(tx.amount);
|
|
69
|
-
} catch (e) {
|
|
70
|
-
return { error: ['invalid amount', tx.amount] };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return { nfa, amount, EUD };
|
|
74
|
-
};
|
|
75
|
-
harden(decodeEventPacket);
|
|
76
|
-
|
|
77
25
|
/**
|
|
78
26
|
* NOTE: not meant to be parsable.
|
|
79
27
|
*
|
|
@@ -83,25 +31,6 @@ harden(decodeEventPacket);
|
|
|
83
31
|
const makeMintedEarlyKey = (addr, amount) =>
|
|
84
32
|
`pendingTx:${JSON.stringify([addr, String(amount)])}`;
|
|
85
33
|
|
|
86
|
-
/** @param {Brand<'nat'>} USDC */
|
|
87
|
-
export const makeAdvanceDetailsShape = USDC =>
|
|
88
|
-
harden({
|
|
89
|
-
destination: ChainAddressShape,
|
|
90
|
-
forwardingAddress: M.string(),
|
|
91
|
-
fullAmount: makeNatAmountShape(USDC),
|
|
92
|
-
txHash: EvmHashShape,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
export const stateShape = harden({
|
|
96
|
-
repayer: M.remotable('Repayer'),
|
|
97
|
-
settlementAccount: M.remotable('Account'),
|
|
98
|
-
registration: M.or(M.undefined(), M.remotable('Registration')),
|
|
99
|
-
sourceChannel: M.string(),
|
|
100
|
-
remoteDenom: M.string(),
|
|
101
|
-
mintedEarly: M.remotable('mintedEarly'),
|
|
102
|
-
intermediateRecipient: M.opt(ChainAddressShape),
|
|
103
|
-
});
|
|
104
|
-
|
|
105
34
|
/**
|
|
106
35
|
* @param {Zone} zone
|
|
107
36
|
* @param {object} caps
|
|
@@ -138,23 +67,26 @@ export const prepareSettler = (
|
|
|
138
67
|
tap: M.interface('SettlerTapI', {
|
|
139
68
|
receiveUpcall: M.call(M.record()).returns(M.promise()),
|
|
140
69
|
}),
|
|
141
|
-
|
|
70
|
+
notify: M.interface('SettlerNotifyI', {
|
|
142
71
|
notifyAdvancingResult: M.call(
|
|
143
|
-
|
|
72
|
+
M.record(), // XXX fill in details TODO
|
|
144
73
|
M.boolean(),
|
|
145
74
|
).returns(),
|
|
146
|
-
checkMintedEarly: M.call(
|
|
147
|
-
CctpTxEvidenceShape,
|
|
148
|
-
ChainAddressShape,
|
|
149
|
-
).returns(M.boolean()),
|
|
150
75
|
}),
|
|
151
76
|
self: M.interface('SettlerSelfI', {
|
|
152
|
-
disburse: M.call(EvmHashShape, M.nat()).returns(
|
|
153
|
-
|
|
77
|
+
disburse: M.call(EvmHashShape, M.string(), M.nat()).returns(
|
|
78
|
+
M.promise(),
|
|
79
|
+
),
|
|
80
|
+
forward: M.call(
|
|
81
|
+
M.opt(EvmHashShape),
|
|
82
|
+
M.string(),
|
|
83
|
+
M.nat(),
|
|
84
|
+
M.string(),
|
|
85
|
+
).returns(),
|
|
154
86
|
}),
|
|
155
87
|
transferHandler: M.interface('SettlerTransferI', {
|
|
156
|
-
onFulfilled: M.call(M.
|
|
157
|
-
onRejected: M.call(M.
|
|
88
|
+
onFulfilled: M.call(M.any(), M.record()).returns(),
|
|
89
|
+
onRejected: M.call(M.any(), M.record()).returns(),
|
|
158
90
|
}),
|
|
159
91
|
},
|
|
160
92
|
/**
|
|
@@ -205,40 +137,61 @@ export const prepareSettler = (
|
|
|
205
137
|
return;
|
|
206
138
|
}
|
|
207
139
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
140
|
+
// TODO: why is it safe to cast this without a runtime check?
|
|
141
|
+
const tx = /** @type {FungibleTokenPacketData} */ (
|
|
142
|
+
JSON.parse(atob(packet.data))
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
// given the sourceChannel check, we can be certain of this cast
|
|
146
|
+
const nfa = /** @type {NobleAddress} */ (tx.sender);
|
|
147
|
+
|
|
148
|
+
if (tx.denom !== remoteDenom) {
|
|
149
|
+
const { denom: actual } = tx;
|
|
150
|
+
log('unexpected denom', { actual, expected: remoteDenom });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let EUD;
|
|
155
|
+
try {
|
|
156
|
+
({ EUD } = decodeAddressHook(tx.receiver).query);
|
|
157
|
+
if (!EUD) {
|
|
158
|
+
log('no EUD parameter', tx.receiver);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (typeof EUD !== 'string') {
|
|
162
|
+
log('EUD is not a string', EUD);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
} catch (e) {
|
|
166
|
+
log('no query params', tx.receiver);
|
|
211
167
|
return;
|
|
212
168
|
}
|
|
213
169
|
|
|
214
|
-
const
|
|
170
|
+
const amount = BigInt(tx.amount); // TODO: what if this throws?
|
|
171
|
+
|
|
215
172
|
const { self } = this.facets;
|
|
216
173
|
const found = statusManager.dequeueStatus(nfa, amount);
|
|
217
174
|
log('dequeued', found, 'for', nfa, amount);
|
|
218
175
|
switch (found?.status) {
|
|
219
176
|
case PendingTxStatus.Advanced:
|
|
220
|
-
return self.disburse(found.txHash, amount);
|
|
177
|
+
return self.disburse(found.txHash, nfa, amount);
|
|
221
178
|
|
|
222
179
|
case PendingTxStatus.Advancing:
|
|
223
|
-
log('⚠️ tap: minted while advancing', nfa, amount);
|
|
224
180
|
this.state.mintedEarly.add(makeMintedEarlyKey(nfa, amount));
|
|
225
181
|
return;
|
|
226
182
|
|
|
227
183
|
case PendingTxStatus.Observed:
|
|
228
184
|
case PendingTxStatus.AdvanceSkipped:
|
|
229
185
|
case PendingTxStatus.AdvanceFailed:
|
|
230
|
-
return self.forward(found.txHash, amount, EUD);
|
|
186
|
+
return self.forward(found.txHash, nfa, amount, EUD);
|
|
231
187
|
|
|
232
188
|
case undefined:
|
|
233
189
|
default:
|
|
234
|
-
log('⚠️ tap:
|
|
235
|
-
// XXX consider capturing in vstorage
|
|
236
|
-
// we would need a new key, as this does not have a txHash
|
|
237
|
-
this.state.mintedEarly.add(makeMintedEarlyKey(nfa, amount));
|
|
190
|
+
log('⚠️ tap: no status for ', nfa, amount);
|
|
238
191
|
}
|
|
239
192
|
},
|
|
240
193
|
},
|
|
241
|
-
|
|
194
|
+
notify: {
|
|
242
195
|
/**
|
|
243
196
|
* @param {object} ctx
|
|
244
197
|
* @param {EvmHash} ctx.txHash
|
|
@@ -257,12 +210,16 @@ export const prepareSettler = (
|
|
|
257
210
|
const key = makeMintedEarlyKey(forwardingAddress, fullValue);
|
|
258
211
|
if (mintedEarly.has(key)) {
|
|
259
212
|
mintedEarly.delete(key);
|
|
260
|
-
statusManager.advanceOutcomeForMintedEarly(txHash, success);
|
|
261
213
|
if (success) {
|
|
262
|
-
void this.facets.self.disburse(
|
|
214
|
+
void this.facets.self.disburse(
|
|
215
|
+
txHash,
|
|
216
|
+
forwardingAddress,
|
|
217
|
+
fullValue,
|
|
218
|
+
);
|
|
263
219
|
} else {
|
|
264
220
|
void this.facets.self.forward(
|
|
265
221
|
txHash,
|
|
222
|
+
forwardingAddress,
|
|
266
223
|
fullValue,
|
|
267
224
|
destination.value,
|
|
268
225
|
);
|
|
@@ -271,39 +228,14 @@ export const prepareSettler = (
|
|
|
271
228
|
statusManager.advanceOutcome(forwardingAddress, fullValue, success);
|
|
272
229
|
}
|
|
273
230
|
},
|
|
274
|
-
/**
|
|
275
|
-
* @param {CctpTxEvidence} evidence
|
|
276
|
-
* @param {ChainAddress} destination
|
|
277
|
-
* @returns {boolean}
|
|
278
|
-
* @throws {Error} if minted early, so advancer doesn't advance
|
|
279
|
-
*/
|
|
280
|
-
checkMintedEarly(evidence, destination) {
|
|
281
|
-
const {
|
|
282
|
-
tx: { forwardingAddress, amount },
|
|
283
|
-
txHash,
|
|
284
|
-
} = evidence;
|
|
285
|
-
const key = makeMintedEarlyKey(forwardingAddress, amount);
|
|
286
|
-
const { mintedEarly } = this.state;
|
|
287
|
-
if (mintedEarly.has(key)) {
|
|
288
|
-
log(
|
|
289
|
-
'matched minted early key, initiating forward',
|
|
290
|
-
forwardingAddress,
|
|
291
|
-
amount,
|
|
292
|
-
);
|
|
293
|
-
mintedEarly.delete(key);
|
|
294
|
-
statusManager.advanceOutcomeForUnknownMint(evidence);
|
|
295
|
-
void this.facets.self.forward(txHash, amount, destination.value);
|
|
296
|
-
return true;
|
|
297
|
-
}
|
|
298
|
-
return false;
|
|
299
|
-
},
|
|
300
231
|
},
|
|
301
232
|
self: {
|
|
302
233
|
/**
|
|
303
234
|
* @param {EvmHash} txHash
|
|
235
|
+
* @param {NobleAddress} nfa
|
|
304
236
|
* @param {NatValue} fullValue
|
|
305
237
|
*/
|
|
306
|
-
async disburse(txHash, fullValue) {
|
|
238
|
+
async disburse(txHash, nfa, fullValue) {
|
|
307
239
|
const { repayer, settlementAccount } = this.state;
|
|
308
240
|
const received = AmountMath.make(USDC, fullValue);
|
|
309
241
|
const { zcfSeat: settlingSeat } = zcf.makeEmptySeatKit();
|
|
@@ -327,67 +259,76 @@ export const prepareSettler = (
|
|
|
327
259
|
harden([[settlingSeat, settlingSeat, { In: received }, split]]),
|
|
328
260
|
);
|
|
329
261
|
repayer.repay(settlingSeat, split);
|
|
330
|
-
settlingSeat.exit();
|
|
331
262
|
|
|
332
|
-
// update status manager, marking tx `
|
|
263
|
+
// update status manager, marking tx `SETTLED`
|
|
333
264
|
statusManager.disbursed(txHash, split);
|
|
334
265
|
},
|
|
335
266
|
/**
|
|
336
267
|
* @param {EvmHash} txHash
|
|
268
|
+
* @param {NobleAddress} nfa
|
|
337
269
|
* @param {NatValue} fullValue
|
|
338
270
|
* @param {string} EUD
|
|
339
271
|
*/
|
|
340
|
-
forward(txHash, fullValue, EUD) {
|
|
272
|
+
forward(txHash, nfa, fullValue, EUD) {
|
|
341
273
|
const { settlementAccount, intermediateRecipient } = this.state;
|
|
342
274
|
|
|
343
|
-
const dest = (
|
|
344
|
-
try {
|
|
345
|
-
return chainHub.makeChainAddress(EUD);
|
|
346
|
-
} catch (e) {
|
|
347
|
-
log('⚠️ forward transfer failed!', e, txHash);
|
|
348
|
-
statusManager.forwarded(txHash, false);
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
351
|
-
})();
|
|
352
|
-
if (!dest) return;
|
|
275
|
+
const dest = chainHub.makeChainAddress(EUD);
|
|
353
276
|
|
|
277
|
+
// TODO? statusManager.forwarding(txHash, sender, amount);
|
|
354
278
|
const txfrV = E(settlementAccount).transfer(
|
|
355
279
|
dest,
|
|
356
280
|
AmountMath.make(USDC, fullValue),
|
|
357
281
|
{ forwardOpts: { intermediateRecipient } },
|
|
358
282
|
);
|
|
359
|
-
void vowTools.watch(txfrV, this.facets.transferHandler,
|
|
283
|
+
void vowTools.watch(txfrV, this.facets.transferHandler, {
|
|
284
|
+
txHash,
|
|
285
|
+
nfa,
|
|
286
|
+
fullValue,
|
|
287
|
+
});
|
|
360
288
|
},
|
|
361
289
|
},
|
|
362
290
|
transferHandler: {
|
|
363
291
|
/**
|
|
364
292
|
* @param {unknown} _result
|
|
365
|
-
* @param {
|
|
293
|
+
* @param {SettlerTransferCtx} ctx
|
|
294
|
+
*
|
|
295
|
+
* @typedef {{
|
|
296
|
+
* txHash: EvmHash;
|
|
297
|
+
* nfa: NobleAddress;
|
|
298
|
+
* fullValue: NatValue;
|
|
299
|
+
* }} SettlerTransferCtx
|
|
366
300
|
*/
|
|
367
|
-
onFulfilled(_result,
|
|
368
|
-
|
|
369
|
-
statusManager.forwarded(txHash,
|
|
301
|
+
onFulfilled(_result, ctx) {
|
|
302
|
+
const { txHash, nfa, fullValue } = ctx;
|
|
303
|
+
statusManager.forwarded(txHash, nfa, fullValue);
|
|
370
304
|
},
|
|
371
305
|
/**
|
|
372
306
|
* @param {unknown} reason
|
|
373
|
-
* @param {
|
|
307
|
+
* @param {SettlerTransferCtx} ctx
|
|
374
308
|
*/
|
|
375
|
-
onRejected(reason,
|
|
376
|
-
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
// update status manager, flagging a terminal state that needs to be
|
|
380
|
-
// manual intervention or a code update to remediate
|
|
381
|
-
statusManager.forwarded(txHash, false);
|
|
309
|
+
onRejected(reason, ctx) {
|
|
310
|
+
log('⚠️ transfer rejected!', reason, ctx);
|
|
311
|
+
// const { txHash, nfa, amount } = ctx;
|
|
312
|
+
// TODO(#10510): statusManager.forwardFailed(txHash, nfa, amount);
|
|
382
313
|
},
|
|
383
314
|
},
|
|
384
315
|
},
|
|
385
316
|
{
|
|
386
|
-
stateShape
|
|
317
|
+
stateShape: harden({
|
|
318
|
+
repayer: M.remotable('Repayer'),
|
|
319
|
+
settlementAccount: M.remotable('Account'),
|
|
320
|
+
registration: M.or(M.undefined(), M.remotable('Registration')),
|
|
321
|
+
sourceChannel: M.string(),
|
|
322
|
+
remoteDenom: M.string(),
|
|
323
|
+
mintedEarly: M.remotable('mintedEarly'),
|
|
324
|
+
intermediateRecipient: M.opt(ChainAddressShape),
|
|
325
|
+
}),
|
|
387
326
|
},
|
|
388
327
|
);
|
|
389
328
|
};
|
|
390
329
|
harden(prepareSettler);
|
|
391
330
|
|
|
392
|
-
|
|
393
|
-
|
|
331
|
+
/**
|
|
332
|
+
* XXX consider using pickFacet (do we have pickFacets?)
|
|
333
|
+
* @typedef {ReturnType<ReturnType<typeof prepareSettler>>} SettlerKit
|
|
334
|
+
*/
|