@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/inter-protocol",
3
- "version": "0.13.2-dev-606cbd2.0+606cbd2",
3
+ "version": "0.13.2-dev-8d17cce.0+8d17cce",
4
4
  "description": "Core cryptoeconomy contracts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -29,29 +29,29 @@
29
29
  },
30
30
  "homepage": "https://github.com/Agoric/agoric-sdk#readme",
31
31
  "dependencies": {
32
- "@agoric/assert": "0.5.2-dev-606cbd2.0+606cbd2",
33
- "@agoric/ertp": "0.15.4-dev-606cbd2.0+606cbd2",
34
- "@agoric/governance": "0.9.2-dev-606cbd2.0+606cbd2",
35
- "@agoric/internal": "0.2.2-dev-606cbd2.0+606cbd2",
36
- "@agoric/notifier": "0.5.2-dev-606cbd2.0+606cbd2",
37
- "@agoric/store": "0.8.4-dev-606cbd2.0+606cbd2",
38
- "@agoric/time": "0.2.2-dev-606cbd2.0+606cbd2",
39
- "@agoric/vat-data": "0.4.4-dev-606cbd2.0+606cbd2",
40
- "@agoric/vats": "0.13.1-dev-606cbd2.0+606cbd2",
41
- "@agoric/zoe": "0.25.4-dev-606cbd2.0+606cbd2",
32
+ "@agoric/assert": "0.5.2-dev-8d17cce.0+8d17cce",
33
+ "@agoric/ertp": "0.15.4-dev-8d17cce.0+8d17cce",
34
+ "@agoric/governance": "0.9.2-dev-8d17cce.0+8d17cce",
35
+ "@agoric/internal": "0.2.2-dev-8d17cce.0+8d17cce",
36
+ "@agoric/notifier": "0.5.2-dev-8d17cce.0+8d17cce",
37
+ "@agoric/store": "0.8.4-dev-8d17cce.0+8d17cce",
38
+ "@agoric/time": "0.2.2-dev-8d17cce.0+8d17cce",
39
+ "@agoric/vat-data": "0.4.4-dev-8d17cce.0+8d17cce",
40
+ "@agoric/vats": "0.13.1-dev-8d17cce.0+8d17cce",
41
+ "@agoric/zoe": "0.25.4-dev-8d17cce.0+8d17cce",
42
42
  "@endo/captp": "^3.1.1",
43
43
  "@endo/eventual-send": "^0.17.2",
44
44
  "@endo/far": "^0.2.18",
45
45
  "@endo/marshal": "^0.8.5",
46
46
  "@endo/nat": "^4.1.27",
47
- "agoric": "0.18.3-dev-606cbd2.0+606cbd2",
47
+ "agoric": "0.18.3-dev-8d17cce.0+8d17cce",
48
48
  "jessie.js": "^0.3.2"
49
49
  },
50
50
  "devDependencies": {
51
- "@agoric/deploy-script-support": "0.9.5-dev-606cbd2.0+606cbd2",
52
- "@agoric/smart-wallet": "0.4.3-dev-606cbd2.0+606cbd2",
53
- "@agoric/swingset-liveslots": "0.9.1-dev-606cbd2.0+606cbd2",
54
- "@agoric/swingset-vat": "0.30.3-dev-606cbd2.0+606cbd2",
51
+ "@agoric/deploy-script-support": "0.9.5-dev-8d17cce.0+8d17cce",
52
+ "@agoric/smart-wallet": "0.4.3-dev-8d17cce.0+8d17cce",
53
+ "@agoric/swingset-liveslots": "0.9.1-dev-8d17cce.0+8d17cce",
54
+ "@agoric/swingset-vat": "0.30.3-dev-8d17cce.0+8d17cce",
55
55
  "@endo/bundle-source": "^2.5.1",
56
56
  "@endo/init": "^0.5.56",
57
57
  "@endo/promise-kit": "^0.2.56",
@@ -77,5 +77,5 @@
77
77
  "publishConfig": {
78
78
  "access": "public"
79
79
  },
80
- "gitHead": "606cbd2c4b76ca1b5ce1b2488cbeab9bdd5a31ec"
80
+ "gitHead": "8d17ccef5b83f5feff95318e234067d1866edd8d"
81
81
  }
@@ -5,6 +5,7 @@ import { getManifestForAddAssetToVault } from '../src/proposals/addAssetToVault.
5
5
  import { getManifestForPsm } from '../src/proposals/startPSM.js';
