@agoric/inter-protocol 0.13.3 → 0.13.4-dev-800c2bb.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 (140) hide show
  1. package/README.md +26 -20
  2. package/bundles/bundle-auctioneer-js-meta.json +722 -0
  3. package/bundles/bundle-auctioneer.js +1 -0
  4. package/bundles/bundle-econCommitteeCharter-js-meta.json +514 -0
  5. package/bundles/bundle-econCommitteeCharter.js +1 -0
  6. package/bundles/bundle-feeDistributor-js-meta.json +458 -0
  7. package/bundles/bundle-feeDistributor.js +1 -1
  8. package/bundles/bundle-fluxAggregatorKit-js-meta.json +674 -0
  9. package/bundles/bundle-fluxAggregatorKit.js +1 -0
  10. package/bundles/bundle-psm-js-meta.json +666 -0
  11. package/bundles/bundle-psm.js +1 -1
  12. package/bundles/bundle-reserve-js-meta.json +662 -0
  13. package/bundles/bundle-reserve.js +1 -1
  14. package/bundles/bundle-scaledPriceAuthority-js-meta.json +542 -0
  15. package/bundles/bundle-scaledPriceAuthority.js +1 -1
  16. package/bundles/bundle-vaultFactory-js-meta.json +786 -0
  17. package/bundles/bundle-vaultFactory.js +1 -1
  18. package/exported.js +0 -2
  19. package/package.json +34 -31
  20. package/scripts/add-collateral-core.js +14 -4
  21. package/scripts/build-bundles.js +9 -2
  22. package/scripts/deploy-contracts.js +1 -4
  23. package/scripts/init-core.js +20 -38
  24. package/scripts/invite-committee-core.js +1 -1
  25. package/scripts/manual-price-feed.js +2 -2
  26. package/scripts/price-feed-core.js +7 -6
  27. package/scripts/start-local-chain.sh +84 -0
  28. package/src/auction/auctionBook.js +755 -0
  29. package/src/auction/auctioneer.js +720 -0
  30. package/src/auction/offerBook.js +221 -0
  31. package/src/auction/params.js +174 -0
  32. package/src/auction/scheduleMath.js +167 -0
  33. package/src/auction/scheduler.js +370 -0
  34. package/src/auction/sortedOffers.js +136 -0
  35. package/src/auction/util.js +55 -0
  36. package/src/clientSupport.js +324 -0
  37. package/src/collectFees.js +8 -14
  38. package/src/contractSupport.js +24 -68
  39. package/src/econCommitteeCharter.js +161 -77
  40. package/src/feeDistributor.js +60 -20
  41. package/src/index.js +2 -0
  42. package/src/interest-math.js +1 -4
  43. package/src/interest.js +20 -13
  44. package/src/price/README.md +57 -0
  45. package/src/price/fluxAggregatorContract.js +190 -0
  46. package/src/price/fluxAggregatorKit.js +375 -0
  47. package/src/price/priceOracleKit.js +127 -0
  48. package/src/price/roundsManager.js +726 -0
  49. package/src/proposals/addAssetToVault.js +89 -94
  50. package/src/proposals/committee-proposal.js +141 -40
  51. package/src/proposals/core-proposal.js +100 -167
  52. package/src/proposals/econ-behaviors.js +320 -564
  53. package/src/proposals/price-feed-proposal.js +175 -78
  54. package/src/proposals/startEconCommittee.js +25 -12
  55. package/src/proposals/startPSM.js +231 -211
  56. package/src/proposals/utils.js +31 -17
  57. package/src/psm/README.md +71 -0
  58. package/src/psm/psm.js +272 -160
  59. package/src/psm/types.js +2 -0
  60. package/src/reserve/assetReserve.js +56 -413
  61. package/src/reserve/assetReserveKit.js +303 -0
  62. package/src/reserve/doc.md +1 -3
  63. package/src/reserve/params.js +2 -5
  64. package/src/typeGuards.js +13 -0
  65. package/src/vaultFactory/Liquidation.md +100 -0
  66. package/src/vaultFactory/burn.js +2 -1
  67. package/src/vaultFactory/liquidation.js +270 -108
  68. package/src/vaultFactory/math.js +95 -0
  69. package/src/vaultFactory/orderedVaultStore.js +0 -1
  70. package/src/vaultFactory/params.js +133 -112
  71. package/src/vaultFactory/prioritizedVaults.js +36 -79
  72. package/src/vaultFactory/storeUtils.js +67 -9
  73. package/src/vaultFactory/type-imports.js +2 -0
  74. package/src/vaultFactory/types.js +22 -26
  75. package/src/vaultFactory/vault.js +772 -709
  76. package/src/vaultFactory/vaultDirector.js +480 -483
  77. package/src/vaultFactory/vaultFactory.js +99 -60
  78. package/src/vaultFactory/vaultHolder.js +121 -95
  79. package/src/vaultFactory/vaultKit.js +39 -31
  80. package/src/vaultFactory/vaultManager.js +1249 -868
  81. package/bundles/bundle-amm-meta.js +0 -618
  82. package/bundles/bundle-amm.js +0 -1
  83. package/bundles/bundle-feeDistributor-meta.js +0 -346
  84. package/bundles/bundle-interchainPool-meta.js +0 -534
  85. package/bundles/bundle-interchainPool.js +0 -1
  86. package/bundles/bundle-liquidateIncrementally-meta.js +0 -442
  87. package/bundles/bundle-liquidateIncrementally.js +0 -1
  88. package/bundles/bundle-liquidateMinimum-meta.js +0 -442
  89. package/bundles/bundle-liquidateMinimum.js +0 -1
  90. package/bundles/bundle-psm-meta.js +0 -538
  91. package/bundles/bundle-psmCharter-meta.js +0 -418
  92. package/bundles/bundle-psmCharter.js +0 -1
  93. package/bundles/bundle-reserve-meta.js +0 -542
  94. package/bundles/bundle-scaledPriceAuthority-meta.js +0 -442
  95. package/bundles/bundle-stakeFactory-meta.js +0 -574
  96. package/bundles/bundle-stakeFactory.js +0 -1
  97. package/bundles/bundle-vaultFactory-meta.js +0 -598
  98. package/scripts/add-initial-liquidity.js +0 -180
  99. package/src/collect.js +0 -13
  100. package/src/interchainPool.js +0 -135
  101. package/src/makeTracer.js +0 -36
  102. package/src/my-lien.js +0 -85
  103. package/src/proposals/demoIssuers.js +0 -685
  104. package/src/proposals/sim-behaviors.js +0 -40
  105. package/src/psm/psmCharter.js +0 -139
  106. package/src/stakeFactory/README.md +0 -62
  107. package/src/stakeFactory/attestation.js +0 -267
  108. package/src/stakeFactory/attestationTool.js +0 -66
  109. package/src/stakeFactory/constants.js +0 -16
  110. package/src/stakeFactory/params.js +0 -31
  111. package/src/stakeFactory/stakeFactory.js +0 -236
  112. package/src/stakeFactory/stakeFactoryKit.js +0 -455
  113. package/src/stakeFactory/stakeFactoryManager.js +0 -296
  114. package/src/stakeFactory/types.js +0 -42
  115. package/src/vaultFactory/liquidateIncrementally.js +0 -350
  116. package/src/vaultFactory/liquidateMinimum.js +0 -100
  117. package/src/vpool-xyk-amm/AttackersGuide.md +0 -89
  118. package/src/vpool-xyk-amm/addLiquidity.js +0 -222
  119. package/src/vpool-xyk-amm/addPool.js +0 -197
  120. package/src/vpool-xyk-amm/constantProduct/AMM-Trade.jpeg +0 -0
  121. package/src/vpool-xyk-amm/constantProduct/README.md +0 -204
  122. package/src/vpool-xyk-amm/constantProduct/calcFees.js +0 -99
  123. package/src/vpool-xyk-amm/constantProduct/calcSwapPrices.js +0 -85
  124. package/src/vpool-xyk-amm/constantProduct/core.js +0 -153
  125. package/src/vpool-xyk-amm/constantProduct/defaults.js +0 -5
  126. package/src/vpool-xyk-amm/constantProduct/getXY.js +0 -52
  127. package/src/vpool-xyk-amm/constantProduct/internal-types.js +0 -116
  128. package/src/vpool-xyk-amm/constantProduct/invariants.js +0 -43
  129. package/src/vpool-xyk-amm/constantProduct/swap.js +0 -272
  130. package/src/vpool-xyk-amm/constantProduct/types.js +0 -32
  131. package/src/vpool-xyk-amm/doublePool.js +0 -194
  132. package/src/vpool-xyk-amm/estimate.js +0 -155
  133. package/src/vpool-xyk-amm/multipoolMarketMaker.js +0 -520
  134. package/src/vpool-xyk-amm/params.js +0 -61
  135. package/src/vpool-xyk-amm/pool.js +0 -396
  136. package/src/vpool-xyk-amm/priceAuthority.js +0 -94
  137. package/src/vpool-xyk-amm/removeLiquidity.js +0 -31
  138. package/src/vpool-xyk-amm/singlePool.js +0 -73
  139. package/src/vpool-xyk-amm/swap.js +0 -69
  140. package/src/vpool-xyk-amm/types.js +0 -154
