@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,370 @@
1
+ import { E } from '@endo/eventual-send';
2
+ import { TimeMath } from '@agoric/time';
3
+ import { Far } from '@endo/marshal';
4
+ import { makeTracer } from '@agoric/internal';
5
+ import { observeIteration, subscribeEach } from '@agoric/notifier';
6
+
7
+ import { AuctionState, makeCancelTokenMaker } from './util.js';
8
+ import {
9
+ computeRoundTiming,
10
+ nextDescendingStepTime,
11
+ timeVsSchedule,
12
+ } from './scheduleMath.js';
13
+
14
+ const { details: X, Fail, quote: q } = assert;
15
+
16
+ const trace = makeTracer('SCHED', true);
17
+
18
+ // If the startAuction wakeup is no more than 5 minutes late, go ahead with it.
19
+ const MAX_LATE_TICK = 300n;
20
+
21
+ /**
22
+ * @file The scheduler is presumed to be quiescent between auction rounds. Each
23
+ * Auction round consists of a sequence of steps with decreasing prices. There
24
+ * should always be a next schedule, but between rounds, liveSchedule is null.
25
+ *
26
+ * The lock period that the liquidators use might start before the previous
27
+ * round has finished, so we need to schedule the next round each time an
28
+ * auction starts. This means if the scheduling parameters change, it'll be a
29
+ * full cycle before we switch. Otherwise, the vaults wouldn't know when to
30
+ * start their lock period. If the lock period for the next auction hasn't
31
+ * started when each aucion ends, we recalculate it, in case the parameters have
32
+ * changed.
33
+ *
34
+ * If the clock skips forward (because of a chain halt, for instance), the
35
+ * scheduler will try to cleanly and quickly finish any round already in
36
+ * progress. It would take additional work on the manual timer to test this
37
+ * thoroughly.
38
+ */
39
+
40
+ const makeCancelToken = makeCancelTokenMaker('scheduler');
41
+
42
+ /**
43
+ * @typedef {object} AuctionDriver
44
+ * @property {() => void} reducePriceAndTrade
45
+ * @property {() => void} finalize
46
+ * @property {() => void} startRound
47
+ * @property {() => void} capturePrices
48
+ */
49
+
50
+ /**
51
+ * @typedef {object} ScheduleNotification
52
+ *
53
+ * @property {Timestamp | null} activeStartTime start time of current
54
+ * auction if auction is active
55
+ * @property {Timestamp | null} nextStartTime start time of next auction
56
+ * @property {Timestamp | null} nextDescendingStepTime when the next descending step
57
+ * will take place
58
+ */
59
+
60
+ const safelyComputeRoundTiming = (params, baseTime) => {
61
+ try {
62
+ return computeRoundTiming(params, baseTime);
63
+ } catch (e) {
64
+ console.error('🚨 No Next Auction', e);
65
+ return null;
66
+ }
67
+ };
68
+
69
+ const nominalStartTime = nextSchedule =>
70
+ TimeMath.subtractAbsRel(nextSchedule.startTime, nextSchedule.startDelay);
71
+
72
+ /**
73
+ * @param {AuctionDriver} auctionDriver
74
+ * @param {import('@agoric/time/src/types').TimerService} timer
75
+ * @param {Awaited<import('./params.js').AuctionParamManager>} params
76
+ * @param {import('@agoric/time/src/types').TimerBrand} timerBrand
77
+ * @param {import('@agoric/zoe/src/contractSupport/recorder.js').Recorder<ScheduleNotification>} scheduleRecorder
78
+ * @param {StoredSubscription<GovernanceSubscriptionState>} paramUpdateSubscription
79
+ */
80
+ export const makeScheduler = async (
81
+ auctionDriver,
82
+ timer,
83
+ params,
84
+ timerBrand,
85
+ scheduleRecorder,
86
+ paramUpdateSubscription,
87
+ ) => {
88
+ /**
89
+ * live version is defined when an auction is active.
90
+ *
91
+ * @type {Schedule | null}
92
+ */
93
+ let liveSchedule = null;
94
+
95
+ /** @returns {Promise<{ now: Timestamp, nextSchedule: Schedule | null }>} */
96
+ const initializeNextSchedule = async () => {
97
+ return E.when(
98
+ // XXX manualTimer returns a bigint, not a timeRecord.
99
+ E(timer).getCurrentTimestamp(),
100
+ now => {
101
+ const nextSchedule = safelyComputeRoundTiming(
102
+ params,
103
+ TimeMath.coerceTimestampRecord(now, timerBrand),
104
+ );
105
+ return { now, nextSchedule };
106
+ },
107
+ );
108
+ };
109
+ let { now, nextSchedule } = await initializeNextSchedule();
110
+ const setTimeMonotonically = time => {
111
+ TimeMath.compareAbs(time, now) >= 0 || Fail`Time only moves forward`;
112
+ now = time;
113
+ };
114
+
115
+ const stepCancelToken = makeCancelToken();
116
+
117
+ /** @type {typeof AuctionState[keyof typeof AuctionState]} */
118
+ let auctionState = AuctionState.WAITING;
119
+
120
+ /**
121
+ * Publish the schedule. To be called after clockTick() (which processes a
122
+ * descending step) or when the next round is scheduled.
123
+ */
124
+ const publishSchedule = () => {
125
+ const sched = harden({
126
+ activeStartTime: liveSchedule?.startTime || null,
127
+ nextStartTime: nextSchedule?.startTime || null,
128
+ nextDescendingStepTime: nextDescendingStepTime(
129
+ liveSchedule,
130
+ nextSchedule,
131
+ now,
132
+ ),
133
+ });
134
+ return scheduleRecorder.write(sched);
135
+ };
136
+
137
+ /**
138
+ * @param {Schedule | null} schedule
139
+ * @returns {void}
140
+ */
141
+ const clockTick = schedule => {
142
+ trace('clockTick', schedule?.startTime, now);
143
+ if (!schedule) {
144
+ return;
145
+ }
146
+
147
+ /** @type {() => void} */
148
+ const finishAuctionRound = () => {
149
+ auctionState = AuctionState.WAITING;
150
+ auctionDriver.finalize();
151
+
152
+ if (nextSchedule) {
153
+ // only recalculate the next schedule at this point if the lock time has
154
+ // not been reached.
155
+ const nextLock = nextSchedule.lockTime;
156
+ if (nextLock && TimeMath.compareAbs(now, nextLock) < 0) {
157
+ const afterNow = TimeMath.addAbsRel(
158
+ now,
159
+ TimeMath.coerceRelativeTimeRecord(1n, timerBrand),
160
+ );
161
+ nextSchedule = safelyComputeRoundTiming(params, afterNow);
162
+ }
163
+ } else {
164
+ console.error(
165
+ '🛠️ finishAuctionRound without scheduling the next; repair with new auctioneer params',
166
+ );
167
+ }
168
+
169
+ liveSchedule = null;
170
+
171
+ // Async because a remote call. In rare event of failure, wakeup is robust.
172
+ void E(timer).cancel(stepCancelToken);
173
+ };
174
+
175
+ const advanceRound = () => {
176
+ if (auctionState === AuctionState.ACTIVE) {
177
+ auctionDriver.reducePriceAndTrade();
178
+ } else {
179
+ auctionState = AuctionState.ACTIVE;
180
+ try {
181
+ auctionDriver.startRound();
182
+ // This has been observed to fail because prices hadn't been locked.
183
+ // This may be an issue about timing during chain start-up.
184
+ } catch (err) {
185
+ console.error(
186
+ 'Unable to start auction cleanly. skipping this auction round.',
187
+ err,
188
+ );
189
+ }
190
+ }
191
+ };
192
+
193
+ switch (timeVsSchedule(now, schedule)) {
194
+ case 'before':
195
+ break;
196
+ case 'during':
197
+ advanceRound();
198
+ break;
199
+ case 'endExactly':
200
+ // do both the "during" and "after" behaviors
201
+ advanceRound();
202
+ finishAuctionRound();
203
+ break;
204
+ case 'after':
205
+ finishAuctionRound();
206
+ break;
207
+ default:
208
+ Fail`invalid case`;
209
+ }
210
+
211
+ void publishSchedule();
212
+ };
213
+
214
+ // set wakeups for the steps of this round
215
+ const scheduleDescendingSteps = () => {
216
+ if (!liveSchedule) {
217
+ console.error('🚨 scheduleDescendingSteps called with no liveSchedule');
218
+ return;
219
+ }
220
+
221
+ const { startTime } = liveSchedule;
222
+ trace('START ', startTime);
223
+
224
+ const delayFromNow =
225
+ TimeMath.compareAbs(startTime, now) > 0
226
+ ? TimeMath.subtractAbsAbs(startTime, now)
227
+ : TimeMath.subtractAbsAbs(startTime, startTime);
228
+
229
+ trace('scheduleDescendingSteps repeating', now, delayFromNow, startTime);
230
+
231
+ void E(timer).repeatAfter(
232
+ delayFromNow,
233
+ liveSchedule.clockStep,
234
+ Far('PriceStepWaker', {
235
+ wake(time) {
236
+ setTimeMonotonically(time);
237
+ trace('wake step', now);
238
+ void clockTick(liveSchedule);
239
+ },
240
+ }),
241
+ stepCancelToken,
242
+ );
243
+ };
244
+
245
+ // schedule a wakeup for the next round
246
+ const scheduleNextRound = start => {
247
+ trace(`nextRound`, start);
248
+ void E(timer).setWakeup(
249
+ start,
250
+ Far('SchedulerWaker', {
251
+ wake(time) {
252
+ setTimeMonotonically(time);
253
+ auctionDriver.capturePrices();
254
+ // eslint-disable-next-line no-use-before-define
255
+ return startAuction();
256
+ },
257
+ }),
258
+ );
259
+ void publishSchedule();
260
+ };
261
+
262
+ const relativeTime = t => TimeMath.coerceRelativeTimeRecord(t, timerBrand);
263
+
264
+ const startAuction = async () => {
265
+ !liveSchedule || Fail`can't start an auction round while one is active`;
266
+
267
+ if (!nextSchedule) {
268
+ console.error(
269
+ assert.error(X`tried to start auction when none is scheduled`),
270
+ );
271
+ return;
272
+ }
273
+
274
+ // The clock tick may have arrived too late to trigger the next scheduled
275
+ // round, for example because of a chain halt. When this happens the oracle
276
+ // quote may out of date and so must be ignored. Recover by returning
277
+ // deposits and scheduling the next round. If it's only a little late,
278
+ // continue with auction, just starting late.
279
+ const nominalStart = nominalStartTime(nextSchedule);
280
+ if (TimeMath.compareAbs(now, nominalStart) > 0) {
281
+ const late = TimeMath.subtractAbsAbs(now, nominalStart);
282
+ const maxLate = relativeTime(MAX_LATE_TICK);
283
+
284
+ if (TimeMath.compareRel(late, maxLate) > 0) {
285
+ console.warn(
286
+ `Auction time jumped to ${q(now)} before next scheduled start ${q(
287
+ nextSchedule.startTime,
288
+ )}. Skipping that round.`,
289
+ );
290
+ nextSchedule = safelyComputeRoundTiming(params, now);
291
+ } else {
292
+ console.warn(`Auction started late by ${q(late)}. Starting ${q(now)}`);
293
+ }
294
+ }
295
+
296
+ if (!nextSchedule) {
297
+ // nothing new to schedule
298
+ return;
299
+ }
300
+ // activate the nextSchedule as the live one
301
+ // (read here and in function calls below)
302
+ liveSchedule = nextSchedule;
303
+ scheduleDescendingSteps();
304
+ nextSchedule = safelyComputeRoundTiming(
305
+ params,
306
+ TimeMath.addAbsRel(liveSchedule.endTime, relativeTime(1n)),
307
+ );
308
+
309
+ if (nextSchedule) {
310
+ scheduleNextRound(nominalStartTime(nextSchedule));
311
+ } else {
312
+ console.warn(
313
+ 'no nextSchedule so cannot schedule next round or price capture',
314
+ );
315
+ }
316
+ };
317
+
318
+ // initial setting: firstStart is startDelay before next's startTime
319
+ const startSchedulingFromScratch = () => {
320
+ trace('startSchedulingFromScratch');
321
+ if (nextSchedule) {
322
+ scheduleNextRound(nominalStartTime(nextSchedule));
323
+ }
324
+ };
325
+ startSchedulingFromScratch();
326
+
327
+ // when auction parameters change, schedule a next auction if one isn't
328
+ // already scheduled.
329
+ // NB: what is already scheduled (live or next) is unaffected by param changes
330
+ void observeIteration(
331
+ subscribeEach(paramUpdateSubscription),
332
+ harden({
333
+ // NB: may be fired with the initial params as well
334
+ async updateState(_newState) {
335
+ trace('received param update', _newState);
336
+ if (!nextSchedule) {
337
+ trace('repairing nextSchedule and restarting');
338
+ ({ nextSchedule } = await initializeNextSchedule());
339
+ startSchedulingFromScratch();
340
+ }
341
+ },
342
+ }),
343
+ );
344
+
345
+ return Far('scheduler', {
346
+ getSchedule: () =>
347
+ harden({
348
+ liveAuctionSchedule: liveSchedule,
349
+ nextAuctionSchedule: nextSchedule,
350
+ }),
351
+ getAuctionState: () => auctionState,
352
+ });
353
+ };
354
+
355
+ /**
356
+ * @typedef {object} Schedule
357
+ * @property {import('@agoric/time/src/types').TimestampRecord} startTime
358
+ * @property {import('@agoric/time/src/types').TimestampRecord} endTime
359
+ * @property {NatValue} steps
360
+ * @property {NatValue} endRate
361
+ * @property {RelativeTime} startDelay
362
+ * @property {RelativeTime} clockStep
363
+ * @property {Timestamp} [lockTime]
364
+ */
365
+
366
+ /**
367
+ * @typedef {object} FullSchedule
368
+ * @property {Schedule | null} nextAuctionSchedule
369
+ * @property {Schedule | null} liveAuctionSchedule
370
+ */
@@ -0,0 +1,136 @@
1
+ // @jessie-check
2
+
3
+ import {
4
+ makeRatio,
5
+ ratioToNumber,
6
+ } from '@agoric/zoe/src/contractSupport/index.js';
7
+ import { M, mustMatch } from '@agoric/store';
8
+ import { RatioShape } from '@agoric/ertp';
9
+
10
+ import { decodeData, encodeData } from '../vaultFactory/storeUtils.js';
11
+
12
+ const { Fail } = assert;
13
+
14
+ /**
15
+ * @file we use a floating point representation of the price or rate as the
16
+ * first part of the key in the store. The second part is the sequence number of
17
+ * the bid, but it doesn't matter for sorting. When we retrieve multiple bids,
18
+ * it's only by bid value, so we don't care how the sequence numbers sort.
19
+ *
20
+ * We take advantage of the fact that encodeData takes a passable and turns it
21
+ * into a sort key. Arrays of passable data sort like composite keys.
22
+ */
23
+
24
+ /**
25
+ * Return a sort key that will compare based only on price. Price is the prefix
26
+ * of the complete sort key, which is sufficient to find offers below a cutoff.
27
+ *
28
+ * @param {Ratio} offerPrice
29
+ */
30
+ export const toPartialOfferKey = offerPrice => {
31
+ assert(offerPrice);
32
+ const mostSignificantPart = ratioToNumber(offerPrice);
33
+ return encodeData(harden([mostSignificantPart, 0n]));
34
+ };
35
+
36
+ /**
37
+ * Return a sort key that distinguishes by Price and sequence number
38
+ *
39
+ * @param {Ratio} offerPrice IST/collateral
40
+ * @param {bigint} sequenceNumber
41
+ * @returns {string} lexically sortable string in which highest price is first,
42
+ * ties will be broken by sequenceNumber of offer
43
+ */
44
+ export const toPriceOfferKey = (offerPrice, sequenceNumber) => {
45
+ mustMatch(offerPrice, RatioShape);
46
+ offerPrice.numerator.brand !== offerPrice.denominator.brand ||
47
+ Fail`offer prices must have different numerator and denominator`;
48
+ mustMatch(sequenceNumber, M.nat());
49
+
50
+ const mostSignificantPart = ratioToNumber(offerPrice);
51
+ return encodeData(harden([mostSignificantPart, sequenceNumber]));
52
+ };
53
+
54
+ /**
55
+ * @param {number} floatPrice
56
+ * @param {Brand<'nat'>} numBrand
57
+ * @param {Brand<'nat'>} denomBrand
58
+ * @param {number} useDecimals
59
+ * @returns {Ratio}
60
+ */
61
+ const priceRatioFromFloat = (floatPrice, numBrand, denomBrand, useDecimals) => {
62
+ const denominatorValue = 10 ** useDecimals;
63
+ return makeRatio(
64
+ BigInt(Math.round(floatPrice * denominatorValue)),
65
+ numBrand,
66
+ BigInt(denominatorValue),
67
+ denomBrand,
68
+ );
69
+ };
70
+
71
+ const bidScalingRatioFromKey = (bidScaleFloat, numBrand, useDecimals) => {
72
+ const denominatorValue = 10 ** useDecimals;
73
+ return makeRatio(
74
+ BigInt(Math.round(bidScaleFloat * denominatorValue)),
75
+ numBrand,
76
+ BigInt(denominatorValue),
77
+ );
78
+ };
79
+
80
+ /**
81
+ * fromPriceOfferKey is only used for diagnostics.
82
+ *
83
+ * @param {string} key
84
+ * @param {Brand<'nat'>} numBrand
85
+ * @param {Brand<'nat'>} denomBrand
86
+ * @param {number} useDecimals
87
+ * @returns {[normalizedPrice: Ratio, sequenceNumber: bigint]}
88
+ */
89
+ export const fromPriceOfferKey = (key, numBrand, denomBrand, useDecimals) => {
90
+ const [pricePart, sequenceNumberPart] = decodeData(key);
91
+ return [
92
+ priceRatioFromFloat(pricePart, numBrand, denomBrand, useDecimals),
93
+ sequenceNumberPart,
94
+ ];
95
+ };
96
+
97
+ /** @type {(rate: Ratio) => string} */
98
+ export const toBidScalingComparator = rate => {
99
+ assert(rate);
100
+ const mostSignificantPart = ratioToNumber(rate);
101
+ return encodeData(harden([mostSignificantPart, 0n]));
102
+ };
103
+
104
+ /**
105
+ * Sorts offers expressed as percentage of the current oracle price.
106
+ *
107
+ * @param {Ratio} rate discount/markup rate expressed as a ratio IST/IST
108
+ * @param {bigint} sequenceNumber
109
+ * @returns {string} lexically sortable string in which highest price is first,
110
+ * ties will be broken by sequenceNumber of offer
111
+ */
112
+ export const toScaledRateOfferKey = (rate, sequenceNumber) => {
113
+ mustMatch(rate, RatioShape);
114
+ rate.numerator.brand === rate.denominator.brand ||
115
+ Fail`bid scaling rate must have the same numerator and denominator`;
116
+ mustMatch(sequenceNumber, M.nat());
117
+
118
+ const mostSignificantPart = ratioToNumber(rate);
119
+ return encodeData(harden([mostSignificantPart, sequenceNumber]));
120
+ };
121
+
122
+ /**
123
+ * fromScaledRateOfferKey is only used for diagnostics.
124
+ *
125
+ * @param {string} key
126
+ * @param {Brand} brand
127
+ * @param {number} useDecimals
128
+ * @returns {[normalizedPrice: Ratio, sequenceNumber: bigint]}
129
+ */
130
+ export const fromScaledRateOfferKey = (key, brand, useDecimals) => {
131
+ const [bidScalingPart, sequenceNumberPart] = decodeData(key);
132
+ return [
133
+ bidScalingRatioFromKey(bidScalingPart, brand, useDecimals),
134
+ sequenceNumberPart,
135
+ ];
136
+ };
@@ -0,0 +1,55 @@
1
+ // @jessie-check
2
+
3
+ import {
4
+ makeRatioFromAmounts,
5
+ multiplyRatios,
6
+ ratioGTE,
7
+ } from '@agoric/zoe/src/contractSupport/index.js';
8
+ import { Far } from '@endo/marshal';
9
+
10
+ /**
11
+ * Constants for Auction State.
12
+ *
13
+ * @type {{ ACTIVE: 'active', WAITING: 'waiting' }}
14
+ */
15
+ export const AuctionState = {
16
+ ACTIVE: 'active',
17
+ WAITING: 'waiting',
18
+ };
19
+
20
+ /**
21
+ * @param {{ brand: Brand, value: Pattern }} numeratorAmountShape
22
+ * @param {{ brand: Brand, value: Pattern }} denominatorAmountShape
23
+ */
24
+ export const makeBrandedRatioPattern = (
25
+ numeratorAmountShape,
26
+ denominatorAmountShape,
27
+ ) => {
28
+ return harden({
29
+ numerator: numeratorAmountShape,
30
+ denominator: denominatorAmountShape,
31
+ });
32
+ };
33
+
34
+ /**
35
+ * @param {Ratio} bidScaling
36
+ * @param {Ratio} currentPrice
37
+ * @param {Ratio} oraclePrice
38
+ * @returns {boolean} TRUE iff the discount(/markup) applied to the price is
39
+ * higher than the quote.
40
+ */
41
+ export const isScaledBidPriceHigher = (bidScaling, currentPrice, oraclePrice) =>
42
+ ratioGTE(multiplyRatios(oraclePrice, bidScaling), currentPrice);
43
+
44
+ /** @type {(quote: PriceQuote) => Ratio} */
45
+ export const priceFrom = quote =>
46
+ makeRatioFromAmounts(
47
+ quote.quoteAmount.value[0].amountOut,
48
+ quote.quoteAmount.value[0].amountIn,
49
+ );
50
+
51
+ export const makeCancelTokenMaker = name => {
52
+ let tokenCount = 1;
53
+
54
+ return () => Far(`cancelToken-${name}-${(tokenCount += 1)}`, {});
55
+ };