Skip to content

Commit 02c6147

Browse files
committed
feat(zoe): add priceAuthorityRegistry
1 parent 3bad7c5 commit 02c6147

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed

packages/zoe/exported.js

+2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ import './src/contractFacet/types';
22
import './src/zoeService/types';
33
import './src/contractSupport/types';
44
import './src/types';
5+
import './tools/types';
6+
import '@agoric/notifier/exports';
57
import '@agoric/ertp/exported';
68
import '@agoric/store/exported';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// @ts-check
2+
3+
import { E } from '@agoric/eventual-send';
4+
import { makeStore } from '@agoric/store';
5+
import { assert, details } from '@agoric/assert';
6+
7+
import '../exported';
8+
9+
/**
10+
* @typedef {Object} Deleter
11+
* @property {() => void} delete
12+
*/
13+
14+
/**
15+
* @typedef {Object} PriceAuthorityRegistryAdmin
16+
* @property {(pa: ERef<PriceAuthority>, assetBrand: Brand, priceBrand: Brand)
17+
* => Deleter} registerPriceAuthority Add a unique price authority for a given
18+
* asset/price pair
19+
*/
20+
21+
/**
22+
* @typedef {Object} PriceAuthorityRegistry A price authority that is a facade
23+
* for other backing price authorities registered for a given asset and price
24+
* brand
25+
* @property {PriceAuthority} priceAuthority
26+
* @property {PriceAuthorityRegistryAdmin} adminFacet
27+
*/
28+
29+
/**
30+
* @returns {PriceAuthorityRegistry}
31+
*/
32+
export const makePriceAuthorityRegistry = () => {
33+
/**
34+
* @typedef {Object} PriceAuthorityRecord A record indicating a registered
35+
* price authority
36+
* @property {ERef<PriceAuthority>} priceAuthority
37+
*/
38+
39+
/** @type {Store<Brand, Store<Brand, PriceAuthorityRecord>>} */
40+
const assetToPriceStore = makeStore('assetBrand');
41+
42+
/**
43+
* @param {Brand} assetBrand
44+
* @param {Brand} priceBrand
45+
*/
46+
const lookup = (assetBrand, priceBrand) => {
47+
const priceStore = assetToPriceStore.get(assetBrand);
48+
return priceStore.get(priceBrand);
49+
};
50+
51+
/**
52+
* This PriceAuthority is just a wrapper for multiple registered
53+
* PriceAuthorities.
54+
*
55+
* @type {PriceAuthority}
56+
*/
57+
const priceAuthority = {
58+
async getQuoteIssuer(assetBrand, priceBrand) {
59+
const record = lookup(assetBrand, priceBrand);
60+
return E(record.priceAuthority).getQuoteIssuer(assetBrand, priceBrand);
61+
},
62+
async getInputPrice(amountIn, brandOut) {
63+
const record = lookup(amountIn.brand, brandOut);
64+
return E(record.priceAuthority).getInputPrice(amountIn, brandOut);
65+
},
66+
async getOutputPrice(amountOut, brandIn) {
67+
const record = lookup(brandIn, amountOut.brand);
68+
return E(record.priceAuthority).getOutputPrice(amountOut, brandIn);
69+
},
70+
async getPriceNotifier(assetBrand, priceBrand) {
71+
const record = lookup(assetBrand, priceBrand);
72+
return E(record.priceAuthority).getPriceNotifier(assetBrand, priceBrand);
73+
},
74+
async priceAtTime(timer, deadline, assetAmount, priceBrand) {
75+
const record = lookup(assetAmount.brand, priceBrand);
76+
return E(record.priceAuthority).priceAtTime(
77+
timer,
78+
deadline,
79+
assetAmount,
80+
priceBrand,
81+
);
82+
},
83+
async priceWhenLT(assetAmount, priceLimit) {
84+
const record = lookup(assetAmount.brand, priceLimit.brand);
85+
return E(record.priceAuthority).priceWhenLT(assetAmount, priceLimit);
86+
},
87+
async priceWhenLTE(assetAmount, priceLimit) {
88+
const record = lookup(assetAmount.brand, priceLimit.brand);
89+
return E(record.priceAuthority).priceWhenLTE(assetAmount, priceLimit);
90+
},
91+
async priceWhenGTE(assetAmount, priceLimit) {
92+
const record = lookup(assetAmount.brand, priceLimit.brand);
93+
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
94+
},
95+
async priceWhenGT(assetAmount, priceLimit) {
96+
const record = lookup(assetAmount.brand, priceLimit.brand);
97+
return E(record.priceAuthority).priceWhenGT(assetAmount, priceLimit);
98+
},
99+
};
100+
101+
/** @type {PriceAuthorityRegistryAdmin} */
102+
const adminFacet = {
103+
registerPriceAuthority(pa, assetBrand, priceBrand) {
104+
/** @type {Store<Brand, PriceAuthorityRecord>} */
105+
let priceStore;
106+
if (assetToPriceStore.has(assetBrand)) {
107+
priceStore = assetToPriceStore.get(assetBrand);
108+
} else {
109+
priceStore = makeStore('priceBrand');
110+
assetToPriceStore.init(assetBrand, priceStore);
111+
}
112+
113+
// Put a box around the authority so that we can be ensured the deleter
114+
// won't delete the wrong thing.
115+
const record = {
116+
priceAuthority: pa,
117+
};
118+
119+
// Set up the record.
120+
priceStore.init(priceBrand, harden(record));
121+
122+
return harden({
123+
delete() {
124+
assert.equal(
125+
priceStore.has(priceBrand) && priceStore.get(priceBrand),
126+
record,
127+
details`Price authority already dropped`,
128+
);
129+
priceStore.delete(priceBrand);
130+
},
131+
});
132+
},
133+
};
134+
135+
return harden({
136+
priceAuthority,
137+
adminFacet,
138+
});
139+
};

