@agoric/inter-protocol 0.13.2-dev-55d06de.0 → 0.13.2-dev-097dca8.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.
Files changed (32) hide show
  1. package/bundles/bundle-amm-meta.js +128 -128
  2. package/bundles/bundle-amm.js +1 -1
  3. package/bundles/bundle-econCommitteeCharter-meta.js +74 -74
  4. package/bundles/bundle-econCommitteeCharter.js +1 -1
  5. package/bundles/bundle-feeDistributor-meta.js +54 -54
  6. package/bundles/bundle-feeDistributor.js +1 -1
  7. package/bundles/bundle-interchainPool-meta.js +104 -104
  8. package/bundles/bundle-interchainPool.js +1 -1
  9. package/bundles/bundle-liquidateIncrementally-meta.js +68 -68
  10. package/bundles/bundle-liquidateIncrementally.js +1 -1
  11. package/bundles/bundle-liquidateMinimum-meta.js +68 -68
  12. package/bundles/bundle-liquidateMinimum.js +1 -1
  13. package/bundles/bundle-priceAggregatorChainlink-meta.js +73 -73
  14. package/bundles/bundle-priceAggregatorChainlink.js +1 -1
  15. package/bundles/bundle-psm-meta.js +107 -107
  16. package/bundles/bundle-psm.js +1 -1
  17. package/bundles/bundle-reserve-meta.js +105 -105
  18. package/bundles/bundle-reserve.js +1 -1
  19. package/bundles/bundle-scaledPriceAuthority-meta.js +80 -80
  20. package/bundles/bundle-scaledPriceAuthority.js +1 -1
  21. package/bundles/bundle-stakeFactory-meta.js +114 -114
  22. package/bundles/bundle-stakeFactory.js +1 -1
  23. package/bundles/bundle-vaultFactory-meta.js +132 -132
  24. package/bundles/bundle-vaultFactory.js +1 -1
  25. package/package.json +14 -14
  26. package/src/contractSupport.js +7 -0
  27. package/src/vaultFactory/vault.js +48 -83
  28. package/src/vaultFactory/vaultDirector.js +427 -528
  29. package/src/vaultFactory/vaultFactory.js +7 -3
  30. package/src/vaultFactory/vaultHolder.js +3 -3
  31. package/src/vaultFactory/vaultKit.js +9 -5
  32. package/src/vaultFactory/vaultManager.js +132 -256
