-
Notifications
You must be signed in to change notification settings - Fork 215
/
priceAuthorityRegistry.js
220 lines (204 loc) · 6.63 KB
/
priceAuthorityRegistry.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import { Fail } from '@endo/errors';
import { E } from '@endo/far';
import { Far } from '@endo/marshal';
import { BrandShape } from '@agoric/ertp';
import {
M,
prepareExo,
makeScalarBigMapStore,
provideDurableMapStore,
} from '@agoric/vat-data';
import { provideLazy } from '@agoric/store';
import { PriceAuthorityI } from '@agoric/zoe/src/contractSupport/priceAuthority.js';
/**
* @import {Baggage} from '@agoric/vat-data';
* @import {MutableQuote, PriceAuthority, PriceQuote, PriceQuoteValue, PriceQuery,} from '@agoric/zoe/tools/types.js';
*/
/**
* @typedef {object} Deleter
* @property {() => void} delete
*/
/**
* @typedef {object} PriceAuthorityRegistryAdmin
* @property {(
* pa: ERef<PriceAuthority>,
* brandIn: Brand,
* brandOut: Brand,
* force?: boolean,
* ) => Promise<Deleter>} registerPriceAuthority
* Add a unique price authority for a given 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
*/
/**
* Make a singleton registry for priceAuthorities
*
* @param {import('@agoric/vat-data').Baggage} baggage
* @returns {PriceAuthorityRegistry}
*/
export const providePriceAuthorityRegistry = baggage => {
/**
* @typedef {object} PriceAuthorityRecord A record indicating a registered
* price authority. We put a box around the priceAuthority to ensure the
* deleter doesn't delete the wrong thing.
* @property {ERef<PriceAuthority>} priceAuthority the sub-authority for a
* given input and output brand pair
*/
/** @type {MapStore<Brand, MapStore<Brand, PriceAuthorityRecord>>} */
const assetToPriceStore = provideDurableMapStore(baggage, 'brandIn');
/**
* Get the registered price authority for a given input and output pair.
*
* @param {Brand} brandIn
* @param {Brand} brandOut
* @returns {ERef<PriceAuthority>}
*/
const paFor = (brandIn, brandOut) => {
const priceStore = assetToPriceStore.get(brandIn);
return priceStore.get(brandOut).priceAuthority;
};
/**
* Create a quoteWhen* method for the given condition.
*
* @param {'LT' | 'LTE' | 'GTE' | 'GT'} relation
*/
const makeQuoteWhen =
relation =>
/**
* Return a quote when relation is true of the arguments.
*
* @param {Amount<'nat'>} amountIn monitor the amountOut corresponding to
* this amountIn
* @param {Amount<'nat'>} amountOutLimit the value to compare with the
* monitored amountOut
* @returns {Promise<PriceQuote>} resolve with a quote when `amountOut
* relation amountOutLimit` is true
*/
async (amountIn, amountOutLimit) => {
const pa = paFor(amountIn.brand, amountOutLimit.brand);
return E(pa)[`quoteWhen${relation}`](amountIn, amountOutLimit);
};
/**
* Create a mutableQuoteWhen* method for the given condition.
*
* @param {'LT' | 'LTE' | 'GTE' | 'GT'} relation
*/
const makeMutableQuoteWhen =
relation =>
/**
* Return a mutable quote when relation is true of the arguments.
*
* @param {Amount} amountIn monitor the amountOut corresponding to this
* amountIn
* @param {Amount} amountOutLimit the value to compare with the monitored
* amountOut
* @returns {Promise<MutableQuote>} resolve with a quote when `amountOut
* relation amountOutLimit` is true
*/
async (amountIn, amountOutLimit) => {
const pa = paFor(amountIn.brand, amountOutLimit.brand);
return E(pa)[`mutableQuoteWhen${relation}`](amountIn, amountOutLimit);
};
/**
* This PriceAuthority is just a wrapper for multiple registered
* PriceAuthorities.
*
* @type {PriceAuthority}
*/
const priceAuthority = prepareExo(
baggage,
'composite price authority',
PriceAuthorityI,
{
getQuoteIssuer(brandIn, brandOut) {
return E(paFor(brandIn, brandOut)).getQuoteIssuer(brandIn, brandOut);
},
getTimerService(brandIn, brandOut) {
return E(paFor(brandIn, brandOut)).getTimerService(brandIn, brandOut);
},
quoteGiven(amountIn, brandOut) {
return E(paFor(amountIn.brand, brandOut)).quoteGiven(
amountIn,
brandOut,
);
},
quoteWanted(brandIn, amountOut) {
return E(paFor(brandIn, amountOut.brand)).quoteWanted(
brandIn,
amountOut,
);
},
makeQuoteNotifier(amountIn, brandOut) {
return E(paFor(amountIn.brand, brandOut)).makeQuoteNotifier(
amountIn,
brandOut,
);
},
quoteAtTime(deadline, amountIn, brandOut) {
return E(paFor(amountIn.brand, brandOut)).quoteAtTime(
deadline,
amountIn,
brandOut,
);
},
quoteWhenLT: makeQuoteWhen('LT'),
quoteWhenLTE: makeQuoteWhen('LTE'),
quoteWhenGTE: makeQuoteWhen('GTE'),
quoteWhenGT: makeQuoteWhen('GT'),
mutableQuoteWhenLT: makeMutableQuoteWhen('LT'),
mutableQuoteWhenLTE: makeMutableQuoteWhen('LTE'),
mutableQuoteWhenGTE: makeMutableQuoteWhen('GTE'),
mutableQuoteWhenGT: makeMutableQuoteWhen('GT'),
},
);
/** @type {PriceAuthorityRegistryAdmin} */
const adminFacet = prepareExo(
baggage,
'price authority admin facet',
M.interface('priceAuthorityRegistryAdmin', {
registerPriceAuthority: M.callWhen(
M.await(M.remotable('priceAuthority')),
BrandShape,
BrandShape,
)
.optional(M.boolean())
.returns(M.remotable('deleter')),
}),
{
registerPriceAuthority(pa, brandIn, brandOut, force = false) {
/** @type {MapStore<Brand, PriceAuthorityRecord>} */
const priceStore = provideLazy(assetToPriceStore, brandIn, () =>
makeScalarBigMapStore('brandOut', { durable: true }),
);
// 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.
if (force && priceStore.has(brandOut)) {
priceStore.set(brandOut, harden(record));
} else {
priceStore.init(brandOut, harden(record));
}
return Far('deleter', {
// @ts-expect-error XXX callWhen
delete() {
(priceStore.has(brandOut) && priceStore.get(brandOut) === record) ||
Fail`Price authority already dropped`;
priceStore.delete(brandOut);
},
});
},
},
);
return harden({
priceAuthority,
adminFacet,
});
};