@agoric/inter-protocol 0.16.2-dev-6ad0038.0.6ad0038 → 0.16.2-dev-b95c1c8.0.b95c1c8

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