packages/zoe/tools/types.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* @typedef {Object} PriceQuote
3+
* @property {Payment} quotePayment The quote wrapped as a payment
4+
* @property {Amount} quoteAmount Amount of `quotePayment` (`quoteIssuer.getAmountOf(quotePayment)`)
5+
*/
6+
7+
/**
8+
* @typedef {Object} PriceQuoteValue An individual quote's value
9+
* @property {Amount} assetAmount The amount of the asset being quoted
10+
* @property {Amount} price The quoted price for the `assetAmount`
11+
* @property {TimerService} timer The service that gave the `timestamp`
12+
* @property {number} timestamp A timestamp for the quote according to `timer`
13+
* @property {any=} conditions Additional conditions for the quote
14+
*/
15+
16+
/**
17+
* @typedef {Object} PriceAuthority An object that mints PriceQuotes and handles
18+
* triggers and notifiers for changes in the price
19+
* @property {(assetBrand: Brand, priceBrand: Brand) => ERef<Issuer>}
20+
* getQuoteIssuer Get the ERTP issuer of PriceQuotes
21+
* @property {(amountIn: Amount, brandOut: Brand) => Promise<PriceQuote>}
22+
* getInputPrice calculate the amount of brandOut that will be returned if the
23+
* amountIn is sold at the current price
24+
* @property {(amountOut: Amount, brandIn: Brand) => Promise<PriceQuote>}
25+
* getOutputPrice calculate the amount of brandIn that is required in order to
26+
* get amountOut using the current price
27+
* @property {(assetBrand: Brand, priceBrand: Brand) => ERef<Notifier<PriceQuote>>}
28+
* getPriceNotifier
29+
* @property {(timer: TimerService, deadline: number, assetAmount: Amount,
30+
* priceBrand: Brand) => Promise<PriceQuote>} priceAtTime Resolves after
31+
* `deadline` passes on `timer` with the price of `assetAmount` at that time
32+
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
33+
* priceWhenGT Resolve when the price of `assetAmount` exceeds `priceLimit`
34+
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
35+
* priceWhenGTE Resolve when the price of `assetAmount` reaches or exceeds
36+
* `priceLimit`
37+
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
38+
* priceWhenLTE Resolve when the price of `assetAmount` reaches or drops below
39+
* `priceLimit`
40+
* @property {(assetAmount: Amount, priceLimit: Amount) => Promise<PriceQuote>}
41+
* priceWhenLT Resolve when the price of `assetAmount` drops below `priceLimit`
42+
*/

0 commit comments

Comments
 (0)