6
6
  import { makeInstallCache } from '../src/proposals/utils.js';
7
7
 
8
+ /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
8
9
  export const defaultProposalBuilder = async (
9
10
  { publishRef, install: install0, wrapInstall },
10
11
  {
@@ -59,6 +60,7 @@ export const defaultProposalBuilder = async (
59
60
  });
60
61
  };
61
62
 
63
+ /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
62
64
  export const psmProposalBuilder = async (
63
65
  { publishRef, install: install0, wrapInstall },
64
66
  { anchorOptions = /** @type {object} */ ({}) } = {},
@@ -121,6 +121,7 @@ export const mainProposalBuilder = async ({
121
121
  };
122
122
 
123
123
  // Build proposal for sim-chain etc.
124
+ /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
124
125
  export const defaultProposalBuilder = async (
125
126
  { publishRef, install },
126
127
  options = {},
@@ -4,6 +4,7 @@ import { makeHelpers } from '@agoric/deploy-script-support';
4
4
  import { getManifestForInviteCommittee } from '../src/proposals/committee-proposal.js';
5
5
 
6
6
  // Build proposal for sim-chain etc.
7
+ /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
7
8
  export const defaultProposalBuilder = async (
8
9
  { publishRef, install },
9
10
  options = {},
@@ -11,6 +11,7 @@ const DEFAULT_CONTRACT_TERMS = {
11
11
  maxSubmissionValue: 2n ** 256n,
12
12
  };
13
13
 
14
+ /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
14
15
  export const defaultProposalBuilder = async (
15
16
  { publishRef, install },
16
17
  options = {},
@@ -358,7 +358,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
358
358
  ) {
359
359
  const { priceBook, curAuctionPrice } = this.state;
360
360
  const { helper } = this.facets;
361
- trace('acceptPrice');
361
+ trace(this.state.collateralBrand, 'acceptPrice');
362
362
 
363
363
  const settleIfPriceExists = () => {
364
364
  if (curAuctionPrice !== null) {
@@ -383,7 +383,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
383
383
  priceBook.add(seat, price, stillWant, exitAfterBuy);
384
384
  }
385
385
 
386
- helper.publishBookData();
386
+ void helper.publishBookData();
387
387
  },
388
388
 
389
389
  /**
@@ -404,7 +404,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
404
404
  maxBuy,
405
405
  { trySettle, exitAfterBuy = false },
406
406
  ) {
407
- trace('accept scaledBid offer');
407
+ trace(this.state.collateralBrand, 'accept scaledBid offer');
408
408
  const { curAuctionPrice, lockedPriceForRound, scaledBidBook } =
409
409
  this.state;
410
410
  const { helper } = this.facets;
@@ -437,7 +437,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
437
437
  scaledBidBook.add(seat, bidScaling, stillWant, exitAfterBuy);
438
438
  }
439
439
 
440
- helper.publishBookData();
440
+ void helper.publishBookData();
441
441
  },
442
442
  publishBookData() {
443
443
  const { state } = this;
@@ -457,7 +457,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
457
457
  collateralAvailable,
458
458
  currentPriceLevel: state.curAuctionPrice,
459
459
  });
460
- state.bookDataKit.recorder.write(bookData);
460
+ return state.bookDataKit.recorder.write(bookData);
461
461
  },
462
462
  },
463
463
  self: {
@@ -534,13 +534,13 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
534
534
  state.startCollateral = state.startCollateral
535
535
  ? AmountMath.add(state.startCollateral, assetAmount)
536
536
  : assetAmount;
537
- facets.helper.publishBookData();
537
+ void facets.helper.publishBookData();
538
538
  },
539
539
  /** @type {(reduction: Ratio) => void} */
540
540
  settleAtNewRate(reduction) {
541
541
  const { state, facets } = this;
542
542
 
543
- trace('settleAtNewRate', reduction);
543
+ trace(this.state.collateralBrand, 'settleAtNewRate', reduction);
544
544
  const { lockedPriceForRound, priceBook, scaledBidBook } = state;
545
545
  lockedPriceForRound !== null ||
546
546
  Fail`price must be locked before auction starts`;
@@ -601,7 +601,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
601
601
  }
602
602
  }
603
603
 
604
- facets.helper.publishBookData();
604
+ void facets.helper.publishBookData();
605
605
  },
606
606
  getCurrentPrice() {
607
607
  return this.state.curAuctionPrice;
@@ -615,7 +615,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
615
615
 
616
616
  trace(`locking `, state.updatingOracleQuote);
617
617
  state.lockedPriceForRound = state.updatingOracleQuote;
618
- facets.helper.publishBookData();
618
+ void facets.helper.publishBookData();
619
619
  },
620
620
 
621
621
  setStartingRate(rate) {
@@ -689,7 +689,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
689
689
  state.curAuctionPrice = null;
690
690
  state.remainingProceedsGoal = null;
691
691
  state.startProceedsGoal = null;
692
- facets.helper.publishBookData();
692
+ void facets.helper.publishBookData();
693
693
  },
694
694
  getDataUpdates() {
695
695
  return this.state.bookDataKit.subscriber;
@@ -738,7 +738,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
738
738
  });
739
739
  },
740
740
  );
