Skip to content

Commit

Permalink
chore(chainlink)!: only smart-wallet oracles
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Dec 21, 2022
1 parent 21018a1 commit 8e61373
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 120 deletions.
17 changes: 7 additions & 10 deletions packages/zoe/src/contracts/priceAggregatorChainlink.js
Original file line number Diff line number Diff line change
Expand Up @@ -691,13 +691,6 @@ const start = async (zcf, privateArgs) => {
return add(currentRound, 1);
};

/**
* @type {Omit<import('./priceAggregator').PriceAggregatorContract['creatorFacet'], 'initOracle'> & {
* initOracle: (instance) => Promise<OracleAdmin<PriceRound>>,
* getRoundData(roundId: bigint | number): Promise<RoundData>,
* oracleRoundState(oracleAddr: string, queriedRoundId: BigInt): Promise<any>
* }}
*/
const creatorFacet = Far('PriceAggregatorChainlinkCreatorFacet', {
/**
* An "oracle invitation" is an invitation to be able to submit data to
Expand All @@ -707,7 +700,7 @@ const start = async (zcf, privateArgs) => {
* directly to manage the price submissions as well as to terminate the
* relationship.
*
* @param {string} oracleAddr
* @param {string} oracleAddr Bech32 of oracle operator smart wallet
*/
makeOracleInvitation: async oracleAddr => {
/**
Expand Down Expand Up @@ -756,8 +749,12 @@ const start = async (zcf, privateArgs) => {
},

// unlike the median case, no query argument is passed, since polling behavior is undesired
/** @param {string} oracleAddr */
/**
* @param {string} oracleAddr Bech32 of oracle operator smart wallet
* @returns {Promise<OracleAdmin<PriceRound>>}
*/
async initOracle(oracleAddr) {
assert.typeof(oracleAddr, 'string');
/** @type {OracleRecord} */
const record = { querier: undefined, lastSample: 0 };

Expand Down Expand Up @@ -885,7 +882,7 @@ const start = async (zcf, privateArgs) => {
* a method to provide all current info oracleStatuses need. Intended only
* only to be callable by oracleStatuses. Not for use by contracts to read state.
*
* @param {string} oracleAddr
* @param {string} oracleAddr Bech32 of oracle operator smart wallet
* @param {bigint} queriedRoundId
*/
async oracleRoundState(oracleAddr, queriedRoundId) {
Expand Down
148 changes: 38 additions & 110 deletions packages/zoe/test/unitTests/contracts/test-priceAggregatorChainlink.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import bundleSource from '@endo/bundle-source';

import { E } from '@endo/eventual-send';
import { Far } from '@endo/marshal';
import { makeIssuerKit, AssetKind, AmountMath } from '@agoric/ertp';
import { makeIssuerKit, AssetKind } from '@agoric/ertp';

import {
eventLoopIteration,
Expand Down Expand Up @@ -65,52 +65,13 @@ const makeContext = async () => {
// else, and they can use it to create a new contract instance
// using the same code.
vatAdminState.installBundle('b1-oracle', oracleBundle);
/** @type {Installation<import('../../../src/contracts/oracle.js').OracleStart>} */
const oracleInstallation = await E(zoe).installBundleID('b1-oracle');
vatAdminState.installBundle('b1-aggregator', aggregatorBundle);
/** @type {Installation<import('../../../src/contracts/priceAggregatorChainlink.js').start>} */
const aggregatorInstallation = await E(zoe).installBundleID('b1-aggregator');

const link = makeIssuerKit('$LINK', AssetKind.NAT);
const usd = makeIssuerKit('$USD', AssetKind.NAT);

/**
* @param {bigint} [valueOut]
* @returns {Promise<OracleKit & { instance: Instance }>}
*/
const makeFakePriceOracle = async valueOut => {
/** @type {OracleHandler} */
const oracleHandler = Far('OracleHandler', {
async onQuery({ increment }, _fee) {
assert(valueOut);
assert(increment);
valueOut += increment;
return harden({
reply: `${valueOut}`,
requiredFee: AmountMath.makeEmpty(link.brand),
});
},
onError(query, reason) {
console.error('query', query, 'failed with', reason);
},
onReply(_query, _reply) {},
});

const startResult = await E(zoe).startInstance(
oracleInstallation,
{ Fee: link.issuer },
{ oracleDescription: 'myOracle' },
);
const creatorFacet = await E(startResult.creatorFacet).initialize({
oracleHandler,
});

return harden({
...startResult,
creatorFacet,
});
};

async function makeChainlinkAggregator(config) {
const {
maxSubmissionCount,
Expand Down Expand Up @@ -150,33 +111,29 @@ const makeContext = async () => {
return { ...aggregator, mockStorageRoot };
}

return { makeChainlinkAggregator, makeFakePriceOracle, zoe };
return { makeChainlinkAggregator, zoe };
};

test.before('setup aggregator and oracles', async t => {
t.context = await makeContext();
});

test('basic', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator(defaultConfig);
/** @type {{ timer: ManualTimer }} */
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: basic consensus
Expand Down Expand Up @@ -221,7 +178,7 @@ test('basic', async t => {
});

test('timeout', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -232,18 +189,14 @@ test('timeout', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: basic consensus w/ ticking: should work EXACTLY the same
Expand Down Expand Up @@ -281,7 +234,7 @@ test('timeout', async t => {
});

test('issue check', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -291,18 +244,14 @@ test('issue check', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: ignore too low values
Expand All @@ -328,7 +277,7 @@ test('issue check', async t => {
});

test('supersede', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -338,18 +287,14 @@ test('supersede', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: round 1 is NOT supersedable when 3 submits, meaning it will be ignored
Expand Down Expand Up @@ -383,7 +328,7 @@ test('supersede', async t => {
});

test('interleaved', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -396,18 +341,14 @@ test('interleaved', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: we now need unanimous submission for a round for it to have consensus
Expand Down Expand Up @@ -513,7 +454,7 @@ test('interleaved', async t => {
});

test('larger', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -525,26 +466,20 @@ test('larger', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();
const priceOracleD = await makeFakePriceOracle();
const priceOracleE = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);
const pricePushAdminD = await E(aggregator.creatorFacet).initOracle(
priceOracleD.instance,
'agorice1priceOracleD',
);
const pricePushAdminE = await E(aggregator.creatorFacet).initOracle(
priceOracleE.instance,
'agorice1priceOracleE',
);

// ----- round 1: usual case
Expand Down Expand Up @@ -587,7 +522,7 @@ test('larger', async t => {
});

test('suggest', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -599,18 +534,14 @@ test('suggest', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();
const priceOracleC = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);
const pricePushAdminC = await E(aggregator.creatorFacet).initOracle(
priceOracleC.instance,
'agorice1priceOracleC',
);

// ----- round 1: basic consensus
Expand All @@ -628,7 +559,7 @@ test('suggest', async t => {
await oracleTimer.tick();
await E(pricePushAdminB).pushResult({ roundId: 2, unitPrice: 1000n });
const oracleCSuggestion = await E(aggregator.creatorFacet).oracleRoundState(
priceOracleC.instance,
'agorice1priceOracleC',
1n,
);

Expand All @@ -637,7 +568,7 @@ test('suggest', async t => {
t.deepEqual(oracleCSuggestion.oracleCount, 3);

const oracleBSuggestion = await E(aggregator.creatorFacet).oracleRoundState(
priceOracleB.instance,
'agorice1priceOracleB',
0n,
);

Expand All @@ -652,7 +583,7 @@ test('suggest', async t => {
await E(pricePushAdminC).pushResult({ roundId: 2, unitPrice: 3000n });

const oracleASuggestion = await E(aggregator.creatorFacet).oracleRoundState(
priceOracleA.instance,
'agorice1priceOracleA',
0n,
);

Expand All @@ -674,7 +605,7 @@ test('suggest', async t => {
});

test('notifications', async t => {
const { makeFakePriceOracle, zoe } = t.context;
const { zoe } = t.context;

const aggregator = await t.context.makeChainlinkAggregator({
...defaultConfig,
Expand All @@ -685,14 +616,11 @@ test('notifications', async t => {
// @ts-expect-error cast
const { timer: oracleTimer } = await E(zoe).getTerms(aggregator.instance);

const priceOracleA = await makeFakePriceOracle();
const priceOracleB = await makeFakePriceOracle();

const pricePushAdminA = await E(aggregator.creatorFacet).initOracle(
priceOracleA.instance,
'agorice1priceOracleA',
);
const pricePushAdminB = await E(aggregator.creatorFacet).initOracle(
priceOracleB.instance,
'agorice1priceOracleB',
);

const latestRoundSubscriber = await E(
Expand Down

0 comments on commit 8e61373

Please sign in to comment.