@agoric/inter-protocol 0.17.0 → 0.17.1-upgrade-23-dev-bd79330.0.bd79330

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 (148) hide show
  1. package/package.json +23 -27
  2. package/source-spec-registry.js +67 -0
  3. package/src/auction/util.d.ts +3 -1
  4. package/src/auction/util.d.ts.map +1 -1
  5. package/src/auction/util.js +1 -2
  6. package/src/clientSupport.d.ts +29 -73
  7. package/src/clientSupport.d.ts.map +1 -1
  8. package/src/clientSupport.js +25 -129
  9. package/src/collectFees.d.ts +4 -0
  10. package/src/collectFees.d.ts.map +1 -1
  11. package/src/collectFees.js +5 -2
  12. package/src/contractSupport.d.ts +10 -2
  13. package/src/contractSupport.d.ts.map +1 -1
  14. package/src/contractSupport.js +8 -3
  15. package/src/econCommitteeCharter.d.ts +19 -8
  16. package/src/econCommitteeCharter.d.ts.map +1 -1
  17. package/src/econCommitteeCharter.js +12 -12
  18. package/src/feeDistributor.d.ts +56 -52
  19. package/src/feeDistributor.d.ts.map +1 -1
  20. package/src/feeDistributor.js +5 -3
  21. package/src/index.js +0 -3
  22. package/src/interest-math.d.ts +1 -0
  23. package/src/interest-math.d.ts.map +1 -1
  24. package/src/interest-math.js +1 -2
  25. package/src/interest.d.ts +13 -1
  26. package/src/interest.d.ts.map +1 -1
  27. package/src/interest.js +6 -3
  28. package/src/price/fluxAggregatorContract.d.ts +38 -14
  29. package/src/price/fluxAggregatorContract.d.ts.map +1 -1
  30. package/src/price/fluxAggregatorContract.js +26 -12
  31. package/src/price/fluxAggregatorKit.d.ts +18 -8
  32. package/src/price/fluxAggregatorKit.d.ts.map +1 -1
  33. package/src/price/fluxAggregatorKit.js +22 -8
  34. package/src/price/priceOracleKit.d.ts +3 -1
  35. package/src/price/priceOracleKit.d.ts.map +1 -1
  36. package/src/price/priceOracleKit.js +3 -1
  37. package/src/price/roundsManager.d.ts +15 -8
  38. package/src/price/roundsManager.d.ts.map +1 -1
  39. package/src/price/roundsManager.js +9 -2
  40. package/src/proposals/addAssetToVault.js +17 -105
  41. package/src/proposals/committee-proposal.js +14 -14
  42. package/src/proposals/core-proposal.js +12 -37
  43. package/src/proposals/deploy-price-feeds.js +12 -5
  44. package/src/proposals/econ-behaviors.js +23 -154
  45. package/src/proposals/price-feed-proposal.js +14 -4
  46. package/src/proposals/replace-fee-distributor.js +8 -4
  47. package/src/proposals/replace-scaledPriceAuthorities.js +8 -2
  48. package/src/proposals/replaceElectorate.js +7 -1
  49. package/src/proposals/startEconCommittee.js +5 -1
  50. package/src/proposals/startPSM.js +15 -6
  51. package/src/proposals/upgrade-scaledPriceAuthorities.js +5 -0
  52. package/src/proposals/utils.d.ts +10 -4
  53. package/src/proposals/utils.d.ts.map +1 -1
  54. package/src/proposals/utils.js +16 -8
  55. package/src/proposals/withdraw-reserve-proposal.js +6 -1
  56. package/src/provisionPool.d.ts +36 -18
  57. package/src/provisionPool.d.ts.map +1 -1
  58. package/src/provisionPool.js +19 -12
  59. package/src/provisionPoolKit.d.ts +29 -77
  60. package/src/provisionPoolKit.d.ts.map +1 -1
  61. package/src/provisionPoolKit.js +33 -23
  62. package/src/psm/psm.d.ts +36 -24
  63. package/src/psm/psm.d.ts.map +1 -1
  64. package/src/psm/psm.js +19 -15
  65. package/src/reserve/assetReserve.d.ts +16 -6
  66. package/src/reserve/assetReserve.d.ts.map +1 -1
  67. package/src/reserve/assetReserve.js +17 -9
  68. package/src/reserve/assetReserveKit.d.ts +13 -10
  69. package/src/reserve/assetReserveKit.d.ts.map +1 -1
  70. package/src/reserve/assetReserveKit.js +7 -5
  71. package/src/reserve/params.d.ts +1 -1
  72. package/src/reserve/params.d.ts.map +1 -1
  73. package/src/reserve/params.js +1 -3
  74. package/src/vaultFactory/burn.d.ts +4 -1
  75. package/src/vaultFactory/burn.d.ts.map +1 -1
  76. package/src/vaultFactory/burn.js +5 -4
  77. package/src/vaultFactory/math.d.ts +2 -0
  78. package/src/vaultFactory/math.d.ts.map +1 -1
  79. package/src/vaultFactory/math.js +5 -3
  80. package/src/vaultFactory/orderedVaultStore.d.ts +36 -36
  81. package/src/vaultFactory/params.d.ts +37 -10
  82. package/src/vaultFactory/params.d.ts.map +1 -1
  83. package/src/vaultFactory/params.js +34 -13
  84. package/src/vaultFactory/prioritizedVaults.d.ts +98 -95
  85. package/src/vaultFactory/prioritizedVaults.d.ts.map +1 -1
  86. package/src/vaultFactory/prioritizedVaults.js +3 -2
  87. package/src/vaultFactory/storeUtils.d.ts +8 -3
  88. package/src/vaultFactory/storeUtils.d.ts.map +1 -1
  89. package/src/vaultFactory/storeUtils.js +8 -4
  90. package/src/vaultFactory/{types-ambient.d.ts → types.d.ts} +40 -40
  91. package/src/vaultFactory/types.d.ts.map +1 -0
  92. package/src/vaultFactory/{types-ambient.js → types.js} +26 -25
  93. package/src/vaultFactory/vault.d.ts +51 -35
  94. package/src/vaultFactory/vault.d.ts.map +1 -1
  95. package/src/vaultFactory/vault.js +29 -17
  96. package/src/vaultFactory/vaultDirector.d.ts +102 -112
  97. package/src/vaultFactory/vaultDirector.d.ts.map +1 -1
  98. package/src/vaultFactory/vaultDirector.js +40 -82
  99. package/src/vaultFactory/vaultFactory.d.ts +47 -95
  100. package/src/vaultFactory/vaultFactory.d.ts.map +1 -1
  101. package/src/vaultFactory/vaultFactory.js +27 -27
  102. package/src/vaultFactory/vaultHolder.d.ts +62 -55
  103. package/src/vaultFactory/vaultHolder.d.ts.map +1 -1
  104. package/src/vaultFactory/vaultHolder.js +10 -4
  105. package/src/vaultFactory/vaultKit.d.ts +15 -10
  106. package/src/vaultFactory/vaultKit.d.ts.map +1 -1
  107. package/src/vaultFactory/vaultKit.js +8 -7
  108. package/src/vaultFactory/vaultManager.d.ts +112 -262
  109. package/src/vaultFactory/vaultManager.d.ts.map +1 -1
  110. package/src/vaultFactory/vaultManager.js +42 -319
  111. package/NEWS.md +0 -0
  112. package/scripts/build-bundles.js +0 -22
  113. package/src/auction/auctionBook.d.ts +0 -147
  114. package/src/auction/auctionBook.d.ts.map +0 -1
  115. package/src/auction/auctionBook.js +0 -794
  116. package/src/auction/auctionMath.d.ts +0 -17
  117. package/src/auction/auctionMath.d.ts.map +0 -1
  118. package/src/auction/auctionMath.js +0 -81
  119. package/src/auction/auctioneer.d.ts +0 -70
  120. package/src/auction/auctioneer.d.ts.map +0 -1
  121. package/src/auction/auctioneer.js +0 -733
  122. package/src/auction/offerBook.d.ts +0 -46
  123. package/src/auction/offerBook.d.ts.map +0 -1
  124. package/src/auction/offerBook.js +0 -226
  125. package/src/auction/params.d.ts +0 -145
  126. package/src/auction/params.d.ts.map +0 -1
  127. package/src/auction/params.js +0 -176
  128. package/src/auction/scheduleMath.d.ts +0 -5
  129. package/src/auction/scheduleMath.d.ts.map +0 -1
  130. package/src/auction/scheduleMath.js +0 -169
  131. package/src/auction/scheduler.d.ts +0 -50
  132. package/src/auction/scheduler.d.ts.map +0 -1
  133. package/src/auction/scheduler.js +0 -376
  134. package/src/auction/sortedOffers.d.ts +0 -8
  135. package/src/auction/sortedOffers.d.ts.map +0 -1
  136. package/src/auction/sortedOffers.js +0 -137
  137. package/src/proposals/add-auction.js +0 -285
  138. package/src/proposals/upgrade-vaults.js +0 -207
  139. package/src/psm/types-ambient.d.ts +0 -2
  140. package/src/psm/types-ambient.d.ts.map +0 -1
  141. package/src/psm/types-ambient.js +0 -3
  142. package/src/vaultFactory/liquidation.d.ts +0 -25
  143. package/src/vaultFactory/liquidation.d.ts.map +0 -1
  144. package/src/vaultFactory/liquidation.js +0 -309
  145. package/src/vaultFactory/proceeds.d.ts +0 -35
  146. package/src/vaultFactory/proceeds.d.ts.map +0 -1
  147. package/src/vaultFactory/proceeds.js +0 -282
  148. package/src/vaultFactory/types-ambient.d.ts.map +0 -1