741
- facets.helper.publishBookData();
741
+ void facets.helper.publishBookData();
742
742
  },
743
743
  stateShape: AuctionBookStateShape,
744
744
  },
@@ -573,16 +573,7 @@ export const start = async (zcf, privateArgs, baggage) => {
573
573
  },
574
574
  });
575
575
 
576
- const scheduler = await E.when(scheduleKit.recorderP, scheduleRecorder =>
577
- makeScheduler(
578
- driver,
579
- timer,
580
- // @ts-expect-error types are correct. How to convince TS?
581
- params,
582
- timerBrand,
583
- scheduleRecorder,
584
- ),
585
- );
576
+ // eslint-disable-next-line no-use-before-define
586
577
  const isActive = () => scheduler.getAuctionState() === AuctionState.ACTIVE;
587
578
 
588
579
  /**
@@ -649,7 +640,8 @@ export const start = async (zcf, privateArgs, baggage) => {
649
640
  );
650
641
  },
651
642
  getSchedules() {
652
- return E(scheduler).getSchedule();
643
+ // eslint-disable-next-line no-use-before-define
644
+ return scheduler.getSchedule();
653
645
  },
654
646
  getScheduleUpdates() {
655
647
  return scheduleKit.subscriber;
@@ -671,6 +663,18 @@ export const start = async (zcf, privateArgs, baggage) => {
671
663
  }),
672
664
  );
673
665
 
666
+ const scheduler = await E.when(scheduleKit.recorderP, scheduleRecorder =>
667
+ makeScheduler(
668
+ driver,
669
+ timer,
670
+ // @ts-expect-error types are correct. How to convince TS?
671
+ params,
672
+ timerBrand,
673
+ scheduleRecorder,
674
+ publicFacet.getSubscription(),
675
+ ),
676
+ );
677
+
674
678
  const creatorFacet = makeFarGovernorFacet(
675
679
  Far('Auctioneer creatorFacet', {
676
680
  /**
@@ -63,7 +63,7 @@ export const auctioneerParamTypes = harden({
63
63
  /**
64
64
  * @typedef {object} AuctionParams
65
65
  * @property {Amount<'set'>} ElectorateInvitationAmount
66
- * @property {RelativeTime} StartFreq
66
+ * @property {RelativeTime} StartFrequency
67
67
  * @property {RelativeTime} ClockStep
68
68
  * @property {bigint} StartingRate
69
69
  * @property {bigint} LowestRate
@@ -78,7 +78,7 @@ export const auctioneerParamTypes = harden({
78
78
  */
