Skip to content

Commit

Permalink
feat(price): addOracles by EC
Browse files Browse the repository at this point in the history
  • Loading branch information
turadg committed Feb 11, 2023
1 parent a5106ef commit e972759
Show file tree
Hide file tree
Showing 5 changed files with 1,193 additions and 60 deletions.
12 changes: 12 additions & 0 deletions packages/governance/src/contractGovernor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { E } from '@endo/eventual-send';
import { Far } from '@endo/marshal';
import { mustMatch } from '@agoric/store';

import { makeTracer } from '@agoric/internal';
import {
CONTRACT_ELECTORATE,
setupParamGovernance,
Expand All @@ -12,6 +13,8 @@ import { ParamChangesQuestionDetailsShape } from './typeGuards.js';

const { Fail } = assert;

const trace = makeTracer('CGov', false);

/**
* Validate that the question details correspond to a parameter change question
* that the electorate hosts, and that the voteCounter and other details are
Expand Down Expand Up @@ -125,6 +128,8 @@ const validateQuestionFromCounter = async (zoe, electorate, voteCounter) => {
*/

/**
* Start an instance of a governor, governing a "governed" contract specified in terms.
*
* @template {GovernableStartFn} SF Start function of governed contract
* @param {ZCF<{
* timer: import('@agoric/time/src/types').TimerService,
Expand All @@ -139,7 +144,9 @@ const validateQuestionFromCounter = async (zoe, electorate, voteCounter) => {
* }} privateArgs
*/
const start = async (zcf, privateArgs) => {
trace('start');
const zoe = zcf.getZoeService();
trace('getTerms', zcf.getTerms());
const {
timer,
governedContractInstallation,
Expand All @@ -148,6 +155,7 @@ const start = async (zcf, privateArgs) => {
terms: contractTerms,
},
} = zcf.getTerms();
trace('contractTerms', contractTerms);
contractTerms.governedParams[CONTRACT_ELECTORATE] ||
Fail`Contract must declare ${CONTRACT_ELECTORATE} as a governed parameter`;

Expand All @@ -156,6 +164,7 @@ const start = async (zcf, privateArgs) => {
electionManager: zcf.getInstance(),
});

trace('starting governedContractInstallation');
const {
creatorFacet: governedCF,
instance: governedInstance,
Expand Down Expand Up @@ -195,9 +204,11 @@ const start = async (zcf, privateArgs) => {
}
return poserFacet;
};
trace('awaiting getUpdatedPoserFacet');
await getUpdatedPoserFacet();
assert(poserFacet, 'question poser facet must be initialized');

trace('awaiting setupParamGovernance');
// All governed contracts have at least a governed electorate
const { voteOnParamChanges, createdQuestion: createdParamQuestion } =
await setupParamGovernance(
Expand All @@ -208,6 +219,7 @@ const start = async (zcf, privateArgs) => {
getUpdatedPoserFacet,
);

trace('awaiting setupFilterGovernance');
const { voteOnFilter, createdFilterQuestion } = await setupFilterGovernance(
zoe,
governedInstance,
Expand Down
66 changes: 61 additions & 5 deletions packages/inter-protocol/src/price/fluxAggregator.contract.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { AssetKind, makeIssuerKit } from '@agoric/ertp';
import { assertAllDefined } from '@agoric/internal';
import { handleParamGovernance } from '@agoric/governance';
import { assertAllDefined, makeTracer } from '@agoric/internal';
import { E } from '@endo/eventual-send';
import { reserveThenDeposit } from '../proposals/utils.js';
import { provideFluxAggregator } from './fluxAggregator.js';

const trace = makeTracer('FluxAgg');
/**
* @typedef {import('@agoric/vat-data').Baggage} Baggage
* @typedef {import('@agoric/time/src/types').TimerService} TimerService
Expand All @@ -20,13 +23,16 @@ import { provideFluxAggregator } from './fluxAggregator.js';
* unitAmountIn?: Amount<'nat'>,
* }>} zcf
* @param {{
* initialPoserInvitation: Invitation,
* marshaller: Marshaller,
* namesByAddressAdmin: ERef<import('@agoric/vats').NameAdmin>,
* quoteMint?: ERef<Mint<'set'>>,
* storageNode: ERef<StorageNode>,
* }} privateArgs
* @param {Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
trace('start');
const { timer: timerP } = zcf.getTerms();

const quoteMintP =
Expand All @@ -40,12 +46,19 @@ export const start = async (zcf, privateArgs, baggage) => {
mint: quoteMint,
};

const { marshaller, storageNode: storageNodeP } = privateArgs;
assertAllDefined({ marshaller, storageNodeP });
const {
initialPoserInvitation,
marshaller,
namesByAddressAdmin,
storageNode: storageNodeP,
} = privateArgs;
assertAllDefined({ initialPoserInvitation, marshaller, storageNodeP });

const timer = await timerP;
const storageNode = await storageNodeP;

trace('awaited args');

const fa = provideFluxAggregator(
baggage,
zcf,
Expand All @@ -54,10 +67,53 @@ export const start = async (zcf, privateArgs, baggage) => {
storageNode,
marshaller,
);
trace('got fa', fa);

const { augmentPublicFacet, makeGovernorFacet } = await handleParamGovernance(
// @ts-expect-error FIXME include Governance params
zcf,
initialPoserInvitation,
{
// No governed parameters. Governance just for API methods.
},
storageNode,
marshaller,
);

/**
* Initialize a new oracle and send an invitation to administer it.
*
* @param {string} addr
*/
const addOracle = async addr => {
const invitation = await E(fa.creatorFacet).makeOracleInvitation(addr);
// XXX imported from 'proposals' path
await reserveThenDeposit(
`fluxAggregator oracle ${addr}`,
namesByAddressAdmin,
addr,
[invitation],
);
return `added ${addr}`;
};

const governedApis = {
/**
* Add the specified oracles. May partially fail, such that some oracles are added and others aren't.
*
* @param {string[]} oracleIds
* @returns {Promise<Array<PromiseSettledResult<string>>>}
*/
addOracles: oracleIds => {
return Promise.allSettled(oracleIds.map(addOracle));
},
};

const governorFacet = makeGovernorFacet(fa.creatorFacet, governedApis);
return harden({
creatorFacet: fa.creatorFacet,
publicFacet: fa.publicFacet,
creatorFacet: governorFacet,
// XXX this is a lot of API to put on every public facet
publicFacet: augmentPublicFacet(fa.publicFacet),
});
};
harden(start);
116 changes: 86 additions & 30 deletions packages/inter-protocol/src/proposals/price-feed-proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import {
import { deeplyFulfilledObject, makeTracer } from '@agoric/internal';

import { unitAmount } from '@agoric/zoe/src/contractSupport/priceQuote.js';
import { CONTRACT_ELECTORATE, ParamTypes } from '@agoric/governance';
import { reserveThenDeposit, reserveThenGetNames } from './utils.js';

const trace = makeTracer('RunPriceFeed');
const trace = makeTracer('RunPriceFeed', false);

/** @type {(name: string) => string} */
const sanitizePathSegment = name => {
Expand Down Expand Up @@ -96,6 +97,8 @@ export const createPriceFeed = async (
chainStorage,
chainTimerService,
client,
econCharterKit,
economicCommitteeCreatorFacet,
namesByAddressAdmin,
priceAuthority,
priceAuthorityAdmin,
Expand Down Expand Up @@ -124,55 +127,107 @@ 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/inter-protocol/src/price/fluxAggregator.contract.js').start>]]}
* @type {[[Brand<'nat'>, Brand<'nat'>], [Installation<import('@agoric/governance/src/contractGovernor.js').start>, Installation<import('@agoric/inter-protocol/src/price/fluxAggregator.contract.js').start>]]}
*/
const [[brandIn, brandOut], [priceAggregator]] = await Promise.all([
reserveThenGetNames(E(agoricNamesAdmin).lookupAdmin('oracleBrand'), [
IN_BRAND_NAME,
OUT_BRAND_NAME,
]),
reserveThenGetNames(E(agoricNamesAdmin).lookupAdmin('installation'), [
'priceAggregator',
]),
]);
const [[brandIn, brandOut], [contractGovernor, priceAggregator]] =
await Promise.all([
reserveThenGetNames(E(agoricNamesAdmin).lookupAdmin('oracleBrand'), [
IN_BRAND_NAME,
OUT_BRAND_NAME,
]),
reserveThenGetNames(E(agoricNamesAdmin).lookupAdmin('installation'), [
'contractGovernor',
'priceAggregator',
]),
]);

trace('getPoserInvitation');
const poserInvitationP = E(
economicCommitteeCreatorFacet,
).getPoserInvitation();
const [initialPoserInvitation, electorateInvitationAmount] =
await Promise.all([
poserInvitationP,
E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP),
]);
trace('got initialPoserInvitation');

const unitAmountIn = await unitAmount(brandIn);
const terms = await deeplyFulfilledObject(
const terms = harden({
...contractTerms,
description: AGORIC_INSTANCE_NAME,
brandIn,
brandOut,
timer,
unitAmountIn,
governedParams: {
[CONTRACT_ELECTORATE]: {
type: ParamTypes.INVITATION,
value: electorateInvitationAmount,
},
},
});
trace('got terms');

const governorTerms = await deeplyFulfilledObject(
harden({
...contractTerms,
description: AGORIC_INSTANCE_NAME,
brandIn,
brandOut,
timer,
unitAmountIn,
timer: chainTimerService,
governedContractInstallation: priceAggregator,
governed: {
terms,
},
}),
);
trace('got governorTerms', governorTerms);

const storageNode = await makeStorageNodeChild(chainStorage, STORAGE_PATH);
const marshaller = E(board).getReadonlyMarshaller();

trace('got contractGovernor', contractGovernor);

trace('awaiting startInstance');
// Create the price feed.
const aggregator = await E(zoe).startInstance(
priceAggregator,
/** @type {{ creatorFacet: import('@agoric/governance/src/contractGovernor.js').GovernedContractFnFacetAccess<import('@agoric/inter-protocol/src/price/fluxAggregator.contract.js').start>, publicFacet: GovernorPublic, instance: Instance, adminFacet: AdminFacet }} */
const aggregatorGovernor = await E(zoe).startInstance(
contractGovernor,
undefined,
terms,
governorTerms,
{
storageNode: E(storageNode).makeChildNode(
sanitizePathSegment(AGORIC_INSTANCE_NAME),
),
marshaller,
governed: {
initialPoserInvitation,
marshaller,
namesByAddressAdmin,
storageNode: E(storageNode).makeChildNode(
sanitizePathSegment(AGORIC_INSTANCE_NAME),
),
},
},
);
const faCreatorFacet = await E(
aggregatorGovernor.creatorFacet,
).getCreatorFacet();
trace('got aggregator', faCreatorFacet);

const faPublic = await E(aggregatorGovernor.creatorFacet).getPublicFacet();
const faInstance = await E(aggregatorGovernor.creatorFacet).getInstance();
trace('got', { faInstance, faPublic });

E(E(agoricNamesAdmin).lookupAdmin('instance')).update(
AGORIC_INSTANCE_NAME,
aggregator.instance,
faInstance,
);

E(E.get(econCharterKit).creatorFacet).addInstance(
faInstance,
aggregatorGovernor.creatorFacet,
AGORIC_INSTANCE_NAME,
);
trace('registered', AGORIC_INSTANCE_NAME, faInstance);

// Publish price feed in home.priceAuthority.
const forceReplace = true;
void E(priceAuthorityAdmin).registerPriceAuthority(
E(aggregator.publicFacet).getPriceAuthority(),
E(faPublic).getPriceAuthority(),
brandIn,
brandOut,
forceReplace,
Expand All @@ -184,9 +239,7 @@ export const createPriceFeed = async (
* @param {string} addr
*/
const addOracle = async addr => {
const invitation = await E(aggregator.creatorFacet).makeOracleInvitation(
addr,
);
const invitation = await E(faCreatorFacet).makeOracleInvitation(addr);
await reserveThenDeposit(
`${AGORIC_INSTANCE_NAME} member ${addr}`,
namesByAddressAdmin,
Expand Down Expand Up @@ -220,6 +273,9 @@ export const getManifestForPriceFeed = async (
chainStorage: t,
chainTimerService: t,
client: t,
contractGovernor: t,
econCharterKit: t,
economicCommitteeCreatorFacet: t,
namesByAddressAdmin: t,
priceAuthority: t,
priceAuthorityAdmin: t,
Expand Down
Loading

0 comments on commit e972759

Please sign in to comment.