@@ -0,0 +1,221 @@
1
+ // book of offers to buy liquidating vaults with prices in terms of
2
+ // discount/markup from the current oracle price.
3
+
4
+ import { AmountMath } from '@agoric/ertp';
5
+ import { M, mustMatch } from '@agoric/store';
6
+ import { makeScalarBigMapStore, prepareExoClass } from '@agoric/vat-data';
7
+
8
+ import {
9
+ toBidScalingComparator,
10
+ toPartialOfferKey,
11
+ toPriceOfferKey,
12
+ toScaledRateOfferKey,
13
+ } from './sortedOffers.js';
14
+
15
+ /** @typedef {import('@agoric/vat-data').Baggage} Baggage */
16
+
17
+ // multiple offers might be provided at the same time (since the time
18
+ // granularity is limited to blocks), so we increment a sequenceNumber with each
19
+ // offer for uniqueness.
20
+ let latestSequenceNumber = 0n;
21
+ const nextSequenceNumber = () => {
22
+ latestSequenceNumber += 1n;
23
+ return latestSequenceNumber;
24
+ };
25
+
26
+ /**
27
+ * @typedef {{
28
+ * seat: ZCFSeat,
29
+ * wanted: Amount<'nat'>,
30
+ * seqNum: NatValue,
31
+ * received: Amount<'nat'>,
32
+ * } & {exitAfterBuy: boolean} & ({ bidScaling: Pattern, price: undefined } | { bidScaling: undefined, price: Ratio})
33
+ * } BidderRecord
34
+ */
35
+
36
+ const ScaledBidBookStateShape = harden({
37
+ bidScalingPattern: M.any(),
38
+ collateralBrand: M.any(),
39
+ records: M.any(),
40
+ });
41
+
42
+ /**
43
+ * Prices in this book are expressed as percentage of the full oracle price
44
+ * snapshot taken when the auction started. .4 is 60% off. 1.1 is 10% above par.
45
+ *
46
+ * @param {Baggage} baggage
47
+ */
48
+ export const prepareScaledBidBook = baggage =>
49
+ prepareExoClass(
50
+ baggage,
51
+ 'scaledBidBook',
52
+ undefined,
53
+ /**
54
+ *
55
+ * @param {Pattern} bidScalingPattern
56
+ * @param {Brand} collateralBrand
57
+ */
58
+ (bidScalingPattern, collateralBrand) => ({
59
+ bidScalingPattern,
60
+ collateralBrand,
61
+ /** @type {MapStore<string, BidderRecord>} */
62
+ records: makeScalarBigMapStore('scaledBidRecords', { durable: true }),
63
+ }),
64
+ {
65
+ /**
66
+ * @param {ZCFSeat} seat
67
+ * @param {Ratio} bidScaling
68
+ * @param {Amount<'nat'>} wanted
69
+ * @param {boolean} exitAfterBuy
70
+ */
71
+ add(seat, bidScaling, wanted, exitAfterBuy) {
72
+ const { bidScalingPattern, collateralBrand, records } = this.state;
73
+ mustMatch(bidScaling, bidScalingPattern);
74
+
75
+ const seqNum = nextSequenceNumber();
76
+ const key = toScaledRateOfferKey(bidScaling, seqNum);
77
+ const empty = AmountMath.makeEmpty(collateralBrand);
78
+ /** @type {BidderRecord} */
79
+ const bidderRecord = {
80
+ bidScaling,
81
+ price: undefined,
82
+ received: empty,
83
+ seat,
84
+ seqNum,
85
+ wanted,
86
+ exitAfterBuy,
87
+ };
88
+ records.init(key, harden(bidderRecord));
89
+ return key;
90
+ },
91
+ /** @param {Ratio} bidScaling */
92
+ offersAbove(bidScaling) {
93
+ const { records } = this.state;
94
+ return [...records.entries(M.gte(toBidScalingComparator(bidScaling)))];
95
+ },
96
+ hasOrders() {
97
+ const { records } = this.state;
98
+ return records.getSize() > 0;
99
+ },
100
+ delete(key) {
101
+ const { records } = this.state;
102
+ records.delete(key);
103
+ },
104
+ updateReceived(key, sold) {
105
+ const { records } = this.state;
106
+ const oldRec = records.get(key);
107
+ records.set(
108
+ key,
109
+ harden({
110
+ ...oldRec,
111
+ received: AmountMath.add(oldRec.received, sold),
112
+ }),
113
+ );
114
+ },
115
+ exitAllSeats() {
116
+ const { records } = this.state;
117
+ for (const [key, { seat }] of records.entries()) {
118
+ if (!seat.hasExited()) {
119
+ seat.exit();
120
+ records.delete(key);
121
+ }
122
+ }
123
+ },
124
+ },
125
+ {
126
+ stateShape: ScaledBidBookStateShape,
127
+ },
128
+ );
129
+
130
+ const PriceBookStateShape = harden({
131
+ priceRatioPattern: M.any(),
132
+ collateralBrand: M.any(),
133
+ records: M.any(),
134
+ });
135
+
136
+ /**
137
+ * Prices in this book are actual prices expressed in terms of bid amount
138
+ * and collateral amount.
139
+ *
140
+ * @param {Baggage} baggage
141
+ */
142
+ export const preparePriceBook = baggage =>
143
+ prepareExoClass(
144
+ baggage,
145
+ 'priceBook',
146
+ undefined,
147
+ /**
148
+ *
149
+ * @param {Pattern} priceRatioPattern
150
+ * @param {Brand} collateralBrand
151
+ */
152
+ (priceRatioPattern, collateralBrand) => ({
153
+ priceRatioPattern,
154
+ collateralBrand,
155
+ /** @type {MapStore<string, BidderRecord>} */
156
+ records: makeScalarBigMapStore('scaledBidRecords', { durable: true }),
157
+ }),
158
+ {
159
+ /**
160
+ * @param {ZCFSeat} seat
161
+ * @param {Ratio} price
162
+ * @param {Amount<'nat'>} wanted
163
+ * @param {boolean} exitAfterBuy
164
+ */
165
+ add(seat, price, wanted, exitAfterBuy) {
166
+ const { priceRatioPattern, collateralBrand, records } = this.state;
167
+ mustMatch(price, priceRatioPattern);
168
+
169
+ const seqNum = nextSequenceNumber();
170
+ const key = toPriceOfferKey(price, seqNum);
171
+ const empty = AmountMath.makeEmpty(collateralBrand);
172
+ /** @type {BidderRecord} */
173
+ const bidderRecord = {
174
+ bidScaling: undefined,
175
+ price,
176
+ received: empty,
177
+ seat,
178
+ seqNum,
179
+ wanted,
180
+ exitAfterBuy,
181
+ };
182
+ records.init(key, harden(bidderRecord));
183
+ return key;
184
+ },
185
+ offersAbove(price) {
186
+ const { records } = this.state;
187
+ return [...records.entries(M.gte(toPartialOfferKey(price)))];
188
+ },
189
+ hasOrders() {
190
+ const { records } = this.state;
191
+ return records.getSize() > 0;
192
+ },
193
+ delete(key) {
194
+ const { records } = this.state;
195
+ records.delete(key);
196
+ },
197
+ updateReceived(key, sold) {
198
+ const { records } = this.state;
199
+ const oldRec = records.get(key);
200
+ records.set(
201
+ key,
202
+ harden({
203
+ ...oldRec,
204
+ received: AmountMath.add(oldRec.received, sold),
205
+ }),
206
+ );
207
+ },
208
+ exitAllSeats() {
209
+ const { records } = this.state;
210
+ for (const [key, { seat }] of records.entries()) {
211
+ if (!seat.hasExited()) {
212
+ seat.exit();
213
+ records.delete(key);
214
+ }
215
+ }
216
+ },
217
+ },
218
+ {
219
+ stateShape: PriceBookStateShape,
220
+ },
221
+ );
@@ -0,0 +1,174 @@
1
+ import {
2
+ CONTRACT_ELECTORATE,
3
+ makeParamManager,
4
+ ParamTypes,
5
+ } from '@agoric/governance';
6
+ import { TimeMath, RelativeTimeRecordShape } from '@agoric/time';
7
+ import { M } from '@agoric/store';
8
+
9
+ /** @typedef {import('@agoric/governance/src/contractGovernance/typedParamManager.js').AsyncSpecTuple} AsyncSpecTuple */
10
+ /** @typedef {import('@agoric/governance/src/contractGovernance/typedParamManager.js').SyncSpecTuple} SyncSpecTuple */
11
+
12
+ // TODO duplicated with zoe/src/TypeGuards.js
13
+ export const InvitationShape = M.remotable('Invitation');
14
+
15
+ /**
16
+ * In seconds, how often to start an auction. The auction will start at
17
+ * AUCTION_START_DELAY seconds after a multiple of START_FREQUENCY, with the
18
+ * price at STARTING_RATE_BP. Every CLOCK_STEP, the price will be reduced by
19
+ * DISCOUNT_STEP_BP, as long as the rate is at or above LOWEST_RATE_BP, or until
20
+ * START_FREQUENCY has elapsed.
21
+ */
22
+ export const START_FREQUENCY = 'StartFrequency';
23
+ /** in seconds, how often to reduce the price */
24
+ export const CLOCK_STEP = 'ClockStep';
25
+ /** discount or markup for starting price in basis points. 9999 = 1bp discount */
26
+ export const STARTING_RATE_BP = 'StartingRate';
27
+ /** A limit below which the price will not be discounted. */
28
+ export const LOWEST_RATE_BP = 'LowestRate';
29
+ /** amount to reduce prices each time step in bp, as % of the start price */
30
+ export const DISCOUNT_STEP_BP = 'DiscountStep';
31
+ /**
32
+ * VaultManagers liquidate vaults at a frequency configured by START_FREQUENCY.
33
+ * Auctions start this long after the hour to give vaults time to finish.
34
+ */
35
+ export const AUCTION_START_DELAY = 'AuctionStartDelay';
36
+
37
+ // /////// used by VaultDirector /////////////////////
38
+ // time before each auction that the prices are locked.
39
+ export const PRICE_LOCK_PERIOD = 'PriceLockPeriod';
40
+
41
+ export const auctioneerParamPattern = M.splitRecord({
42
+ [CONTRACT_ELECTORATE]: InvitationShape,
43
+ [START_FREQUENCY]: RelativeTimeRecordShape,
44
+ [CLOCK_STEP]: RelativeTimeRecordShape,
45
+ [STARTING_RATE_BP]: M.nat(),
46
+ [LOWEST_RATE_BP]: M.nat(),
47
+ [DISCOUNT_STEP_BP]: M.nat(),
48
+ [AUCTION_START_DELAY]: RelativeTimeRecordShape,
49
+ [PRICE_LOCK_PERIOD]: RelativeTimeRecordShape,
50
+ });
51
+
52
+ export const auctioneerParamTypes = harden({
53
+ [CONTRACT_ELECTORATE]: ParamTypes.INVITATION,
54
+ [START_FREQUENCY]: ParamTypes.RELATIVE_TIME,
55
+ [CLOCK_STEP]: ParamTypes.RELATIVE_TIME,
56
+ [STARTING_RATE_BP]: ParamTypes.NAT,
57
+ [LOWEST_RATE_BP]: ParamTypes.NAT,
58
+ [DISCOUNT_STEP_BP]: ParamTypes.NAT,
59
+ [AUCTION_START_DELAY]: ParamTypes.RELATIVE_TIME,
60
+ [PRICE_LOCK_PERIOD]: ParamTypes.RELATIVE_TIME,
61
+ });
62
+
63
+ /**
64
+ * @typedef {object} AuctionParams
65
+ * @property {Amount<'set'>} ElectorateInvitationAmount
66
+ * @property {RelativeTime} StartFrequency
67
+ * @property {RelativeTime} ClockStep
68
+ * @property {bigint} StartingRate
69
+ * @property {bigint} LowestRate
70
+ * @property {bigint} DiscountStep
71
+ * @property {RelativeTime} AuctionStartDelay
72
+ * @property {RelativeTime} PriceLockPeriod
73
+ * @property {import('@agoric/time/src/types').TimerBrand} TimerBrand
74
+ */
75
+
76
+ /**
77
+ * @param {AuctionParams} initial
78
+ */
79
+ export const makeAuctioneerParams = ({
80
+ ElectorateInvitationAmount,
81
+ StartFrequency,
82
+ ClockStep,
83
+ LowestRate,
84
+ StartingRate,
85
+ DiscountStep,
86
+ AuctionStartDelay,
87
+ PriceLockPeriod,
88
+ TimerBrand,
89
+ }) => {
90
+ return harden({
91
+ [CONTRACT_ELECTORATE]: {
92
+ type: ParamTypes.INVITATION,
93
+ value: ElectorateInvitationAmount,
94
+ },
95
+ [START_FREQUENCY]: {
96
+ type: ParamTypes.RELATIVE_TIME,
97
+ value: TimeMath.coerceRelativeTimeRecord(StartFrequency, TimerBrand),
98
+ },
99
+ [CLOCK_STEP]: {
100
+ type: ParamTypes.RELATIVE_TIME,
101
+ value: TimeMath.coerceRelativeTimeRecord(ClockStep, TimerBrand),
102
+ },
103
+ [AUCTION_START_DELAY]: {
104
+ type: ParamTypes.RELATIVE_TIME,
105
+ value: TimeMath.coerceRelativeTimeRecord(AuctionStartDelay, TimerBrand),
106
+ },
107
+ [PRICE_LOCK_PERIOD]: {
108
+ type: ParamTypes.RELATIVE_TIME,
109
+ value: TimeMath.coerceRelativeTimeRecord(PriceLockPeriod, TimerBrand),
110
+ },
111
+ [STARTING_RATE_BP]: { type: ParamTypes.NAT, value: StartingRate },
112
+ [LOWEST_RATE_BP]: { type: ParamTypes.NAT, value: LowestRate },
113
+ [DISCOUNT_STEP_BP]: { type: ParamTypes.NAT, value: DiscountStep },
114
+ });
115
+ };
116
+ harden(makeAuctioneerParams);
117
+
118
+ /**
119
+ * @param {import('@agoric/notifier').StoredPublisherKit<GovernanceSubscriptionState>} publisherKit
120
+ * @param {ZCF} zcf
121
+ * @param {AuctionParams} initial
122
+ */
123
+ export const makeAuctioneerParamManager = (publisherKit, zcf, initial) => {
124
+ return makeParamManager(
125
+ publisherKit,
126
+ {
127
+ [CONTRACT_ELECTORATE]: [
128
+ ParamTypes.INVITATION,
129
+ initial[CONTRACT_ELECTORATE],
130
+ ],
131
+ [START_FREQUENCY]: [ParamTypes.RELATIVE_TIME, initial[START_FREQUENCY]],
132
+ [CLOCK_STEP]: [ParamTypes.RELATIVE_TIME, initial[CLOCK_STEP]],
133
+ [STARTING_RATE_BP]: [ParamTypes.NAT, initial[STARTING_RATE_BP]],
134
+ [LOWEST_RATE_BP]: [ParamTypes.NAT, initial[LOWEST_RATE_BP]],
135
+ [DISCOUNT_STEP_BP]: [ParamTypes.NAT, initial[DISCOUNT_STEP_BP]],
136
+ [AUCTION_START_DELAY]: [
137
+ ParamTypes.RELATIVE_TIME,
138
+ initial[AUCTION_START_DELAY],
139
+ ],
140
+ [PRICE_LOCK_PERIOD]: [
141
+ ParamTypes.RELATIVE_TIME,
142
+ initial[PRICE_LOCK_PERIOD],
143
+ ],
144
+ },
145
+ zcf,
146
+ );
147
+ };
148
+ harden(makeAuctioneerParamManager);
149
+
150
+ /**
151
+ * @param {{storageNode: ERef<StorageNode>, marshaller: ERef<Marshaller>}} caps
152
+ * @param {ERef<Timer>} timer
153
+ * @param {ERef<PriceAuthority>} priceAuthority
154
+ * @param {ERef<AssetReservePublicFacet>} reservePublicFacet
155
+ * @param {AuctionParams} params
156
+ */
157
+ export const makeGovernedTerms = (
158
+ { storageNode: _storageNode, marshaller: _marshaller },
159
+ timer,
160
+ priceAuthority,
161
+ reservePublicFacet,
162
+ params,
163
+ ) => {
164
+ // XXX use storageNode and Marshaller
165
+ return harden({
166
+ priceAuthority,
167
+ reservePublicFacet,
168
+ timerService: timer,
169
+ governedParams: makeAuctioneerParams(params),
170
+ });
171
+ };
172
+ harden(makeGovernedTerms);
173
+
174
+ /** @typedef {ReturnType<typeof makeAuctioneerParamManager>} AuctionParamManager */
@@ -0,0 +1,167 @@
1
+ // @jessie-check
2
+
3
+ import { TimeMath } from '@agoric/time';
4
+ import { natSafeMath } from '@agoric/zoe/src/contractSupport/index.js';
5
+ import { assertAllDefined, makeTracer } from '@agoric/internal';
6
+
7
+ const { subtract, multiply, floorDivide } = natSafeMath;
8
+ const { Fail } = assert;
9
+
10
+ const trace = makeTracer('SMath', true);
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
+ */
40
+ export const computeRoundTiming = (params, baseTime) => {
41
+ const freq = params.getStartFrequency();
42
+ const clockStep = params.getClockStep();
43
+ const startingRate = params.getStartingRate();
44
+ const discountStep = params.getDiscountStep();
45
+ const lockPeriod = params.getPriceLockPeriod();
46
+ const lowestRate = params.getLowestRate();
47
+ const startDelay = params.getAuctionStartDelay();
48
+
49
+ TimeMath.compareRel(freq, startDelay) > 0 ||
50
+ Fail`startFrequency must exceed startDelay, ${freq}, ${startDelay}`;
51
+ TimeMath.compareRel(freq, lockPeriod) > 0 ||
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`;
59
+
60
+ startingRate > lowestRate ||
61
+ Fail`startingRate ${startingRate} must be more than lowest: ${lowestRate}`;
62
+
63
+ const rateChange = subtract(startingRate, lowestRate);
64
+ const requestedSteps = floorDivide(rateChange, discountStep);
65
+ requestedSteps > 0n ||
66
+ Fail`discountStep ${discountStep} too large for requested rates`;
67
+
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;
74
+
75
+ const duration = TimeMath.multiplyRelNat(clockStep, steps);
76
+ steps > 0n ||
77
+ Fail`clockStep ${clockStep} too long for auction duration ${duration}`;
78
+ const endRate = subtract(startingRate, multiply(steps, discountStep));
79
+
80
+ // Use a duration that exactly matches the last step completing (no remainder)
81
+ const actualDuration = TimeMath.multiplyRelNat(clockStep, steps);
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.
85
+ /** @type {import('@agoric/time/src/types').TimestampRecord} */
86
+ const startTime = TimeMath.addAbsRel(
87
+ TimeMath.addAbsRel(
88
+ baseTime,
89
+ TimeMath.subtractRelRel(freq, TimeMath.modAbsRel(baseTime, freq)),
90
+ ),
91
+ startDelay,
92
+ );
93
+ const endTime = TimeMath.addAbsRel(startTime, actualDuration);
94
+ const lockTime = TimeMath.subtractAbsRel(startTime, lockPeriod);
95
+
96
+ /** @type {import('./scheduler.js').Schedule} */
97
+ const next = {
98
+ startTime,
99
+ endTime,
100
+ steps,
101
+ endRate,
102
+ startDelay,
103
+ clockStep,
104
+ lockTime,
105
+ };
106
+ return harden(next);
107
+ };
108
+ harden(computeRoundTiming);
109
+
110
+ /**
111
+ * Calculate when the next descending step will start. If we're between
112
+ * auctions (i.e. liveSchedule is undefined), or the last step of the current
113
+ * auction has started, then it'll be nextSchedule.startTime. Otherwise, it's
114
+ * the start of the step following the current step.
115
+ *
116
+ * @param {import('./scheduler.js').Schedule | null} liveSchedule
117
+ * @param {import('./scheduler.js').Schedule | null} nextSchedule
118
+ * @param {Timestamp} now
119
+ * @returns {Timestamp | null}
120
+ */
121
+ export const nextDescendingStepTime = (liveSchedule, nextSchedule, now) => {
122
+ if (!liveSchedule) {
123
+ return nextSchedule?.startTime || null;
124
+ }
125
+
126
+ const { startTime, endTime, clockStep } = liveSchedule;
127
+ trace('calc', startTime, now);
128
+ if (TimeMath.compareAbs(startTime, now) > 0) {
129
+ return startTime;
130
+ }
131
+
132
+ const elapsed = TimeMath.subtractAbsAbs(now, startTime);
133
+ const sinceLastStep = TimeMath.modRelRel(elapsed, clockStep);
134
+ const lastStepStart = TimeMath.subtractAbsRel(now, sinceLastStep);
135
+ const expectedNext = TimeMath.addAbsRel(lastStepStart, clockStep);
136
+
137
+ if (TimeMath.compareAbs(expectedNext, endTime) > 0) {
138
+ return nextSchedule?.startTime || null;
139
+ }
140
+
141
+ return expectedNext;
142
+ };
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);