79
79
  export const makeAuctioneerParams = ({
80
80
  ElectorateInvitationAmount,
81
- StartFreq,
81
+ StartFrequency,
82
82
  ClockStep,
83
83
  LowestRate,
84
84
  StartingRate,
@@ -94,7 +94,7 @@ export const makeAuctioneerParams = ({
94
94
  },
95
95
  [START_FREQUENCY]: {
96
96
  type: ParamTypes.RELATIVE_TIME,
97
- value: TimeMath.coerceRelativeTimeRecord(StartFreq, TimerBrand),
97
+ value: TimeMath.coerceRelativeTimeRecord(StartFrequency, TimerBrand),
98
98
  },
99
99
  [CLOCK_STEP]: {
100
100
  type: ParamTypes.RELATIVE_TIME,
@@ -171,4 +171,4 @@ export const makeGovernedTerms = (
171
171
  };
172
172
  harden(makeGovernedTerms);
173
173
 
174
- /** @typedef {ReturnType<typeof makeAuctioneerParamManager>} AuctionParamManaager */
174
+ /** @typedef {ReturnType<typeof makeAuctioneerParamManager>} AuctionParamManager */
@@ -2,64 +2,86 @@
2
2
 
3
3
  import { TimeMath } from '@agoric/time';
4
4
  import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js';
5
- import { makeTracer } from '@agoric/internal';
5
+ import { assertAllDefined, makeTracer } from '@agoric/internal';
6
6
 
7
7
  const { subtract, multiply, floorDivide } = natSafeMath;
8
8
  const { Fail } = assert;
9
9
 
10
10
  const trace = makeTracer('SMath', false);
11
11
 
12
+ const subtract1 = relTime =>
13
+ TimeMath.subtractRelRel(
14
+ relTime,
15
+ TimeMath.coerceRelativeTimeRecord(1n, relTime.timerBrand),
16
+ );
17
+
18
+ /**
19
+ * The length of the auction has to be inferred from the governed params.
20
+ *
21
+ * 1. The auction starts by offering collateral at a `StartingRate` (e.g., 105%)
22
+ * of the market price at auction start, and continues until it reaches
23
+ * (or would exceed on its next step) LowestRate (e.g., 65%)
24
+ * 2. The offer price changes every `ClockStep` seconds
25
+ * 3. The offer price changes by `DiscountStep` amount (e.g., 5%) each step So
26
+ * it must run however many `ClockSteps` are required to get from
27
+ * `StartingRate` to `LowestRate` changing by `DiscountStep` each time.
28
+ *
29
+ * Therefore, the duration is `(StartingRate - LowestRate) / DiscountStep * ClockStep`.
30
+ *
31
+ * Note that this is what's *scheduled*. More than one auction can be running
32
+ * simultaneously, and some conditions can cause some of the auctions to stop
33
+ * selling early (e.g. reaching their target debt to raise or selling all of
34
+ * their collateral).
35
+ *
36
+ * @param {Awaited<import('./params.js').AuctionParamManager>} params
37
+ * @param {Timestamp} baseTime
38
+ * @returns {import('./scheduler.js').Schedule}
39
+ */
12
40
  export const computeRoundTiming = (params, baseTime) => {
13
- // currently a TimeValue; hopefully a TimeRecord soon
14
- /** @type {RelativeTime} */
15
41
  const freq = params.getStartFrequency();
16
- /** @type {RelativeTime} */
17
42
  const clockStep = params.getClockStep();
18
- /** @type {NatValue} */
19
43
  const startingRate = params.getStartingRate();
20
- /** @type {NatValue} */
21
44
  const discountStep = params.getDiscountStep();
22
- /** @type {RelativeTime} */
23
45
  const lockPeriod = params.getPriceLockPeriod();
24
- /** @type {NatValue} */
25
46
  const lowestRate = params.getLowestRate();
26
-
27
- /** @type {RelativeTime} */
28
47
  const startDelay = params.getAuctionStartDelay();
48
+
29
49
  TimeMath.compareRel(freq, startDelay) > 0 ||
30
50
  Fail`startFrequency must exceed startDelay, ${freq}, ${startDelay}`;
31
51
  TimeMath.compareRel(freq, lockPeriod) > 0 ||
32
52
  Fail`startFrequency must exceed lock period, ${freq}, ${lockPeriod}`;
53
+ TimeMath.compareRel(freq, clockStep) >= 0 ||
54
+ Fail`clockStep ${TimeMath.relValue(
55
+ clockStep,
56
+ )} must be shorter than startFrequency ${TimeMath.relValue(
57
+ freq,
58
+ )} to allow at least one step down`;
33
59
 
34
60
  startingRate > lowestRate ||
35
61
  Fail`startingRate ${startingRate} must be more than lowest: ${lowestRate}`;
62
+
36
63
  const rateChange = subtract(startingRate, lowestRate);
37
64
  const requestedSteps = floorDivide(rateChange, discountStep);
38
65
  requestedSteps > 0n ||
39
66
  Fail`discountStep ${discountStep} too large for requested rates`;
40
- TimeMath.compareRel(freq, clockStep) >= 0 ||
41
- Fail`clockStep ${TimeMath.relValue(
42
- clockStep,
43
- )} must be shorter than startFrequency ${TimeMath.relValue(
44
- freq,
45
- )} to allow at least one step down`;
46
67
 
47
- const requestedDuration = TimeMath.multiplyRelNat(clockStep, requestedSteps);
48
- const targetDuration =
49
- TimeMath.compareRel(requestedDuration, freq) < 0
50
- ? requestedDuration
51
- : TimeMath.subtractRelRel(freq, 1n);
52
- const steps = TimeMath.divideRelRel(targetDuration, clockStep);
53
- const duration = TimeMath.multiplyRelNat(clockStep, steps);
68
+ // How many steps fit in a full auction? We want the biggest integer for which
69
+ // steps * discountStep <= startingRate - lowestRate, and
70
+ // steps * clockStep < freq
71
+ const maxRateSteps = floorDivide(startingRate - lowestRate, discountStep);
72
+ const maxTimeSteps = TimeMath.divideRelRel(subtract1(freq), clockStep);
73
+ const steps = maxRateSteps < maxTimeSteps ? maxRateSteps : maxTimeSteps;
54
74
 
75
+ const duration = TimeMath.multiplyRelNat(clockStep, steps);
55
76
  steps > 0n ||
56
77
  Fail`clockStep ${clockStep} too long for auction duration ${duration}`;
57
78
  const endRate = subtract(startingRate, multiply(steps, discountStep));
58
79
 
80
+ // Use a duration that exactly matches the last step completing (no remainder)
59
81
  const actualDuration = TimeMath.multiplyRelNat(clockStep, steps);
60
- // computed start is baseTime + freq - (now mod freq). if there are hourly
61
- // starts, we add an hour to the time, and subtract now mod freq.
62
- // Then we add the delay
82
+ // computed start is `startDelay + baseTime + freq - (baseTime mod freq)`.
83
+ // That is, if there are hourly starts, we add an hour to the time, and
84
+ // subtract baseTime mod freq. Then we add the delay.
63
85
  /** @type {import('@agoric/time/src/types').TimestampRecord} */
64
86
  const startTime = TimeMath.addAbsRel(
65
87
  TimeMath.addAbsRel(
@@ -91,15 +113,14 @@ harden(computeRoundTiming);
91
113
  * auction has started, then it'll be nextSchedule.startTime. Otherwise, it's
92
114
  * the start of the step following the current step.
93
115
  *
94
- * @param {import('./scheduler.js').Schedule | undefined} liveSchedule
95
- * @param {import('./scheduler.js').Schedule} nextSchedule
116
+ * @param {import('./scheduler.js').Schedule | null} liveSchedule
117
+ * @param {import('./scheduler.js').Schedule | null} nextSchedule
96
118
  * @param {Timestamp} now
119
+ * @returns {Timestamp | null}
97
120
  */
98
121
  export const nextDescendingStepTime = (liveSchedule, nextSchedule, now) => {
99
- nextSchedule || Fail`nextSchedule must always be defined`;
100
-
101
122
  if (!liveSchedule) {
102
- return nextSchedule.startTime;
123
+ return nextSchedule?.startTime || null;
103
124
  }
104
125
 
105
126
  const { startTime, endTime, clockStep } = liveSchedule;
@@ -114,9 +135,33 @@ export const nextDescendingStepTime = (liveSchedule, nextSchedule, now) => {
114
135
  const expectedNext = TimeMath.addAbsRel(lastStepStart, clockStep);
115
136
 
116
137
  if (TimeMath.compareAbs(expectedNext, endTime) > 0) {
117
- return nextSchedule.startTime;
138
+ return nextSchedule?.startTime || null;
118
139
  }
119
140
 
120
141
  return expectedNext;
121
142
  };
122
143
  harden(nextDescendingStepTime);
144
+
145
+ /**
146
+ *
147
+ * @param {Timestamp} time
148
+ * @param {import('./scheduler.js').Schedule} schedule
149
+ * @returns {'before' | 'during' | 'endExactly' | 'after'}
150
+ */
151
+ export const timeVsSchedule = (time, schedule) => {
152
+ const { startTime, endTime } = schedule;
153
+ assertAllDefined({ startTime, endTime });
154
+
155
+ if (TimeMath.compareAbs(time, schedule.startTime) < 0) {
156
+ return 'before';
157
+ }
158
+ if (TimeMath.compareAbs(time, schedule.endTime) < 0) {
159
+ return 'during';
160
+ }
161
+ if (TimeMath.compareAbs(time, schedule.endTime) === 0) {
162
+ return 'endExactly';
163
+ }
164
+
165
+ return 'after';
166
+ };
167
+ harden(timeVsSchedule);