Skip to content

Commit

Permalink
fix: add more types and update APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Oct 31, 2020
1 parent 02c6147 commit 8b1c582
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 91 deletions.
79 changes: 58 additions & 21 deletions packages/zoe/tools/manualTimer.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,77 @@
// @ts-check

import { E } from '@agoric/eventual-send';
import { makeStore } from '@agoric/store';

import './types';

/**
* @typedef {Object} ManualTimerAdmin
* @property {(msg: string | undefined) => Promise<void>} tick
*/

// A fake clock that also logs progress in tests.
/**
* @typedef {ManualTimerAdmin & TimerService} ManualTimer
*/

/**
* A fake clock that also logs progress.
*
* @param {(...args: any[]) => void} log
* @param {Timestamp} [startValue=0]
* @returns {ManualTimer}
*/
export default function buildManualTimer(log, startValue = 0) {
let ticks = startValue;
const schedule = new Map();

/** @type {Store<Timestamp, Array<TimerServiceHandler>>} */
const schedule = makeStore('Timestamp');

/** @type {ManualTimer} */
const timer = {
setWakeup(deadline, handler) {
if (deadline <= ticks) {
log(`&& task was past its deadline when scheduled: ${deadline} &&`);
handler.wake(ticks);
return undefined;
}
log(`@@ schedule task for:${deadline}, currently: ${ticks} @@`);
if (!schedule.has(deadline)) {
schedule.set(deadline, []);
}
schedule.get(deadline).push(handler);
return deadline;
},
// This function will only be called in testing code to advance the clock.
async tick(msg) {
ticks += 1;
log(`@@ tick:${ticks}${msg ? `: ${msg}` : ''} @@`);
if (schedule.has(ticks)) {
const handlers = schedule.get(ticks);
schedule.delete(ticks);
await Promise.allSettled(
schedule.get(ticks).map(h => {
handlers.map(h => {
log(`&& running a task scheduled for ${ticks}. &&`);
return E(h).wake(ticks);
}),
);
}
},
createRepeater(delaySecs, interval) {
getCurrentTimestamp() {
return ticks;
},
setWakeup(baseTime, handler) {
if (baseTime <= ticks) {
log(`&& task was past its deadline when scheduled: ${baseTime} &&`);
handler.wake(ticks);
return undefined;
}
log(`@@ schedule task for:${baseTime}, currently: ${ticks} @@`);
if (!schedule.has(baseTime)) {
schedule.init(baseTime, []);
}
schedule.get(baseTime).push(handler);
return baseTime;
},
removeWakeup(handler) {
const baseTimes = [];
for (const [baseTime, handlers] of schedule.entries()) {
const index = handlers.indexOf(handler);
if (index >= 0) {
handlers.splice(index, 1);
baseTimes.push(baseTime);
}
}
return baseTimes;
},
createRepeater(baseTime, interval) {
let handlers = [];
const repeater = {
schedule(h) {
Expand All @@ -51,12 +91,9 @@ export default function buildManualTimer(log, startValue = 0) {
await Promise.allSettled(handlers.map(h => E(h).wake(timestamp)));
},
};
timer.setWakeup(ticks + delaySecs, repeaterHandler);
timer.setWakeup(ticks + baseTime, repeaterHandler);
return repeater;
},
getCurrentTimestamp() {
return ticks;
},
};
harden(timer);
return timer;
Expand Down
86 changes: 43 additions & 43 deletions packages/zoe/tools/priceAuthorityRegistry.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import '../exported';

/**
* @typedef {Object} PriceAuthorityRegistryAdmin
* @property {(pa: ERef<PriceAuthority>, assetBrand: Brand, priceBrand: Brand)
* @property {(pa: ERef<PriceAuthority>, brandIn: Brand, brandOut: Brand)
* => Deleter} registerPriceAuthority Add a unique price authority for a given
* asset/price pair
* pair
*/

/**
Expand All @@ -37,15 +37,15 @@ export const makePriceAuthorityRegistry = () => {
*/

/** @type {Store<Brand, Store<Brand, PriceAuthorityRecord>>} */
const assetToPriceStore = makeStore('assetBrand');
const assetToPriceStore = makeStore('brandIn');

/**
* @param {Brand} assetBrand
* @param {Brand} priceBrand
* @param {Brand} brandIn
* @param {Brand} brandOut
*/
const lookup = (assetBrand, priceBrand) => {
const priceStore = assetToPriceStore.get(assetBrand);
return priceStore.get(priceBrand);
const lookup = (brandIn, brandOut) => {
const priceStore = assetToPriceStore.get(brandIn);
return priceStore.get(brandOut);
};

/**
Expand All @@ -55,59 +55,59 @@ export const makePriceAuthorityRegistry = () => {
* @type {PriceAuthority}
*/
const priceAuthority = {
async getQuoteIssuer(assetBrand, priceBrand) {
const record = lookup(assetBrand, priceBrand);
return E(record.priceAuthority).getQuoteIssuer(assetBrand, priceBrand);
async getQuoteIssuer(brandIn, brandOut) {
const record = lookup(brandIn, brandOut);
return E(record.priceAuthority).getQuoteIssuer(brandIn, brandOut);
},
async getInputPrice(amountIn, brandOut) {
async getAmountInQuote(amountIn, brandOut) {
const record = lookup(amountIn.brand, brandOut);
return E(record.priceAuthority).getInputPrice(amountIn, brandOut);
return E(record.priceAuthority).getAmountInQuote(amountIn, brandOut);
},
async getOutputPrice(amountOut, brandIn) {
async getAmountOutQuote(brandIn, amountOut) {
const record = lookup(brandIn, amountOut.brand);
return E(record.priceAuthority).getOutputPrice(amountOut, brandIn);
return E(record.priceAuthority).getAmountOutQuote(brandIn, amountOut);
},
async getPriceNotifier(assetBrand, priceBrand) {
const record = lookup(assetBrand, priceBrand);
return E(record.priceAuthority).getPriceNotifier(assetBrand, priceBrand);
async getPriceNotifier(brandIn, brandOut) {
const record = lookup(brandIn, brandOut);
return E(record.priceAuthority).getPriceNotifier(brandIn, brandOut);
},
async priceAtTime(timer, deadline, assetAmount, priceBrand) {
const record = lookup(assetAmount.brand, priceBrand);
return E(record.priceAuthority).priceAtTime(
async quoteAtTime(timer, deadline, amountIn, brandOut) {
const record = lookup(amountIn.brand, brandOut);
return E(record.priceAuthority).quoteAtTime(
timer,
deadline,
assetAmount,
priceBrand,
amountIn,
brandOut,
);
},
async priceWhenLT(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenLT(assetAmount, priceLimit);
async quoteWhenLT(amountIn, amountOutLimit) {
const record = lookup(amountIn.brand, amountOutLimit.brand);
return E(record.priceAuthority).quoteWhenLT(amountIn, amountOutLimit);
},
async priceWhenLTE(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenLTE(assetAmount, priceLimit);
async quoteWhenLTE(amountIn, amountOutLimit) {
const record = lookup(amountIn.brand, amountOutLimit.brand);
return E(record.priceAuthority).quoteWhenLTE(amountIn, amountOutLimit);
},
async priceWhenGTE(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
async quoteWhenGTE(amountIn, amountOutLimit) {
const record = lookup(amountIn.brand, amountOutLimit.brand);
return E(record.priceAuthority).quoteWhenGT(amountIn, amountOutLimit);
},
async priceWhenGT(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
async quoteWhenGT(amountIn, amountOutLimit) {
const record = lookup(amountIn.brand, amountOutLimit.brand);
return E(record.priceAuthority).quoteWhenGT(amountIn, amountOutLimit);
},
};

/** @type {PriceAuthorityRegistryAdmin} */
const adminFacet = {
registerPriceAuthority(pa, assetBrand, priceBrand) {
registerPriceAuthority(pa, brandIn, brandOut) {
/** @type {Store<Brand, PriceAuthorityRecord>} */
let priceStore;
if (assetToPriceStore.has(assetBrand)) {
priceStore = assetToPriceStore.get(assetBrand);
if (assetToPriceStore.has(brandIn)) {
priceStore = assetToPriceStore.get(brandIn);
} else {
priceStore = makeStore('priceBrand');
assetToPriceStore.init(assetBrand, priceStore);
priceStore = makeStore('brandOut');
assetToPriceStore.init(brandIn, priceStore);
}

// Put a box around the authority so that we can be ensured the deleter
Expand All @@ -117,16 +117,16 @@ export const makePriceAuthorityRegistry = () => {
};

// Set up the record.
priceStore.init(priceBrand, harden(record));
priceStore.init(brandOut, harden(record));

return harden({
delete() {
assert.equal(
priceStore.has(priceBrand) && priceStore.get(priceBrand),
priceStore.has(brandOut) && priceStore.get(brandOut),
record,
details`Price authority already dropped`,
);
priceStore.delete(priceBrand);
priceStore.delete(brandOut);
},
});
},
Expand Down
104 changes: 77 additions & 27 deletions packages/zoe/tools/types.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,92 @@
/**
* @typedef {Object} TimerService Gives the ability to get the current time,
* schedule a single wake() call, create a repeater that will allow scheduling
* of events at regular intervals, or remove scheduled calls.
* @property {() => Timestamp} getCurrentTimestamp Retrieve the latest timestamp
* @property {(baseTime: Timestamp, handler: TimerServiceHandler) => Timestamp}
* setWakeup Return value is the time at which the call is scheduled to take
* place
* @property {(handler: TimerServiceHandler) => Array<Timestamp>} removeWakeup Remove
* the handler from all its scheduled wakeup, whether produced by
* `timer.setWakeup(h)` or `repeater.schedule(h)`.
* @property {(baseTime: Timestamp, interval: RelativeTime) => TimerRepeater}
* createRepeater Create and return a repeater that will schedule `wake()` calls
* repeatedly at times that are a multiple of interval following baseTime.
* Interval is the delay between successive times at which wake will be called.
* When `schedule(h)` is called, `h.wake()` will be scheduled to be called after
* the next multiple of interval from the base. Since times can be
* coarse-grained, the actual call may occur later, but this won't change when
* the next event will be called.
*/

/**
* @typedef {number} Timestamp An absolute individual stamp returned by a
* TimerService. Note that different timer services may have different
* interpretations of actual Timestamp values.
* @typedef {number} RelativeTime Difference between two Timestamps. Note that
* different timer services may have different interpretations of actual
* RelativeTime values.
*/

/**
* @typedef {Object} TimerServiceHandler
* @property {(timestamp: Timestamp) => void} wake The timestamp passed to
* `wake()` is the time that the call was scheduled to occur.
*/

/**
* @typedef {Object} TimerRepeater
* @property {(handler: TimerHandler) => void} schedule Returns the time
* scheduled for the first call on handler. The handler will continue to be
* scheduled for a `wake()` call every interval until the repeater is disabled.
* @property {() => void} disable Disable this repeater, so `schedule()` can't
* be called, and handlers already scheduled with this repeater won't be
* rescheduled again after `wake()` is next called on them.
*/

/**
* @typedef {Object} PriceQuote
* @property {Payment} quotePayment The quote wrapped as a payment
* @property {Amount} quoteAmount Amount of `quotePayment` (`quoteIssuer.getAmountOf(quotePayment)`)
* @property {Amount} quoteAmount Amount whose value is a PriceQuoteValue
* @property {Payment} quotePayment The `quoteAmount` wrapped as a payment
*/

/**
* @typedef {Array<PriceDescription>} PriceQuoteValue A set of PriceDescriptions
*/

/**
* @typedef {Object} PriceQuoteValue An individual quote's value
* @property {Amount} assetAmount The amount of the asset being quoted
* @property {Amount} price The quoted price for the `assetAmount`
* @typedef {Object} PriceDescription A description of a single quote
* @property {Amount} amountIn The amount supplied to a trade
* @property {Amount} amountOut The quoted result of trading `amountIn`
* @property {TimerService} timer The service that gave the `timestamp`
* @property {number} timestamp A timestamp for the quote according to `timer`
* @property {Timestamp} timestamp A timestamp according to `timer` for the
* quote
* @property {any=} conditions Additional conditions for the quote
*/

/**
* @typedef {Object} PriceAuthority An object that mints PriceQuotes and handles
* triggers and notifiers for changes in the price
* @property {(assetBrand: Brand, priceBrand: Brand) => ERef<Issuer>}
* getQuoteIssuer Get the ERTP issuer of PriceQuotes
* @property {(brandIn: Brand, brandOut: Brand) => ERef<Issuer>} getQuoteIssuer
* Get the ERTP issuer of PriceQuotes
* @property {(amountIn: Amount, brandOut: Brand) => Promise<PriceQuote>}
* getInputPrice calculate the amount of brandOut that will be returned if the
* amountIn is sold at the current price
* @property {(amountOut: Amount, brandIn: Brand) => Promise<PriceQuote>}
* getOutputPrice calculate the amount of brandIn that is required in order to
* get amountOut using the current price
* @property {(assetBrand: Brand, priceBrand: Brand) => ERef<Notifier<PriceQuote>>}
* getAmountInQuote get a quote corresponding to the specified amountIn
* @property {(brandIn: Brand, amountOut: Amount) => Promise<PriceQuote>}
* getAmountOutQuote get a quote corresponding to the specified amountOut
* @property {(brandIn: Brand, brandOut: Brand) => ERef<Notifier<PriceQuote>>}
* getPriceNotifier
* @property {(timer: TimerService, deadline: number, assetAmount: Amount,
* priceBrand: Brand) => Promise<PriceQuote>} priceAtTime Resolves after
* `deadline` passes on `timer` with the price of `assetAmount` at that time
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
* priceWhenGT Resolve when the price of `assetAmount` exceeds `priceLimit`
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
* priceWhenGTE Resolve when the price of `assetAmount` reaches or exceeds
* `priceLimit`
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
* priceWhenLTE Resolve when the price of `assetAmount` reaches or drops below
* `priceLimit`
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
* priceWhenLT Resolve when the price of `assetAmount` drops below `priceLimit`
* @property {(timer: TimerService, deadline: number, amountIn: Amount,
* brandOut: Brand) => Promise<PriceQuote>} quoteAtTime Resolves after
* `deadline` passes on `timer` with the price quote of `amountIn` at that time
* @property {(amountIn: Amount, amountOutLimit: Amount) => Promise<PriceQuote>}
* quoteWhenGT Resolve when a price quote of `amountIn` exceeds `amountOutLimit`
* @property {(amountIn: Amount, amountOutLimit: Amount) => Promise<PriceQuote>}
* quoteWhenGTE Resolve when a price quote of `amountIn` reaches or exceeds
* `amountOutLimit`
* @property {(amountIn: Amount, amountOutLimit: Amount) => Promise<PriceQuote>}
* quoteWhenLTE Resolve when a price quote of `amountIn` reaches or drops below
* `amountOutLimit`
* @property {(amountIn: Amount, amountOutLimit: Amount) => Promise<PriceQuote>}
* quoteWhenLT Resolve when the price quote of `amountIn` drops below
* `amountOutLimit`
*/

0 comments on commit 8b1c582

Please sign in to comment.