Skip to content

Commit

Permalink
Merge pull request #6629 from Agoric/6628-chainLink-working
Browse files Browse the repository at this point in the history
make Chainlink the canonical priceAggregator
  • Loading branch information
mergify[bot] authored Dec 21, 2022
2 parents a3962a7 + 8e61373 commit e74278c
Show file tree
Hide file tree
Showing 14 changed files with 855 additions and 660 deletions.
40 changes: 38 additions & 2 deletions packages/agoric-cli/src/commands/oracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const makeOracleCommand = async logger => {

oracle
.command('pushPrice')
.description('add a current price sample')
.description('add a current price sample to a priceAggregator')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.requiredOption(
'--oracleAdminAcceptOfferId [number]',
Expand All @@ -114,7 +114,7 @@ export const makeOracleCommand = async logger => {
invitationSpec: {
source: 'continuing',
previousOffer: opts.oracleAdminAcceptOfferId,
invitationMakerName: 'makePushPriceInvitation',
invitationMakerName: 'PushPrice',
invitationArgs: harden([opts.price]),
},
proposal: {},
Expand All @@ -128,6 +128,42 @@ export const makeOracleCommand = async logger => {
console.warn('Now execute the prepared offer');
});

oracle
.command('pushPriceRound')
.description('add a price for a round to a priceAggregatorChainlink')
.option('--offerId [number]', 'Offer id', Number, Date.now())
.requiredOption(
'--oracleAdminAcceptOfferId [number]',
'offer that had continuing invitation result',
Number,
)
.requiredOption('--price [number]', 'price (per unitAmount)', BigInt)
.requiredOption('--roundId [number]', 'round', Number)
.action(async function () {
// @ts-expect-error this implicit any
const opts = this.opts();

/** @type {import('../lib/psm.js').OfferSpec} */
const offer = {
id: Number(opts.offerId),
invitationSpec: {
source: 'continuing',
previousOffer: opts.oracleAdminAcceptOfferId,
invitationMakerName: 'PushPrice',
invitationArgs: harden([
{ unitPrice: opts.price, roundId: opts.roundId },
]),
},
proposal: {},
};

outputAction({
method: 'executeOffer',
offer,
});

console.warn('Now execute the prepared offer');
});
oracle
.command('query')
.description('return current aggregated (median) price')
Expand Down
37 changes: 35 additions & 2 deletions packages/agoric-cli/test/agops-oracle-smoketest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ if [ -z "$WALLET" ]; then
fi
set -x

# this is in economy-template.json in the oracleAddresses list (agoric1dy0yegdsev4xvce3dx7zrz2ad9pesf5svzud6y)
# to use it run `agd keys oracle2 --interactive` and enter this mnenomic:
# dizzy scale gentle good play scene certain acquire approve alarm retreat recycle inch journey fitness grass minimum learn funny way unlock what buzz upon
WALLET2=oracle2

# Accept invitation to admin an oracle
ORACLE_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle accept >|"$ORACLE_OFFER"
Expand All @@ -33,17 +38,45 @@ agoric wallet send --from "$WALLET" --offer "$ORACLE_OFFER"
agoric wallet show --from "$WALLET"
ORACLE_OFFER_ID=$(jq ".body | fromjson | .offer.id" <"$ORACLE_OFFER")

### Now we have the continuing invitationMakers saved in the wallet
# repeat for oracle2
ORACLE_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle accept >|"$ORACLE_OFFER"
jq ".body | fromjson" <"$ORACLE_OFFER"
agoric wallet send --from "$WALLET2" --offer "$ORACLE_OFFER"
ORACLE2_OFFER_ID=$(jq ".body | fromjson | .offer.id" <"$ORACLE_OFFER")

### Now we have the continuing invitationMakers saved in the wallets

# Use invitation result, with continuing invitationMakers to propose a vote
PROPOSAL_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle pushPrice --price 1.01 --oracleAdminAcceptOfferId "$ORACLE_OFFER_ID" >|"$PROPOSAL_OFFER"
bin/agops oracle pushPriceRound --price 101 --roundId 1 --oracleAdminAcceptOfferId "$ORACLE_OFFER_ID" >|"$PROPOSAL_OFFER"
jq ".body | fromjson" <"$PROPOSAL_OFFER"
agoric wallet send --from "$WALLET" --offer "$PROPOSAL_OFFER"

# verify that the offer was satisfied
echo "Offer $ORACLE_OFFER_ID should have numWantsSatisfied: 1"
agoric wallet show --from "$WALLET"

# verify feed publishing
agd query vstorage keys published.priceFeed

# verify that the round started
agoric follow :published.priceFeed.ATOM-USD_price_feed.latestRound

# submit another price in the round from the second oracle
PROPOSAL_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle pushPriceRound --price 201 --roundId 1 --oracleAdminAcceptOfferId "$ORACLE2_OFFER_ID" >|"$PROPOSAL_OFFER"
jq ".body | fromjson" <"$PROPOSAL_OFFER"
agoric wallet send --from "$WALLET2" --offer "$PROPOSAL_OFFER"

# second round, first oracle
PROPOSAL_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle pushPriceRound --price 1102 --roundId 2 --oracleAdminAcceptOfferId "$ORACLE_OFFER_ID" >|"$PROPOSAL_OFFER"
agoric wallet send --from "$WALLET" --offer "$PROPOSAL_OFFER"
# second round, second oracle
PROPOSAL_OFFER=$(mktemp -t agops.XXX)
bin/agops oracle pushPriceRound --price 1202 --roundId 2 --oracleAdminAcceptOfferId "$ORACLE2_OFFER_ID" >|"$PROPOSAL_OFFER"
agoric wallet send --from "$WALLET2" --offer "$PROPOSAL_OFFER"

# see new price
agoric follow :published.priceFeed.ATOM-USD_price_feed
3 changes: 2 additions & 1 deletion packages/cosmic-swingset/economy-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@
{
"AGORIC_INSTANCE_NAME": "ATOM-USD price feed",
"oracleAddresses": [
"@PRIMARY_ADDRESS@"
"@PRIMARY_ADDRESS@",
"agoric1dy0yegdsev4xvce3dx7zrz2ad9pesf5svzud6y"
],
"IN_BRAND_LOOKUP": [
"agoricNames",
Expand Down
4 changes: 2 additions & 2 deletions packages/inter-protocol/scripts/price-feed-core.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export const defaultProposalBuilder = async (
brandOutRef: brandOut && publishRef(brandOut),
priceAggregatorRef: publishRef(
install(
'@agoric/zoe/src/contracts/priceAggregator.js',
'../bundles/bundle-priceAggregator.js',
'@agoric/zoe/src/contracts/priceAggregatorChainlink.js',
'../bundles/bundle-priceAggregatorChainlink.js',
),
),
},
Expand Down
9 changes: 9 additions & 0 deletions packages/inter-protocol/scripts/start-local-chain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,12 @@ sleep 15
# verify
agoric wallet list
agoric wallet show --from "$WALLET"

echo "Repeating for oracle2 account..."
# this is in economy-template.json in the oracleAddresses list (agoric1dy0yegdsev4xvce3dx7zrz2ad9pesf5svzud6y)
# to use it run `agd keys oracle2 --interactive` and enter this mnenomic:
# dizzy scale gentle good play scene certain acquire approve alarm retreat recycle inch journey fitness grass minimum learn funny way unlock what buzz upon
WALLET2=oracle2
WALLET2_BECH32=$(agd keys show "$WALLET2" --output json | jq -r .address)
make ACCT_ADDR="$WALLET2_BECH32" FUNDS=20000000ubld,20000000ibc/usdc1234 fund-acct
agoric wallet provision --spend --account "$WALLET2"
18 changes: 11 additions & 7 deletions packages/inter-protocol/src/proposals/price-feed-proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export const ensureOracleBrands = async (

/**
* @param {ChainBootstrapSpace} powers
* @param {{options: {priceFeedOptions: {AGORIC_INSTANCE_NAME: string, oracleAddresses: string[], contractTerms: unknown, IN_BRAND_NAME: string, OUT_BRAND_NAME: string}}}} config
* @param {{options: {priceFeedOptions: {AGORIC_INSTANCE_NAME: string, oracleAddresses: string[], contractTerms: import('@agoric/zoe/src/contracts/priceAggregatorChainlink.js').ChainlinkConfig, IN_BRAND_NAME: string, OUT_BRAND_NAME: string}}}} config
*/
export const createPriceFeed = async (
{
Expand Down Expand Up @@ -129,7 +129,7 @@ export const createPriceFeed = async (
/**
* Values come from economy-template.json, which at this writing had IN:ATOM, OUT:USD
*
* @type {[[Brand<'nat'>, Brand<'nat'>], [Installation<import('@agoric/zoe/src/contracts/priceAggregator.js').start>]]}
* @type {[[Brand<'nat'>, Brand<'nat'>], [Installation<import('@agoric/zoe/src/contracts/priceAggregatorChainlink.js').start>]]}
*/
const [[brandIn, brandOut], [priceAggregator]] = await Promise.all([
reserveThenGetNames(E(agoricNamesAdmin).lookupAdmin('oracleBrand'), [
Expand All @@ -142,7 +142,6 @@ export const createPriceFeed = async (
]);

const unitAmountIn = await unitAmount(brandIn);
/** @type {import('@agoric/zoe/src/contracts/priceAggregator.js').PriceAggregatorContract['terms']} */
const terms = await deeplyFulfilledObject(
harden({
...contractTerms,
Expand Down Expand Up @@ -188,11 +187,11 @@ export const createPriceFeed = async (
.then(deleter => E(aggregators).set(terms, { aggregator, deleter }));

/**
* Send an invitation to one of the oracles.
* Initialize a new oracle and send an invitation to administer it.
*
* @param {string} addr
*/
const distributeInvitation = async addr => {
const addOracle = async addr => {
const invitation = await E(aggregator.creatorFacet).makeOracleInvitation(
addr,
);
Expand All @@ -205,7 +204,7 @@ export const createPriceFeed = async (
};

trace('distributing invitations', oracleAddresses);
await Promise.all(oracleAddresses.map(distributeInvitation));
await Promise.all(oracleAddresses.map(addOracle));
trace('createPriceFeed complete');
};

Expand Down Expand Up @@ -306,7 +305,12 @@ export const startPriceFeeds = async (
priceFeedOptions: {
AGORIC_INSTANCE_NAME: `${inBrandName}-${outBrandName} price feed`,
contractTerms: {
POLL_INTERVAL: 1n,
minSubmissionCount: 2,
minSubmissionValue: 1,
maxSubmissionCount: 5,
maxSubmissionValue: 99999,
restartDelay: 1n,
timeout: 10,
},
oracleAddresses: demoOracleAddresses,
IN_BRAND_NAME: inBrandName,
Expand Down
11 changes: 8 additions & 3 deletions packages/inter-protocol/test/smartWallet/contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ export const makeDefaultTestContext = async (t, makeSpace) => {
'installation',
);
const paBundle = await bundleCache.load(
'../zoe/src/contracts/priceAggregator.js',
'../zoe/src/contracts/priceAggregatorChainlink.js',
'priceAggregator',
);
/** @type {Promise<Installation<import('@agoric/zoe/src/contracts/priceAggregator.js').start>>} */
/** @type {Promise<Installation<import('@agoric/zoe/src/contracts/priceAggregatorChainlink.js').start>>} */
const paInstallation = E(zoe).install(paBundle);
await E(installAdmin).update('priceAggregator', paInstallation);

Expand All @@ -87,7 +87,12 @@ export const makeDefaultTestContext = async (t, makeSpace) => {
priceFeedOptions: {
AGORIC_INSTANCE_NAME: `${inBrandName}-${outBrandName} price feed`,
contractTerms: {
POLL_INTERVAL: 1n,
minSubmissionCount: 2,
minSubmissionValue: 1,
maxSubmissionCount: 5,
maxSubmissionValue: 99999,
restartDelay: 1n,
timeout: 10,
},
oracleAddresses,
IN_BRAND_NAME: inBrandName,
Expand Down
34 changes: 13 additions & 21 deletions packages/inter-protocol/test/smartWallet/test-oracle-integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import {
import { eventLoopIteration } from '@agoric/zoe/tools/eventLoopIteration.js';
import { E } from '@endo/far';

import { INVITATION_MAKERS_DESC } from '@agoric/zoe/src/contracts/priceAggregator.js';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { AmountMath } from '@agoric/ertp';
import { coalesceUpdates } from '@agoric/smart-wallet/src/utils.js';
import { INVITATION_MAKERS_DESC } from '@agoric/zoe/src/contracts/priceAggregatorChainlink.js';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { ensureOracleBrands } from '../../src/proposals/price-feed-proposal.js';
import { makeDefaultTestContext } from './contexts.js';
import { headValue } from '../supports.js';
import { makeDefaultTestContext } from './contexts.js';

/**
* @type {import('ava').TestFn<Awaited<ReturnType<makeDefaultTestContext>>
Expand Down Expand Up @@ -87,18 +86,15 @@ test('admin price', async t => {
const currentSub = E(wallet).getCurrentSubscriber();

await t.context.simpleCreatePriceFeed([operatorAddress], 'ATOM', 'USD');
const atomBrand = await E(agoricNames).lookup('oracleBrand', 'ATOM');
const usdBrand = await E(agoricNames).lookup('oracleBrand', 'USD');

const offersFacet = wallet.getOffersFacet();

/** @type {import('@agoric/zoe/src/zoeService/utils.js').Instance<import('@agoric/zoe/src/contracts/priceAggregatorChainlink.js').start>} */
const priceAggregator = await E(agoricNames).lookup(
'instance',
'ATOM-USD price feed',
);
/** @type {import('@agoric/zoe/src/contracts/priceAggregator.js').PriceAggregatorContract['publicFacet']} */
const paPublicFacet = await E(zoe).getPublicFacet(priceAggregator);
const priceAuthority = await E(paPublicFacet).getPriceAuthority();

/**
* get invitation details the way a user would
Expand Down Expand Up @@ -159,12 +155,15 @@ test('admin price', async t => {

// Push a new price result /////////////////////////

/** @type {import('@agoric/zoe/src/contracts/priceAggregatorChainlink.js').PriceRound} */
const result = { roundId: 1, unitPrice: 123n };

/** @type {import('@agoric/smart-wallet/src/invitations.js').ContinuingInvitationSpec} */
const proposeInvitationSpec = {
source: 'continuing',
previousOffer: 44,
invitationMakerName: 'makePushPriceInvitation',
invitationArgs: harden([123n]),
invitationMakerName: 'PushPrice',
invitationArgs: harden([result]),
};

/** @type {import('@agoric/smart-wallet/src/offers').OfferSpec} */
Expand All @@ -185,17 +184,10 @@ test('admin price', async t => {
// trigger an aggregation (POLL_INTERVAL=1n in context)
E(manualTimer).tickN(1);

const quote = await priceAuthority.quoteGiven(
AmountMath.make(atomBrand, 1_000n),
usdBrand,
);
const latestRoundSubscriber = await E(paPublicFacet).getRoundStartNotifier();

t.deepEqual(quote.quoteAmount.value[0].amountIn, {
brand: atomBrand,
value: 1_000n,
});
t.deepEqual(quote.quoteAmount.value[0].amountOut, {
brand: usdBrand,
value: 123_000n,
t.deepEqual((await latestRoundSubscriber.subscribeAfter()).head.value, {
roundId: 1n,
startedAt: 0n,
});
});
4 changes: 4 additions & 0 deletions packages/zoe/scripts/build-bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ const sourceToBundle = [
'../src/contracts/priceAggregator.js',
'../bundles/bundle-priceAggregator.js',
],
[
'../src/contracts/priceAggregatorChainlink.js',
'../bundles/bundle-priceAggregatorChainlink.js',
],
];

await createBundles(sourceToBundle, dirname);
2 changes: 1 addition & 1 deletion packages/zoe/src/contractSupport/priceAuthority.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ export function makeOnewayPriceAuthorityKit(opts) {
amountIn,
amountOut: calcAmountOut(amountIn),
}));
assert(quote);
assert(quote, 'createQuote returned falsey');

const value = await quote;
return harden({
Expand Down
Loading

0 comments on commit e74278c

Please sign in to comment.