@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.
- package/README.md +43 -0
- package/package.json +29 -25
- package/src/add-operators.core.js +63 -0
- package/src/cli/bridge-action.js +40 -0
- package/src/cli/cli.js +46 -153
- package/src/cli/config-commands.js +108 -0
- package/src/cli/config.js +15 -9
- package/src/cli/lp-commands.js +160 -0
- package/src/cli/operator-commands.js +146 -0
- package/src/cli/transfer.js +63 -13
- package/src/{util → cli/util}/agoric.js +1 -1
- package/src/cli/util/bank.js +12 -0
- package/src/{util → cli/util}/cctp.js +1 -1
- package/src/{util → cli/util}/file.js +1 -1
- package/src/clientSupport.js +98 -0
- package/src/constants.js +25 -2
- package/src/distribute-fees.core.js +93 -0
- package/src/exos/advancer.js +220 -106
- package/src/exos/liquidity-pool.js +130 -81
- package/src/exos/operator-kit.js +16 -12
- package/src/exos/settler.js +360 -64
- package/src/exos/status-manager.js +316 -65
- package/src/exos/transaction-feed.js +121 -42
- package/src/fast-usdc-policy.core.js +65 -0
- package/src/fast-usdc.contract.js +165 -84
- package/src/fast-usdc.flows.js +10 -0
- package/src/main.js +1 -0
- package/src/pool-share-math.js +55 -9
- package/src/{fast-usdc.start.js → start-fast-usdc.core.js} +48 -86
- package/src/type-guards.js +75 -24
- package/src/types.ts +89 -14
- package/src/utils/chain-policies.js +140 -0
- package/src/utils/core-eval.js +73 -0
- package/src/utils/deploy-config.js +127 -0
- package/src/utils/fees.js +3 -4
- 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/{util → cli/util}/noble.js +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/** @file core eval module to collect fees. */
|
|
2
|
+
import { AmountMath } from '@agoric/ertp';
|
|
3
|
+
import { floorMultiplyBy } from '@agoric/zoe/src/contractSupport/index.js';
|
|
4
|
+
import { E } from '@endo/far';
|
|
5
|
+
import { makeTracer } from '@agoric/internal';
|
|
6
|
+
import { fromExternalConfig } from './utils/config-marshal.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @import {DepositFacet} from '@agoric/ertp';
|
|
10
|
+
* @import {FastUSDCCorePowers} from '@agoric/fast-usdc/src/start-fast-usdc.core.js';
|
|
11
|
+
* @import {CopyRecord} from '@endo/pass-style'
|
|
12
|
+
* @import {BootstrapManifestPermit} from '@agoric/vats/src/core/lib-boot.js'
|
|
13
|
+
* @import {LegibleCapData} from './utils/config-marshal.js'
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {{ destinationAddress: string } &
|
|
18
|
+
* ({ feePortion: Ratio} | {fixedFees: Amount<'nat'>}) &
|
|
19
|
+
* CopyRecord
|
|
20
|
+
* } FeeDistributionTerms
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const kwUSDC = 'USDC'; // keyword in AmountKeywordRecord
|
|
24
|
+
const issUSDC = 'USDC'; // issuer name
|
|
25
|
+
|
|
26
|
+
const trace = makeTracer('FUCF', true);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {BootstrapPowers & FastUSDCCorePowers } permittedPowers
|
|
30
|
+
* @param {{ options: LegibleCapData<{ feeTerms: FeeDistributionTerms}> }} config
|
|
31
|
+
*/
|
|
32
|
+
export const distributeFees = async (permittedPowers, config) => {
|
|
33
|
+
trace('distributeFees...', config.options);
|
|
34
|
+
|
|
35
|
+
const { agoricNames, namesByAddress, zoe } = permittedPowers.consume;
|
|
36
|
+
/** @type {Brand<'nat'>} */
|
|
37
|
+
const usdcBrand = await E(agoricNames).lookup('brand', issUSDC);
|
|
38
|
+
/** @type {{ feeTerms: FeeDistributionTerms}} */
|
|
39
|
+
const { feeTerms: terms } = fromExternalConfig(config.options, {
|
|
40
|
+
USDC: usdcBrand,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const { creatorFacet } = await permittedPowers.consume.fastUsdcKit;
|
|
44
|
+
const want = {
|
|
45
|
+
[kwUSDC]: await ('fixedFees' in terms
|
|
46
|
+
? terms.fixedFees
|
|
47
|
+
: E(creatorFacet)
|
|
48
|
+
.getContractFeeBalance()
|
|
49
|
+
.then(balance => floorMultiplyBy(balance, terms.feePortion))),
|
|
50
|
+
};
|
|
51
|
+
const proposal = harden({ want });
|
|
52
|
+
|
|
53
|
+
/** @type {DepositFacet} */
|
|
54
|
+
const depositFacet = await E(namesByAddress).lookup(
|
|
55
|
+
terms.destinationAddress,
|
|
56
|
+
'depositFacet',
|
|
57
|
+
);
|
|
58
|
+
trace('to:', terms.destinationAddress, depositFacet);
|
|
59
|
+
|
|
60
|
+
const toWithdraw = await E(creatorFacet).makeWithdrawFeesInvitation();
|
|
61
|
+
trace('invitation:', toWithdraw, 'proposal:', proposal);
|
|
62
|
+
const seat = E(zoe).offer(toWithdraw, proposal);
|
|
63
|
+
const result = await E(seat).getOfferResult();
|
|
64
|
+
trace('offer result', result);
|
|
65
|
+
const payout = await E(seat).getPayout(kwUSDC);
|
|
66
|
+
/** @type {Amount<'nat'>} */
|
|
67
|
+
// @ts-expect-error USDC is a nat brand
|
|
68
|
+
const rxd = await E(depositFacet).receive(payout);
|
|
69
|
+
trace('received', rxd);
|
|
70
|
+
if (!AmountMath.isGTE(rxd, proposal.want[kwUSDC])) {
|
|
71
|
+
trace('🚨 expected', proposal.want[kwUSDC], 'got', rxd);
|
|
72
|
+
}
|
|
73
|
+
trace('done');
|
|
74
|
+
};
|
|
75
|
+
harden(distributeFees);
|
|
76
|
+
|
|
77
|
+
/** @satisfies {BootstrapManifestPermit} */
|
|
78
|
+
const permit = {
|
|
79
|
+
consume: {
|
|
80
|
+
fastUsdcKit: true,
|
|
81
|
+
agoricNames: true,
|
|
82
|
+
namesByAddress: true,
|
|
83
|
+
zoe: true,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @param {unknown} _utils
|
|
89
|
+
* @param {Parameters<typeof distributeFees>[1]} config
|
|
90
|
+
*/
|
|
91
|
+
export const getManifestForDistributeFees = (_utils, { options }) => {
|
|
92
|
+
return { manifest: { [distributeFees.name]: permit }, options };
|
|
93
|
+
};
|
package/src/exos/advancer.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
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 {Amount, Brand} from '@agoric/ertp';
|
|
20
|
+
* @import {TypedPattern} from '@agoric/internal'
|
|
17
21
|
* @import {NatAmount} from '@agoric/ertp';
|
|
18
22
|
* @import {ChainAddress, ChainHub, Denom, OrchestrationAccount} from '@agoric/orchestration';
|
|
19
23
|
* @import {ZoeTools} from '@agoric/orchestration/src/utils/zoe-tools.js';
|
|
20
24
|
* @import {VowTools} from '@agoric/vow';
|
|
21
25
|
* @import {Zone} from '@agoric/zone';
|
|
22
|
-
* @import {
|
|
26
|
+
* @import {AddressHook, EvmHash, FeeConfig, LogFn, NobleAddress, EvidenceWithRisk} from '../types.js';
|
|
23
27
|
* @import {StatusManager} from './status-manager.js';
|
|
24
28
|
* @import {LiquidityPoolKit} from './liquidity-pool.js';
|
|
25
29
|
*/
|
|
@@ -28,45 +32,73 @@ const { isGTE } = AmountMath;
|
|
|
28
32
|
* @typedef {{
|
|
29
33
|
* chainHub: ChainHub;
|
|
30
34
|
* feeConfig: FeeConfig;
|
|
31
|
-
* localTransfer: ZoeTools['localTransfer'];
|
|
32
35
|
* log?: LogFn;
|
|
33
36
|
* statusManager: StatusManager;
|
|
34
37
|
* usdc: { brand: Brand<'nat'>; denom: Denom; };
|
|
35
38
|
* vowTools: VowTools;
|
|
36
39
|
* zcf: ZCF;
|
|
40
|
+
* zoeTools: ZoeTools;
|
|
37
41
|
* }} AdvancerKitPowers
|
|
38
42
|
*/
|
|
39
43
|
|
|
44
|
+
/** @type {TypedPattern<AdvancerVowCtx>} */
|
|
45
|
+
const AdvancerVowCtxShape = M.splitRecord(
|
|
46
|
+
{
|
|
47
|
+
fullAmount: AnyNatAmountShape,
|
|
48
|
+
advanceAmount: AnyNatAmountShape,
|
|
49
|
+
destination: ChainAddressShape,
|
|
50
|
+
forwardingAddress: M.string(),
|
|
51
|
+
txHash: EvmHashShape,
|
|
52
|
+
},
|
|
53
|
+
{ tmpSeat: M.remotable() },
|
|
54
|
+
);
|
|
55
|
+
|
|
40
56
|
/** type guards internal to the AdvancerKit */
|
|
41
57
|
const AdvancerKitI = harden({
|
|
42
58
|
advancer: M.interface('AdvancerI', {
|
|
43
|
-
handleTransactionEvent: M.callWhen(
|
|
59
|
+
handleTransactionEvent: M.callWhen(EvidenceWithRiskShape).returns(),
|
|
60
|
+
setIntermediateRecipient: M.call(ChainAddressShape).returns(),
|
|
44
61
|
}),
|
|
45
62
|
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(),
|
|
63
|
+
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(VowShape),
|
|
64
|
+
onRejected: M.call(M.error(), AdvancerVowCtxShape).returns(),
|
|
56
65
|
}),
|
|
57
66
|
transferHandler: M.interface('TransferHandlerI', {
|
|
58
|
-
|
|
67
|
+
onFulfilled: M.call(M.undefined(), AdvancerVowCtxShape).returns(
|
|
68
|
+
M.undefined(),
|
|
69
|
+
),
|
|
70
|
+
onRejected: M.call(M.error(), AdvancerVowCtxShape).returns(M.undefined()),
|
|
71
|
+
}),
|
|
72
|
+
withdrawHandler: M.interface('WithdrawHandlerI', {
|
|
59
73
|
onFulfilled: M.call(M.undefined(), {
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
advanceAmount: AnyNatAmountShape,
|
|
75
|
+
tmpReturnSeat: M.remotable(),
|
|
62
76
|
}).returns(M.undefined()),
|
|
63
77
|
onRejected: M.call(M.error(), {
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
advanceAmount: AnyNatAmountShape,
|
|
79
|
+
tmpReturnSeat: M.remotable(),
|
|
66
80
|
}).returns(M.undefined()),
|
|
67
81
|
}),
|
|
68
82
|
});
|
|
69
83
|
|
|
84
|
+
/**
|
|
85
|
+
* @typedef {{
|
|
86
|
+
* fullAmount: NatAmount;
|
|
87
|
+
* advanceAmount: NatAmount;
|
|
88
|
+
* destination: ChainAddress;
|
|
89
|
+
* forwardingAddress: NobleAddress;
|
|
90
|
+
* txHash: EvmHash;
|
|
91
|
+
* }} AdvancerVowCtx
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
export const stateShape = harden({
|
|
95
|
+
notifier: M.remotable(),
|
|
96
|
+
borrower: M.remotable(),
|
|
97
|
+
poolAccount: M.remotable(),
|
|
98
|
+
intermediateRecipient: M.opt(ChainAddressShape),
|
|
99
|
+
settlementAddress: M.opt(ChainAddressShape),
|
|
100
|
+
});
|
|
101
|
+
|
|
70
102
|
/**
|
|
71
103
|
* @param {Zone} zone
|
|
72
104
|
* @param {AdvancerKitPowers} caps
|
|
@@ -76,13 +108,13 @@ export const prepareAdvancerKit = (
|
|
|
76
108
|
{
|
|
77
109
|
chainHub,
|
|
78
110
|
feeConfig,
|
|
79
|
-
localTransfer,
|
|
80
111
|
log = makeTracer('Advancer', true),
|
|
81
112
|
statusManager,
|
|
82
113
|
usdc,
|
|
83
114
|
vowTools: { watch, when },
|
|
84
115
|
zcf,
|
|
85
|
-
|
|
116
|
+
zoeTools: { localTransfer, withdrawToSeat },
|
|
117
|
+
},
|
|
86
118
|
) => {
|
|
87
119
|
assertAllDefined({
|
|
88
120
|
chainHub,
|
|
@@ -100,11 +132,19 @@ export const prepareAdvancerKit = (
|
|
|
100
132
|
AdvancerKitI,
|
|
101
133
|
/**
|
|
102
134
|
* @param {{
|
|
103
|
-
*
|
|
135
|
+
* notifier: import('./settler.js').SettlerKit['notifier'];
|
|
136
|
+
* borrower: LiquidityPoolKit['borrower'];
|
|
104
137
|
* poolAccount: HostInterface<OrchestrationAccount<{chainId: 'agoric'}>>;
|
|
138
|
+
* settlementAddress: ChainAddress;
|
|
139
|
+
* intermediateRecipient?: ChainAddress;
|
|
105
140
|
* }} config
|
|
106
141
|
*/
|
|
107
|
-
config =>
|
|
142
|
+
config =>
|
|
143
|
+
harden({
|
|
144
|
+
...config,
|
|
145
|
+
// make sure the state record has this property, perhaps with an undefined value
|
|
146
|
+
intermediateRecipient: config.intermediateRecipient,
|
|
147
|
+
}),
|
|
108
148
|
{
|
|
109
149
|
advancer: {
|
|
110
150
|
/**
|
|
@@ -115,130 +155,204 @@ export const prepareAdvancerKit = (
|
|
|
115
155
|
* `StatusManager` - so we don't need to concern ourselves with
|
|
116
156
|
* preserving the vow chain for callers.
|
|
117
157
|
*
|
|
118
|
-
* @param {
|
|
158
|
+
* @param {EvidenceWithRisk} evidenceWithRisk
|
|
119
159
|
*/
|
|
120
|
-
async handleTransactionEvent(evidence) {
|
|
160
|
+
async handleTransactionEvent({ evidence, risk }) {
|
|
121
161
|
await null;
|
|
122
162
|
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);
|
|
163
|
+
if (statusManager.hasBeenObserved(evidence)) {
|
|
164
|
+
log('txHash already seen:', evidence.txHash);
|
|
143
165
|
return;
|
|
144
166
|
}
|
|
145
167
|
|
|
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());
|
|
168
|
+
if (risk.risksIdentified?.length) {
|
|
169
|
+
log('risks identified, skipping advance');
|
|
170
|
+
statusManager.skipAdvance(evidence, risk.risksIdentified);
|
|
154
171
|
return;
|
|
155
172
|
}
|
|
173
|
+
const { settlementAddress } = this.state;
|
|
174
|
+
const { recipientAddress } = evidence.aux;
|
|
175
|
+
const decoded = decodeAddressHook(recipientAddress);
|
|
176
|
+
mustMatch(decoded, AddressHookShape);
|
|
177
|
+
if (decoded.baseAddress !== settlementAddress.value) {
|
|
178
|
+
throw Fail`⚠️ baseAddress of address hook ${q(decoded.baseAddress)} does not match the expected address ${q(settlementAddress.value)}`;
|
|
179
|
+
}
|
|
180
|
+
const { EUD } = /** @type {AddressHook['query']} */ (decoded.query);
|
|
181
|
+
log(`decoded EUD: ${EUD}`);
|
|
182
|
+
// throws if the bech32 prefix is not found
|
|
183
|
+
const destination = chainHub.makeChainAddress(EUD);
|
|
184
|
+
|
|
185
|
+
const fullAmount = toAmount(evidence.tx.amount);
|
|
186
|
+
const { borrower, notifier, poolAccount } = this.state;
|
|
187
|
+
// do not advance if we've already received a mint/settlement
|
|
188
|
+
const mintedEarly = notifier.checkMintedEarly(
|
|
189
|
+
evidence,
|
|
190
|
+
destination,
|
|
191
|
+
);
|
|
192
|
+
if (mintedEarly) return;
|
|
193
|
+
|
|
194
|
+
// throws if requested does not exceed fees
|
|
195
|
+
const advanceAmount = feeTools.calculateAdvance(fullAmount);
|
|
156
196
|
|
|
157
197
|
const { zcfSeat: tmpSeat } = zcf.makeEmptySeatKit();
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// between .getBalance() and .borrow().
|
|
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());
|
|
167
|
-
}
|
|
198
|
+
// throws if the pool has insufficient funds
|
|
199
|
+
borrower.borrow(tmpSeat, advanceAmount);
|
|
200
|
+
|
|
201
|
+
// this cannot throw since `.isSeen()` is called in the same turn
|
|
202
|
+
statusManager.advance(evidence);
|
|
168
203
|
|
|
169
204
|
const depositV = localTransfer(
|
|
170
205
|
tmpSeat,
|
|
171
206
|
// @ts-expect-error LocalAccountMethods vs OrchestrationAccount
|
|
172
207
|
poolAccount,
|
|
173
|
-
|
|
208
|
+
harden({ USDC: advanceAmount }),
|
|
174
209
|
);
|
|
175
210
|
void watch(depositV, this.facets.depositHandler, {
|
|
176
|
-
|
|
211
|
+
advanceAmount,
|
|
177
212
|
destination,
|
|
213
|
+
forwardingAddress: evidence.tx.forwardingAddress,
|
|
214
|
+
fullAmount,
|
|
178
215
|
tmpSeat,
|
|
216
|
+
txHash: evidence.txHash,
|
|
179
217
|
});
|
|
180
|
-
} catch (
|
|
181
|
-
log('Advancer error:',
|
|
182
|
-
statusManager.
|
|
218
|
+
} catch (error) {
|
|
219
|
+
log('Advancer error:', error);
|
|
220
|
+
statusManager.skipAdvance(evidence, [error.message]);
|
|
183
221
|
}
|
|
184
222
|
},
|
|
223
|
+
/** @param {ChainAddress} intermediateRecipient */
|
|
224
|
+
setIntermediateRecipient(intermediateRecipient) {
|
|
225
|
+
this.state.intermediateRecipient = intermediateRecipient;
|
|
226
|
+
},
|
|
185
227
|
},
|
|
186
228
|
depositHandler: {
|
|
187
229
|
/**
|
|
188
230
|
* @param {undefined} result
|
|
189
|
-
* @param {
|
|
231
|
+
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
190
232
|
*/
|
|
191
|
-
onFulfilled(result,
|
|
192
|
-
const { poolAccount } =
|
|
193
|
-
|
|
233
|
+
onFulfilled(result, ctx) {
|
|
234
|
+
const { poolAccount, intermediateRecipient, settlementAddress } =
|
|
235
|
+
this.state;
|
|
236
|
+
const { destination, advanceAmount, tmpSeat, ...detail } = ctx;
|
|
237
|
+
tmpSeat.exit();
|
|
238
|
+
const amount = harden({
|
|
194
239
|
denom: usdc.denom,
|
|
195
|
-
value:
|
|
240
|
+
value: advanceAmount.value,
|
|
196
241
|
});
|
|
197
|
-
|
|
242
|
+
const transferOrSendV =
|
|
243
|
+
destination.chainId === settlementAddress.chainId
|
|
244
|
+
? E(poolAccount).send(destination, amount)
|
|
245
|
+
: E(poolAccount).transfer(destination, amount, {
|
|
246
|
+
forwardOpts: { intermediateRecipient },
|
|
247
|
+
});
|
|
248
|
+
return watch(transferOrSendV, this.facets.transferHandler, {
|
|
198
249
|
destination,
|
|
199
|
-
|
|
250
|
+
advanceAmount,
|
|
251
|
+
...detail,
|
|
200
252
|
});
|
|
201
253
|
},
|
|
202
254
|
/**
|
|
255
|
+
* We do not expect this to be a common failure. it should only occur
|
|
256
|
+
* if USDC is not registered in vbank or the tmpSeat has less than
|
|
257
|
+
* `advanceAmount`.
|
|
258
|
+
*
|
|
259
|
+
* If we do hit this path, we return funds to the Liquidity Pool and
|
|
260
|
+
* notify of Advancing failure.
|
|
261
|
+
*
|
|
203
262
|
* @param {Error} error
|
|
204
|
-
* @param {
|
|
263
|
+
* @param {AdvancerVowCtx & { tmpSeat: ZCFSeat }} ctx
|
|
205
264
|
*/
|
|
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
|
|
265
|
+
onRejected(error, { tmpSeat, advanceAmount, ...restCtx }) {
|
|
211
266
|
log(
|
|
212
|
-
'
|
|
213
|
-
|
|
267
|
+
'⚠️ deposit to localOrchAccount failed, attempting to return payment to LP',
|
|
268
|
+
error,
|
|
214
269
|
);
|
|
270
|
+
try {
|
|
271
|
+
const { borrower, notifier } = this.state;
|
|
272
|
+
notifier.notifyAdvancingResult(restCtx, false);
|
|
273
|
+
borrower.returnToPool(tmpSeat, advanceAmount);
|
|
274
|
+
tmpSeat.exit();
|
|
275
|
+
} catch (e) {
|
|
276
|
+
log('🚨 deposit to localOrchAccount failure recovery failed', e);
|
|
277
|
+
}
|
|
215
278
|
},
|
|
216
279
|
},
|
|
217
280
|
transferHandler: {
|
|
218
281
|
/**
|
|
219
|
-
* @param {undefined} result
|
|
220
|
-
* @param {
|
|
282
|
+
* @param {undefined} result
|
|
283
|
+
* @param {AdvancerVowCtx} ctx
|
|
221
284
|
*/
|
|
222
|
-
onFulfilled(result,
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
log(
|
|
226
|
-
|
|
227
|
-
|
|
285
|
+
onFulfilled(result, ctx) {
|
|
286
|
+
const { notifier } = this.state;
|
|
287
|
+
const { advanceAmount, destination, ...detail } = ctx;
|
|
288
|
+
log('Advance succeeded', { advanceAmount, destination });
|
|
289
|
+
// During development, due to a bug, this call threw.
|
|
290
|
+
// The failure was silent (no diagnostics) due to:
|
|
291
|
+
// - #10576 Vows do not report unhandled rejections
|
|
292
|
+
// For now, the advancer kit relies on consistency between
|
|
293
|
+
// notify, statusManager, and callers of handleTransactionEvent().
|
|
294
|
+
// TODO: revisit #10576 during #10510
|
|
295
|
+
notifier.notifyAdvancingResult({ destination, ...detail }, true);
|
|
296
|
+
},
|
|
297
|
+
/**
|
|
298
|
+
* @param {Error} error
|
|
299
|
+
* @param {AdvancerVowCtx} ctx
|
|
300
|
+
*/
|
|
301
|
+
onRejected(error, ctx) {
|
|
302
|
+
const { notifier, poolAccount } = this.state;
|
|
303
|
+
log('Advance failed', error);
|
|
304
|
+
const { advanceAmount, ...restCtx } = ctx;
|
|
305
|
+
notifier.notifyAdvancingResult(restCtx, false);
|
|
306
|
+
const { zcfSeat: tmpReturnSeat } = zcf.makeEmptySeatKit();
|
|
307
|
+
const withdrawV = withdrawToSeat(
|
|
308
|
+
// @ts-expect-error LocalAccountMethods vs OrchestrationAccount
|
|
309
|
+
poolAccount,
|
|
310
|
+
tmpReturnSeat,
|
|
311
|
+
harden({ USDC: advanceAmount }),
|
|
228
312
|
);
|
|
313
|
+
void watch(withdrawV, this.facets.withdrawHandler, {
|
|
314
|
+
advanceAmount,
|
|
315
|
+
tmpReturnSeat,
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
withdrawHandler: {
|
|
320
|
+
/**
|
|
321
|
+
*
|
|
322
|
+
* @param {undefined} result
|
|
323
|
+
* @param {{ advanceAmount: Amount<'nat'>; tmpReturnSeat: ZCFSeat; }} ctx
|
|
324
|
+
*/
|
|
325
|
+
onFulfilled(result, { advanceAmount, tmpReturnSeat }) {
|
|
326
|
+
const { borrower } = this.state;
|
|
327
|
+
try {
|
|
328
|
+
borrower.returnToPool(tmpReturnSeat, advanceAmount);
|
|
329
|
+
} catch (e) {
|
|
330
|
+
// If we reach here, the unused advance funds will remain in `tmpReturnSeat`
|
|
331
|
+
// and must be retrieved from recovery sets.
|
|
332
|
+
log(
|
|
333
|
+
`🚨 return ${q(advanceAmount)} to pool failed. funds remain on "tmpReturnSeat"`,
|
|
334
|
+
e,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
tmpReturnSeat.exit();
|
|
229
338
|
},
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
339
|
+
/**
|
|
340
|
+
* @param {Error} error
|
|
341
|
+
* @param {{ advanceAmount: Amount<'nat'>; tmpReturnSeat: ZCFSeat; }} ctx
|
|
342
|
+
*/
|
|
343
|
+
onRejected(error, { advanceAmount, tmpReturnSeat }) {
|
|
344
|
+
log(
|
|
345
|
+
`🚨 withdraw ${q(advanceAmount)} from "poolAccount" to return to pool failed`,
|
|
346
|
+
error,
|
|
347
|
+
);
|
|
348
|
+
// If we reach here, the unused advance funds will remain in the `poolAccount`.
|
|
349
|
+
// A contract update will be required to return them to the LiquidityPool.
|
|
350
|
+
tmpReturnSeat.exit();
|
|
234
351
|
},
|
|
235
352
|
},
|
|
236
353
|
},
|
|
237
354
|
{
|
|
238
|
-
stateShape
|
|
239
|
-
borrowerFacet: M.remotable(),
|
|
240
|
-
poolAccount: M.remotable(),
|
|
241
|
-
}),
|
|
355
|
+
stateShape,
|
|
242
356
|
},
|
|
243
357
|
);
|
|
244
358
|
};
|