@agoric/fast-usdc 0.1.1-other-dev-3eb1a1d.0 → 0.1.1-other-dev-d15096d.0.d15096d
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 +162 -36
- package/package.json +39 -38
- package/src/cli/bridge-action.js +41 -0
- package/src/cli/cli.js +47 -154
- package/src/cli/config-commands.js +108 -0
- package/src/cli/config.js +15 -9
- package/src/cli/lp-commands.js +161 -0
- package/src/cli/operator-commands.js +143 -0
- package/src/cli/transfer.js +84 -23
- package/src/cli/util/agoric.js +11 -0
- 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 +101 -0
- package/src/constants.js +29 -6
- package/src/main.js +1 -0
- package/src/operator-kit-interface.js +29 -0
- package/src/pool-share-math.js +72 -34
- package/src/type-guards.js +122 -34
- package/src/types.ts +136 -15
- package/src/utils/fees.js +105 -20
- package/tools/cli-tools.ts +9 -0
- package/tools/mock-evidence.ts +205 -0
- package/tools/mock-io.ts +14 -0
- package/src/exos/README.md +0 -26
- package/src/exos/advancer.js +0 -255
- package/src/exos/liquidity-pool.js +0 -365
- package/src/exos/operator-kit.js +0 -120
- package/src/exos/settler.js +0 -97
- package/src/exos/status-manager.js +0 -176
- package/src/exos/transaction-feed.js +0 -180
- package/src/fast-usdc.contract.js +0 -235
- package/src/fast-usdc.flows.js +0 -13
- package/src/fast-usdc.start.js +0 -284
- package/src/util/agoric.js +0 -12
- package/src/utils/address.js +0 -71
- package/src/utils/config-marshal.js +0 -130
- package/src/utils/zoe.js +0 -28
- /package/src/{util → cli/util}/noble.js +0 -0
package/src/exos/advancer.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import { AmountMath, AmountShape } from '@agoric/ertp';
|
|
2
|
-
import { assertAllDefined, makeTracer } from '@agoric/internal';
|
|
3
|
-
import { ChainAddressShape } from '@agoric/orchestration';
|
|
4
|
-
import { pickFacet } from '@agoric/vat-data';
|
|
5
|
-
import { VowShape } from '@agoric/vow';
|
|
6
|
-
import { q } from '@endo/errors';
|
|
7
|
-
import { E } from '@endo/far';
|
|
8
|
-
import { M } from '@endo/patterns';
|
|
9
|
-
import { CctpTxEvidenceShape, EudParamShape } from '../type-guards.js';
|
|
10
|
-
import { addressTools } from '../utils/address.js';
|
|
11
|
-
import { makeFeeTools } from '../utils/fees.js';
|
|
12
|
-
|
|
13
|
-
const { isGTE } = AmountMath;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @import {HostInterface} from '@agoric/async-flow';
|
|
17
|
-
* @import {NatAmount} from '@agoric/ertp';
|
|
18
|
-
* @import {ChainAddress, ChainHub, Denom, OrchestrationAccount} from '@agoric/orchestration';
|
|
19
|
-
* @import {ZoeTools} from '@agoric/orchestration/src/utils/zoe-tools.js';
|
|
20
|
-
* @import {VowTools} from '@agoric/vow';
|
|
21
|
-
* @import {Zone} from '@agoric/zone';
|
|
22
|
-
* @import {CctpTxEvidence, FeeConfig, LogFn} from '../types.js';
|
|
23
|
-
* @import {StatusManager} from './status-manager.js';
|
|
24
|
-
* @import {LiquidityPoolKit} from './liquidity-pool.js';
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {{
|
|
29
|
-
* chainHub: ChainHub;
|
|
30
|
-
* feeConfig: FeeConfig;
|
|
31
|
-
* localTransfer: ZoeTools['localTransfer'];
|
|
32
|
-
* log?: LogFn;
|
|
33
|
-
* statusManager: StatusManager;
|
|
34
|
-
* usdc: { brand: Brand<'nat'>; denom: Denom; };
|
|
35
|
-
* vowTools: VowTools;
|
|
36
|
-
* zcf: ZCF;
|
|
37
|
-
* }} AdvancerKitPowers
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
/** type guards internal to the AdvancerKit */
|
|
41
|
-
const AdvancerKitI = harden({
|
|
42
|
-
advancer: M.interface('AdvancerI', {
|
|
43
|
-
handleTransactionEvent: M.callWhen(CctpTxEvidenceShape).returns(),
|
|
44
|
-
}),
|
|
45
|
-
depositHandler: M.interface('DepositHandlerI', {
|
|
46
|
-
onFulfilled: M.call(M.undefined(), {
|
|
47
|
-
amount: AmountShape,
|
|
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(),
|
|
56
|
-
}),
|
|
57
|
-
transferHandler: M.interface('TransferHandlerI', {
|
|
58
|
-
// TODO confirm undefined, and not bigint (sequence)
|
|
59
|
-
onFulfilled: M.call(M.undefined(), {
|
|
60
|
-
amount: AmountShape,
|
|
61
|
-
destination: ChainAddressShape,
|
|
62
|
-
}).returns(M.undefined()),
|
|
63
|
-
onRejected: M.call(M.error(), {
|
|
64
|
-
amount: AmountShape,
|
|
65
|
-
destination: ChainAddressShape,
|
|
66
|
-
}).returns(M.undefined()),
|
|
67
|
-
}),
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {Zone} zone
|
|
72
|
-
* @param {AdvancerKitPowers} caps
|
|
73
|
-
*/
|
|
74
|
-
export const prepareAdvancerKit = (
|
|
75
|
-
zone,
|
|
76
|
-
{
|
|
77
|
-
chainHub,
|
|
78
|
-
feeConfig,
|
|
79
|
-
localTransfer,
|
|
80
|
-
log = makeTracer('Advancer', true),
|
|
81
|
-
statusManager,
|
|
82
|
-
usdc,
|
|
83
|
-
vowTools: { watch, when },
|
|
84
|
-
zcf,
|
|
85
|
-
} = /** @type {AdvancerKitPowers} */ ({}),
|
|
86
|
-
) => {
|
|
87
|
-
assertAllDefined({
|
|
88
|
-
chainHub,
|
|
89
|
-
feeConfig,
|
|
90
|
-
statusManager,
|
|
91
|
-
watch,
|
|
92
|
-
when,
|
|
93
|
-
});
|
|
94
|
-
const feeTools = makeFeeTools(feeConfig);
|
|
95
|
-
/** @param {bigint} value */
|
|
96
|
-
const toAmount = value => AmountMath.make(usdc.brand, value);
|
|
97
|
-
|
|
98
|
-
return zone.exoClassKit(
|
|
99
|
-
'Fast USDC Advancer',
|
|
100
|
-
AdvancerKitI,
|
|
101
|
-
/**
|
|
102
|
-
* @param {{
|
|
103
|
-
* borrowerFacet: LiquidityPoolKit['borrower'];
|
|
104
|
-
* poolAccount: HostInterface<OrchestrationAccount<{chainId: 'agoric'}>>;
|
|
105
|
-
* }} config
|
|
106
|
-
*/
|
|
107
|
-
config => harden(config),
|
|
108
|
-
{
|
|
109
|
-
advancer: {
|
|
110
|
-
/**
|
|
111
|
-
* Must perform a status update for every observed transaction.
|
|
112
|
-
*
|
|
113
|
-
* We do not expect any callers to depend on the settlement of
|
|
114
|
-
* `handleTransactionEvent` - errors caught are communicated to the
|
|
115
|
-
* `StatusManager` - so we don't need to concern ourselves with
|
|
116
|
-
* preserving the vow chain for callers.
|
|
117
|
-
*
|
|
118
|
-
* @param {CctpTxEvidence} evidence
|
|
119
|
-
*/
|
|
120
|
-
async handleTransactionEvent(evidence) {
|
|
121
|
-
await null;
|
|
122
|
-
try {
|
|
123
|
-
const { borrowerFacet, poolAccount } = this.state;
|
|
124
|
-
const { recipientAddress } = evidence.aux;
|
|
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);
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
// Mark as Advanced since `transferV` initiates the advance.
|
|
148
|
-
// Will throw if we've already .skipped or .advanced this evidence.
|
|
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());
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const { zcfSeat: tmpSeat } = zcf.makeEmptySeatKit();
|
|
158
|
-
const amountKWR = harden({ USDC: advanceAmount });
|
|
159
|
-
try {
|
|
160
|
-
borrowerFacet.borrow(tmpSeat, amountKWR);
|
|
161
|
-
} catch (e) {
|
|
162
|
-
// We do not expect this to fail since there are no turn boundaries
|
|
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
|
-
}
|
|
168
|
-
|
|
169
|
-
const depositV = localTransfer(
|
|
170
|
-
tmpSeat,
|
|
171
|
-
// @ts-expect-error LocalAccountMethods vs OrchestrationAccount
|
|
172
|
-
poolAccount,
|
|
173
|
-
amountKWR,
|
|
174
|
-
);
|
|
175
|
-
void watch(depositV, this.facets.depositHandler, {
|
|
176
|
-
amount: advanceAmount,
|
|
177
|
-
destination,
|
|
178
|
-
tmpSeat,
|
|
179
|
-
});
|
|
180
|
-
} catch (e) {
|
|
181
|
-
log('Advancer error:', q(e).toString());
|
|
182
|
-
statusManager.observe(evidence);
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
},
|
|
186
|
-
depositHandler: {
|
|
187
|
-
/**
|
|
188
|
-
* @param {undefined} result
|
|
189
|
-
* @param {{ amount: Amount<'nat'>; destination: ChainAddress; tmpSeat: ZCFSeat }} ctx
|
|
190
|
-
*/
|
|
191
|
-
onFulfilled(result, { amount, destination }) {
|
|
192
|
-
const { poolAccount } = this.state;
|
|
193
|
-
const transferV = E(poolAccount).transfer(destination, {
|
|
194
|
-
denom: usdc.denom,
|
|
195
|
-
value: amount.value,
|
|
196
|
-
});
|
|
197
|
-
return watch(transferV, this.facets.transferHandler, {
|
|
198
|
-
destination,
|
|
199
|
-
amount,
|
|
200
|
-
});
|
|
201
|
-
},
|
|
202
|
-
/**
|
|
203
|
-
* @param {Error} error
|
|
204
|
-
* @param {{ amount: Amount<'nat'>; destination: ChainAddress; tmpSeat: ZCFSeat }} ctx
|
|
205
|
-
*/
|
|
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
|
|
211
|
-
log(
|
|
212
|
-
'TODO live payment on seat to return to LP',
|
|
213
|
-
q(tmpSeat).toString(),
|
|
214
|
-
);
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
transferHandler: {
|
|
218
|
-
/**
|
|
219
|
-
* @param {undefined} result TODO confirm this is not a bigint (sequence)
|
|
220
|
-
* @param {{ destination: ChainAddress; amount: NatAmount; }} ctx
|
|
221
|
-
*/
|
|
222
|
-
onFulfilled(result, { destination, amount }) {
|
|
223
|
-
// TODO vstorage update? We don't currently have a status for
|
|
224
|
-
// Advanced + transferV settled
|
|
225
|
-
log(
|
|
226
|
-
'Advance transfer fulfilled',
|
|
227
|
-
q({ amount, destination, result }).toString(),
|
|
228
|
-
);
|
|
229
|
-
},
|
|
230
|
-
onRejected(error) {
|
|
231
|
-
// TODO #10510 (comprehensive error testing) determine
|
|
232
|
-
// course of action here. This might fail due to timeout.
|
|
233
|
-
log('Advance transfer rejected', q(error).toString());
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
stateShape: harden({
|
|
239
|
-
borrowerFacet: M.remotable(),
|
|
240
|
-
poolAccount: M.remotable(),
|
|
241
|
-
}),
|
|
242
|
-
},
|
|
243
|
-
);
|
|
244
|
-
};
|
|
245
|
-
harden(prepareAdvancerKit);
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* @param {Zone} zone
|
|
249
|
-
* @param {AdvancerKitPowers} caps
|
|
250
|
-
*/
|
|
251
|
-
export const prepareAdvancer = (zone, caps) => {
|
|
252
|
-
const makeAdvancerKit = prepareAdvancerKit(zone, caps);
|
|
253
|
-
return pickFacet(makeAdvancerKit, 'advancer');
|
|
254
|
-
};
|
|
255
|
-
harden(prepareAdvancer);
|
|
@@ -1,365 +0,0 @@
|
|
|
1
|
-
import { AmountMath, AmountShape } from '@agoric/ertp';
|
|
2
|
-
import {
|
|
3
|
-
makeRecorderTopic,
|
|
4
|
-
TopicsRecordShape,
|
|
5
|
-
} from '@agoric/zoe/src/contractSupport/topics.js';
|
|
6
|
-
import { SeatShape } from '@agoric/zoe/src/typeGuards.js';
|
|
7
|
-
import { M } from '@endo/patterns';
|
|
8
|
-
import { Fail, q } from '@endo/errors';
|
|
9
|
-
import {
|
|
10
|
-
borrowCalc,
|
|
11
|
-
depositCalc,
|
|
12
|
-
makeParity,
|
|
13
|
-
repayCalc,
|
|
14
|
-
withdrawCalc,
|
|
15
|
-
} from '../pool-share-math.js';
|
|
16
|
-
import {
|
|
17
|
-
makeNatAmountShape,
|
|
18
|
-
makeProposalShapes,
|
|
19
|
-
PoolMetricsShape,
|
|
20
|
-
} from '../type-guards.js';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @import {Zone} from '@agoric/zone';
|
|
24
|
-
* @import {Remote} from '@agoric/internal'
|
|
25
|
-
* @import {StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
|
|
26
|
-
* @import {MakeRecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js'
|
|
27
|
-
* @import {USDCProposalShapes, ShareWorth} from '../pool-share-math.js'
|
|
28
|
-
* @import {PoolStats} from '../types.js';
|
|
29
|
-
*/
|
|
30
|
-
|
|
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
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @typedef {{
|
|
60
|
-
* Principal: Amount<'nat'>;
|
|
61
|
-
* PoolFee: Amount<'nat'>;
|
|
62
|
-
* ContractFee: Amount<'nat'>;
|
|
63
|
-
* }} RepayAmountKWR
|
|
64
|
-
*/
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @typedef {{
|
|
68
|
-
* Principal: Payment<'nat'>;
|
|
69
|
-
* PoolFee: Payment<'nat'>;
|
|
70
|
-
* ContractFee: Payment<'nat'>;
|
|
71
|
-
* }} RepayPaymentKWR
|
|
72
|
-
*/
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* @param {Zone} zone
|
|
76
|
-
* @param {ZCF} zcf
|
|
77
|
-
* @param {Brand<'nat'>} USDC
|
|
78
|
-
* @param {{
|
|
79
|
-
* makeRecorderKit: MakeRecorderKit;
|
|
80
|
-
* }} tools
|
|
81
|
-
*/
|
|
82
|
-
export const prepareLiquidityPoolKit = (zone, zcf, USDC, tools) => {
|
|
83
|
-
return zone.exoClassKit(
|
|
84
|
-
'Liquidity Pool',
|
|
85
|
-
{
|
|
86
|
-
borrower: M.interface('borrower', {
|
|
87
|
-
getBalance: M.call().returns(AmountShape),
|
|
88
|
-
borrow: M.call(
|
|
89
|
-
SeatShape,
|
|
90
|
-
harden({ USDC: makeNatAmountShape(USDC, 1n) }),
|
|
91
|
-
).returns(),
|
|
92
|
-
}),
|
|
93
|
-
repayer: M.interface('repayer', {
|
|
94
|
-
repay: M.call(
|
|
95
|
-
SeatShape,
|
|
96
|
-
harden({
|
|
97
|
-
Principal: makeNatAmountShape(USDC, 1n),
|
|
98
|
-
PoolFee: makeNatAmountShape(USDC, 0n),
|
|
99
|
-
ContractFee: makeNatAmountShape(USDC, 0n),
|
|
100
|
-
}),
|
|
101
|
-
).returns(),
|
|
102
|
-
}),
|
|
103
|
-
external: M.interface('external', {
|
|
104
|
-
publishPoolMetrics: M.call().returns(),
|
|
105
|
-
}),
|
|
106
|
-
depositHandler: M.interface('depositHandler', {
|
|
107
|
-
handle: M.call(SeatShape, M.any()).returns(M.promise()),
|
|
108
|
-
}),
|
|
109
|
-
withdrawHandler: M.interface('withdrawHandler', {
|
|
110
|
-
handle: M.call(SeatShape, M.any()).returns(M.promise()),
|
|
111
|
-
}),
|
|
112
|
-
public: M.interface('public', {
|
|
113
|
-
makeDepositInvitation: M.call().returns(M.promise()),
|
|
114
|
-
makeWithdrawInvitation: M.call().returns(M.promise()),
|
|
115
|
-
getPublicTopics: M.call().returns(TopicsRecordShape),
|
|
116
|
-
}),
|
|
117
|
-
},
|
|
118
|
-
/**
|
|
119
|
-
* @param {ZCFMint<'nat'>} shareMint
|
|
120
|
-
* @param {Remote<StorageNode>} node
|
|
121
|
-
*/
|
|
122
|
-
(shareMint, node) => {
|
|
123
|
-
const { brand: PoolShares } = shareMint.getIssuerRecord();
|
|
124
|
-
const proposalShapes = makeProposalShapes({ USDC, PoolShares });
|
|
125
|
-
const shareWorth = makeParity(makeDust(USDC), PoolShares);
|
|
126
|
-
const { zcfSeat: poolSeat } = zcf.makeEmptySeatKit();
|
|
127
|
-
const { zcfSeat: feeSeat } = zcf.makeEmptySeatKit();
|
|
128
|
-
const poolMetricsRecorderKit = tools.makeRecorderKit(
|
|
129
|
-
node,
|
|
130
|
-
PoolMetricsShape,
|
|
131
|
-
);
|
|
132
|
-
const encumberedBalance = makeEmpty(USDC);
|
|
133
|
-
/** @type {PoolStats} */
|
|
134
|
-
const poolStats = harden({
|
|
135
|
-
totalBorrows: makeEmpty(USDC),
|
|
136
|
-
totalContractFees: makeEmpty(USDC),
|
|
137
|
-
totalPoolFees: makeEmpty(USDC),
|
|
138
|
-
totalRepays: makeEmpty(USDC),
|
|
139
|
-
});
|
|
140
|
-
return {
|
|
141
|
-
/** used for `checkPoolBalance` invariant. aka 'outstanding borrows' */
|
|
142
|
-
encumberedBalance,
|
|
143
|
-
feeSeat,
|
|
144
|
-
poolStats,
|
|
145
|
-
poolMetricsRecorderKit,
|
|
146
|
-
poolSeat,
|
|
147
|
-
PoolShares,
|
|
148
|
-
proposalShapes,
|
|
149
|
-
shareMint,
|
|
150
|
-
shareWorth,
|
|
151
|
-
};
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
borrower: {
|
|
155
|
-
getBalance() {
|
|
156
|
-
const { poolSeat } = this.state;
|
|
157
|
-
return poolSeat.getAmountAllocated('USDC', USDC);
|
|
158
|
-
},
|
|
159
|
-
/**
|
|
160
|
-
* @param {ZCFSeat} toSeat
|
|
161
|
-
* @param {{ USDC: Amount<'nat'>}} amountKWR
|
|
162
|
-
*/
|
|
163
|
-
borrow(toSeat, amountKWR) {
|
|
164
|
-
const { encumberedBalance, poolSeat, poolStats } = this.state;
|
|
165
|
-
|
|
166
|
-
// Validate amount is available in pool
|
|
167
|
-
const post = borrowCalc(
|
|
168
|
-
amountKWR.USDC,
|
|
169
|
-
poolSeat.getAmountAllocated('USDC', USDC),
|
|
170
|
-
encumberedBalance,
|
|
171
|
-
poolStats,
|
|
172
|
-
);
|
|
173
|
-
|
|
174
|
-
// 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
|
-
}
|
|
182
|
-
|
|
183
|
-
Object.assign(this.state, post);
|
|
184
|
-
this.facets.external.publishPoolMetrics();
|
|
185
|
-
},
|
|
186
|
-
// TODO method to repay failed `LOA.deposit()`
|
|
187
|
-
},
|
|
188
|
-
repayer: {
|
|
189
|
-
/**
|
|
190
|
-
* @param {ZCFSeat} fromSeat
|
|
191
|
-
* @param {RepayAmountKWR} amounts
|
|
192
|
-
*/
|
|
193
|
-
repay(fromSeat, amounts) {
|
|
194
|
-
const {
|
|
195
|
-
encumberedBalance,
|
|
196
|
-
feeSeat,
|
|
197
|
-
poolSeat,
|
|
198
|
-
poolStats,
|
|
199
|
-
shareWorth,
|
|
200
|
-
} = this.state;
|
|
201
|
-
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
202
|
-
|
|
203
|
-
const fromSeatAllocation = fromSeat.getCurrentAllocation();
|
|
204
|
-
// Validate allocation equals amounts and Principal <= encumberedBalance
|
|
205
|
-
const post = repayCalc(
|
|
206
|
-
shareWorth,
|
|
207
|
-
fromSeatAllocation,
|
|
208
|
-
amounts,
|
|
209
|
-
encumberedBalance,
|
|
210
|
-
poolStats,
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
const { ContractFee, ...rest } = amounts;
|
|
214
|
-
|
|
215
|
-
// 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
|
-
}
|
|
233
|
-
|
|
234
|
-
Object.assign(this.state, post);
|
|
235
|
-
this.facets.external.publishPoolMetrics();
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
external: {
|
|
239
|
-
publishPoolMetrics() {
|
|
240
|
-
const { poolStats, shareWorth, encumberedBalance } = this.state;
|
|
241
|
-
const { recorder } = this.state.poolMetricsRecorderKit;
|
|
242
|
-
// Consumers of this .write() are off-chain / outside the VM.
|
|
243
|
-
// And there's no way to recover from a failed write.
|
|
244
|
-
// So don't await.
|
|
245
|
-
void recorder.write({
|
|
246
|
-
encumberedBalance,
|
|
247
|
-
shareWorth,
|
|
248
|
-
...poolStats,
|
|
249
|
-
});
|
|
250
|
-
},
|
|
251
|
-
},
|
|
252
|
-
|
|
253
|
-
depositHandler: {
|
|
254
|
-
/** @param {ZCFSeat} lp */
|
|
255
|
-
async handle(lp) {
|
|
256
|
-
const { shareWorth, shareMint, poolSeat, encumberedBalance } =
|
|
257
|
-
this.state;
|
|
258
|
-
const { external } = this.facets;
|
|
259
|
-
|
|
260
|
-
/** @type {USDCProposalShapes['deposit']} */
|
|
261
|
-
// @ts-expect-error ensured by proposalShape
|
|
262
|
-
const proposal = lp.getProposal();
|
|
263
|
-
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
264
|
-
const post = depositCalc(shareWorth, proposal);
|
|
265
|
-
|
|
266
|
-
// COMMIT POINT
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
const mint = shareMint.mintGains(post.payouts);
|
|
270
|
-
this.state.shareWorth = post.shareWorth;
|
|
271
|
-
zcf.atomicRearrange(
|
|
272
|
-
harden([
|
|
273
|
-
// zoe guarantees lp has proposal.give allocated
|
|
274
|
-
[lp, poolSeat, proposal.give],
|
|
275
|
-
// mintGains() above establishes that mint has post.payouts
|
|
276
|
-
[mint, lp, post.payouts],
|
|
277
|
-
]),
|
|
278
|
-
);
|
|
279
|
-
lp.exit();
|
|
280
|
-
mint.exit();
|
|
281
|
-
} catch (cause) {
|
|
282
|
-
const reason = Error('🚨 cannot commit deposit', { cause });
|
|
283
|
-
console.error(reason.message, cause);
|
|
284
|
-
zcf.shutdownWithFailure(reason);
|
|
285
|
-
}
|
|
286
|
-
external.publishPoolMetrics();
|
|
287
|
-
},
|
|
288
|
-
},
|
|
289
|
-
withdrawHandler: {
|
|
290
|
-
/** @param {ZCFSeat} lp */
|
|
291
|
-
async handle(lp) {
|
|
292
|
-
const { shareWorth, shareMint, poolSeat, encumberedBalance } =
|
|
293
|
-
this.state;
|
|
294
|
-
const { external } = this.facets;
|
|
295
|
-
|
|
296
|
-
/** @type {USDCProposalShapes['withdraw']} */
|
|
297
|
-
// @ts-expect-error ensured by proposalShape
|
|
298
|
-
const proposal = lp.getProposal();
|
|
299
|
-
const { zcfSeat: burn } = zcf.makeEmptySeatKit();
|
|
300
|
-
checkPoolBalance(poolSeat, shareWorth, USDC, encumberedBalance);
|
|
301
|
-
const post = withdrawCalc(shareWorth, proposal);
|
|
302
|
-
|
|
303
|
-
// COMMIT POINT
|
|
304
|
-
|
|
305
|
-
try {
|
|
306
|
-
this.state.shareWorth = post.shareWorth;
|
|
307
|
-
zcf.atomicRearrange(
|
|
308
|
-
harden([
|
|
309
|
-
// zoe guarantees lp has proposal.give allocated
|
|
310
|
-
[lp, burn, proposal.give],
|
|
311
|
-
// checkPoolBalance() + withdrawCalc() guarantee poolSeat has enough
|
|
312
|
-
[poolSeat, lp, post.payouts],
|
|
313
|
-
]),
|
|
314
|
-
);
|
|
315
|
-
shareMint.burnLosses(proposal.give, burn);
|
|
316
|
-
lp.exit();
|
|
317
|
-
burn.exit();
|
|
318
|
-
} catch (cause) {
|
|
319
|
-
const reason = Error('🚨 cannot commit withdraw', { cause });
|
|
320
|
-
console.error(reason.message, cause);
|
|
321
|
-
zcf.shutdownWithFailure(reason);
|
|
322
|
-
}
|
|
323
|
-
external.publishPoolMetrics();
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
public: {
|
|
327
|
-
makeDepositInvitation() {
|
|
328
|
-
return zcf.makeInvitation(
|
|
329
|
-
this.facets.depositHandler,
|
|
330
|
-
'Deposit',
|
|
331
|
-
undefined,
|
|
332
|
-
this.state.proposalShapes.deposit,
|
|
333
|
-
);
|
|
334
|
-
},
|
|
335
|
-
makeWithdrawInvitation() {
|
|
336
|
-
return zcf.makeInvitation(
|
|
337
|
-
this.facets.withdrawHandler,
|
|
338
|
-
'Withdraw',
|
|
339
|
-
undefined,
|
|
340
|
-
this.state.proposalShapes.withdraw,
|
|
341
|
-
);
|
|
342
|
-
},
|
|
343
|
-
getPublicTopics() {
|
|
344
|
-
const { poolMetricsRecorderKit } = this.state;
|
|
345
|
-
return {
|
|
346
|
-
poolMetrics: makeRecorderTopic(
|
|
347
|
-
'poolMetrics',
|
|
348
|
-
poolMetricsRecorderKit,
|
|
349
|
-
),
|
|
350
|
-
};
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
{
|
|
355
|
-
finish: ({ facets: { external } }) => {
|
|
356
|
-
void external.publishPoolMetrics();
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
);
|
|
360
|
-
};
|
|
361
|
-
harden(prepareLiquidityPoolKit);
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* @typedef {ReturnType<ReturnType<typeof prepareLiquidityPoolKit>>} LiquidityPoolKit
|
|
365
|
-
*/
|