Skip to content

Commit

Permalink
refactor: cleanups from working on vault liquidation
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris-Hibbert committed Feb 22, 2023
1 parent 20d3bd0 commit 48d7f88
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 94 deletions.
51 changes: 35 additions & 16 deletions packages/inter-protocol/src/auction/auctionBook.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,19 @@ export const makeAuctionBook = async (
let lockedPrice = makeZeroRatio();
let updatingOracleQuote = makeZeroRatio();
E.when(E(collateralBrand).getDisplayInfo(), ({ decimalPlaces = 9n }) => {
// TODO(CTH) use this to keep a current price that can be published in state.
// TODO(#6946) use this to keep a current price that can be published in state.
const quoteNotifier = E(priceAuthority).makeQuoteNotifier(
AmountMath.make(collateralBrand, 10n ** decimalPlaces),
currencyBrand,
);

observeNotifier(quoteNotifier, {
updateState: quote => {
trace(`BOOK notifier ${priceFrom(quote).numerator.value}`);
trace(
`BOOK notifier ${priceFrom(quote).numerator.value}/${
priceFrom(quote).denominator.value
}`,
);
return (updatingOracleQuote = priceFrom(quote));
},
fail: reason => {
Expand Down Expand Up @@ -122,6 +126,15 @@ export const makeAuctionBook = async (
return makePriceBook(priceStore, currencyBrand, collateralBrand);
});

const removeFromOneBook = (isPriceBook, key) => {
if (isPriceBook) {
priceBook.delete(key);
} else {
discountBook.delete(key);
}
};

// Settle with seat. The caller is responsible for updating the book, if any.
const settle = (seat, collateralWanted) => {
const { Currency: currencyAvailable } = seat.getCurrentAllocation();
const { Collateral: collateralAvailable } =
Expand Down Expand Up @@ -178,6 +191,7 @@ export const makeAuctionBook = async (

const isActive = auctionState => auctionState === AuctionState.ACTIVE;

// accept new offer.
const acceptOffer = (seat, price, want, timestamp, auctionState) => {
trace('acceptPrice');
// Offer has ZcfSeat, offerArgs (w/price) and timeStamp
Expand All @@ -194,12 +208,14 @@ export const makeAuctionBook = async (

const stillWant = AmountMath.subtract(want, collateralSold);
if (!AmountMath.isEmpty(stillWant)) {
trace('added Offer ', price, stillWant.value);
priceBook.add(seat, price, stillWant, timestamp);
} else {
seat.exit();
}
};

// accept new discount offer.
const acceptDiscountOffer = (
seat,
discount,
Expand Down Expand Up @@ -252,20 +268,23 @@ export const makeAuctionBook = async (

trace(`settling`, pricedOffers.length, discOffers.length);
prioritizedOffers.forEach(([key, { seat, price: p, wanted }]) => {
const collateralSold = settle(seat, wanted);

if (AmountMath.isEmpty(seat.getCurrentAllocation().Currency)) {
seat.exit();
if (p) {
priceBook.delete(key);
} else {
discountBook.delete(key);
}
} else if (!AmountMath.isEmpty(collateralSold)) {
if (p) {
priceBook.updateReceived(key, collateralSold);
} else {
discountBook.updateReceived(key, collateralSold);
if (seat.hasExited()) {
removeFromOneBook(p, key);
} else {
const collateralSold = settle(seat, wanted);

if (
AmountMath.isEmpty(seat.getCurrentAllocation().Currency) ||
AmountMath.isGTE(seat.getCurrentAllocation().Collateral, wanted)
) {
seat.exit();
removeFromOneBook(p, key);
} else if (!AmountMath.isGTE(collateralSold, wanted)) {
if (p) {
priceBook.updateReceived(key, collateralSold);
} else {
discountBook.updateReceived(key, collateralSold);
}
}
}
});
Expand Down
117 changes: 71 additions & 46 deletions packages/inter-protocol/src/auction/auctioneer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
makeRatio,
natSafeMath,
floorMultiplyBy,
provideEmptySeat,
} from '@agoric/zoe/src/contractSupport/index.js';
import { AmountKeywordRecordShape } from '@agoric/zoe/src/typeGuards.js';
import { handleParamGovernance } from '@agoric/governance';
Expand All @@ -26,7 +27,7 @@ import { auctioneerParamTypes } from './params.js';

const { Fail, quote: q } = assert;

const trace = makeTracer('Auction', true);
const trace = makeTracer('Auction', false);

const makeBPRatio = (rate, currencyBrand, collateralBrand = currencyBrand) =>
makeRatioFromAmounts(
Expand Down Expand Up @@ -60,23 +61,26 @@ export const start = async (zcf, privateArgs, baggage) => {
durable: true,
}),
);
const brandToKeyword = provide(baggage, 'brandToKeyword', () =>
makeScalarBigMapStore('deposits', {
durable: true,
}),
);

const reserveFunds = provideEmptySeat(zcf, baggage, 'collateral');

const addDeposit = (seat, amount) => {
if (deposits.has(amount.brand)) {
const depositListForBrand = deposits.get(amount.brand);
deposits.set(
amount.brand,
harden([...depositListForBrand, { seat, amount }]),
);
} else {
deposits.init(amount.brand, harden([{ seat, amount }]));
}
const depositListForBrand = deposits.get(amount.brand);
deposits.set(
amount.brand,
harden([...depositListForBrand, { seat, amount }]),
);
};

// could be above or below 100%. in basis points
let currentDiscountRate;

const distributeProceeds = () => {
// assert collaterals in map match known collaterals
for (const brand of deposits.keys()) {
const book = books.get(brand);
const { collateralSeat, currencySeat } = book.getSeats();
Expand All @@ -85,44 +89,64 @@ export const start = async (zcf, privateArgs, baggage) => {
if (depositsForBrand.length === 1) {
// send it all to the one
const liqSeat = depositsForBrand[0].seat;

atomicRearrange(
zcf,
harden([
[collateralSeat, liqSeat, collateralSeat.getCurrentAllocation()],
[currencySeat, liqSeat, currencySeat.getCurrentAllocation()],
]),
);
liqSeat.exit();
} else {
const totalDeposits = depositsForBrand.reduce((prev, { amount }) => {
deposits.set(brand, []);
} else if (depositsForBrand.length > 1) {
const totCollDeposited = depositsForBrand.reduce((prev, { amount }) => {
return AmountMath.add(prev, amount);
}, AmountMath.makeEmpty(brand));
const curCollateral =
depositsForBrand[0].seat.getCurrentAllocation().Collateral;
if (AmountMath.isEmpty(curCollateral)) {
const currencyRaised = currencySeat.getCurrentAllocation().Currency;
for (const { seat, amount } of deposits.get(brand).values()) {
const payment = floorMultiplyBy(
amount,
makeRatioFromAmounts(currencyRaised, totalDeposits),
);
atomicRearrange(
zcf,
harden([[currencySeat, seat, { Currency: payment }]]),
);
seat.exit();
}
// TODO(cth) sweep away dust
} else {
Fail`Split up incomplete sale`;

const collatRaise = collateralSeat.getCurrentAllocation().Collateral;
const currencyRaise = currencySeat.getCurrentAllocation().Currency;

const collShare = makeRatioFromAmounts(collatRaise, totCollDeposited);
const currShare = makeRatioFromAmounts(currencyRaise, totCollDeposited);
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
const transfers = [];
let currencyLeft = currencyRaise;
let collateralLeft = collatRaise;

// each depositor gets as share that equals their amount deposited
// divided by the total deposited multplied by the currency and
// collateral being distributed.
for (const { seat, amount } of deposits.get(brand).values()) {
const currPortion = floorMultiplyBy(amount, currShare);
currencyLeft = AmountMath.subtract(currencyLeft, currPortion);
const collPortion = floorMultiplyBy(amount, collShare);
collateralLeft = AmountMath.subtract(collateralLeft, collPortion);
transfers.push([currencySeat, seat, { Currency: currPortion }]);
transfers.push([collateralSeat, seat, { Collateral: collPortion }]);
}

// TODO The leftovers should go to the reserve, and should be visible.
const keyword = brandToKeyword.get(brand);
transfers.push([
currencySeat,
reserveFunds,
{ Currency: currencyLeft },
]);
transfers.push([
collateralSeat,
reserveFunds,
{ Collateral: collateralLeft },
{ [keyword]: collateralLeft },
]);
atomicRearrange(zcf, harden(transfers));

for (const { seat } of depositsForBrand) {
seat.exit();
}
}
}
};
const releaseSeats = () => {
for (const brand of deposits.keys()) {
books.get(brand).exitAllSeats();
}
};

const { publicMixin, creatorMixin, makeFarGovernorFacet, params } =
await handleParamGovernance(
Expand Down Expand Up @@ -159,15 +183,10 @@ export const start = async (zcf, privateArgs, baggage) => {
);

tradeEveryBook();

if (!natSafeMath.isGTE(currentDiscountRate, params.getDiscountStep())) {
// end trading
}
},
finalize: () => {
trace('finalize');
distributeProceeds();
releaseSeats();
},
startRound() {
trace('startRound');
Expand Down Expand Up @@ -231,20 +250,24 @@ export const start = async (zcf, privateArgs, baggage) => {

const limitedCreatorFacet = Far('creatorFacet', {
async addBrand(issuer, collateralBrand, kwd) {
if (!baggage.has(kwd)) {
baggage.init(kwd, makeScalarBigMapStore(kwd, { durable: true }));
}
zcf.assertUniqueKeyword(kwd);
!baggage.has(kwd) ||
Fail`cannot add brand with keyword ${kwd}. it's in use`;

zcf.saveIssuer(issuer, kwd);
baggage.init(kwd, makeScalarBigMapStore(kwd, { durable: true }));
const newBook = await makeAuctionBook(
baggage.get(kwd),
zcf,
brands.Currency,
collateralBrand,
priceAuthority,
);
zcf.saveIssuer(issuer, kwd);
deposits.init(collateralBrand, harden([]));
books.init(collateralBrand, newBook);
brandToKeyword.init(collateralBrand, kwd);
},
// TODO (cth) if it's in public, doesn't also need to be in creatorFacet.
// XXX if it's in public, doesn't also need to be in creatorFacet.
getDepositInvitation,
getSchedule() {
return E(scheduler).getSchedule();
Expand All @@ -260,3 +283,5 @@ export const start = async (zcf, privateArgs, baggage) => {
/** @typedef {ContractOf<typeof start>} AuctioneerContract */
/** @typedef {AuctioneerContract['publicFacet']} AuctioneerPublicFacet */
/** @typedef {AuctioneerContract['creatorFacet']} AuctioneerCreatorFacet */

export const AuctionPFShape = M.remotable('Auction Public Facet');
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/auction/discountBook.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const nextTimestamp = makeNextTimestamp();
export const makeDiscountBook = (store, currencyBrand, collateralBrand) => {
return Far('discountBook ', {
add(seat, discount, wanted, proposedTimestamp) {
// TODO(cth) mustMatch(discount, DISCOUNT_PATTERN);
// XXX mustMatch(discount, DISCOUNT_PATTERN);

const time = nextTimestamp(proposedTimestamp);
const key = toDiscountedRateOfferKey(discount, time);
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/auction/params.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ export const makeGovernedTerms = (
timerBrand,
},
) => {
// TODO(CTH) use storageNode and Marshaller
// XXX use storageNode and Marshaller
return harden({
priceAuthority,
timerService: timer,
Expand Down
2 changes: 1 addition & 1 deletion packages/inter-protocol/src/auction/scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ export const makeScheduler = async (
};

const scheduleNextRound = start => {
console.log(`SCHED nextRound`);
trace(`SCHED nextRound`, start);
E(timer).setWakeup(
start,
Far('SchedulerWaker', {
Expand Down
Loading

0 comments on commit 48d7f88

Please sign in to comment.