diff --git a/packages/inter-protocol/src/auction/auctionBook.js b/packages/inter-protocol/src/auction/auctionBook.js index 772f4074923..4b157405d9b 100644 --- a/packages/inter-protocol/src/auction/auctionBook.js +++ b/packages/inter-protocol/src/auction/auctionBook.js @@ -379,6 +379,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { * @param {ZCFSeat} seat * @param {Ratio} price * @param {Amount<'nat'>} maxBuy + * @param {Timestamp} timestamp * @param {object} opts * @param {boolean} opts.trySettle * @param {boolean} [opts.exitAfterBuy] @@ -387,6 +388,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { seat, price, maxBuy, + timestamp, { trySettle, exitAfterBuy = false }, ) { const { priceBook, curAuctionPrice } = this.state; @@ -413,7 +415,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { seat.exit(); } else { trace('added Offer ', price, stillWant.value); - priceBook.add(seat, price, stillWant, exitAfterBuy); + priceBook.add(seat, price, stillWant, exitAfterBuy, timestamp); helper.publishBidData(); } @@ -428,6 +430,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { * @param {ZCFSeat} seat * @param {Ratio} bidScaling * @param {Amount<'nat'>} maxBuy + * @param {Timestamp} timestamp * @param {object} opts * @param {boolean} opts.trySettle * @param {boolean} [opts.exitAfterBuy] @@ -436,6 +439,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { seat, bidScaling, maxBuy, + timestamp, { trySettle, exitAfterBuy = false }, ) { trace(this.state.collateralBrand, 'accept scaledBid offer'); @@ -468,7 +472,13 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { ) { seat.exit(); } else { - scaledBidBook.add(seat, bidScaling, stillWant, exitAfterBuy); + scaledBidBook.add( + seat, + bidScaling, + stillWant, + exitAfterBuy, + timestamp, + ); helper.publishBidData(); } @@ -688,8 +698,9 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { * @param {OfferSpec} offerSpec * @param {ZCFSeat} seat * @param {boolean} trySettle + * @param {Timestamp} timestamp */ - addOffer(offerSpec, seat, trySettle) { + addOffer(offerSpec, seat, trySettle, timestamp) { const { bidBrand, collateralBrand } = this.state; const offerSpecShape = makeOfferSpecShape(bidBrand, collateralBrand); @@ -705,6 +716,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { seat, offerSpec.offerPrice, offerSpec.maxBuy, + timestamp, { trySettle, exitAfterBuy, @@ -715,6 +727,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => { seat, offerSpec.offerBidScaling, offerSpec.maxBuy, + timestamp, { trySettle, exitAfterBuy, diff --git a/packages/inter-protocol/src/auction/auctioneer.js b/packages/inter-protocol/src/auction/auctioneer.js index 55c7a874730..83c62ca5fa8 100644 --- a/packages/inter-protocol/src/auction/auctioneer.js +++ b/packages/inter-protocol/src/auction/auctioneer.js @@ -624,11 +624,12 @@ export const start = async (zcf, privateArgs, baggage) => { * @param {ZCFSeat} zcfSeat * @param {import('./auctionBook.js').OfferSpec} offerSpec */ - const newBidHandler = (zcfSeat, offerSpec) => { + const newBidHandler = async (zcfSeat, offerSpec) => { // xxx consider having Zoe guard the offerArgs with a provided shape mustMatch(offerSpec, offerSpecShape); const auctionBook = books.get(collateralBrand); - auctionBook.addOffer(offerSpec, zcfSeat, isActive()); + const timestamp = await E(timer).getCurrentTimestamp(); + auctionBook.addOffer(offerSpec, zcfSeat, isActive(), timestamp); return 'Your bid has been accepted'; }; diff --git a/packages/inter-protocol/src/auction/offerBook.js b/packages/inter-protocol/src/auction/offerBook.js index 3920b4e5bc5..14b303d6c13 100644 --- a/packages/inter-protocol/src/auction/offerBook.js +++ b/packages/inter-protocol/src/auction/offerBook.js @@ -29,6 +29,7 @@ const nextSequenceNumber = () => { * wanted: Amount<'nat'>, * seqNum: NatValue, * received: Amount<'nat'>, + * timestamp: Timestamp, * } & {exitAfterBuy: boolean} & ({ bidScaling: Pattern, price: undefined } | { bidScaling: undefined, price: Ratio}) * } BidderRecord */ @@ -67,8 +68,9 @@ export const prepareScaledBidBook = baggage => * @param {Ratio} bidScaling * @param {Amount<'nat'>} wanted * @param {boolean} exitAfterBuy + * @param {Timestamp} timestamp */ - add(seat, bidScaling, wanted, exitAfterBuy) { + add(seat, bidScaling, wanted, exitAfterBuy, timestamp) { const { bidScalingPattern, collateralBrand, records } = this.state; mustMatch(bidScaling, bidScalingPattern); @@ -84,6 +86,7 @@ export const prepareScaledBidBook = baggage => seqNum, wanted, exitAfterBuy, + timestamp, }; records.init(key, harden(bidderRecord)); return key; @@ -100,6 +103,9 @@ export const prepareScaledBidBook = baggage => bidScaling: r.bidScaling, wanted: r.wanted, exitAfterBuy: r.exitAfterBuy, + timestamp: r.timestamp, + balance: r.seat.getCurrentAllocation().Bid, + sequence: r.seqNum, }); }); }, @@ -171,8 +177,9 @@ export const preparePriceBook = baggage => * @param {Ratio} price * @param {Amount<'nat'>} wanted * @param {boolean} exitAfterBuy + * @param {Timestamp} timestamp */ - add(seat, price, wanted, exitAfterBuy) { + add(seat, price, wanted, exitAfterBuy, timestamp) { const { priceRatioPattern, collateralBrand, records } = this.state; mustMatch(price, priceRatioPattern); @@ -188,6 +195,7 @@ export const preparePriceBook = baggage => seqNum, wanted, exitAfterBuy, + timestamp, }; records.init(key, harden(bidderRecord)); return key; @@ -203,6 +211,9 @@ export const preparePriceBook = baggage => price: r.price, wanted: r.wanted, exitAfterBuy: r.exitAfterBuy, + timestamp: r.timestamp, + balance: r.seat.getCurrentAllocation().Bid, + sequence: r.seqNum, }); }); }, diff --git a/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md b/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md index 8f1c4d36f5b..c2fc4da9821 100644 --- a/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md +++ b/packages/inter-protocol/test/auction/snapshots/test-auctionContract.js.md @@ -17,30 +17,112 @@ Generated by [AVA](https://avajs.dev). { pricedBids: [ { + balance: { + brand: Object @Alleged: Bid brand {}, + value: 200n, + }, exitAfterBuy: false, price: { denominator: { - brand: { - iface: 'Alleged: Collateral brand', - }, + brand: Object @Alleged: Collateral brand {}, + value: 250n, + }, + numerator: { + brand: Object @Alleged: Bid brand {}, + value: 200n, + }, + }, + sequence: 2n, + timestamp: { + absValue: 167n, + timerBrand: Object @Alleged: timerBrand {}, + }, + wanted: { + brand: Object @Alleged: Collateral brand {}, + value: 250n, + }, + }, + { + balance: { + brand: Object @Alleged: Bid brand {}, + value: 134n, + }, + exitAfterBuy: false, + price: { + denominator: { + brand: Object @Alleged: Collateral brand {}, value: 200n, }, numerator: { - brand: { - iface: 'Alleged: Bid brand', - }, + brand: Object @Alleged: Bid brand {}, value: 250n, }, }, + sequence: 1n, + timestamp: { + absValue: 167n, + timerBrand: Object @Alleged: timerBrand {}, + }, wanted: { - brand: { - iface: 'Alleged: Collateral brand', + brand: Object @Alleged: Collateral brand {}, + value: 200n, + }, + }, + ], + scaledBids: [ + { + balance: { + brand: Object @Alleged: Bid brand {}, + value: 20n, + }, + bidScaling: { + denominator: { + brand: Object @Alleged: Bid brand {}, + value: 100n, + }, + numerator: { + brand: Object @Alleged: Bid brand {}, + value: 50n, }, + }, + exitAfterBuy: false, + sequence: 3n, + timestamp: { + absValue: 170n, + timerBrand: Object @Alleged: timerBrand {}, + }, + wanted: { + brand: Object @Alleged: Collateral brand {}, value: 200n, }, }, + { + balance: { + brand: Object @Alleged: Bid brand {}, + value: 40n, + }, + bidScaling: { + denominator: { + brand: Object @Alleged: Bid brand {}, + value: 100n, + }, + numerator: { + brand: Object @Alleged: Bid brand {}, + value: 80n, + }, + }, + exitAfterBuy: false, + sequence: 4n, + timestamp: { + absValue: 170n, + timerBrand: Object @Alleged: timerBrand {}, + }, + wanted: { + brand: Object @Alleged: Collateral brand {}, + value: 2000n, + }, + }, ], - scaledBids: [], }, ], [ diff --git a/packages/inter-protocol/test/auction/test-auctionContract.js b/packages/inter-protocol/test/auction/test-auctionContract.js index 62f7fea6d64..9285f620d8c 100644 --- a/packages/inter-protocol/test/auction/test-auctionContract.js +++ b/packages/inter-protocol/test/auction/test-auctionContract.js @@ -928,10 +928,14 @@ test.serial('onDeadline exit, with chainStorage RPC snapshot', async t => { /** @type {BookDataTracker} */ const bookTracker = await driver.getBookDataTracker(collateral.brand); - - await bookTracker.assertChange({ - collateralAvailable: { value: 100n }, - startCollateral: { value: 100n }, + await bookTracker.assertInitial({ + collateralAvailable: collateral.make(100n), + currentPriceLevel: null, + proceedsRaised: undefined, + remainingProceedsGoal: null, + startCollateral: collateral.make(100n), + startPrice: null, + startProceedsGoal: null, }); await driver.updatePriceAuthority( @@ -964,6 +968,22 @@ test.serial('onDeadline exit, with chainStorage RPC snapshot', async t => { t.is(await E(exitingSeat).getOfferResult(), 'Your bid has been accepted'); t.false(await E(exitingSeat).hasExited()); + const pricedSeat = await driver.bidForCollateralSeat( + bid.make(200n), + collateral.make(250n), + undefined, + ); + const discountSeat1 = driver.bidForCollateralSeat( + bid.make(20n), + collateral.make(200n), + makeRatioFromAmounts(bid.make(50n), bid.make(100n)), + ); + const discountSeat2 = driver.bidForCollateralSeat( + bid.make(40n), + collateral.make(2000n), + makeRatioFromAmounts(bid.make(80n), bid.make(100n)), + ); + await bookTracker.assertChange({ startPrice: makeRatioFromAmounts( bid.make(1_100_000_000n), @@ -1067,9 +1087,14 @@ test.serial('add assets to open auction', async t => { ); const bookTracker = await driver.getBookDataTracker(collateral.brand); - await bookTracker.assertChange({ - collateralAvailable: { value: 1000n }, - startCollateral: { value: 1000n }, + await bookTracker.assertInitial({ + collateralAvailable: collateral.make(1000n), + currentPriceLevel: null, + proceedsRaised: undefined, + remainingProceedsGoal: null, + startCollateral: collateral.make(1000n), + startPrice: null, + startProceedsGoal: null, }); const scheduleTracker = await driver.getScheduleTracker(); await scheduleTracker.assertInitial({ @@ -1209,12 +1234,14 @@ test.serial('multiple collaterals', async t => { price, ); t.is(await E(bidderSeat1C).getOfferResult(), 'Your bid has been accepted'); + const timestamp = driver.getTimerService().getCurrentTimestamp(); collatBidTracker.assertChange({ pricedBids: { 0: { exitAfterBuy: false, wanted: collateral.make(300n), price, + timestamp, }, }, }); @@ -1233,6 +1260,7 @@ test.serial('multiple collaterals', async t => { exitAfterBuy: false, wanted: collateral.make(2000n), bidScaling: scale2C, + timestamp, }, }, }); @@ -1251,6 +1279,7 @@ test.serial('multiple collaterals', async t => { exitAfterBuy: false, wanted: asset.make(200n), bidScaling: scale1A, + timestamp, }, }, }); @@ -1268,6 +1297,7 @@ test.serial('multiple collaterals', async t => { 0: { exitAfterBuy: false, wanted: asset.make(300n), + timestamp, price: price2A, }, },