Skip to content

Commit

Permalink
feat(zoe): add priceAuthorityRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Oct 31, 2020
1 parent 3bad7c5 commit 02c6147
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 0 deletions.
2 changes: 2 additions & 0 deletions packages/zoe/exported.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ import './src/contractFacet/types';
import './src/zoeService/types';
import './src/contractSupport/types';
import './src/types';
import './tools/types';
import '@agoric/notifier/exports';
import '@agoric/ertp/exported';
import '@agoric/store/exported';
139 changes: 139 additions & 0 deletions packages/zoe/tools/priceAuthorityRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// @ts-check

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

import '../exported';

/**
* @typedef {Object} Deleter
* @property {() => void} delete
*/

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

/**
* @typedef {Object} PriceAuthorityRegistry A price authority that is a facade
* for other backing price authorities registered for a given asset and price
* brand
* @property {PriceAuthority} priceAuthority
* @property {PriceAuthorityRegistryAdmin} adminFacet
*/

/**
* @returns {PriceAuthorityRegistry}
*/
export const makePriceAuthorityRegistry = () => {
/**
* @typedef {Object} PriceAuthorityRecord A record indicating a registered
* price authority
* @property {ERef<PriceAuthority>} priceAuthority
*/

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

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

/**
* This PriceAuthority is just a wrapper for multiple registered
* PriceAuthorities.
*
* @type {PriceAuthority}
*/
const priceAuthority = {
async getQuoteIssuer(assetBrand, priceBrand) {
const record = lookup(assetBrand, priceBrand);
return E(record.priceAuthority).getQuoteIssuer(assetBrand, priceBrand);
},
async getInputPrice(amountIn, brandOut) {
const record = lookup(amountIn.brand, brandOut);
return E(record.priceAuthority).getInputPrice(amountIn, brandOut);
},
async getOutputPrice(amountOut, brandIn) {
const record = lookup(brandIn, amountOut.brand);
return E(record.priceAuthority).getOutputPrice(amountOut, brandIn);
},
async getPriceNotifier(assetBrand, priceBrand) {
const record = lookup(assetBrand, priceBrand);
return E(record.priceAuthority).getPriceNotifier(assetBrand, priceBrand);
},
async priceAtTime(timer, deadline, assetAmount, priceBrand) {
const record = lookup(assetAmount.brand, priceBrand);
return E(record.priceAuthority).priceAtTime(
timer,
deadline,
assetAmount,
priceBrand,
);
},
async priceWhenLT(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenLT(assetAmount, priceLimit);
},
async priceWhenLTE(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenLTE(assetAmount, priceLimit);
},
async priceWhenGTE(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
},
async priceWhenGT(assetAmount, priceLimit) {
const record = lookup(assetAmount.brand, priceLimit.brand);
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
},
};

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

// Put a box around the authority so that we can be ensured the deleter
// won't delete the wrong thing.
const record = {
priceAuthority: pa,
};

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

return harden({
delete() {
assert.equal(
priceStore.has(priceBrand) && priceStore.get(priceBrand),
record,
details`Price authority already dropped`,
);
priceStore.delete(priceBrand);
},
});
},
};

return harden({
priceAuthority,
adminFacet,
});
};
42 changes: 42 additions & 0 deletions packages/zoe/tools/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @typedef {Object} PriceQuote
* @property {Payment} quotePayment The quote wrapped as a payment
* @property {Amount} quoteAmount Amount of `quotePayment` (`quoteIssuer.getAmountOf(quotePayment)`)
*/

/**
* @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`
* @property {TimerService} timer The service that gave the `timestamp`
* @property {number} timestamp A timestamp for the quote according to `timer`
* @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 {(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>>}
* 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`
*/

0 comments on commit 02c6147

Please sign in to comment.