@@ -24,7 +24,7 @@ import {
24
24
  import {
25
25
  defineDurableExoClassKit,
26
26
  makeKindHandle,
27
- makeScalarBigMapStore,
27
+ provideDurableMapStore,
28
28
  } from '@agoric/vat-data';
29
29
  import { assertKeywordName } from '@agoric/zoe/src/cleanProposal.js';
30
30
  import { unitAmount } from '@agoric/zoe/src/contractSupport/priceQuote.js';
@@ -37,34 +37,10 @@ import {
37
37
  vaultParamPattern,
38
38
  } from './params.js';
39
39
  import { prepareVaultManagerKit } from './vaultManager.js';
40
- import { provideChildBaggage } from '../contractSupport.js';
40
+ import { provideChildBaggage, provideEmptySeat } from '../contractSupport.js';
41
41
 
42
42
  const { details: X, quote: q, Fail } = assert;
43
43
 
44
- /** @typedef {{
45
- * debtMint: ZCFMint<'nat'>,
46
- * directorParamManager: import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager<import('./params.js').VaultDirectorParams>,
47
- * managerBaggages: import('../contractSupport.js').ChildBaggageManager,
48
- * marshaller: ERef<Marshaller>,
49
- * shortfallReporter: import('../reserve/assetReserve.js').ShortfallReporter,
50
- * storedMetricsSubscriber: StoredSubscriber<MetricsNotification>,
51
- * storageNode: ERef<StorageNode>,
52
- * vaultParamManagers: MapStore<Brand, import('./params.js').VaultParamManager>,
53
- * zcf: import('./vaultFactory.js').VaultFactoryZCF,
54
- * }} Ephemera
55
- */
56
-
57
- /**
58
- * Ephemera is the state we cannot (or merely need not) keep durably. For
59
- * vaultDirector we can keep it in module scope because there is (exactly) one
60
- * vaultDirector per vaultFactory contract and (exactly) one contract per vat.
61
- *
62
- * @type {Ephemera}
63
- */
64
- // @ts-expect-error not actually full until after initState
65
- // UNTIL resolution to https://github.com/Agoric/agoric-sdk/issues/5759
66
- const ephemera = {};
67
-
68
44
  /**
69
45
  * @typedef {{
70
46
  * collaterals: Brand[],
@@ -72,12 +48,6 @@ const ephemera = {};
72
48
  * }} MetricsNotification
73
49
  *
74
50
  * @typedef {Readonly<{
75
- * collateralTypes: MapStore<Brand,VaultManager>,
76
- * metricsPublisher: Publisher<MetricsNotification>
77
- * metricsSubscriber: Subscriber<MetricsNotification>
78
- * mintSeat: ZCFSeat,
79
- * rewardPoolSeat: ZCFSeat,
80
- * shortfallInvitation: Invitation,
81
51
  * }>} ImmutableState
82
52
  *
83
53
  * @typedef {{
@@ -99,94 +69,40 @@ const ephemera = {};
99
69
  */
100
70
  // TODO find a way to type 'finish' with the context (state and facets)
101
71
 
102
- /**
103
- * @param {ERef<ZoeService>} zoe
104
- * @param {Ephemera['directorParamManager']} paramMgr
105
- * @param {import('../reserve/assetReserve.js').ShortfallReporter} [oldShortfallReporter]
106
- * @param {ERef<Invitation>} [oldInvitation]
107
- * @returns {Promise<{
108
- * shortfallInvitation: ERef<Invitation>,
109
- * shortfallReporter: import('../reserve/assetReserve.js').ShortfallReporter,
110
- * }>}
111
- */
112
- const updateShortfallReporter = async (
113
- zoe,
114
- paramMgr,
115
- oldShortfallReporter,
116
- oldInvitation,
117
- ) => {
118
- const newInvitation = paramMgr.getInternalParamValue(
119
- SHORTFALL_INVITATION_KEY,
120
- );
121
-
122
- if (newInvitation !== oldInvitation) {
123
- return {
124
- // @ts-expect-error cast
125
- shortfallReporter: E(E(zoe).offer(newInvitation)).getOfferResult(),
126
- shortfallInvitation: newInvitation,
127
- };
128
- } else {
129
- assert(
130
- oldShortfallReporter,
131
- 'updateShortFallReported called with repeat invitation and no oldShortfallReporter',
132
- );
133
- return {
134
- shortfallReporter: oldShortfallReporter,
135
- shortfallInvitation: oldInvitation,
136
- };
137
- }
138
- };
139
-
140
- /**
141
- * @param {Pick<State, 'collateralTypes' | 'rewardPoolSeat'>} state
142
- * @returns {MetricsNotification}
143
- */
144
- const metricsOf = state => {
145
- return harden({
146
- collaterals: Array.from(state.collateralTypes.keys()),
147
- rewardPoolAllocation: state.rewardPoolSeat.getCurrentAllocation(),
148
- });
149
- };
150
-
151
- // UNTIL https://github.com/Agoric/agoric-sdk/issues/6791
152
- const baggage = makeScalarBigMapStore('Vault Director baggage', {
153
- durable: true,
154
- });
155
-
156
- const makeVaultDirectorMetricsPublishKit = prepareDurablePublishKit(
157
- baggage,
158
- 'Vault Director metrics',
159
- );
72
+ const shortfallInvitationKey = 'shortfallInvitation';
160
73
 
161
74
  /**
162
- * @param {Ephemera['zcf']} zcf
163
- * @param {Ephemera['directorParamManager']} directorParamManager
164
- * @param {Ephemera['debtMint']} debtMint
75
+ * @param {import('@agoric/ertp').Baggage} baggage
76
+ * @param {import('./vaultFactory.js').VaultFactoryZCF} zcf
77
+ * @param {import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager<import('./params.js').VaultDirectorParams>} directorParamManager
78
+ * @param {ZCFMint<"nat">} debtMint
165
79
  * @param {ERef<StorageNode>} storageNode
166
80
  * @param {ERef<Marshaller>} marshaller
167
- * @returns {State}
168
81
  */
169
- const initState = (
82
+ export const prepareVaultDirector = (
83
+ baggage,
170
84
  zcf,
171
85
  directorParamManager,
172
86
  debtMint,
173
87
  storageNode,
174
88
  marshaller,
175
89
  ) => {
90
+ const makeVaultDirectorPublishKit = prepareDurablePublishKit(
91
+ baggage,
92
+ 'Vault Director publish kit',
93
+ );
176
94
  /** For temporary staging of newly minted tokens */
177
- const { zcfSeat: mintSeat } = zcf.makeEmptySeatKit();
178
- const { zcfSeat: rewardPoolSeat } = zcf.makeEmptySeatKit();
95
+ const mintSeat = provideEmptySeat(zcf, baggage, 'mintSeat');
96
+ const rewardPoolSeat = provideEmptySeat(zcf, baggage, 'rewardPoolSeat');
179
97
 
180
- const collateralTypes = makeScalarBigMapStore('collateralTypes', {
181
- durable: true,
182
- });
98
+ const collateralTypes = provideDurableMapStore(baggage, 'collateralTypes');
183
99
 
184
100
  // Non-durable map because param managers aren't durable.
185
101
  // In the event they're needed they can be reconstructed from contract terms and off-chain data.
186
102
  const vaultParamManagers = makeScalarMapStore('vaultParamManagers');
187
103
 
188
104
  const { publisher: metricsPublisher, subscriber: metricsSubscriber } =
189
- makeVaultDirectorMetricsPublishKit();
105
+ makeVaultDirectorPublishKit();
190
106
 
191
107
  const storedMetricsSubscriber = makeStoredSubscriber(
192
108
  metricsSubscriber,
@@ -196,450 +112,433 @@ const initState = (
196
112
 
197
113
  const managerBaggages = provideChildBaggage(baggage, 'Vault Manager baggage');
198
114
 
199
- Object.assign(ephemera, {
200
- debtMint,
201
- directorParamManager,
202
- managerBaggages,
203
- marshaller,
204
- storedMetricsSubscriber,
205
- storageNode,
206
- vaultParamManagers,
207
- zcf,
208
- });
115
+ /** @type {import('../reserve/assetReserve.js').ShortfallReporter} */
116
+ let shortfallReporter;
209
117
 
210
- return {
211
- collateralTypes,
212
- managerCounter: 0,
213
- metricsPublisher,
214
- metricsSubscriber,
215
- mintSeat,
216
- rewardPoolSeat,
217
- // @ts-expect-error defined in finish()
218
- shortfallInvitation: undefined,
118
+ /**
119
+ * @returns {State}
120
+ */
121
+ const initState = () => {
122
+ return {
123
+ managerCounter: 0,
124
+ };
219
125
  };
220
- };
221
126
 
222
- /**
223
- * @param {Ephemera['directorParamManager']} directorParamManager
224
- */
225
- const getLiquidationConfig = directorParamManager => ({
226
- install: directorParamManager.getLiquidationInstall(),
227
- terms: directorParamManager.getLiquidationTerms(),
228
- });
229
-
230
- /**
231
- *
232
- * @param {Ephemera['directorParamManager']} govParams
233
- * @param {VaultManager} vaultManager
234
- * @param {Installation<unknown>} oldInstall
235
- * @param {unknown} oldTerms
236
- */
237
- const watchGovernance = (govParams, vaultManager, oldInstall, oldTerms) => {
238
- const subscription = govParams.getSubscription();
239
- void observeIteration(subscription, {
240
- updateState(_paramUpdate) {
241
- const { install, terms } = getLiquidationConfig(govParams);
242
- if (install === oldInstall && keyEQ(terms, oldTerms)) {
243
- return;
244
- }
245
- oldInstall = install;
246
- oldTerms = terms;
247
- vaultManager
248
- .setupLiquidator(install, terms)
249
- .catch(e => console.error('Failed to setup liquidator', e));
250
- },
127
+ const getLiquidationConfig = () => ({
128
+ install: directorParamManager.getLiquidationInstall(),
129
+ terms: directorParamManager.getLiquidationTerms(),
251
130
  });
252
- };
253
131
 
254
- /** @param {MethodContext} context */
255
- const finish = async ({ state }) => {
256
- const { shortfallReporter, shortfallInvitation } =
257
- await updateShortfallReporter(
258
- ephemera.zcf.getZoeService(),
259
- ephemera.directorParamManager,
260
- );
261
- ephemera.shortfallReporter = shortfallReporter;
262
- // @ts-expect-error write once
263
- state.shortfallInvitation = shortfallInvitation;
264
- };
132
+ /**
133
+ * @returns {MetricsNotification}
134
+ */
135
+ const sampleMetrics = () => {
136
+ return harden({
137
+ collaterals: Array.from(collateralTypes.keys()),
138
+ rewardPoolAllocation: rewardPoolSeat.getCurrentAllocation(),
139
+ });
140
+ };
265
141
 
266
- /**
267
- * "Director" of the vault factory, overseeing "vault managers".
268
- *
269
- * @param {ZCF<GovernanceTerms<{}> & {
270
- * ammPublicFacet: AutoswapPublicFacet,
271
- * liquidationInstall: Installation<import('./liquidateMinimum.js').start>,
272
- * loanTimingParams: {ChargingPeriod: ParamValueTyped<'nat'>, RecordingPeriod: ParamValueTyped<'nat'>},
273
- * reservePublicFacet: AssetReservePublicFacet,
274
- * timerService: TimerService,
275
- * priceAuthority: ERef<PriceAuthority>
276
- * }>} zcf
277
- * @param {import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager<import('./params.js').VaultDirectorParams>} directorParamManager
278
- * @param {ZCFMint<"nat">} debtMint
279
- */
280
- const makeVaultDirector = defineDurableExoClassKit(
281
- makeKindHandle('VaultDirector'),
282
- {
283
- creator: M.interface('creator', {
284
- getParamMgrRetriever: M.call().returns(M.remotable()),
285
- getInvitation: M.call(M.string()).returns(M.promise()),
286
- getLimitedCreatorFacet: M.call().returns(M.remotable()),
287
- getGovernedApis: M.call().returns(M.record()),
288
- getGovernedApiNames: M.call().returns(M.record()),
289
- }),
290
- machine: M.interface('machine', {
291
- addVaultType: M.call(IssuerShape, M.string(), M.record()).returns(
292
- M.promise(),
293
- ),
294
- makeCollectFeesInvitation: M.call().returns(M.promise()),
295
- getContractGovernor: M.call().returns(M.remotable()),
296
- updateMetrics: M.call().returns(),
297
- getRewardAllocation: M.call().returns({ Minted: AmountShape }),
298
- }),
299
- public: M.interface('public', {
300
- getCollateralManager: M.call(BrandShape).returns(M.remotable()),
301
- getCollaterals: M.call().returns(M.promise()),
302
- getMetrics: M.call().returns(SubscriberShape),
303
- makeVaultInvitation: M.call().returns(M.promise()),
304
- getRunIssuer: M.call().returns(IssuerShape),
305
- getSubscription: M.call({ collateralBrand: BrandShape }).returns(
306
- SubscriberShape,
307
- ),
308
- getElectorateSubscription: M.call().returns(SubscriberShape),
309
- getGovernedParams: M.call({ collateralBrand: BrandShape }).returns(
310
- M.record(),
311
- ),
312
- getContractGovernor: M.call().returns(M.remotable()),
313
- getInvitationAmount: M.call(M.string()).returns(AmountShape),
314
- }),
315
- },
316
- initState,
317
- {
318
- creator: {
319
- getParamMgrRetriever: () =>
320
- Far('paramManagerRetriever', {
321
- /** @param {VaultFactoryParamPath} paramPath */
322
- get: paramPath => {
323
- if (paramPath.key === 'governedParams') {
324
- return ephemera.directorParamManager;
325
- } else if (paramPath.key.collateralBrand) {
326
- return ephemera.vaultParamManagers.get(
327
- paramPath.key.collateralBrand,
328
- );
329
- } else {
330
- assert.fail('Unsupported paramPath');
331
- }
332
- },
333
- }),
334
- /**
335
- * @param {string} name
336
- */
337
- getInvitation(name) {
338
- return ephemera.directorParamManager.getInternalParamValue(name);
339
- },
340
- getLimitedCreatorFacet() {
341
- return this.facets.machine;
142
+ /**
143
+ *
144
+ * @param {VaultManager} vaultManager
145
+ * @param {Installation<unknown>} oldInstall
146
+ * @param {unknown} oldTerms
147
+ */
148
+ const watchGovernance = (vaultManager, oldInstall, oldTerms) => {
149
+ const subscription = directorParamManager.getSubscription();
150
+ void observeIteration(subscription, {
151
+ updateState(_paramUpdate) {
152
+ const { install, terms } = getLiquidationConfig();
153
+ if (install === oldInstall && keyEQ(terms, oldTerms)) {
154
+ return;
155
+ }
156
+ oldInstall = install;
157
+ oldTerms = terms;
158
+ vaultManager
159
+ .setupLiquidator(install, terms)
160
+ .catch(e => console.error('Failed to setup liquidator', e));
342
161
  },
343
- getGovernedApis() {
344
- return harden({});
345
- },
346
- getGovernedApiNames() {
347
- return harden({});
348
- },
349
- },
350
- machine: {
351
- // TODO move this under governance #3924
352
- /**
353
- * @param {Issuer<'nat'>} collateralIssuer
354
- * @param {Keyword} collateralKeyword
355
- * @param {VaultManagerParamValues} initialParamValues
356
- */
357
- async addVaultType(
358
- collateralIssuer,
359
- collateralKeyword,
360
- initialParamValues,
361
- ) {
362
- const { state, facets } = this;
363
- const {
364
- debtMint,
365
- vaultParamManagers,
366
- directorParamManager,
367
- marshaller,
368
- storageNode,
369
- zcf,
370
- } = ephemera;
371
- const { collateralTypes, mintSeat, rewardPoolSeat } = state;
372
- mustMatch(collateralIssuer, M.remotable(), 'collateralIssuer');
373
- assertKeywordName(collateralKeyword);
374
- mustMatch(initialParamValues, vaultParamPattern, 'initialParamValues');
375
- await zcf.saveIssuer(collateralIssuer, collateralKeyword);
376
- const collateralBrand = zcf.getBrandForIssuer(collateralIssuer);
377
- // We create only one vault per collateralType.
378
- !collateralTypes.has(collateralBrand) ||
379
- Fail`Collateral brand ${q(collateralBrand)} has already been added`;
380
-
381
- const managerStorageNode =
382
- storageNode &&
383
- E(storageNode).makeChildNode(`manager${state.managerCounter}`);
384
- state.managerCounter += 1;
385
-
386
- /** a powerful object; can modify parameters */
387
- const vaultParamManager = makeVaultParamManager(
388
- makeStoredPublisherKit(managerStorageNode, marshaller, 'governance'),
389
- initialParamValues,
390
- );
391
- vaultParamManagers.init(collateralBrand, vaultParamManager);
162
+ });
163
+ };
392
164
 
393
- const { timerService } = zcf.getTerms();
394
- const startTimeStamp = await E(timerService).getCurrentTimestamp();
165
+ const updateShortfallReporter = async () => {
166
+ const oldInvitation = baggage.has(shortfallInvitationKey)
167
+ ? baggage.get(shortfallInvitationKey)
168
+ : undefined;
169
+ const newInvitation = directorParamManager.getInternalParamValue(
170
+ SHORTFALL_INVITATION_KEY,
171
+ );
395
172
 
396
- /**
397
- * We provide an easy way for the vaultManager to add rewards to
398
- * the rewardPoolSeat, without directly exposing the rewardPoolSeat to them.
399
- *
400
- * @type {MintAndReallocate}
401
- */
402
- const mintAndReallocate = (toMint, fee, seat, ...otherSeats) => {
403
- const kept = AmountMath.subtract(toMint, fee);
404
- debtMint.mintGains(harden({ Minted: toMint }), mintSeat);
405
- try {
406
- rewardPoolSeat.incrementBy(
407
- mintSeat.decrementBy(harden({ Minted: fee })),
408
- );
409
- seat.incrementBy(mintSeat.decrementBy(harden({ Minted: kept })));
410
- zcf.reallocate(rewardPoolSeat, mintSeat, seat, ...otherSeats);
411
- } catch (e) {
412
- console.error('mintAndReallocate caught', e);
413
- mintSeat.clear();
414
- rewardPoolSeat.clear();
415
- // Make best efforts to burn the newly minted tokens, for hygiene.
416
- // That only relies on the internal mint, so it cannot fail without
417
- // there being much larger problems. There's no risk of tokens being
418
- // stolen here because the staging for them was already cleared.
419
- debtMint.burnLosses(harden({ Minted: toMint }), mintSeat);
420
- throw e;
421
- } finally {
422
- // Note that if this assertion may fail because of an error in the
423
- // try{} block, but that error won't be thrown because this executes
424
- // before the catch that rethrows it.
425
- assert(
426
- Object.values(mintSeat.getCurrentAllocation()).every(a =>
427
- AmountMath.isEmpty(a),
428
- ),
429
- X`Stage should be empty of Minted`,
430
- );
431
- }
432
- facets.machine.updateMetrics();
433
- };
173
+ if (newInvitation === oldInvitation) {
174
+ shortfallReporter ||
175
+ 'updateShortFallReported called with repeat invitation and no prior shortfallReporter';
176
+ return;
177
+ }
178
+
179
+ // Update the values
180
+ const zoe = zcf.getZoeService();
181
+ // @ts-expect-error cast
182
+ shortfallReporter = E(E(zoe).offer(newInvitation)).getOfferResult();
183
+ if (oldInvitation === undefined) {
184
+ baggage.init(shortfallInvitationKey, newInvitation);
185
+ } else {
186
+ baggage.set(shortfallInvitationKey, newInvitation);
187
+ }
188
+ };
434
189
 
190
+ const finish = async () => {
191
+ await updateShortfallReporter();
192
+ };
193
+
194
+ /**
195
+ * "Director" of the vault factory, overseeing "vault managers".
196
+ *
197
+ * @param {import('./vaultFactory.js').VaultFactoryZCF} zcf
198
+ * @param {import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager<import('./params.js').VaultDirectorParams>} directorParamManager
199
+ * @param {ZCFMint<"nat">} debtMint
200
+ */
201
+ const makeVaultDirector = defineDurableExoClassKit(
202
+ makeKindHandle('VaultDirector'),
203
+ {
204
+ creator: M.interface('creator', {
205
+ getParamMgrRetriever: M.call().returns(M.remotable()),
206
+ getInvitation: M.call(M.string()).returns(M.promise()),
207
+ getLimitedCreatorFacet: M.call().returns(M.remotable()),
208
+ getGovernedApis: M.call().returns(M.record()),
209
+ getGovernedApiNames: M.call().returns(M.record()),
210
+ }),
211
+ machine: M.interface('machine', {
212
+ addVaultType: M.call(IssuerShape, M.string(), M.record()).returns(
213
+ M.promise(),
214
+ ),
215
+ makeCollectFeesInvitation: M.call().returns(M.promise()),
216
+ getContractGovernor: M.call().returns(M.remotable()),
217
+ updateMetrics: M.call().returns(),
218
+ getRewardAllocation: M.call().returns({ Minted: AmountShape }),
219
+ }),
220
+ public: M.interface('public', {
221
+ getCollateralManager: M.call(BrandShape).returns(M.remotable()),
222
+ getCollaterals: M.call().returns(M.promise()),
223
+ getMetrics: M.call().returns(SubscriberShape),
224
+ makeVaultInvitation: M.call().returns(M.promise()),
225
+ getRunIssuer: M.call().returns(IssuerShape),
226
+ getSubscription: M.call({ collateralBrand: BrandShape }).returns(
227
+ SubscriberShape,
228
+ ),
229
+ getElectorateSubscription: M.call().returns(SubscriberShape),
230
+ getGovernedParams: M.call({ collateralBrand: BrandShape }).returns(
231
+ M.record(),
232
+ ),
233
+ getContractGovernor: M.call().returns(M.remotable()),
234
+ getInvitationAmount: M.call(M.string()).returns(AmountShape),
235
+ }),
236
+ },
237
+ initState,
238
+ {
239
+ creator: {
240
+ getParamMgrRetriever: () =>
241
+ Far('paramManagerRetriever', {
242
+ /** @param {VaultFactoryParamPath} paramPath */
243
+ get: paramPath => {
244
+ if (paramPath.key === 'governedParams') {
245
+ return directorParamManager;
246
+ } else if (paramPath.key.collateralBrand) {
247
+ return vaultParamManagers.get(paramPath.key.collateralBrand);
248
+ } else {
249
+ assert.fail('Unsupported paramPath');
250
+ }
251
+ },
252
+ }),
435
253
  /**
436
- * @param {Amount<'nat'>} toBurn
437
- * @param {ZCFSeat} seat
254
+ * @param {string} name
438
255
  */
439
- const burnDebt = (toBurn, seat) => {
440
- debtMint.burnLosses(harden({ Minted: toBurn }), seat);
441
- };
442
-
443
- const { loanTimingParams } = zcf.getTerms();
444
-
445
- const factoryPowers = Far('vault factory powers', {
446
- getGovernedParams: () =>
447
- Far('vault manager param manager', {
448
- ...vaultParamManager.readonly(),
449
- /** @type {() => Amount<'nat'>} */
450
- // @ts-expect-error cast
451
- getDebtLimit: vaultParamManager.readonly().getDebtLimit,
452
- getChargingPeriod: () =>
453
- loanTimingParams[CHARGING_PERIOD_KEY].value,
454
- getRecordingPeriod: () =>
455
- loanTimingParams[RECORDING_PERIOD_KEY].value,
456
- }),
457
- mintAndReallocate,
458
- getShortfallReporter: async () => {
459
- const reporterKit = await updateShortfallReporter(
460
- zcf.getZoeService(),
461
- directorParamManager,
462
- ephemera.shortfallReporter,
463
- state.shortfallInvitation,
464
- );
465
- return reporterKit.shortfallReporter;
466
- },
467
- burnDebt,
468
- });
469
-
470
- const { managerBaggages } = ephemera;
471
- // alleged okay because used only as a diagnostic tag
472
- const brandName = await E(collateralBrand).getAllegedName();
473
- const collateralUnit = await unitAmount(collateralBrand);
474
-
475
- const makeVaultManager = managerBaggages.addChild(
476
- brandName,
477
- prepareVaultManagerKit,
478
- zcf,
479
- debtMint,
480
- collateralBrand,
481
- collateralUnit,
482
- factoryPowers,
483
- startTimeStamp,
484
- managerStorageNode,
485
- marshaller,
486
- );
487
-
488
- const { self: vm } = makeVaultManager();
489
- collateralTypes.init(collateralBrand, vm);
490
- const { install, terms } = getLiquidationConfig(directorParamManager);
491
- await vm.setupLiquidator(install, terms);
492
- watchGovernance(directorParamManager, vm, install, terms);
493
- facets.machine.updateMetrics();
494
- return vm;
495
- },
496
- makeCollectFeesInvitation() {
497
- const { state } = this;
498
- const { debtMint, zcf } = ephemera;
499
- const { rewardPoolSeat } = state;
500
- return makeMakeCollectFeesInvitation(
501
- zcf,
502
- rewardPoolSeat,
503
- debtMint.getIssuerRecord().brand,
504
- 'Minted',
505
- ).makeCollectFeesInvitation();
506
- },
507
- getContractGovernor() {
508
- return ephemera.zcf.getTerms().electionManager;
509
- },
510
- updateMetrics() {
511
- const { state } = this;
512
- return state.metricsPublisher.publish(metricsOf(harden(state)));
513
- },
514
- // XXX accessors for tests
515
- getRewardAllocation() {
516
- return this.state.rewardPoolSeat.getCurrentAllocation();
256
+ getInvitation(name) {
257
+ return directorParamManager.getInternalParamValue(name);
258
+ },
259
+ getLimitedCreatorFacet() {
260
+ return this.facets.machine;
261
+ },
262
+ getGovernedApis() {
263
+ return harden({});
264
+ },
265
+ getGovernedApiNames() {
266
+ return harden({});
267
+ },
517
268
  },
518
- },
519
- public: {
520
- /**
521
- * @param {Brand} brandIn
522
- */
523
- getCollateralManager(brandIn) {
524
- const { state } = this;
525
- const { collateralTypes } = state;
526
- collateralTypes.has(brandIn) ||
527
- Fail`Not a supported collateral type ${brandIn}`;
528
- /** @type {VaultManager} */
529
- return collateralTypes.get(brandIn).getPublicFacet();
530
- },
531
- /**
532
- * @deprecated get `collaterals` list from metrics
533
- */
534
- async getCollaterals() {
535
- const { collateralTypes } = this.state;
536
- // should be collateralTypes.map((vm, brand) => ({
537
- return harden(
538
- Promise.all(
539
- [...collateralTypes.entries()].map(async ([brand, vm]) => {
540
- const priceQuote = await vm.getCollateralQuote();
541
- return {
542
- brand,
543
- interestRate: vm.getGovernedParams().getInterestRate(),
544
- liquidationMargin: vm
545
- .getGovernedParams()
546
- .getLiquidationMargin(),
547
- stabilityFee: vm.getGovernedParams().getLoanFee(),
548
- marketPrice: makeRatioFromAmounts(
549
- getAmountOut(priceQuote),
550
- getAmountIn(priceQuote),
269
+ machine: {
270
+ // TODO move this under governance #3924
271
+ /**
272
+ * @param {Issuer<'nat'>} collateralIssuer
273
+ * @param {Keyword} collateralKeyword
274
+ * @param {VaultManagerParamValues} initialParamValues
275
+ */
276
+ async addVaultType(
277
+ collateralIssuer,
278
+ collateralKeyword,
279
+ initialParamValues,
280
+ ) {
281
+ const { state, facets } = this;
282
+ mustMatch(collateralIssuer, M.remotable(), 'collateralIssuer');
283
+ assertKeywordName(collateralKeyword);
284
+ mustMatch(
285
+ initialParamValues,
286
+ vaultParamPattern,
287
+ 'initialParamValues',
288
+ );
289
+ await zcf.saveIssuer(collateralIssuer, collateralKeyword);
290
+ const collateralBrand = zcf.getBrandForIssuer(collateralIssuer);
291
+ // We create only one vault per collateralType.
292
+ !collateralTypes.has(collateralBrand) ||
293
+ Fail`Collateral brand ${q(collateralBrand)} has already been added`;
294
+
295
+ const managerStorageNode =
296
+ storageNode &&
297
+ E(storageNode).makeChildNode(`manager${state.managerCounter}`);
298
+ state.managerCounter += 1;
299
+
300
+ /** a powerful object; can modify parameters */
301
+ const vaultParamManager = makeVaultParamManager(
302
+ makeStoredPublisherKit(
303
+ managerStorageNode,
304
+ marshaller,
305
+ 'governance',
306
+ ),
307
+ initialParamValues,
308
+ );
309
+ vaultParamManagers.init(collateralBrand, vaultParamManager);
310
+
311
+ const { timerService } = zcf.getTerms();
312
+ const startTimeStamp = await E(timerService).getCurrentTimestamp();
313
+
314
+ /**
315
+ * We provide an easy way for the vaultManager to add rewards to
316
+ * the rewardPoolSeat, without directly exposing the rewardPoolSeat to them.
317
+ *
318
+ * @type {MintAndReallocate}
319
+ */
320
+ const mintAndReallocate = (toMint, fee, seat, ...otherSeats) => {
321
+ const kept = AmountMath.subtract(toMint, fee);
322
+ debtMint.mintGains(harden({ Minted: toMint }), mintSeat);
323
+ try {
324
+ rewardPoolSeat.incrementBy(
325
+ mintSeat.decrementBy(harden({ Minted: fee })),
326
+ );
327
+ seat.incrementBy(mintSeat.decrementBy(harden({ Minted: kept })));
328
+ zcf.reallocate(rewardPoolSeat, mintSeat, seat, ...otherSeats);
329
+ } catch (e) {
330
+ console.error('mintAndReallocate caught', e);
331
+ mintSeat.clear();
332
+ rewardPoolSeat.clear();
333
+ // Make best efforts to burn the newly minted tokens, for hygiene.
334
+ // That only relies on the internal mint, so it cannot fail without
335
+ // there being much larger problems. There's no risk of tokens being
336
+ // stolen here because the staging for them was already cleared.
337
+ debtMint.burnLosses(harden({ Minted: toMint }), mintSeat);
338
+ throw e;
339
+ } finally {
340
+ // Note that if this assertion may fail because of an error in the
341
+ // try{} block, but that error won't be thrown because this executes
342
+ // before the catch that rethrows it.
343
+ assert(
344
+ Object.values(mintSeat.getCurrentAllocation()).every(a =>
345
+ AmountMath.isEmpty(a),
551
346
  ),
552
- };
553
- }),
554
- ),
555
- );
556
- },
557
- getMetrics() {
558
- return ephemera.storedMetricsSubscriber;
559
- },
560
- /**
561
- * @deprecated use getCollateralManager and then makeVaultInvitation instead
562
- *
563
- * Make a vault in the vaultManager based on the collateral type.
564
- */
565
- makeVaultInvitation() {
566
- const { zcf } = ephemera;
567
-
568
- const { collateralTypes } = this.state;
569
-
570
- /** @param {ZCFSeat} seat */
571
- const makeVaultHook = async seat => {
572
- assertProposalShape(seat, {
573
- give: { Collateral: null },
574
- want: { Minted: null },
347
+ X`Stage should be empty of Minted`,
348
+ );
349
+ }
350
+ facets.machine.updateMetrics();
351
+ };
352
+
353
+ /**
354
+ * @param {Amount<'nat'>} toBurn
355
+ * @param {ZCFSeat} seat
356
+ */
357
+ const burnDebt = (toBurn, seat) => {
358
+ debtMint.burnLosses(harden({ Minted: toBurn }), seat);
359
+ };
360
+
361
+ const { loanTimingParams } = zcf.getTerms();
362
+
363
+ const factoryPowers = Far('vault factory powers', {
364
+ getGovernedParams: () =>
365
+ Far('vault manager param manager', {
366
+ ...vaultParamManager.readonly(),
367
+ /** @type {() => Amount<'nat'>} */
368
+ // @ts-expect-error cast
369
+ getDebtLimit: vaultParamManager.readonly().getDebtLimit,
370
+ getChargingPeriod: () =>
371
+ loanTimingParams[CHARGING_PERIOD_KEY].value,
372
+ getRecordingPeriod: () =>
373
+ loanTimingParams[RECORDING_PERIOD_KEY].value,
374
+ }),
375
+ mintAndReallocate,
376
+ getShortfallReporter: async () => {
377
+ await updateShortfallReporter();
378
+ return shortfallReporter;
379
+ },
380
+ burnDebt,
575
381
  });
576
- const {
577
- give: { Collateral: collateralAmount },
578
- want: { Minted: requestedAmount },
579
- } = seat.getProposal();
580
- const { brand: brandIn } = collateralAmount;
382
+
383
+ // alleged okay because used only as a diagnostic tag
384
+ const brandName = await E(collateralBrand).getAllegedName();
385
+ const collateralUnit = await unitAmount(collateralBrand);
386
+
387
+ const makeVaultManager = managerBaggages.addChild(
388
+ brandName,
389
+ prepareVaultManagerKit,
390
+ zcf,
391
+ debtMint,
392
+ collateralBrand,
393
+ collateralUnit,
394
+ factoryPowers,
395
+ startTimeStamp,
396
+ managerStorageNode,
397
+ marshaller,
398
+ );
399
+
400
+ const { self: vm } = makeVaultManager();
401
+ collateralTypes.init(collateralBrand, vm);
402
+ const { install, terms } = getLiquidationConfig();
403
+ await vm.setupLiquidator(install, terms);
404
+ watchGovernance(vm, install, terms);
405
+ facets.machine.updateMetrics();
406
+ return vm;
407
+ },
408
+ makeCollectFeesInvitation() {
409
+ return makeMakeCollectFeesInvitation(
410
+ zcf,
411
+ rewardPoolSeat,
412
+ debtMint.getIssuerRecord().brand,
413
+ 'Minted',
414
+ ).makeCollectFeesInvitation();
415
+ },
416
+ getContractGovernor() {
417
+ return zcf.getTerms().electionManager;
418
+ },
419
+ updateMetrics() {
420
+ return metricsPublisher.publish(sampleMetrics());
421
+ },
422
+ // XXX accessors for tests
423
+ getRewardAllocation() {
424
+ return rewardPoolSeat.getCurrentAllocation();
425
+ },
426
+ },
427
+ public: {
428
+ /**
429
+ * @param {Brand} brandIn
430
+ */
431
+ getCollateralManager(brandIn) {
581
432
  collateralTypes.has(brandIn) ||
582
433
  Fail`Not a supported collateral type ${brandIn}`;
583
-
584
- AmountMath.isGTE(
585
- requestedAmount,
586
- ephemera.directorParamManager.getMinInitialDebt(),
587
- ) ||
588
- Fail`The request must be for at least ${
589
- ephemera.directorParamManager.getMinInitialDebt().value
590
- }. ${requestedAmount.value} is too small`;
591
-
592
434
  /** @type {VaultManager} */
593
- const mgr = collateralTypes.get(brandIn);
594
- return mgr.makeVaultKit(seat);
595
- };
596
- return zcf.makeInvitation(makeVaultHook, 'MakeVault');
597
- },
598
- getRunIssuer() {
599
- return ephemera.debtMint.getIssuerRecord().issuer;
600
- },
601
- /**
602
- * subscription for the paramManager for a particular vaultManager
603
- *
604
- * @param {{ collateralBrand: Brand }} selector
605
- */
606
- getSubscription({ collateralBrand }) {
607
- return ephemera.vaultParamManagers
608
- .get(collateralBrand)
609
- .getSubscription();
610
- },
611
- /**
612
- * subscription for the paramManager for the vaultFactory's electorate
613
- */
614
- getElectorateSubscription() {
615
- return ephemera.directorParamManager.getSubscription();
616
- },
617
- /**
618
- * @param {{ collateralBrand: Brand }} selector
619
- */
620
- getGovernedParams({ collateralBrand }) {
621
- // TODO use named getters of TypedParamManager
622
- return ephemera.vaultParamManagers.get(collateralBrand).getParams();
623
- },
624
- /**
625
- * @returns {Promise<GovernorPublic>}
626
- */
627
- getContractGovernor() {
628
- // PERF consider caching
629
- return E(ephemera.zcf.getZoeService()).getPublicFacet(
630
- ephemera.zcf.getTerms().electionManager,
631
- );
632
- },
633
- /**
634
- * @param {string} name
635
- */
636
- getInvitationAmount(name) {
637
- return ephemera.directorParamManager.getInvitationAmount(name);
435
+ return collateralTypes.get(brandIn).getPublicFacet();
436
+ },
437
+ /**
438
+ * @deprecated get `collaterals` list from metrics
439
+ */
440
+ async getCollaterals() {
441
+ // should be collateralTypes.map((vm, brand) => ({
442
+ return harden(
443
+ Promise.all(
444
+ [...collateralTypes.entries()].map(async ([brand, vm]) => {
445
+ const priceQuote = await vm.getCollateralQuote();
446
+ return {
447
+ brand,
448
+ interestRate: vm.getGovernedParams().getInterestRate(),
449
+ liquidationMargin: vm
450
+ .getGovernedParams()
451
+ .getLiquidationMargin(),
452
+ stabilityFee: vm.getGovernedParams().getLoanFee(),
453
+ marketPrice: makeRatioFromAmounts(
454
+ getAmountOut(priceQuote),
455
+ getAmountIn(priceQuote),
456
+ ),
457
+ };
458
+ }),
459
+ ),
460
+ );
461
+ },
462
+ getMetrics() {
463
+ return storedMetricsSubscriber;
464
+ },
465
+ /**
466
+ * @deprecated use getCollateralManager and then makeVaultInvitation instead
467
+ *
468
+ * Make a vault in the vaultManager based on the collateral type.
469
+ */
470
+ makeVaultInvitation() {
471
+ /** @param {ZCFSeat} seat */
472
+ const makeVaultHook = async seat => {
473
+ assertProposalShape(seat, {
474
+ give: { Collateral: null },
475
+ want: { Minted: null },
476
+ });
477
+ const {
478
+ give: { Collateral: collateralAmount },
479
+ want: { Minted: requestedAmount },
480
+ } = seat.getProposal();
481
+ const { brand: brandIn } = collateralAmount;
482
+ collateralTypes.has(brandIn) ||
483
+ Fail`Not a supported collateral type ${brandIn}`;
484
+
485
+ AmountMath.isGTE(
486
+ requestedAmount,
487
+ directorParamManager.getMinInitialDebt(),
488
+ ) ||
489
+ Fail`The request must be for at least ${
490
+ directorParamManager.getMinInitialDebt().value
491
+ }. ${requestedAmount.value} is too small`;
492
+
493
+ /** @type {VaultManager} */
494
+ const mgr = collateralTypes.get(brandIn);
495
+ return mgr.makeVaultKit(seat);
496
+ };
497
+ return zcf.makeInvitation(makeVaultHook, 'MakeVault');
498
+ },
499
+ getRunIssuer() {
500
+ return debtMint.getIssuerRecord().issuer;
501
+ },
502
+ /**
503
+ * subscription for the paramManager for a particular vaultManager
504
+ *
505
+ * @param {{ collateralBrand: Brand }} selector
506
+ */
507
+ getSubscription({ collateralBrand }) {
508
+ return vaultParamManagers.get(collateralBrand).getSubscription();
509
+ },
510
+ /**
511
+ * subscription for the paramManager for the vaultFactory's electorate
512
+ */
513
+ getElectorateSubscription() {
514
+ return directorParamManager.getSubscription();
515
+ },
516
+ /**
517
+ * @param {{ collateralBrand: Brand }} selector
518
+ */
519
+ getGovernedParams({ collateralBrand }) {
520
+ // TODO use named getters of TypedParamManager
521
+ return vaultParamManagers.get(collateralBrand).getParams();
522
+ },
523
+ /**
524
+ * @returns {Promise<GovernorPublic>}
525
+ */
526
+ getContractGovernor() {
527
+ // PERF consider caching
528
+ return E(zcf.getZoeService()).getPublicFacet(
529
+ zcf.getTerms().electionManager,
530
+ );
531
+ },
532
+ /**
533
+ * @param {string} name
534
+ */
535
+ getInvitationAmount(name) {
536
+ return directorParamManager.getInvitationAmount(name);
537
+ },
638
538
  },
639
539
  },
640
- },
641
- { finish },
642
- );
643
-
644
- harden(makeVaultDirector);
645
- export { makeVaultDirector };
540
+ { finish },
541
+ );
542
+ return makeVaultDirector;
543
+ };
544
+ harden(prepareVaultDirector);