@agoric/zoe
Version:
Zoe: the Smart Contract Framework for Offer Enforcement
120 lines (102 loc) • 3.42 kB
JavaScript
// @ts-check
// @jessie-check
import { E } from '@endo/far';
import { Far } from '@endo/marshal';
import { makeNotifier } from '@agoric/notifier';
import { AmountMath } from '@agoric/ertp';
import { multiplyBy } from '@agoric/ertp/src/ratio.js';
import { mintQuote } from './priceAuthorityTransform.js';
/**
* @import {Amount, Brand, Mint} from '@agoric/ertp';
* @import {Notifier} from '@agoric/notifier';
* @import {PriceAuthority, PriceDescription, PriceQuote, PriceQuoteValue, PriceQuery,} from '@agoric/zoe/tools/types.js';
*/
/**
* Override `makeQuoteNotifier`, `quoteGiven` to provide an initial price
* in case one is not yet available from the source.
*
* Mainly for testing vaults without waiting for oracle operators to PushPrice.
*
* @param {Ratio} priceOutPerIn
* @param {PriceAuthority} priceAuthority
* @param {ERef<Mint<'set', PriceDescription>>} quoteMint
* @param {Brand<'nat'>} brandIn
* @param {Brand<'nat'>} brandOut
* @returns {PriceAuthority}
*/
export const makeInitialTransform = (
priceOutPerIn,
priceAuthority,
quoteMint,
brandIn,
brandOut,
) => {
assert.equal(priceOutPerIn.numerator.brand, brandOut);
assert.equal(priceOutPerIn.denominator.brand, brandIn);
let initialMode = true;
const quoteBrandP = E(E(quoteMint).getIssuer()).getBrand();
const timerP = E(priceAuthority).getTimerService(brandIn, brandOut);
/**
* @param {Amount<'nat'>} amountIn
* @param {Amount<'nat'>} amountOut
* @returns {Promise<PriceQuote>}
*/
const mintCurrentQuote = async (amountIn, amountOut) => {
const [quoteBrand, timer, timestamp] = await Promise.all([
quoteBrandP,
timerP,
E(timerP).getCurrentTimestamp(),
]);
return mintQuote(
quoteBrand,
amountIn,
amountOut,
timer,
timestamp,
quoteMint,
);
};
/** @type {PriceAuthority['makeQuoteNotifier']} */
const makeQuoteNotifier = (amountIn, bOut) => {
AmountMath.coerce(brandIn, amountIn);
assert.equal(bOut, brandOut);
const notifier = E(priceAuthority).makeQuoteNotifier(amountIn, brandOut);
const initialUpdateP = mintCurrentQuote(
priceOutPerIn.denominator,
priceOutPerIn.numerator,
).then(value => harden({ value, updateCount: 0n }));
// Wrap our underlying notifier.
const prefixedNotifier = harden({
async getUpdateSince(updateCount = -1n) {
if (initialMode && updateCount === -1n) {
return initialUpdateP;
}
const quote = E(notifier).getUpdateSince(updateCount);
void quote.then(() => (initialMode = false));
return quote;
},
});
/** @type {Notifier<PriceQuote>} */
const farNotifier = Far('QuoteNotifier', {
...makeNotifier(prefixedNotifier),
// TODO stop exposing baseNotifier methods directly.
...prefixedNotifier,
});
return farNotifier;
};
/** @type {PriceAuthority['quoteGiven']} */
const quoteGiven = async (amountIn, bOut) => {
AmountMath.coerce(brandIn, amountIn);
assert.equal(bOut, brandOut);
const quoteP = E(priceAuthority).quoteGiven(amountIn, brandOut);
void quoteP.then(() => (initialMode = false));
return initialMode
? mintCurrentQuote(amountIn, multiplyBy(amountIn, priceOutPerIn))
: quoteP;
};
return Far('PriceAuthorityWithInitialValue', {
...priceAuthority,
makeQuoteNotifier,
quoteGiven,
});
};