Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auction): auction publish all bids off-chain #7618

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions packages/inter-protocol/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ The canonical keys (under `published`) are as follows. Non-terminal nodes could
- `vaults`
- `vault0`
- `auction` - [snapshot of details](./test/auction/snapshots/test-auctionContract.js.md)
- `schedule`
- `schedule` - for global schedule info
- `governance`
- `book0`
- `books.book0` - stats about the bid book for one collateral
- `books.book0.bids.bid0` - data about one bid
- `reserve` - [snapshot of details](./test/reserve/snapshots/test-reserve.js.md)
- `governance`
- `metrics`
Expand Down
1 change: 1 addition & 0 deletions packages/inter-protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@endo/far": "^0.2.19",
"@endo/marshal": "^0.8.6",
"@endo/nat": "^4.1.28",
"@endo/promise-kit": "^0.2.57",
"jessie.js": "^0.3.2"
},
"devDependencies": {
Expand Down
96 changes: 75 additions & 21 deletions packages/inter-protocol/src/auction/auctionBook.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import '@agoric/governance/exported.js';
import '@agoric/zoe/exported.js';
import '@agoric/zoe/src/contracts/exported.js';

import { AmountMath } from '@agoric/ertp';
import { AmountMath, AmountShape, RatioShape } from '@agoric/ertp';
import { mustMatch } from '@agoric/store';
import { M, prepareExoClassKit } from '@agoric/vat-data';
import {
M,
prepareExoClassKit,
provideDurableMapStore,
} from '@agoric/vat-data';

import { assertAllDefined, makeTracer } from '@agoric/internal';
import {
Expand All @@ -20,7 +24,11 @@ import { E } from '@endo/captp';
import { observeNotifier } from '@agoric/notifier';

import { makeNatAmountShape } from '../contractSupport.js';
import { preparePriceBook, prepareScaledBidBook } from './offerBook.js';
import {
BidsDataNotificationShape,
preparePriceBook,
prepareScaledBidBook,
} from './offerBook.js';
import {
isScaledBidPriceHigher,
makeBrandedRatioPattern,
Expand Down Expand Up @@ -72,8 +80,8 @@ const trace = makeTracer('AucBook', true);
* @param {Brand<'nat'>} collateralBrand
*/
export const makeOfferSpecShape = (bidBrand, collateralBrand) => {
const bidAmountShape = makeNatAmountShape(bidBrand);
const collateralAmountShape = makeNatAmountShape(collateralBrand);
const bidAmountShape = makeNatAmountShape(bidBrand, 0n);
const collateralAmountShape = makeNatAmountShape(collateralBrand, 0n);
return M.splitRecord(
{ maxBuy: collateralAmountShape },
{
Expand Down Expand Up @@ -106,15 +114,28 @@ export const makeOfferSpecShape = (bidBrand, collateralBrand) => {
* @property {Amount<'nat'> | null} collateralAvailable The amount of collateral
* remaining
*/
export const BookDataNotificationShape = M.splitRecord(
{
startPrice: M.or(RatioShape, null),
currentPriceLevel: M.or(RatioShape, null),
startCollateral: AmountShape,
collateralAvailable: M.or(AmountShape, null),
},
{
proceedsRaised: AmountShape,
},
);
harden(BookDataNotificationShape);

/**
* @param {Baggage} baggage
* @param {ZCF} zcf
* @param {import('@agoric/zoe/src/contractSupport/recorder.js').MakeRecorderKit} makeRecorderKit
*/
export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
const makeScaledBidBook = prepareScaledBidBook(baggage);
const makePriceBook = preparePriceBook(baggage);
const bidDataKits = provideDurableMapStore(baggage, 'bidDataKits');
const makeScaledBidBook = prepareScaledBidBook(baggage, makeRecorderKit);
const makePriceBook = preparePriceBook(baggage, makeRecorderKit);

const AuctionBookStateShape = harden({
collateralBrand: M.any(),
Expand All @@ -126,6 +147,8 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
priceAuthority: M.any(),
updatingOracleQuote: M.any(),
bookDataKit: M.any(),
bidDataKits: M.any(),
bidsDataKit: M.any(),
priceBook: M.any(),
scaledBidBook: M.any(),
startCollateral: M.any(),
Expand All @@ -143,9 +166,9 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
* @param {Brand<'nat'>} bidBrand
* @param {Brand<'nat'>} collateralBrand
* @param {PriceAuthority} pAuthority
* @param {StorageNode} node
* @param {[bookNode: StorageNode, bidsNode: StorageNode]} nodes
*/
(bidBrand, collateralBrand, pAuthority, node) => {
(bidBrand, collateralBrand, pAuthority, nodes) => {
assertAllDefined({ bidBrand, collateralBrand, pAuthority });
const zeroBid = makeEmpty(bidBrand);
const zeroRatio = makeRatioFromAmounts(
Expand All @@ -159,23 +182,36 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
// returned to the funders.
const { zcfSeat: collateralSeat } = zcf.makeEmptySeatKit();
const { zcfSeat: bidHoldingSeat } = zcf.makeEmptySeatKit();
const [bookNode, bidsNode] = nodes;

const bidAmountShape = makeNatAmountShape(bidBrand, 0n);
const collateralAmountShape = makeNatAmountShape(collateralBrand, 0n);

const bidAmountShape = makeNatAmountShape(bidBrand);
const collateralAmountShape = makeNatAmountShape(collateralBrand);
const scaledBidBook = makeScaledBidBook(
makeBrandedRatioPattern(bidAmountShape, bidAmountShape),
collateralBrand,
bidsNode,
);

const priceBook = makePriceBook(
makeBrandedRatioPattern(bidAmountShape, collateralAmountShape),
collateralBrand,
bidsNode,
);

const bookDataKit = makeRecorderKit(
node,
bookNode,
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<BookDataNotification>} */ (
M.any()
BookDataNotificationShape
),
);

/** @typedef {import('./offerBook.js').BidDataNotification} BidDataNotification */

const bidsDataKit = makeRecorderKit(
bidsNode,
/** @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<BidDataNotification>} */ (
BidsDataNotificationShape
),
);

Expand All @@ -191,6 +227,8 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
updatingOracleQuote: zeroRatio,

bookDataKit,
bidDataKits,
bidsDataKit,

priceBook,
scaledBidBook,
Expand Down Expand Up @@ -346,7 +384,6 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {

return collateralTarget;
},

/**
* Accept an offer expressed as a price. If the auction is active,
* attempt to buy collateral. If any of the offer remains add it to the
Expand All @@ -355,14 +392,17 @@ 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]
*/

acceptPriceOffer(
seat,
price,
maxBuy,
timestamp,
{ trySettle, exitAfterBuy = false },
) {
const { priceBook, curAuctionPrice } = this.state;
Expand All @@ -389,7 +429,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);
}

void helper.publishBookData();
Expand All @@ -403,6 +443,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]
Expand All @@ -411,6 +452,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
seat,
bidScaling,
maxBuy,
timestamp,
{ trySettle, exitAfterBuy = false },
) {
trace(this.state.collateralBrand, 'accept scaledBid offer');
Expand Down Expand Up @@ -443,7 +485,13 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
) {
seat.exit();
} else {
scaledBidBook.add(seat, bidScaling, stillWant, exitAfterBuy);
scaledBidBook.add(
seat,
bidScaling,
stillWant,
exitAfterBuy,
timestamp,
);
}

void helper.publishBookData();
Expand All @@ -466,6 +514,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
collateralAvailable,
currentPriceLevel: state.curAuctionPrice,
});

return state.bookDataKit.recorder.write(bookData);
},
},
Expand Down Expand Up @@ -588,7 +637,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
const { remainingProceedsGoal } = state;
const { helper } = facets;
for (const [key, seatRecord] of prioritizedOffers) {
const { seat, price: p, wanted, exitAfterBuy } = seatRecord;
const { seat, price: p } = seatRecord;
if (
remainingProceedsGoal &&
AmountMath.isEmpty(remainingProceedsGoal)
Expand All @@ -597,18 +646,19 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
} else if (seat.hasExited()) {
helper.removeFromItsBook(key, p);
} else {
const collateralSold = helper.settle(seat, wanted);
const { remainingWant, originalWant, exitAfterBuy } = seatRecord;
const collateralSold = helper.settle(seat, remainingWant);

const alloc = seat.getCurrentAllocation();
if (
(exitAfterBuy && !AmountMath.isEmpty(collateralSold)) ||
AmountMath.isEmpty(alloc.Bid) ||
('Collateral' in alloc &&
AmountMath.isGTE(alloc.Collateral, wanted))
AmountMath.isGTE(alloc.Collateral, originalWant))
) {
seat.exit();
helper.removeFromItsBook(key, p);
} else if (!AmountMath.isGTE(collateralSold, wanted)) {
} else if (!AmountMath.isGTE(collateralSold, remainingWant)) {
helper.updateItsBook(key, collateralSold, p);
}
}
Expand Down Expand Up @@ -652,8 +702,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);

Expand All @@ -669,6 +720,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
seat,
offerSpec.offerPrice,
offerSpec.maxBuy,
timestamp,
{
trySettle,
exitAfterBuy,
Expand All @@ -679,6 +731,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
seat,
offerSpec.offerBidScaling,
offerSpec.maxBuy,
timestamp,
{
trySettle,
exitAfterBuy,
Expand Down Expand Up @@ -718,6 +771,7 @@ export const prepareAuctionBook = (baggage, zcf, makeRecorderKit) => {
'Auction schedule',
this.state.bookDataKit,
),
bids: makeRecorderTopic('Auction Bids', this.state.bidsDataKit),
};
},
},
Expand Down
21 changes: 14 additions & 7 deletions packages/inter-protocol/src/auction/auctioneer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import {
makeRatioFromAmounts,
makeRecorderTopic,
natSafeMath,
offerTo,
prepareRecorder,
provideEmptySeat,
offerTo,
} from '@agoric/zoe/src/contractSupport/index.js';
import { FullProposalShape } from '@agoric/zoe/src/typeGuards.js';
import { E } from '@endo/eventual-send';
Expand All @@ -32,7 +32,7 @@ import { Far } from '@endo/marshal';
import { makeNatAmountShape } from '../contractSupport.js';
import { makeOfferSpecShape, prepareAuctionBook } from './auctionBook.js';
import { auctioneerParamTypes } from './params.js';
import { makeScheduler } from './scheduler.js';
import { makeScheduler, ScheduleNotificationShape } from './scheduler.js';
import { AuctionState } from './util.js';

/** @typedef {import('@agoric/vat-data').Baggage} Baggage */
Expand Down Expand Up @@ -441,7 +441,7 @@ export const start = async (zcf, privateArgs, baggage) => {
* @type {import('@agoric/zoe/src/contractSupport/recorder.js').TypedMatcher<
* import('./scheduler.js').ScheduleNotification
* >}
*/ (M.any()),
*/ (ScheduleNotificationShape),
);

/**
Expand Down Expand Up @@ -636,11 +636,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';
};

Expand Down Expand Up @@ -687,6 +688,7 @@ export const start = async (zcf, privateArgs, baggage) => {
),
);

const booksNode = await E(privateArgs.storageNode).makeChildNode('books');
const creatorFacet = makeFarGovernorFacet(
Far('Auctioneer creatorFacet', {
/**
Expand All @@ -701,13 +703,18 @@ export const start = async (zcf, privateArgs, baggage) => {

const bookId = `book${bookCounter}`;
bookCounter += 1;
const bNode = await E(privateArgs.storageNode).makeChildNode(bookId);

const bookNodeP = E(booksNode).makeChildNode(bookId);
const [bookNode, bidsNode] = await Promise.all([
bookNodeP,
E(bookNodeP).makeChildNode('bids'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

something goofy is happening with bidsNode. see the snapshot...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. rearranged the tree so bids are where they should have been.

]);

const newBook = await makeAuctionBook(
brands.Bid,
brand,
priceAuthority,
bNode,
[bookNode, bidsNode],
);

// These three store.init() calls succeed or fail atomically
Expand Down
Loading