@agoric/inter-protocol 0.13.2-dev-606cbd2.0 → 0.13.2-dev-8d17cce.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 (43) hide show
  1. package/bundles/bundle-auctioneer-js-meta.json +123 -119
  2. package/bundles/bundle-auctioneer.js +1 -1
  3. package/bundles/bundle-econCommitteeCharter-js-meta.json +71 -71
  4. package/bundles/bundle-econCommitteeCharter.js +1 -1
  5. package/bundles/bundle-feeDistributor-js-meta.json +59 -59
  6. package/bundles/bundle-feeDistributor.js +1 -1
  7. package/bundles/bundle-fluxAggregatorKit-js-meta.json +143 -139
  8. package/bundles/bundle-fluxAggregatorKit.js +1 -1
  9. package/bundles/bundle-psm-js-meta.json +136 -132
  10. package/bundles/bundle-psm.js +1 -1
  11. package/bundles/bundle-reserve-js-meta.json +115 -111
  12. package/bundles/bundle-reserve.js +1 -1
  13. package/bundles/bundle-scaledPriceAuthority-js-meta.json +125 -121
  14. package/bundles/bundle-scaledPriceAuthority.js +1 -1
  15. package/bundles/bundle-vaultFactory-js-meta.json +188 -184
  16. package/bundles/bundle-vaultFactory.js +1 -1
  17. package/package.json +17 -17
  18. package/scripts/add-collateral-core.js +2 -0
  19. package/scripts/init-core.js +1 -0
  20. package/scripts/invite-committee-core.js +1 -0
  21. package/scripts/price-feed-core.js +1 -0
  22. package/src/auction/auctionBook.js +11 -11
  23. package/src/auction/auctioneer.js +15 -11
  24. package/src/auction/params.js +4 -4
  25. package/src/auction/scheduleMath.js +77 -32
  26. package/src/auction/scheduler.js +191 -79
  27. package/src/auction/util.js +7 -0
  28. package/src/clientSupport.js +1 -2
  29. package/src/price/fluxAggregatorContract.js +29 -6
  30. package/src/proposals/addAssetToVault.js +6 -34
  31. package/src/proposals/committee-proposal.js +9 -9
  32. package/src/proposals/core-proposal.js +58 -1
  33. package/src/proposals/econ-behaviors.js +56 -45
  34. package/src/proposals/price-feed-proposal.js +9 -21
  35. package/src/proposals/startEconCommittee.js +14 -6
  36. package/src/proposals/startPSM.js +51 -34
  37. package/src/psm/psm.js +11 -6
  38. package/src/reserve/assetReserveKit.js +1 -1
  39. package/src/vaultFactory/liquidation.js +168 -33
  40. package/src/vaultFactory/vault.js +5 -4
  41. package/src/vaultFactory/vaultDirector.js +27 -17
  42. package/src/vaultFactory/vaultFactory.js +26 -11
  43. package/src/vaultFactory/vaultManager.js +139 -95
@@ -1,13 +1,15 @@
1
1
  // @jessie-check
2
2
 
3
- import { E } from '@endo/eventual-send';
4
3
  import { AmountMath } from '@agoric/ertp';
5
4
  import { makeTracer } from '@agoric/internal';
5
+ import { observeIteration, subscribeEach } from '@agoric/notifier';
6
+ import { makeScalarMapStore } from '@agoric/store';
6
7
  import { TimeMath } from '@agoric/time';
7
8
  import { atomicRearrange } from '@agoric/zoe/src/contractSupport/index.js';
8
- import { makeScalarMapStore } from '@agoric/store';
9
+ import { E } from '@endo/eventual-send';
9
10
 
10
11
  import { AUCTION_START_DELAY, PRICE_LOCK_PERIOD } from '../auction/params.js';
12
+ import { makeCancelTokenMaker } from '../auction/util.js';
11
13
 
12
14
  const trace = makeTracer('LIQ', false);
13
15
 
@@ -16,32 +18,65 @@ const trace = makeTracer('LIQ', false);
16
18
  /** @typedef {import('@agoric/time/src/types').CancelToken} CancelToken */
17
19
  /** @typedef {import('@agoric/time/src/types').RelativeTimeRecord} RelativeTimeRecord */
18
20
 