@@ -1,794 +0,0 @@
1
- /// <reference types="@agoric/internal/exported" />
2
- /// <reference types="@agoric/governance/exported" />
3
- /// <reference types="@agoric/zoe/exported" />
4
-
5
- import { Fail } from '@endo/errors';
6
- import { E } from '@endo/captp';
7
- import { AmountMath, RatioShape } from '@agoric/ertp';
8
- import { mustMatch } from '@agoric/store';
9
- import { M, prepareExoClassKit } from '@agoric/vat-data';
10
-
11
- import { assertAllDefined, makeTracer } from '@agoric/internal';
12
- import {
13
- ceilMultiplyBy,
14
- makeRatioFromAmounts,
15
- makeRecorderTopic,
16
- multiplyRatios,
17
- ratioGTE,
18
- } from '@agoric/zoe/src/contractSupport/index.js';
19
- import { observeNotifier } from '@agoric/notifier';
20
-
21
- import { makeNatAmountShape } from '../contractSupport.js';
22
- import { amountsToSettle } from './auctionMath.js';
23
- import { preparePriceBook, prepareScaledBidBook } from './offerBook.js';
24
- import {
25
- isScaledBidPriceHigher,
26
- makeBrandedRatioPattern,
27
- priceFrom,
28
- } from './util.js';
29
-
30
- /**
31
- * @import {EReturn} from '@endo/far';
32
- * @import {Baggage} from '@agoric/vat-data';
33
- * @import {PriceAuthority} from '@agoric/zoe/tools/types.js';
34
- * @import {TypedPattern} from '@agoric/internal';
35
- */
36
-
37
- const { makeEmpty } = AmountMath;
38
-
39
- const QUOTE_SCALE = 10n ** 9n;
40
-
41
- /**
42
- * @file The book represents the collateral-specific state of an ongoing
43
- * auction. It holds the book, the capturedPrice, and the collateralSeat that
44
- * has the allocation of assets for sale.
45
- *
46
- * The book contains orders for the collateral. It holds two kinds of orders:
47
- *
48
- * - Prices express the bidding offer in terms of a Bid amount
49
- * - ScaledBids express the offer in terms of a discount (or markup) from the most
50
- * recent oracle price.
51
- *
52
- * Offers can be added in three ways. 1) When the auction is not active, prices
53
- * are automatically added to the appropriate collection. When the auction is
54
- * active, 2) if a new offer is at or above the current price, it will be
55
- * settled immediately; 2) If the offer is below the current price, it will be
56
- * added in the appropriate place and settled when the price reaches that
57
- * level.
58
- */
59
-
60
- const trace = makeTracer('AucBook', true);
61
-
62
- /**
63
- * @typedef {{
64
- * maxBuy: Amount<'nat'>;
65
- * } & {
66
- * exitAfterBuy?: boolean;
67
- * } & (
68
- * | {
69
- * offerPrice: Ratio;
70
- * }
71
- * | {
72
- * offerBidScaling: Ratio;
73
- * }
74
- * )} OfferSpec
75
- */
76
- /**
77
- * @param {Brand<'nat'>} bidBrand
78
- * @param {Brand<'nat'>} collateralBrand
79
- */
80
- export const makeOfferSpecShape = (bidBrand, collateralBrand) => {
81
- const bidAmountShape = makeNatAmountShape(bidBrand);
82
- const collateralAmountShape = makeNatAmountShape(collateralBrand);
83
- return M.splitRecord(
84
- { maxBuy: collateralAmountShape },
85
- {
86
- exitAfterBuy: M.boolean(),
87
- // xxx should have exactly one of these properties
88
- offerPrice: makeBrandedRatioPattern(
89
- bidAmountShape,
90
- collateralAmountShape,
91
- ),
92
- offerBidScaling: makeBrandedRatioPattern(bidAmountShape, bidAmountShape),
93
- },
94
- );
95
- };
96
-
97
- /**
98
- * @typedef {object} BookDataNotification
99
- * @property {Ratio | null} startPrice identifies the priceAuthority and price
100
- * @property {Ratio | null} currentPriceLevel the price at the current auction
101
- * tier
102
- * @property {Amount<'nat'> | null} startProceedsGoal The proceeds the sellers
103
- * were targeting to raise
104
- * @property {Amount<'nat'> | null} remainingProceedsGoal The remainder of the
105
- * proceeds the sellers were targeting to raise
106
- * @property {Amount<'nat'> | undefined} proceedsRaised The proceeds raised so
107
- * far in the auction
108
- * @property {Amount<'nat'>} startCollateral How much collateral was available
109
- * for sale at the start. (If more is deposited later, it'll be added in.)
110
- * @property {Amount<'nat'> | null} collateralAvailable The amount of collateral
111
- * remaining
112
- */
113
-
114
- /**
115
- * @param {Baggage} baggage
116
- * @param {ZCF} zcf
117
- * @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorderKit} makeRecorderKit
118
- */
119
- export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
120
- const makeScaledBidBook = prepareScaledBidBook(baggage);
121
- const makePriceBook = preparePriceBook(baggage);
122
- // Brands that have or are making active quoteNotifier Observers
123
- const observedBrands = new Set();
124
-
125
- const AuctionBookStateShape = harden({
126
- collateralBrand: M.any(),
127
- collateralSeat: M.any(),
128
- collateralAmountShape: M.any(),
129
- bidBrand: M.any(),
130
- bidHoldingSeat: M.any(),
131
- bidAmountShape: M.any(),
132
- priceAuthority: M.any(),
133
- updatingOracleQuote: M.or(RatioShape, M.null()),
134
- bookDataKit: M.any(),
135
- priceBook: M.any(),
136
- scaledBidBook: M.any(),
137
- startCollateral: M.any(),
138
- startProceedsGoal: M.any(),
139
- capturedPriceForRound: M.any(),
140
- curAuctionPrice: M.any(),
141
- remainingProceedsGoal: M.any(),
142
- });
143
-
144
- const makeAuctionBookKit = prepareExoClassKit(
145
- baggage,
146
- 'AuctionBook',
147
- undefined,
148
- /**
149
- * @param {Brand<'nat'>} bidBrand
150
- * @param {Brand<'nat'>} collateralBrand
151
- * @param {PriceAuthority} pAuthority
152
- * @param {StorageNode} node
153
- */
154
- (bidBrand, collateralBrand, pAuthority, node) => {
155
- assertAllDefined({ bidBrand, collateralBrand, pAuthority });
156
-
157
- // these don't have to be durable, since we're currently assuming that upgrade
158
- // from a quiescent state is sufficient. When the auction is quiescent, there
159
- // may be offers in the book, but these seats will be empty, with all assets
160
- // returned to the funders.
161
- const { zcfSeat: collateralSeat } = zcf.makeEmptySeatKit();
162
- const { zcfSeat: bidHoldingSeat } = zcf.makeEmptySeatKit();
163
-
164
- const bidAmountShape = makeNatAmountShape(bidBrand);
165
- const collateralAmountShape = makeNatAmountShape(collateralBrand);
166
- const scaledBidBook = makeScaledBidBook(
167
- makeBrandedRatioPattern(bidAmountShape, bidAmountShape),
168
- collateralBrand,
169
- );
170
-
171
- const priceBook = makePriceBook(
172
- makeBrandedRatioPattern(bidAmountShape, collateralAmountShape),
173
- collateralBrand,
174
- );
175
-
176
- const bookDataKit = makeRecorderKit(
177
- node,
178
- /** @type {TypedPattern<BookDataNotification>} */ (M.any()),
179
- );
180
-
181
- return {
182
- collateralBrand,
183
- collateralSeat,
184
- collateralAmountShape,
185
- bidBrand,
186
- bidHoldingSeat,
187
- bidAmountShape,
188
-
189
- priceAuthority: pAuthority,
190
- updatingOracleQuote: /** @type {Ratio | null} */ (null),
191
-
192
- bookDataKit,
193
-
194
- priceBook,
195
- scaledBidBook,
196
- /**
197
- * Set to empty at the end of an auction. It increases when
198
- * `addAssets()` is called
199
- */
200
- startCollateral: AmountMath.makeEmpty(collateralBrand),
201
-
202
- /**
203
- * Null indicates no limit; empty indicates limit exhausted. It is reset
204
- * at the end of each auction. It increases when `addAssets()` is called
205
- * with a goal.
206
- *
207
- * @type {Amount<'nat'> | null}
208
- */
209
- startProceedsGoal: null,
210
-
211
- /**
212
- * Assigned a value to capture the price and reset to null at the end of
213
- * each auction.
214
- *
215
- * @type {Ratio | null}
216
- */
217
- capturedPriceForRound: null,
218
-
219
- /**
220
- * non-null during auctions. It is assigned a value at the beginning of
221
- * each descending step, and reset at the end of the auction.
222
- *
223
- * @type {Ratio | null}
224
- */
225
- curAuctionPrice: null,
226
-
227
- /**
228
- * null outside of auctions. during an auction null indicates no limit;
229
- * empty indicates limit exhausted
230
- *
231
- * @type {Amount<'nat'> | null}
232
- */
233
- remainingProceedsGoal: null,
234
- };
235
- },
236
- {
237
- helper: {
238
- /**
239
- * remove the key from the appropriate book, indicated by whether the
240
- * price is defined.
241
- *
242
- * @param {string} key
243
- * @param {Ratio | undefined} price
244
- */
245
- removeFromItsBook(key, price) {
246
- const { priceBook, scaledBidBook } = this.state;
247
- if (price) {
248
- priceBook.delete(key);
249
- } else {
250
- scaledBidBook.delete(key);
251
- }
252
- },
253
-
254
- /**
255
- * Update the entry in the appropriate book, indicated by whether the
256
- * price is defined.
257
- *
258
- * @param {string} key
259
- * @param {Amount} collateralSold
260
- * @param {Ratio | undefined} price
261
- */
262
- updateItsBook(key, collateralSold, price) {
263
- const { priceBook, scaledBidBook } = this.state;
264
- if (price) {
265
- priceBook.updateReceived(key, collateralSold);
266
- } else {
267
- scaledBidBook.updateReceived(key, collateralSold);
268
- }
269
- },
270
-
271
- /**
272
- * Settle with seat. The caller is responsible for updating the book, if
273
- * any.
274
- *
275
- * @param {ZCFSeat} seat
276
- * @param {Amount<'nat'>} collateralWanted
277
- */
278
- settle(seat, collateralWanted) {
279
- const { collateralSeat, collateralBrand } = this.state;
280
- const { Bid: bidAlloc } = seat.getCurrentAllocation();
281
- const { Collateral: collateralAvailable } =
282
- collateralSeat.getCurrentAllocation();
283
- if (!collateralAvailable || AmountMath.isEmpty(collateralAvailable)) {
284
- return makeEmpty(collateralBrand);
285
- }
286
-
287
- const { curAuctionPrice, bidHoldingSeat, remainingProceedsGoal } =
288
- this.state;
289
- curAuctionPrice !== null ||
290
- Fail`auctionPrice must be set before each round`;
291
- assert(curAuctionPrice);
292
-
293
- const { proceedsExpected, proceedsTarget, collateralTarget } =
294
- amountsToSettle(
295
- {
296
- bidAlloc,
297
- collateralWanted,
298
- collateralAvailable,
299
- curAuctionPrice,
300
- remainingProceedsGoal,
301
- },
302
- trace,
303
- );
304
-
305
- if (proceedsExpected === null) {
306
- seat.fail(Error('price fell to zero'));
307
- return makeEmpty(collateralBrand);
308
- }
309
-
310
- // check that the requested amount could be satisfied
311
- const { Collateral } = seat.getProposal().want;
312
- if (Collateral && AmountMath.isGTE(Collateral, collateralTarget)) {
313
- seat.exit('unable to satisfy want');
314
- }
315
-
316
- zcf.atomicRearrange(
317
- harden([
318
- [collateralSeat, seat, { Collateral: collateralTarget }],
319
- [seat, bidHoldingSeat, { Bid: proceedsTarget }],
320
- ]),
321
- );
322
-
323
- if (remainingProceedsGoal) {
324
- this.state.remainingProceedsGoal = AmountMath.subtract(
325
- remainingProceedsGoal,
326
- proceedsTarget,
327
- );
328
- }
329
-
330
- trace('settled', {
331
- collateralTarget,
332
- proceedsTarget,
333
- remainingProceedsGoal: this.state.remainingProceedsGoal,
334
- });
335
-
336
- return collateralTarget;
337
- },
338
-
339
- /**
340
- * Accept an offer expressed as a price. If the auction is active,
341
- * attempt to buy collateral. If any of the offer remains add it to the
342
- * book.
343
- *
344
- * @param {ZCFSeat} seat
345
- * @param {Ratio} price
346
- * @param {Amount<'nat'>} maxBuy
347
- * @param {object} opts
348
- * @param {boolean} opts.trySettle
349
- * @param {boolean} [opts.exitAfterBuy]
350
- */
351
- acceptPriceOffer(
352
- seat,
353
- price,
354
- maxBuy,
355
- { trySettle, exitAfterBuy = false },
356
- ) {
357
- const { priceBook, curAuctionPrice } = this.state;
358
- const { helper } = this.facets;
359
- trace(this.state.collateralBrand, 'acceptPrice');
360
-
361
- const settleIfPriceExists = () => {
362
- if (curAuctionPrice !== null) {
363
- return trySettle && ratioGTE(price, curAuctionPrice)
364
- ? helper.settle(seat, maxBuy)
365
- : AmountMath.makeEmptyFromAmount(maxBuy);
366
- } else {
367
- return AmountMath.makeEmptyFromAmount(maxBuy);
368
- }
369
- };
370
- const collateralSold = settleIfPriceExists();
371
-
372
- const stillWant = AmountMath.subtract(maxBuy, collateralSold);
373
- if (
374
- (exitAfterBuy && !AmountMath.isEmpty(collateralSold)) ||
375
- AmountMath.isEmpty(stillWant) ||
376
- AmountMath.isEmpty(seat.getCurrentAllocation().Bid)
377
- ) {
378
- seat.exit();
379
- } else {
380
- trace('added Offer ', price, stillWant.value);
381
- priceBook.add(seat, price, stillWant, exitAfterBuy);
382
- }
383
-
384
- void helper.publishBookData();
385
- },
386
-
387
- /**
388
- * Accept an offer expressed as a discount (or markup). If the auction
389
- * is active, attempt to buy collateral. If any of the offer remains add
390
- * it to the book.
391
- *
392
- * @param {ZCFSeat} seat
393
- * @param {Ratio} bidScaling
394
- * @param {Amount<'nat'>} maxBuy
395
- * @param {object} opts
396
- * @param {boolean} opts.trySettle
397
- * @param {boolean} [opts.exitAfterBuy]
398
- */
399
- acceptScaledBidOffer(
400
- seat,
401
- bidScaling,
402
- maxBuy,
403
- { trySettle, exitAfterBuy = false },
404
- ) {
405
- trace(this.state.collateralBrand, 'accept scaledBid offer');
406
- const { curAuctionPrice, capturedPriceForRound, scaledBidBook } =
407
- this.state;
408
- const { helper } = this.facets;
409
-
410
- const settleIfPricesDefined = () => {
411
- if (
412
- curAuctionPrice &&
413
- capturedPriceForRound &&
414
- trySettle &&
415
- isScaledBidPriceHigher(
416
- bidScaling,
417
- curAuctionPrice,
418
- capturedPriceForRound,
419
- )
420
- ) {
421
- return helper.settle(seat, maxBuy);
422
- }
423
- return AmountMath.makeEmptyFromAmount(maxBuy);
424
- };
425
- const collateralSold = settleIfPricesDefined();
426
-
427
- const stillWant = AmountMath.subtract(maxBuy, collateralSold);
428
- if (
429
- (exitAfterBuy && !AmountMath.isEmpty(collateralSold)) ||
430
- AmountMath.isEmpty(stillWant) ||
431
- AmountMath.isEmpty(seat.getCurrentAllocation().Bid)
432
- ) {
433
- seat.exit();
434
- } else {
435
- scaledBidBook.add(seat, bidScaling, stillWant, exitAfterBuy);
436
- }
437
-
438
- void helper.publishBookData();
439
- },
440
- publishBookData() {
441
- const { state } = this;
442
-
443
- const allocation = state.collateralSeat.getCurrentAllocation();
444
- const collateralAvailable =
445
- 'Collateral' in allocation
446
- ? allocation.Collateral
447
- : makeEmpty(state.collateralBrand);
448
-
449
- const bookData = harden({
450
- startPrice: state.capturedPriceForRound,
451
- startProceedsGoal: state.startProceedsGoal,
452
- remainingProceedsGoal: state.remainingProceedsGoal,
453
- proceedsRaised: allocation.Bid,
454
- startCollateral: state.startCollateral,
455
- collateralAvailable,
456
- currentPriceLevel: state.curAuctionPrice,
457
- });
458
- return state.bookDataKit.recorder.write(bookData);
459
- },
460
- // Ensure that there is an observer monitoring the quoteNotifier. We
461
- // assume that all failure modes for quoteNotifier eventually lead to
462
- // fail or finish.
463
- ensureQuoteNotifierObserved() {
464
- const { state, facets } = this;
465
- const { collateralBrand, bidBrand, priceAuthority } = state;
466
-
467
- if (observedBrands.has(collateralBrand)) {
468
- return;
469
- }
470
- observedBrands.add(collateralBrand);
471
- trace('observing');
472
-
473
- const quoteNotifierP = E(priceAuthority).makeQuoteNotifier(
474
- AmountMath.make(collateralBrand, QUOTE_SCALE),
475
- bidBrand,
476
- );
477
-
478
- void E.when(
479
- quoteNotifierP,
480
- quoteNotifier =>
481
- observeNotifier(quoteNotifier, {
482
- updateState: quote => {
483
- trace(
484
- `BOOK notifier ${priceFrom(quote).numerator.value}/${
485
- priceFrom(quote).denominator.value
486
- }`,
487
- );
488
- state.updatingOracleQuote = priceFrom(quote);
489
- },
490
- fail: reason => {
491
- trace(
492
- `Failure from quoteNotifier (${reason}) setting to null`,
493
- );
494
- // lack of quote will trigger restart
495
- state.updatingOracleQuote = null;
496
- observedBrands.delete(collateralBrand);
497
- },
498
- finish: done => {
499
- trace(
500
- `quoteNotifier invoked finish(${done}). setting quote to null`,
501
- );
502
- // lack of quote will trigger restart
503
- state.updatingOracleQuote = null;
504
- observedBrands.delete(collateralBrand);
505
- },
506
- }),
507
- e => {
508
- trace('makeQuoteNotifier failed, resetting', e);
509
- state.updatingOracleQuote = null;
510
- observedBrands.delete(collateralBrand);
511
- },
512
- );
513
-
514
- void facets.helper.publishBookData();
515
- },
516
- },
517
- self: {
518
- /**
519
- * @param {Amount<'nat'>} assetAmount
520
- * @param {ZCFSeat} sourceSeat
521
- * @param {Amount<'nat'>} [proceedsGoal] an amount that the depositor
522
- * would like to raise. The auction is requested to not sell more
523
- * collateral than required to raise that much. The auctioneer might
524
- * sell more if there is more than one supplier of collateral, and
525
- * they request inconsistent limits.
526
- */
527
- addAssets(assetAmount, sourceSeat, proceedsGoal) {
528
- const { state, facets } = this;
529
- trace('add assets', { assetAmount, proceedsGoal });
530
- const { collateralBrand, collateralSeat, startProceedsGoal } = state;
531
-
532
- // When adding assets, the new ratio of totalCollectionGoal to collateral
533
- // allocation will be the larger of the existing ratio and the ratio
534
- // implied by the new deposit. Add the new collateral and raise
535
- // startProceedsGoal so it's proportional to the new ratio. This can
536
- // result in raising more Bid than one depositor wanted, but
537
- // that's better than not selling as much as the other desired.
538
-
539
- const allocation = collateralSeat.getCurrentAllocation();
540
- const curCollateral =
541
- 'Collateral' in allocation
542
- ? allocation.Collateral
543
- : makeEmpty(collateralBrand);
544
-
545
- // when neither proceedsGoal nor startProceedsGoal is defined, we don't need an
546
- // update and the call immediately below won't invoke this function.
547
- const calcTargetRatio = () => {
548
- if (startProceedsGoal && !proceedsGoal) {
549
- return makeRatioFromAmounts(startProceedsGoal, curCollateral);
550
- } else if (!startProceedsGoal && proceedsGoal) {
551
- return makeRatioFromAmounts(proceedsGoal, assetAmount);
552
- } else if (startProceedsGoal && proceedsGoal) {
553
- const curRatio = makeRatioFromAmounts(
554
- startProceedsGoal,
555
- AmountMath.add(curCollateral, assetAmount),
556
- );
557
- const newRatio = makeRatioFromAmounts(proceedsGoal, assetAmount);
558
- return ratioGTE(newRatio, curRatio) ? newRatio : curRatio;
559
- }
560
-
561
- throw Fail`calcTargetRatio called with !remainingProceedsGoal && !proceedsGoal`;
562
- };
563
-
564
- if (proceedsGoal || startProceedsGoal) {
565
- const nextProceedsGoal = ceilMultiplyBy(
566
- AmountMath.add(curCollateral, assetAmount),
567
- calcTargetRatio(),
568
- );
569
-
570
- if (
571
- state.remainingProceedsGoal !== null ||
572
- // XXX use this as an indication that the auction is active
573
- state.curAuctionPrice !== null
574
- ) {
575
- const incrementToGoal = state.startProceedsGoal
576
- ? AmountMath.subtract(nextProceedsGoal, state.startProceedsGoal)
577
- : nextProceedsGoal;
578
-
579
- state.remainingProceedsGoal = state.remainingProceedsGoal
580
- ? AmountMath.add(state.remainingProceedsGoal, incrementToGoal)
581
- : incrementToGoal;
582
- }
583
-
584
- state.startProceedsGoal = nextProceedsGoal;
585
- }
586
-
587
- zcf.atomicRearrange(
588
- harden([[sourceSeat, collateralSeat, { Collateral: assetAmount }]]),
589
- );
590
-
591
- state.startCollateral = state.startCollateral
592
- ? AmountMath.add(state.startCollateral, assetAmount)
593
- : assetAmount;
594
- void facets.helper.publishBookData();
595
- },
596
- /** @type {(reduction: Ratio) => void} */
597
- settleAtNewRate(reduction) {
598
- const { state, facets } = this;
599
-
600
- trace(this.state.collateralBrand, 'settleAtNewRate', reduction);
601
- const { capturedPriceForRound, priceBook, scaledBidBook } = state;
602
- if (!capturedPriceForRound) {
603
- console.error(
604
- `⚠️No price for ${this.state.collateralBrand}, skipping auction.`,
605
- );
606
- return;
607
- }
608
-
609
- state.curAuctionPrice = multiplyRatios(
610
- reduction,
611
- capturedPriceForRound,
612
- );
613
- // extract after it's set in state
614
- const { curAuctionPrice } = state;
615
-
616
- const pricedOffers = priceBook.offersAbove(curAuctionPrice);
617
- const scaledBidOffers = scaledBidBook.offersAbove(reduction);
618
-
619
- // requested price or BidScaling gives no priority beyond specifying which
620
- // round the order will be serviced in.
621
- const prioritizedOffers = [...pricedOffers, ...scaledBidOffers].sort(
622
- (a, b) => Number(a[1].seqNum - b[1].seqNum),
623
- );
624
-
625
- trace(
626
- `settling ${prioritizedOffers.length} offers at ${curAuctionPrice} (priced ${pricedOffers.length}, scaled ${scaledBidOffers.length}) `,
627
- );
628
-
629
- const { remainingProceedsGoal } = state;
630
- const { helper } = facets;
631
- for (const [key, seatRecord] of prioritizedOffers) {
632
- const { seat, price: p, wanted, exitAfterBuy } = seatRecord;
633
- if (
634
- remainingProceedsGoal &&
635
- AmountMath.isEmpty(remainingProceedsGoal)
636
- ) {
637
- break;
638
- } else if (seat.hasExited()) {
639
- helper.removeFromItsBook(key, p);
640
- } else {
641
- const collateralSold = helper.settle(seat, wanted);
642
-
643
- const alloc = seat.getCurrentAllocation();
644
- if (
645
- (exitAfterBuy && !AmountMath.isEmpty(collateralSold)) ||
646
- AmountMath.isEmpty(alloc.Bid) ||
647
- ('Collateral' in alloc &&
648
- AmountMath.isGTE(alloc.Collateral, wanted))
649
- ) {
650
- seat.exit();
651
- helper.removeFromItsBook(key, p);
652
- } else if (!AmountMath.isGTE(collateralSold, wanted)) {
653
- helper.updateItsBook(key, collateralSold, p);
654
- }
655
- }
656
- }
657
-
658
- void facets.helper.publishBookData();
659
- },
660
- getCurrentPrice() {
661
- return this.state.curAuctionPrice;
662
- },
663
- hasOrders() {
664
- const { scaledBidBook, priceBook } = this.state;
665
- return scaledBidBook.hasOrders() || priceBook.hasOrders();
666
- },
667
- captureOraclePriceForRound() {
668
- const { facets, state } = this;
669
-
670
- trace(`capturing oracle price `, state.updatingOracleQuote);
671
- if (!state.updatingOracleQuote) {
672
- // if the price feed has died (or hasn't been started for this
673
- // incarnation), (re)start it.
674
- facets.helper.ensureQuoteNotifierObserved();
675
- return;
676
- }
677
-
678
- state.capturedPriceForRound = state.updatingOracleQuote;
679
- void facets.helper.publishBookData();
680
- },
681
-
682
- setStartingRate(rate) {
683
- const { capturedPriceForRound } = this.state;
684
- capturedPriceForRound !== null ||
685
- Fail`capturedPriceForRound must be set before each round`;
686
- assert(capturedPriceForRound);
687
-
688
- trace(
689
- 'set startPrice',
690
- capturedPriceForRound,
691
- this.state.startProceedsGoal,
692
- );
693
- this.state.remainingProceedsGoal = this.state.startProceedsGoal;
694
- this.state.curAuctionPrice = multiplyRatios(
695
- capturedPriceForRound,
696
- rate,
697
- );
698
- },
699
- /**
700
- * @param {OfferSpec} offerSpec
701
- * @param {ZCFSeat} seat
702
- * @param {boolean} trySettle
703
- */
704
- addOffer(offerSpec, seat, trySettle) {
705
- const { bidBrand, collateralBrand } = this.state;
706
- const offerSpecShape = makeOfferSpecShape(bidBrand, collateralBrand);
707
-
708
- mustMatch(offerSpec, offerSpecShape);
709
- const { give } = seat.getProposal();
710
- const { bidAmountShape } = this.state;
711
- mustMatch(give.Bid, bidAmountShape, 'give must include "Bid"');
712
-
713
- const { helper } = this.facets;
714
- const { exitAfterBuy } = offerSpec;
715
- if ('offerPrice' in offerSpec) {
716
- return helper.acceptPriceOffer(
717
- seat,
718
- offerSpec.offerPrice,
719
- offerSpec.maxBuy,
720
- {
721
- trySettle,
722
- exitAfterBuy,
723
- },
724
- );
725
- } else if ('offerBidScaling' in offerSpec) {
726
- return helper.acceptScaledBidOffer(
727
- seat,
728
- offerSpec.offerBidScaling,
729
- offerSpec.maxBuy,
730
- {
731
- trySettle,
732
- exitAfterBuy,
733
- },
734
- );
735
- } else {
736
- throw Fail`Offer was neither a price nor a scaledBid`;
737
- }
738
- },
739
- getSeats() {
740
- const { collateralSeat, bidHoldingSeat } = this.state;
741
- return { collateralSeat, bidHoldingSeat };
742
- },
743
- exitAllSeats() {
744
- const { priceBook, scaledBidBook } = this.state;
745
- priceBook.exitAllSeats();
746
- scaledBidBook.exitAllSeats();
747
- },
748
- endAuction() {
749
- trace('endAuction');
750
- const { state, facets } = this;
751
-
752
- state.startCollateral = AmountMath.makeEmpty(state.collateralBrand);
753
-
754
- state.capturedPriceForRound = null;
755
- state.curAuctionPrice = null;
756
- state.remainingProceedsGoal = null;
757
- state.startProceedsGoal = null;
758
- void facets.helper.publishBookData();
759
- },
760
- getDataUpdates() {
761
- return this.state.bookDataKit.subscriber;
762
- },
763
- getPublicTopics() {
764
- return {
765
- bookData: makeRecorderTopic(
766
- 'Auction schedule',
767
- this.state.bookDataKit,
768
- ),
769
- };
770
- },
771
- },
772
- },
773
- {
774
- finish: ({ state, facets }) => {
775
- const { collateralBrand, bidBrand, priceAuthority } = state;
776
- assertAllDefined({ collateralBrand, bidBrand, priceAuthority });
777
-
778
- facets.helper.ensureQuoteNotifierObserved();
779
- },
780
- stateShape: AuctionBookStateShape,
781
- },
782
- );
783
-
784
- /**
785
- * @type {(
786
- * ...args: Parameters<typeof makeAuctionBookKit>
787
- * ) => ReturnType<typeof makeAuctionBookKit>['self']}
788
- */
789
- const makeAuctionBook = (...args) => makeAuctionBookKit(...args).self;
790
- return makeAuctionBook;
791
- };
792
- harden(prepareAuctionBook);
793
-
794
- /** @typedef {EReturn<EReturn<typeof prepareAuctionBook>>} AuctionBook */