@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
|
@@ -1,41 +1,44 @@
|
|
|
1
1
|
import { AssetKind } from '@agoric/ertp';
|
|
2
|
-
import {
|
|
3
|
-
assertAllDefined,
|
|
4
|
-
deeplyFulfilledObject,
|
|
5
|
-
makeTracer,
|
|
6
|
-
} from '@agoric/internal';
|
|
2
|
+
import { makeTracer } from '@agoric/internal';
|
|
7
3
|
import { observeIteration, subscribeEach } from '@agoric/notifier';
|
|
8
4
|
import {
|
|
5
|
+
CosmosChainInfoShape,
|
|
6
|
+
DenomDetailShape,
|
|
7
|
+
DenomShape,
|
|
9
8
|
OrchestrationPowersShape,
|
|
9
|
+
registerChainsAndAssets,
|
|
10
10
|
withOrchestration,
|
|
11
11
|
} from '@agoric/orchestration';
|
|
12
|
+
import { makeZoeTools } from '@agoric/orchestration/src/utils/zoe-tools.js';
|
|
12
13
|
import { provideSingleton } from '@agoric/zoe/src/contractSupport/durability.js';
|
|
13
14
|
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
|
|
14
|
-
import {
|
|
15
|
-
import { depositToSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
|
|
15
|
+
import { Fail } from '@endo/errors';
|
|
16
16
|
import { E } from '@endo/far';
|
|
17
|
-
import { M
|
|
17
|
+
import { M } from '@endo/patterns';
|
|
18
18
|
import { prepareAdvancer } from './exos/advancer.js';
|
|
19
19
|
import { prepareLiquidityPoolKit } from './exos/liquidity-pool.js';
|
|
20
20
|
import { prepareSettler } from './exos/settler.js';
|
|
21
21
|
import { prepareStatusManager } from './exos/status-manager.js';
|
|
22
22
|
import { prepareTransactionFeedKit } from './exos/transaction-feed.js';
|
|
23
|
-
import { defineInertInvitation } from './utils/zoe.js';
|
|
24
|
-
import { FastUSDCTermsShape, FeeConfigShape } from './type-guards.js';
|
|
25
23
|
import * as flows from './fast-usdc.flows.js';
|
|
24
|
+
import { FastUSDCTermsShape, FeeConfigShape } from './type-guards.js';
|
|
26
25
|
|
|
27
26
|
const trace = makeTracer('FastUsdc');
|
|
28
27
|
|
|
28
|
+
const TXNS_NODE = 'txns';
|
|
29
|
+
const FEE_NODE = 'feeConfig';
|
|
30
|
+
const ADDRESSES_BAGGAGE_KEY = 'addresses';
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
|
-
* @import {Denom} from '@agoric/orchestration';
|
|
31
33
|
* @import {HostInterface} from '@agoric/async-flow';
|
|
32
|
-
* @import {OrchestrationAccount} from '@agoric/orchestration';
|
|
34
|
+
* @import {ChainAddress, CosmosChainInfo, Denom, DenomDetail, OrchestrationAccount} from '@agoric/orchestration';
|
|
33
35
|
* @import {OrchestrationPowers, OrchestrationTools} from '@agoric/orchestration/src/utils/start-helper.js';
|
|
34
|
-
* @import {
|
|
36
|
+
* @import {Remote} from '@agoric/internal';
|
|
37
|
+
* @import {Marshaller, StorageNode} from '@agoric/internal/src/lib-chainStorage.js'
|
|
35
38
|
* @import {Zone} from '@agoric/zone';
|
|
39
|
+
* @import {OperatorOfferResult} from './exos/transaction-feed.js';
|
|
36
40
|
* @import {OperatorKit} from './exos/operator-kit.js';
|
|
37
|
-
* @import {CctpTxEvidence, FeeConfig} from './types.js';
|
|
38
|
-
* @import {RepayAmountKWR, RepayPaymentKWR} from './exos/liquidity-pool.js';
|
|
41
|
+
* @import {CctpTxEvidence, ContractRecord, FeeConfig} from './types.js';
|
|
39
42
|
*/
|
|
40
43
|
|
|
41
44
|
/**
|
|
@@ -51,17 +54,43 @@ export const meta = {
|
|
|
51
54
|
privateArgsShape: {
|
|
52
55
|
// @ts-expect-error TypedPattern not recognized as record
|
|
53
56
|
...OrchestrationPowersShape,
|
|
57
|
+
assetInfo: M.arrayOf([DenomShape, DenomDetailShape]),
|
|
58
|
+
chainInfo: M.recordOf(M.string(), CosmosChainInfoShape),
|
|
54
59
|
feeConfig: FeeConfigShape,
|
|
55
60
|
marshaller: M.remotable(),
|
|
61
|
+
poolMetricsNode: M.remotable(),
|
|
56
62
|
},
|
|
57
63
|
};
|
|
58
64
|
harden(meta);
|
|
59
65
|
|
|
66
|
+
/**
|
|
67
|
+
* @param {Remote<StorageNode>} node
|
|
68
|
+
* @param {ERef<Marshaller>} marshaller
|
|
69
|
+
* @param {FeeConfig} feeConfig
|
|
70
|
+
*/
|
|
71
|
+
const publishFeeConfig = async (node, marshaller, feeConfig) => {
|
|
72
|
+
const feeNode = E(node).makeChildNode(FEE_NODE);
|
|
73
|
+
const value = await E(marshaller).toCapData(feeConfig);
|
|
74
|
+
return E(feeNode).setValue(JSON.stringify(value));
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @param {Remote<StorageNode>} contractNode
|
|
79
|
+
* @param {ContractRecord} addresses
|
|
80
|
+
*/
|
|
81
|
+
const publishAddresses = (contractNode, addresses) => {
|
|
82
|
+
return E(contractNode).setValue(JSON.stringify(addresses));
|
|
83
|
+
};
|
|
84
|
+
|
|
60
85
|
/**
|
|
61
86
|
* @param {ZCF<FastUsdcTerms>} zcf
|
|
62
87
|
* @param {OrchestrationPowers & {
|
|
63
|
-
*
|
|
88
|
+
* assetInfo: [Denom, DenomDetail & { brandKey?: string}][];
|
|
89
|
+
* chainInfo: Record<string, CosmosChainInfo>;
|
|
64
90
|
* feeConfig: FeeConfig;
|
|
91
|
+
* marshaller: Marshaller;
|
|
92
|
+
* poolMetricsNode: Remote<StorageNode>;
|
|
93
|
+
* storageNode: Remote<StorageNode>;
|
|
65
94
|
* }} privateArgs
|
|
66
95
|
* @param {Zone} zone
|
|
67
96
|
* @param {OrchestrationTools} tools
|
|
@@ -71,19 +100,36 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
71
100
|
const terms = zcf.getTerms();
|
|
72
101
|
assert('USDC' in terms.brands, 'no USDC brand');
|
|
73
102
|
assert('usdcDenom' in terms, 'no usdcDenom');
|
|
74
|
-
|
|
103
|
+
|
|
104
|
+
const { feeConfig, marshaller, storageNode } = privateArgs;
|
|
75
105
|
const { makeRecorderKit } = prepareRecorderKitMakers(
|
|
76
106
|
zone.mapStore('vstorage'),
|
|
77
107
|
marshaller,
|
|
78
108
|
);
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
109
|
+
|
|
110
|
+
const statusManager = prepareStatusManager(
|
|
111
|
+
zone,
|
|
112
|
+
E(storageNode).makeChildNode(TXNS_NODE),
|
|
113
|
+
{ marshaller },
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const { USDC } = terms.brands;
|
|
117
|
+
const { withdrawToSeat } = tools.zoeTools;
|
|
118
|
+
const { baggage, chainHub, orchestrateAll, vowTools } = tools;
|
|
119
|
+
const makeSettler = prepareSettler(zone, {
|
|
120
|
+
statusManager,
|
|
121
|
+
USDC,
|
|
122
|
+
withdrawToSeat,
|
|
123
|
+
feeConfig,
|
|
124
|
+
vowTools: tools.vowTools,
|
|
125
|
+
zcf,
|
|
126
|
+
chainHub,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const zoeTools = makeZoeTools(zcf, vowTools);
|
|
83
130
|
const makeAdvancer = prepareAdvancer(zone, {
|
|
84
131
|
chainHub,
|
|
85
132
|
feeConfig,
|
|
86
|
-
localTransfer,
|
|
87
133
|
usdc: harden({
|
|
88
134
|
brand: terms.brands.USDC,
|
|
89
135
|
denom: terms.usdcDenom,
|
|
@@ -91,9 +137,11 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
91
137
|
statusManager,
|
|
92
138
|
vowTools,
|
|
93
139
|
zcf,
|
|
140
|
+
zoeTools,
|
|
94
141
|
});
|
|
142
|
+
|
|
95
143
|
const makeFeedKit = prepareTransactionFeedKit(zone, zcf);
|
|
96
|
-
|
|
144
|
+
|
|
97
145
|
const makeLiquidityPoolKit = prepareLiquidityPoolKit(
|
|
98
146
|
zone,
|
|
99
147
|
zcf,
|
|
@@ -101,66 +149,57 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
101
149
|
{ makeRecorderKit },
|
|
102
150
|
);
|
|
103
151
|
|
|
104
|
-
const
|
|
105
|
-
zcf,
|
|
106
|
-
'test of forcing evidence',
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const { makeLocalAccount } = orchestrateAll(flows, {});
|
|
152
|
+
const { makeLocalAccount, makeNobleAccount } = orchestrateAll(flows, {});
|
|
110
153
|
|
|
111
154
|
const creatorFacet = zone.exo('Fast USDC Creator', undefined, {
|
|
112
|
-
/** @type {(operatorId: string) => Promise<Invitation<
|
|
155
|
+
/** @type {(operatorId: string) => Promise<Invitation<OperatorOfferResult>>} */
|
|
113
156
|
async makeOperatorInvitation(operatorId) {
|
|
114
|
-
// eslint-disable-next-line no-use-before-define
|
|
115
157
|
return feedKit.creator.makeOperatorInvitation(operatorId);
|
|
116
158
|
},
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
159
|
+
/** @type {(operatorId: string) => void} */
|
|
160
|
+
removeOperator(operatorId) {
|
|
161
|
+
return feedKit.creator.removeOperator(operatorId);
|
|
162
|
+
},
|
|
163
|
+
async getContractFeeBalance() {
|
|
164
|
+
return poolKit.feeRecipient.getContractFeeBalance();
|
|
165
|
+
},
|
|
166
|
+
/** @type {() => Promise<Invitation<unknown>>} */
|
|
167
|
+
async makeWithdrawFeesInvitation() {
|
|
168
|
+
return poolKit.feeRecipient.makeWithdrawFeesInvitation();
|
|
125
169
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
170
|
+
async connectToNoble() {
|
|
171
|
+
return vowTools.when(nobleAccountV, nobleAccount => {
|
|
172
|
+
trace('nobleAccount', nobleAccount);
|
|
173
|
+
return vowTools.when(
|
|
174
|
+
E(nobleAccount).getAddress(),
|
|
175
|
+
intermediateRecipient => {
|
|
176
|
+
trace('intermediateRecipient', intermediateRecipient);
|
|
177
|
+
advancer.setIntermediateRecipient(intermediateRecipient);
|
|
178
|
+
settlerKit.creator.setIntermediateRecipient(intermediateRecipient);
|
|
179
|
+
return intermediateRecipient;
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
},
|
|
184
|
+
async publishAddresses() {
|
|
185
|
+
!baggage.has(ADDRESSES_BAGGAGE_KEY) || Fail`Addresses already published`;
|
|
186
|
+
const [poolAccountAddress] = await vowTools.when(
|
|
187
|
+
vowTools.all([E(poolAccount).getAddress()]),
|
|
142
188
|
);
|
|
143
|
-
|
|
144
|
-
|
|
189
|
+
const addresses = harden({
|
|
190
|
+
poolAccount: poolAccountAddress.value,
|
|
191
|
+
settlementAccount: settlementAddress.value,
|
|
192
|
+
});
|
|
193
|
+
baggage.init(ADDRESSES_BAGGAGE_KEY, addresses);
|
|
194
|
+
await publishAddresses(storageNode, addresses);
|
|
195
|
+
return addresses;
|
|
196
|
+
},
|
|
197
|
+
deleteCompletedTxs() {
|
|
198
|
+
return statusManager.deleteCompletedTxs();
|
|
145
199
|
},
|
|
146
200
|
});
|
|
147
201
|
|
|
148
202
|
const publicFacet = zone.exo('Fast USDC Public', undefined, {
|
|
149
|
-
// XXX to be removed before production
|
|
150
|
-
/**
|
|
151
|
-
* NB: Any caller with access to this invitation maker has the ability to
|
|
152
|
-
* force handling of evidence.
|
|
153
|
-
*
|
|
154
|
-
* Provide an API call in the form of an invitation maker, so that the
|
|
155
|
-
* capability is available in the smart-wallet bridge during UI testing.
|
|
156
|
-
*
|
|
157
|
-
* @param {CctpTxEvidence} evidence
|
|
158
|
-
*/
|
|
159
|
-
makeTestPushInvitation(evidence) {
|
|
160
|
-
// eslint-disable-next-line no-use-before-define
|
|
161
|
-
void advancer.handleTransactionEvent(evidence);
|
|
162
|
-
return makeTestInvitation();
|
|
163
|
-
},
|
|
164
203
|
makeDepositInvitation() {
|
|
165
204
|
return poolKit.public.makeDepositInvitation();
|
|
166
205
|
},
|
|
@@ -170,6 +209,13 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
170
209
|
getPublicTopics() {
|
|
171
210
|
return poolKit.public.getPublicTopics();
|
|
172
211
|
},
|
|
212
|
+
getStaticInfo() {
|
|
213
|
+
baggage.has(ADDRESSES_BAGGAGE_KEY) ||
|
|
214
|
+
Fail`no addresses. creator must 'publishAddresses' first`;
|
|
215
|
+
return harden({
|
|
216
|
+
[ADDRESSES_BAGGAGE_KEY]: baggage.get(ADDRESSES_BAGGAGE_KEY),
|
|
217
|
+
});
|
|
218
|
+
},
|
|
173
219
|
});
|
|
174
220
|
|
|
175
221
|
// ^^^ Define all kinds above this line. Keep remote calls below. vvv
|
|
@@ -185,6 +231,8 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
185
231
|
// So we use zone.exoClassKit above to define the liquidity pool kind
|
|
186
232
|
// and pass the shareMint into the maker / init function.
|
|
187
233
|
|
|
234
|
+
void publishFeeConfig(storageNode, marshaller, feeConfig);
|
|
235
|
+
|
|
188
236
|
const shareMint = await provideSingleton(
|
|
189
237
|
zone.mapStore('mint'),
|
|
190
238
|
'PoolShare',
|
|
@@ -195,37 +243,70 @@ export const contract = async (zcf, privateArgs, zone, tools) => {
|
|
|
195
243
|
);
|
|
196
244
|
|
|
197
245
|
const poolKit = zone.makeOnce('Liquidity Pool kit', () =>
|
|
198
|
-
makeLiquidityPoolKit(shareMint, privateArgs.
|
|
246
|
+
makeLiquidityPoolKit(shareMint, privateArgs.poolMetricsNode),
|
|
199
247
|
);
|
|
200
248
|
|
|
249
|
+
/** Chain, connection, and asset info can only be registered once */
|
|
250
|
+
const firstIncarnationKey = 'firstIncarnationKey';
|
|
251
|
+
if (!baggage.has(firstIncarnationKey)) {
|
|
252
|
+
baggage.init(firstIncarnationKey, true);
|
|
253
|
+
registerChainsAndAssets(
|
|
254
|
+
chainHub,
|
|
255
|
+
terms.brands,
|
|
256
|
+
privateArgs.chainInfo,
|
|
257
|
+
privateArgs.assetInfo,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const nobleAccountV = zone.makeOnce('NobleAccount', () => makeNobleAccount());
|
|
262
|
+
|
|
201
263
|
const feedKit = zone.makeOnce('Feed Kit', () => makeFeedKit());
|
|
202
264
|
|
|
203
|
-
const poolAccountV =
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
265
|
+
const poolAccountV = zone.makeOnce('PoolAccount', () => makeLocalAccount());
|
|
266
|
+
const settleAccountV = zone.makeOnce('SettleAccount', () =>
|
|
267
|
+
makeLocalAccount(),
|
|
268
|
+
);
|
|
269
|
+
// when() is OK here since this clearly resolves promptly.
|
|
270
|
+
/** @type {[HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>, HostInterface<OrchestrationAccount<{chainId: 'agoric-3';}>>]} */
|
|
271
|
+
const [poolAccount, settlementAccount] = await vowTools.when(
|
|
272
|
+
vowTools.all([poolAccountV, settleAccountV]),
|
|
273
|
+
);
|
|
274
|
+
trace('settlementAccount', settlementAccount);
|
|
275
|
+
trace('poolAccount', poolAccount);
|
|
276
|
+
const settlementAddress = await E(settlementAccount).getAddress();
|
|
277
|
+
trace('settlementAddress', settlementAddress);
|
|
278
|
+
|
|
279
|
+
const [_agoric, _noble, agToNoble] = await vowTools.when(
|
|
280
|
+
chainHub.getChainsAndConnection('agoric', 'noble'),
|
|
281
|
+
);
|
|
282
|
+
const settlerKit = makeSettler({
|
|
283
|
+
repayer: poolKit.repayer,
|
|
284
|
+
sourceChannel: agToNoble.transferChannel.counterPartyChannelId,
|
|
285
|
+
remoteDenom: 'uusdc',
|
|
286
|
+
settlementAccount,
|
|
287
|
+
});
|
|
211
288
|
|
|
212
289
|
const advancer = zone.makeOnce('Advancer', () =>
|
|
213
290
|
makeAdvancer({
|
|
214
|
-
|
|
291
|
+
borrower: poolKit.borrower,
|
|
292
|
+
notifier: settlerKit.notifier,
|
|
215
293
|
poolAccount,
|
|
294
|
+
settlementAddress,
|
|
216
295
|
}),
|
|
217
296
|
);
|
|
218
297
|
// Connect evidence stream to advancer
|
|
219
298
|
void observeIteration(subscribeEach(feedKit.public.getEvidenceSubscriber()), {
|
|
220
|
-
updateState(
|
|
299
|
+
updateState(evidenceWithRisk) {
|
|
221
300
|
try {
|
|
222
|
-
void advancer.handleTransactionEvent(
|
|
301
|
+
void advancer.handleTransactionEvent(evidenceWithRisk);
|
|
223
302
|
} catch (err) {
|
|
224
303
|
trace('🚨 Error handling transaction event', err);
|
|
225
304
|
}
|
|
226
305
|
},
|
|
227
306
|
});
|
|
228
307
|
|
|
308
|
+
await settlerKit.creator.monitorMintingDeposits();
|
|
309
|
+
|
|
229
310
|
return harden({ creatorFacet, publicFacet });
|
|
230
311
|
};
|
|
231
312
|
harden(contract);
|
package/src/fast-usdc.flows.js
CHANGED
|
@@ -11,3 +11,13 @@ export const makeLocalAccount = async orch => {
|
|
|
11
11
|
return agoricChain.makeAccount();
|
|
12
12
|
};
|
|
13
13
|
harden(makeLocalAccount);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @satisfies {OrchestrationFlow}
|
|
17
|
+
* @param {Orchestrator} orch
|
|
18
|
+
*/
|
|
19
|
+
export const makeNobleAccount = async orch => {
|
|
20
|
+
const nobleChain = await orch.getChain('noble');
|
|
21
|
+
return nobleChain.makeAccount();
|
|
22
|
+
};
|
|
23
|
+
harden(makeNobleAccount);
|
package/src/main.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types.js';
|
package/src/pool-share-math.js
CHANGED
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
} from '@agoric/zoe/src/contractSupport/ratio.js';
|
|
8
8
|
import { Fail, q } from '@endo/errors';
|
|
9
9
|
|
|
10
|
-
const {
|
|
10
|
+
const { keys } = Object;
|
|
11
|
+
const { add, isEmpty, isEqual, isGTE, make, makeEmpty, subtract } = AmountMath;
|
|
11
12
|
|
|
12
13
|
/**
|
|
14
|
+
* @import {Amount, Brand, DepositFacet, NatValue, Payment} from '@agoric/ertp';
|
|
13
15
|
* @import {PoolStats} from './types';
|
|
14
16
|
* @import {RepayAmountKWR} from './exos/liquidity-pool';
|
|
15
17
|
*/
|
|
@@ -17,8 +19,7 @@ const { getValue, add, isEmpty, isEqual, isGTE, subtract } = AmountMath;
|
|
|
17
19
|
/**
|
|
18
20
|
* Invariant: shareWorth is the pool balance divided by shares outstanding.
|
|
19
21
|
*
|
|
20
|
-
* Use `makeParity(
|
|
21
|
-
* value, for some negligible `epsilon` such as 1n.
|
|
22
|
+
* Use `makeParity(USDC, PoolShares)` for an initial value.
|
|
22
23
|
*
|
|
23
24
|
* @typedef {Ratio} ShareWorth
|
|
24
25
|
*/
|
|
@@ -26,23 +27,26 @@ const { getValue, add, isEmpty, isEqual, isGTE, subtract } = AmountMath;
|
|
|
26
27
|
/**
|
|
27
28
|
* Make a 1-to-1 ratio between amounts of 2 brands.
|
|
28
29
|
*
|
|
29
|
-
* @param {
|
|
30
|
+
* @param {Brand<'nat'>} numeratorBrand
|
|
30
31
|
* @param {Brand<'nat'>} denominatorBrand
|
|
31
32
|
*/
|
|
32
|
-
export const makeParity = (
|
|
33
|
-
const
|
|
34
|
-
return makeRatio(
|
|
33
|
+
export const makeParity = (numeratorBrand, denominatorBrand) => {
|
|
34
|
+
const dust = 1n;
|
|
35
|
+
return makeRatio(dust, numeratorBrand, dust, denominatorBrand);
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* @typedef {{
|
|
39
40
|
* deposit: {
|
|
40
41
|
* give: { USDC: Amount<'nat'> },
|
|
41
|
-
* want
|
|
42
|
+
* want: { PoolShare: Amount<'nat'> }
|
|
42
43
|
* },
|
|
43
44
|
* withdraw: {
|
|
44
45
|
* give: { PoolShare: Amount<'nat'> }
|
|
45
46
|
* want: { USDC: Amount<'nat'> },
|
|
47
|
+
* },
|
|
48
|
+
* withdrawFees: {
|
|
49
|
+
* want: { USDC: Amount<'nat'> }
|
|
46
50
|
* }
|
|
47
51
|
* }} USDCProposalShapes
|
|
48
52
|
*/
|
|
@@ -91,14 +95,54 @@ export const depositCalc = (shareWorth, { give, want }) => {
|
|
|
91
95
|
});
|
|
92
96
|
};
|
|
93
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Verifies that the total pool balance (unencumbered + encumbered) matches the
|
|
100
|
+
* shareWorth numerator. The total pool balance consists of:
|
|
101
|
+
* 1. unencumbered balance - USDC available in the pool for borrowing
|
|
102
|
+
* 2. encumbered balance - USDC currently lent out
|
|
103
|
+
*
|
|
104
|
+
* A negligible `dust` amount is used to initialize shareWorth with a non-zero
|
|
105
|
+
* denominator. It must remain in the pool at all times.
|
|
106
|
+
*
|
|
107
|
+
* @param {Allocation} poolAlloc
|
|
108
|
+
* @param {ShareWorth} shareWorth
|
|
109
|
+
* @param {Amount<'nat'>} encumberedBalance
|
|
110
|
+
*/
|
|
111
|
+
export const checkPoolBalance = (poolAlloc, shareWorth, encumberedBalance) => {
|
|
112
|
+
const { brand: usdcBrand } = encumberedBalance;
|
|
113
|
+
const unencumberedBalance = poolAlloc.USDC || makeEmpty(usdcBrand);
|
|
114
|
+
const kwds = keys(poolAlloc);
|
|
115
|
+
kwds.length === 0 ||
|
|
116
|
+
(kwds.length === 1 && kwds[0] === 'USDC') ||
|
|
117
|
+
Fail`unexpected pool allocations: ${poolAlloc}`;
|
|
118
|
+
const dust = make(usdcBrand, 1n);
|
|
119
|
+
const grossBalance = add(add(unencumberedBalance, dust), encumberedBalance);
|
|
120
|
+
isEqual(grossBalance, shareWorth.numerator) ||
|
|
121
|
+
Fail`🚨 pool balance ${q(unencumberedBalance)} and encumbered balance ${q(encumberedBalance)} inconsistent with shareWorth ${q(shareWorth)}`;
|
|
122
|
+
return harden({ unencumberedBalance, grossBalance });
|
|
123
|
+
};
|
|
124
|
+
|
|
94
125
|
/**
|
|
95
126
|
* Compute payout from a withdraw proposal, along with updated shareWorth
|
|
96
127
|
*
|
|
97
128
|
* @param {ShareWorth} shareWorth
|
|
98
129
|
* @param {USDCProposalShapes['withdraw']} proposal
|
|
130
|
+
* @param {Allocation} poolAlloc
|
|
131
|
+
* @param {Amount<'nat'>} [encumberedBalance]
|
|
99
132
|
* @returns {{ shareWorth: ShareWorth, payouts: { USDC: Amount<'nat'> }}}
|
|
100
133
|
*/
|
|
101
|
-
export const withdrawCalc = (
|
|
134
|
+
export const withdrawCalc = (
|
|
135
|
+
shareWorth,
|
|
136
|
+
{ give, want },
|
|
137
|
+
poolAlloc,
|
|
138
|
+
encumberedBalance = makeEmpty(shareWorth.numerator.brand),
|
|
139
|
+
) => {
|
|
140
|
+
const { unencumberedBalance } = checkPoolBalance(
|
|
141
|
+
poolAlloc,
|
|
142
|
+
shareWorth,
|
|
143
|
+
encumberedBalance,
|
|
144
|
+
);
|
|
145
|
+
|
|
102
146
|
assert(!isEmpty(give.PoolShare));
|
|
103
147
|
assert(!isEmpty(want.USDC));
|
|
104
148
|
|
|
@@ -108,6 +152,8 @@ export const withdrawCalc = (shareWorth, { give, want }) => {
|
|
|
108
152
|
const { denominator: sharesOutstanding, numerator: poolBalance } = shareWorth;
|
|
109
153
|
!isGTE(want.USDC, poolBalance) ||
|
|
110
154
|
Fail`cannot withdraw ${q(want.USDC)}; only ${q(poolBalance)} in pool`;
|
|
155
|
+
isGTE(unencumberedBalance, want.USDC) ||
|
|
156
|
+
Fail`cannot withdraw ${q(want.USDC)}; ${q(encumberedBalance)} is in use; stand by for pool to return to ${q(poolBalance)}`;
|
|
111
157
|
const balancePost = subtract(poolBalance, payout);
|
|
112
158
|
// giving more shares than are outstanding is impossible,
|
|
113
159
|
// so it's not worth a custom diagnostic. subtract will fail
|