21
+ const makeCancelToken = makeCancelTokenMaker('liq');
22
+
19
23
  /**
20
- * @param {ERef<import('../auction/auctioneer.js').AuctioneerPublicFacet>} auctioneerPublicFacet
21
- * @param {ERef<TimerService>} timer
22
- * @param {TimerWaker} priceLockWaker
23
- * @param {TimerWaker} liquidationWaker
24
- * @param {TimerWaker} reschedulerWaker
24
+ * This will normally be set. If the schedule goes sideways, we'll unschedule
25
+ * all events and unset it. When auction params are changed, we'll restart the schedule
26
+ *
27
+ * @type {object | undefined}
25
28
  */
26
- const scheduleLiquidationWakeups = async (
27
- auctioneerPublicFacet,
28
- timer,
29
- priceLockWaker,
30
- liquidationWaker,
31
- reschedulerWaker,
32
- ) => {
33
- const [schedules, params] = await Promise.all([
34
- E(auctioneerPublicFacet).getSchedules(),
35
- E(auctioneerPublicFacet).getGovernedParams(),
36
- ]);
29
+ let cancelToken = makeCancelToken();
37
30
 
38
- trace('SCHEDULE', schedules.nextAuctionSchedule);
39
- if (!schedules.nextAuctionSchedule?.startTime) {
40
- // The schedule says there's no next auction.
41
- return;
31
+ const waitForRepair = () => {
32
+ // the params observer will setWakeup to resume.
33
+ console.warn(
34
+ '🛠️ No wake for reschedule. Repair by resetting auction params.',
35
+ );
36
+ };
37
+
38
+ // Generally, canceling wakups has to be followed by setting up new ones but
39
+ // not in the case of pausing wakeups. We do that in the event of invalid
40
+ // schedule params. When they are repaired by governance, the wakers resume.
41
+ /** @param {ERef<TimerService>} timer */
42
+ const cancelWakeups = timer => {
43
+ if (cancelToken) {
44
+ void E(timer).cancel(cancelToken);
42
45
  }
46
+ cancelToken = undefined;
47
+ };
43
48
 
44
- const { startTime } = schedules.nextAuctionSchedule;
49
+ /**
50
+ * Schedule wakeups for the *next* auction round.
51
+ *
52
+ * In practice, there are these cases to handle (with N as "live" and N+1 is "next"):
53
+ *
54
+ * | when (now within the range) | what |
55
+ * | -------------------------------- | --------------------------------------- |
56
+ * | [start N, nominalStart N+1] | good: schedule normally the three wakers|
57
+ * | (nominalStart N+1, endTime N+1] | recover: skip round N+1 and schedule N+2|
58
+ * | (endTime N+1, ∞) | give up: wait for repair by governance |
59
+ *
60
+ * @param {object} opts
61
+ * @param {ERef<TimerService>} opts.timer
62
+ * @param {TimerWaker} opts.priceLockWaker
63
+ * @param {TimerWaker} opts.liquidationWaker
64
+ * @param {TimerWaker} opts.reschedulerWaker
65
+ * @param {import('../auction/scheduler.js').Schedule} opts.nextAuctionSchedule
66
+ * @param {import('@agoric/time/src/types').TimestampRecord} opts.now
67
+ * @param {ParamStateRecord} opts.params
68
+ * @returns {void}
69
+ */
70
+ const setWakeups = ({
71
+ nextAuctionSchedule,
72
+ now,
73
+ timer,
74
+ reschedulerWaker,
75
+ liquidationWaker,
76
+ priceLockWaker,
77
+ params,
78
+ }) => {
79
+ const { startTime, endTime } = nextAuctionSchedule;
45
80
 
46
81
  /** @type {RelativeTimeRecord} */
47
82
  // @ts-expect-error Casting
@@ -52,17 +87,87 @@ const scheduleLiquidationWakeups = async (
52
87
  // eslint-disable-next-line no-restricted-syntax -- https://github.com/Agoric/eslint-config-jessie/issues/33
53
88
  const auctionStartDelay = params[AUCTION_START_DELAY].value;
54
89
 
90
+ // nominal is the declared start time, but the actual auctioning begins after auctionStartDelay
55
91
  const nominalStart = TimeMath.subtractAbsRel(startTime, auctionStartDelay);
92
+ const afterStart = TimeMath.addAbsRel(startTime, 1n);
93
+
94
+ // Is there a problem (case 2 or 3)?
95
+ // setWakeupsForNextAuction is supposed to be called just after an auction
96
+ // started. If we're late but there's still time for the nominal start, then
97
+ // we'll proceed. Reschedule for the next round if that's still in the future.
98
+ // Otherwise, wait for governance params to change.
99
+ if (TimeMath.compareAbs(now, nominalStart) > 0) {
100
+ // nominalStart is past
101
+ cancelWakeups(timer);
102
+
103
+ if (TimeMath.compareAbs(now, endTime) < 0) {
104
+ // endTime is in the future or now to reschedule waking (case 2)
105
+ void E(timer).setWakeup(afterStart, reschedulerWaker);
106
+ } else {
107
+ // case 3
108
+ waitForRepair();
109
+ }
110
+
111
+ return;
112
+ }
113
+ // nominalStart is now or in the future (case 1)
114
+
115
+ cancelToken = cancelToken || makeCancelToken();
56
116
  const priceLockWakeTime = TimeMath.subtractAbsRel(
57
117
  nominalStart,
58
118
  priceLockPeriod,
59
119
  );
60
- const afterStart = TimeMath.addAbsRel(startTime, 1n);
61
120
  const a = t => TimeMath.absValue(t);
62
121
  trace('scheduling ', a(priceLockWakeTime), a(nominalStart), a(startTime));
63
- void E(timer).setWakeup(priceLockWakeTime, priceLockWaker);
64
- void E(timer).setWakeup(nominalStart, liquidationWaker);
65
- void E(timer).setWakeup(afterStart, reschedulerWaker);
122
+ void E(timer).setWakeup(priceLockWakeTime, priceLockWaker, cancelToken);
123
+ void E(timer).setWakeup(nominalStart, liquidationWaker, cancelToken);
124
+ // Call setWakeupsForNextAuction again one tick after nominalStart
125
+ void E(timer).setWakeup(afterStart, reschedulerWaker, cancelToken);
126
+ };
127
+
128
+ /**
129
+ * Schedule wakeups for the *next* auction round.
130
+ *
131
+ * Called by vaultDirector's resetWakeupsForNextAuction at start() and every
132
+ * time there's a "reschedule" wakeup.
133
+ *
134
+ * @param {ERef<import('../auction/auctioneer.js').AuctioneerPublicFacet>} auctioneerPublicFacet
135
+ * @param {ERef<TimerService>} timer
136
+ * @param {TimerWaker} priceLockWaker
137
+ * @param {TimerWaker} liquidationWaker
138
+ * @param {TimerWaker} reschedulerWaker
139
+ * @returns {Promise<void>}
140
+ */
141
+ const setWakeupsForNextAuction = async (
142
+ auctioneerPublicFacet,
143
+ timer,
144
+ priceLockWaker,
145
+ liquidationWaker,
146
+ reschedulerWaker,
147
+ ) => {
148
+ const [{ nextAuctionSchedule }, params, now] = await Promise.all([
149
+ E(auctioneerPublicFacet).getSchedules(),
150
+ E(auctioneerPublicFacet).getGovernedParams(),
151
+ E(timer).getCurrentTimestamp(),
152
+ ]);
153
+
154
+ trace('SCHEDULE', nextAuctionSchedule);
155
+ if (!nextAuctionSchedule) {
156
+ // There should always be a nextAuctionSchedule. If there isn't, give up for now.
157
+ cancelWakeups(timer);
158
+ waitForRepair();
159
+ return;
160
+ }
161
+
162
+ setWakeups({
163
+ nextAuctionSchedule,
164
+ now,
165
+ timer,
166
+ reschedulerWaker,
167
+ liquidationWaker,
168
+ priceLockWaker,
169
+ params,
170
+ });
66
171
  };
67
172
 
68
173
  /**
@@ -94,6 +199,40 @@ const liquidationResults = (debt, minted) => {
94
199
  };
95
200
  };
96
201
 
202
+ /**
203
+ * Watch governed params for change
204
+ *
205
+ * @param {ERef<import('../auction/auctioneer.js').AuctioneerPublicFacet>} auctioneerPublicFacet
206
+ * @param {ERef<TimerService>} timer
207
+ * @param {TimerWaker} reschedulerWaker
208
+ * @returns {void}
209
+ */
210
+ export const watchForGovernanceChange = (
211
+ auctioneerPublicFacet,
212
+ timer,
213
+ reschedulerWaker,
214
+ ) => {
215
+ void E.when(E(timer).getCurrentTimestamp(), now =>
216
+ // make one observer that will usually ignore the update.
217
+ observeIteration(
218
+ subscribeEach(E(auctioneerPublicFacet).getSubscription()),
219
+ harden({
220
+ async updateState(_newState) {
221
+ if (!cancelToken) {
222
+ cancelToken = makeCancelToken();
223
+ void E(timer).setWakeup(
224
+ // bump one tick to prevent an infinite loop
225
+ TimeMath.addAbsRel(now, 1n),
226
+ reschedulerWaker,
227
+ cancelToken,
228
+ );
229
+ }
230
+ },
231
+ }),
232
+ ),
233
+ );
234
+ };
235
+
97
236
  /**
98
237
  * @param {ZCF} zcf
99
238
  * @param {object} collateralizationDetails
@@ -154,12 +293,8 @@ const getLiquidatableVaults = (
154
293
  return { vaultData, totalDebt, totalCollateral, liqSeat };
155
294
  };
156
295
 
157
- harden(scheduleLiquidationWakeups);
296
+ harden(setWakeupsForNextAuction);
158
297
  harden(liquidationResults);
159
298
  harden(getLiquidatableVaults);
160
299
 
161
- export {
162
- scheduleLiquidationWakeups,
163
- liquidationResults,
164
- getLiquidatableVaults,
165
- };
300
+ export { setWakeupsForNextAuction, liquidationResults, getLiquidatableVaults };
@@ -85,7 +85,7 @@ const validTransitions = {
85
85
  * @property {(base: string) => string} scopeDescription
86
86
  * @property {() => Brand<'nat'>} getDebtBrand
87
87
  * @property {MintAndTransfer} mintAndTransfer
88
- * @property {(amount: Amount, seat: ZCFSeat) => void} burnAndRecord
88
+ * @property {(amount: Amount, seat: ZCFSeat) => void} burn
89
89
  * @property {() => Ratio} getCompoundedInterest
90
90
  * @property {(oldDebt: import('./storeUtils.js').NormalizedDebt, oldCollateral: Amount<'nat'>, vaultId: VaultId, vaultPhase: VaultPhase, vault: Vault) => void} handleBalanceChange
91
91
  * @property {() => import('./vaultManager.js').GovernedParamGetters} getGovernedParams
@@ -350,7 +350,8 @@ export const prepareVault = (baggage, makeRecorderKit, zcf) => {
350
350
  }
351
351
  const { phase } = state;
352
352
  const uiState = facets.helper.getStateSnapshot(phase);
353
- trace('updateUiState', state.idInManager, uiState);
353
+ const brand = facets.helper.collateralBrand();
354
+ trace(brand, 'updateUiState', state.idInManager, uiState);
354
355
 
355
356
  switch (phase) {
356
357
  case Phase.ACTIVE:
@@ -404,7 +405,7 @@ export const prepareVault = (baggage, makeRecorderKit, zcf) => {
404
405
  vaultSeat.getCurrentAllocation(),
405
406
  );
406
407
 
407
- state.manager.burnAndRecord(debt, seat);
408
+ state.manager.burn(debt, seat);
408
409
  } else if (phase === Phase.LIQUIDATED) {
409
410
  // Simply reallocate vault assets to the offer seat.
410
411
  // Don't take anything from the offer, even if vault is underwater.
@@ -558,7 +559,7 @@ export const prepareVault = (baggage, makeRecorderKit, zcf) => {
558
559
  collateralPre,
559
560
  newDebt,
560
561
  );
561
- state.manager.burnAndRecord(giveMintedTaken, vaultSeat);
562
+ state.manager.burn(giveMintedTaken, vaultSeat);
562
563
  helper.assertVaultHoldsNoMinted();
563
564
 
564
565
  helper.updateUiState();
@@ -27,7 +27,10 @@ import {
27
27
  import { E } from '@endo/eventual-send';
28
28
  import { Far } from '@endo/marshal';
29
29
  import { makeCollectFeesInvitation } from '../collectFees.js';
30
- import { scheduleLiquidationWakeups } from './liquidation.js';
30
+ import {
31
+ setWakeupsForNextAuction,
32
+ watchForGovernanceChange,
33
+ } from './liquidation.js';
31
34
  import {
32
35
  provideVaultParamManagers,
33
36
  SHORTFALL_INVITATION_KEY,
@@ -135,7 +138,7 @@ export const prepareVaultDirector = (
135
138
  rewardPoolAllocation: rewardPoolSeat.getCurrentAllocation(),
136
139
  });
137
140
  };
138
- const updateMetrics = () => E(metricsKit.recorderP).write(sampleMetrics());
141
+ const writeMetrics = () => E(metricsKit.recorderP).write(sampleMetrics());
139
142
 
140
143
  const updateShortfallReporter = async () => {
141
144
  const oldInvitation = baggage.has(shortfallInvitationKey)
@@ -212,7 +215,7 @@ export const prepareVaultDirector = (
212
215
  debtMint.burnLosses(harden({ Minted: toMint }), mintSeat);
213
216
  throw e;
214
217
  }
215
- void updateMetrics();
218
+ void writeMetrics();
216
219
  },
217
220
  getShortfallReporter: async () => {
218
221
  await updateShortfallReporter();
@@ -310,7 +313,7 @@ export const prepareVaultDirector = (
310
313
  getPublicTopics: M.call().returns(TopicsRecordShape),
311
314
  }),
312
315
  helper: M.interface('helper', {
313
- rescheduleLiquidationWakeups: M.call().returns(M.promise()),
316
+ resetWakeupsForNextAuction: M.call().returns(M.promise()),
314
317
  start: M.call().returns(M.promise()),
315
318
  }),
316
319
  },
@@ -408,7 +411,7 @@ export const prepareVaultDirector = (
408
411
  const { self: vm } = kit;
409
412
  vm || Fail`no vault`;
410
413
  collateralManagers.init(collateralBrand, managerIndex);
411
- void updateMetrics();
414
+ void writeMetrics();
412
415
  return vm;
413
416
  },
414
417
  makeCollectFeesInvitation() {
@@ -432,7 +435,7 @@ export const prepareVaultDirector = (
432
435
  makeReschedulerWaker() {
433
436
  const { facets } = this;
434
437
  return makeWaker('reschedulerWaker', () => {
435
- void facets.helper.rescheduleLiquidationWakeups();
438
+ void facets.helper.resetWakeupsForNextAuction();
436
439
  });
437
440
  },
438
441
  makePriceLockWaker() {
@@ -520,13 +523,13 @@ export const prepareVaultDirector = (
520
523
  },
521
524
  },
522
525
  helper: {
523
- rescheduleLiquidationWakeups() {
526
+ resetWakeupsForNextAuction() {
524
527
  const { facets } = this;
525
528
 
526
529
  const priceLockWaker = facets.machine.makePriceLockWaker();
527
530
  const liquidationWaker = facets.machine.makeLiquidationWaker();
528
531
  const rescheduleWaker = facets.machine.makeReschedulerWaker();
529
- return scheduleLiquidationWakeups(
532
+ return setWakeupsForNextAuction(
530
533
  auctioneer,
531
534
  timer,
532
535
  priceLockWaker,
@@ -538,9 +541,18 @@ export const prepareVaultDirector = (
538
541
  * Start non-durable processes (or restart if needed after vat restart)
539
542
  */
540
543
  async start() {
541
- const { helper } = this.facets;
542
- helper.rescheduleLiquidationWakeups();
543
- await updateShortfallReporter();
544
+ const { helper, machine } = this.facets;
545
+
546
+ await helper.resetWakeupsForNextAuction();
547
+ updateShortfallReporter().catch(err =>
548
+ console.error(
549
+ '🛠️ updateShortfallReporter failed during start(); repair by updating governance',
550
+ err,
551
+ ),
552
+ );
553
+ // independent of the other one which can be canceled
554
+ const rescheduleWaker = machine.makeReschedulerWaker();
555
+ void watchForGovernanceChange(auctioneer, timer, rescheduleWaker);
544
556
  },
545
557
  },
546
558
  },
@@ -550,17 +562,15 @@ export const prepareVaultDirector = (
550
562
  harden(prepareVaultDirector);
551
563
 
552
564
  /**
553
- * Prepare the VaultDirector kind, get or make the singleton, and call .start() to kick off processes.
565
+ * Prepare the VaultDirector kind, get or make the singleton
554
566
  *
555
567
  * @type {(...pvdArgs: Parameters<typeof prepareVaultDirector>) => ReturnType<ReturnType<typeof prepareVaultDirector>>}
556
568
  */
557
- export const provideAndStartDirector = (...args) => {
569
+ export const provideDirector = (...args) => {
558
570
  const makeVaultDirector = prepareVaultDirector(...args);
559
571
 
560
572
  const [baggage] = args;
561
573
 
562
- const director = provide(baggage, 'director', makeVaultDirector);
563
- director.helper.start();
564
- return director;
574
+ return provide(baggage, 'director', makeVaultDirector);
565
575
  };
566
- harden(provideAndStartDirector);
576
+ harden(provideDirector);
@@ -20,13 +20,16 @@ import '@agoric/zoe/src/contracts/exported.js';
20
20
  import { CONTRACT_ELECTORATE } from '@agoric/governance';
21
21
  import { makeParamManagerFromTerms } from '@agoric/governance/src/contractGovernance/typedParamManager.js';
22
22
  import { validateElectorate } from '@agoric/governance/src/contractHelper.js';
23
- import { assertAllDefined, makeTracer } from '@agoric/internal';
23
+ import { makeTracer, StorageNodeShape } from '@agoric/internal';
24
24
  import { makeStoredSubscription, makeSubscriptionKit } from '@agoric/notifier';
25
+ import { M } from '@agoric/store';
25
26
  import { provideAll } from '@agoric/zoe/src/contractSupport/durability.js';
26
27
  import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
27
28
  import { E } from '@endo/eventual-send';
29
+ import { FeeMintAccessShape } from '@agoric/zoe/src/typeGuards.js';
30
+ import { InvitationShape } from '../auction/params.js';
28
31
  import { SHORTFALL_INVITATION_KEY, vaultDirectorParamTypes } from './params.js';
29
- import { provideAndStartDirector } from './vaultDirector.js';
32
+ import { provideDirector } from './vaultDirector.js';
30
33
 
31
34
  const trace = makeTracer('VF', false);
32
35
 
@@ -39,6 +42,20 @@ const trace = makeTracer('VF', false);
39
42
  * }>} VaultFactoryZCF
40
43
  */
41
44
 
45
+ export const privateArgsShape = M.splitRecord(
46
+ harden({
47
+ marshaller: M.remotable('Marshaller'),
48
+ storageNode: StorageNodeShape,
49
+ }),
50
+ harden({
51
+ // only necessary on first invocation, not subsequent
52
+ feeMintAccess: FeeMintAccessShape,
53
+ initialPoserInvitation: InvitationShape,
54
+ initialShortfallInvitation: InvitationShape,
55
+ }),
56
+ );
57
+ harden(privateArgsShape);
58
+
42
59
  /**
43
60
  * @param {VaultFactoryZCF} zcf
44
61
  * @param {{
@@ -53,19 +70,11 @@ const trace = makeTracer('VF', false);
53
70
  export const prepare = async (zcf, privateArgs, baggage) => {
54
71
  trace('prepare start', privateArgs, [...baggage.keys()]);
55
72
  const {
56
- feeMintAccess,
57
73
  initialPoserInvitation,
58
74
  initialShortfallInvitation,
59
75
  marshaller,
60
76
  storageNode,
61
77
  } = privateArgs;
62
- assertAllDefined({
63
- feeMintAccess,
64
- initialPoserInvitation,
65
- initialShortfallInvitation,
66
- marshaller,
67
- storageNode,
68
- });
69
78
 
70
79
  trace('awaiting debtMint');
71
80
  const { debtMint } = await provideAll(baggage, {
@@ -107,7 +116,7 @@ export const prepare = async (zcf, privateArgs, baggage) => {
107
116
  vaultDirectorParamTypes,
108
117
  );
109
118
 
110
- const director = provideAndStartDirector(
119
+ const director = provideDirector(
111
120
  baggage,
112
121
  zcf,
113
122
  vaultDirectorParamManager,
@@ -121,6 +130,12 @@ export const prepare = async (zcf, privateArgs, baggage) => {
121
130
  makeERecorderKit,
122
131
  );
123
132
 
133
+ // cannot await because it would make remote calls during vat restart
134
+ director.helper.start().catch(err => {
135
+ console.error('💀 vaultDirector failed to start:', err);
136
+ zcf.shutdownWithFailure(err);
137
+ });
138
+
124
139
  // validate async to wait for params to be finished
125
140
  // UNTIL https://github.com/Agoric/agoric-sdk/issues/4343
126
141
  void validateElectorate(zcf, vaultDirectorParamManager);