Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

btest adding collateral #7892

Merged
merged 5 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/agoric-cli/src/commands/oracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Nat } from '@endo/nat';
import { Command } from 'commander';
import * as cp from 'child_process';
import { inspect } from 'util';
import { instanceNameFor } from '@agoric/inter-protocol/src/proposals/price-feed-proposal.js';
import { normalizeAddressWithOptions } from '../lib/chain.js';
import { getNetworkConfig, makeRpcUtils, storageHelper } from '../lib/rpc.js';
import {
Expand Down Expand Up @@ -75,7 +76,7 @@ export const makeOracleCommand = (logger, io = {}) => {
const utils = await makeRpcUtils({ fetch });

const lookupPriceAggregatorInstance = ([brandIn, brandOut]) => {
const name = `${brandIn}-${brandOut} price feed`;
const name = instanceNameFor(brandIn, brandOut);
const instance = utils.agoricNames.instance[name];
if (!instance) {
logger.debug('known instances:', utils.agoricNames.instance);
Expand Down
1 change: 1 addition & 0 deletions packages/inter-protocol/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"scripts": {
"build": "yarn build:bundles",
"build:bundles": "node ./scripts/build-bundles.js",
"build:add-STARS-proposal": "agoric run scripts/add-STARS.js",
"test": "ava",
"test:c8": "c8 $C8_OPTIONS ava --config=ava-nesm.config.js",
"test:xs": "exit 0",
Expand Down
43 changes: 43 additions & 0 deletions packages/inter-protocol/scripts/add-STARS.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { makeHelpers } from '@agoric/deploy-script-support';
import { defaultProposalBuilder as vaultProposalBuilder } from './add-collateral-core.js';
import { defaultProposalBuilder as oraclesProposalBuilder } from './price-feed-core.js';

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
export const starsVaultProposalBuilder = async powers => {
return vaultProposalBuilder(powers, {
interchainAssetOptions: {
// Values for the Stargaze token on Osmosis
denom:
'ibc/987C17B11ABC2B20019178ACE62929FE9840202CE79498E29FE8E5CB02B7C0A4',
decimalPlaces: 6,
keyword: 'STARS',
oracleBrand: 'STARS',
proposedName: 'STARS',
},
});
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').ProposalBuilder} */
export const starsOraclesProposalBuilder = async powers => {
return oraclesProposalBuilder(powers, {
AGORIC_INSTANCE_NAME: `STARS-USD price feed`,
IN_BRAND_LOOKUP: ['agoricNames', 'oracleBrand', 'STARS'],
IN_BRAND_DECIMALS: 6,
OUT_BRAND_LOOKUP: ['agoricNames', 'oracleBrand', 'USD'],
OUT_BRAND_DECIMALS: 4,
oracleAddresses: [
// copied from decentral-main-vaults-config.json
'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr',
'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8',
'agoric144rrhh4m09mh7aaffhm6xy223ym76gve2x7y78',
'agoric19d6gnr9fyp6hev4tlrg87zjrzsd5gzr5qlfq2p',
'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj',
],
});
};

export default async (homeP, endowments) => {
const { writeCoreProposal } = await makeHelpers(homeP, endowments);
await writeCoreProposal('add-STARS', starsVaultProposalBuilder);
await writeCoreProposal('add-STARS-oracles', starsOraclesProposalBuilder);
};
16 changes: 14 additions & 2 deletions packages/inter-protocol/src/proposals/addAssetToVault.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @jessie-check

import { AmountMath, AssetKind } from '@agoric/ertp';
import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js';
import { deeplyFulfilledObject } from '@agoric/internal';
import { Stable } from '@agoric/vats/src/tokens.js';
import { E } from '@endo/far';
import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js';
import { parseRatio } from '@agoric/zoe/src/contractSupport/ratio.js';
import { E } from '@endo/far';
import { instanceNameFor } from './price-feed-proposal.js';
import { reserveThenGetNames } from './utils.js';

export * from './startPSM.js';
Expand Down Expand Up @@ -219,6 +220,7 @@ export const addAssetToVault = async (
brand: {
consume: { [Stable.symbol]: stableP },
},
instance: { consume: consumeInstance },
},
{
options: {
Expand All @@ -239,6 +241,11 @@ export const addAssetToVault = async (
[keyword],
);

const oracleInstanceName = instanceNameFor(oracleBrand, 'USD');
// don't add the collateral offering to vaultFactory until its price feed is available
// eslint-disable-next-line no-restricted-syntax -- allow this computed property
await consumeInstance[oracleInstanceName];

const stable = await stableP;
const vaultFactoryCreator = E.get(vaultFactoryKit).creatorFacet;
await E(vaultFactoryCreator).addVaultType(interchainIssuer, oracleBrand, {
Expand Down Expand Up @@ -321,6 +328,11 @@ export const getManifestForAddAssetToVault = (
brand: {
consume: { [Stable.symbol]: true },
},
instance: {
// allow any instance because the AGORIC_INSTANCE_NAME of
// priceFeedOptions cannot be known statically.
consume: true,
},
},
},
installations: {
Expand Down
43 changes: 29 additions & 14 deletions packages/inter-protocol/src/proposals/price-feed-proposal.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// @ts-nocheck -- lots of type errors. low prio b/c proposals are like scripts
import { E } from '@endo/far';
import { makeTracer } from '@agoric/internal';
import {
makeStorageNodeChild,
assertPathSegment,
makeStorageNodeChild,
} from '@agoric/internal/src/lib-chainStorage.js';
import { makeTracer } from '@agoric/internal';
import { E } from '@endo/far';

import { unitAmount } from '@agoric/zoe/src/contractSupport/priceQuote.js';
import { reserveThenDeposit, reserveThenGetNames } from './utils.js';
Expand All @@ -18,6 +18,9 @@ const sanitizePathSegment = name => {
return candidate;
};

export const instanceNameFor = (inBrandName, outBrandName) =>
`${inBrandName}-${outBrandName} price feed`;

/**
* @typedef {{
* brandIn?: ERef<Brand<'nat'> | undefined>,
Expand Down Expand Up @@ -95,6 +98,7 @@ export const createPriceFeed = async (
priceAuthorityAdmin,
startGovernedUpgradable,
},
instance: { produce: produceInstance },
},
{
options: {
Expand Down Expand Up @@ -160,11 +164,27 @@ export const createPriceFeed = async (
installation: priceAggregator,
});

E(E(agoricNamesAdmin).lookupAdmin('instance'))
// Publish price feed in home.priceAuthority.
const forceReplace = true;
// Make sure this PA is registered before sharing the instance in agoricNames,
// which allows contracts that depend on the registry value to wait for it and
// prevent a race.
await E(priceAuthorityAdmin).registerPriceAuthority(
E(faKit.publicFacet).getPriceAuthority(),
brandIn,
brandOut,
forceReplace,
);

await E(E(agoricNamesAdmin).lookupAdmin('instance'))
.update(AGORIC_INSTANCE_NAME, faKit.instance)
.catch(err =>
console.error(`🚨 failed to update ${AGORIC_INSTANCE_NAME}`, err),
);
// being after the above awaits means that when this resolves, the consumer
// gets notified that the authority is in the registry and its instance is in
// agoricNames.
produceInstance[AGORIC_INSTANCE_NAME].resolve(faKit.instance);

E(E.get(econCharterKit).creatorFacet).addInstance(
faKit.instance,
Expand All @@ -173,15 +193,6 @@ export const createPriceFeed = async (
);
trace('registered', AGORIC_INSTANCE_NAME, faKit.instance);

// Publish price feed in home.priceAuthority.
const forceReplace = true;
void E(priceAuthorityAdmin).registerPriceAuthority(
E(faKit.publicFacet).getPriceAuthority(),
brandIn,
brandOut,
forceReplace,
);

/**
* Initialize a new oracle and send an invitation to administer it.
*
Expand Down Expand Up @@ -231,6 +242,9 @@ export const getManifestForPriceFeed = async (
priceAuthorityAdmin: t,
startGovernedUpgradable: t,
},
instance: {
produce: t,
},
},
[ensureOracleBrands.name]: {
namedVat: {
Expand All @@ -242,6 +256,7 @@ export const getManifestForPriceFeed = async (
},
},
installations: {
// ??? will every eval of price-feed-proposal install priceAggregator ?
priceAggregator: restoreRef(priceFeedOptions.priceAggregatorRef),
},
options: {
Expand Down Expand Up @@ -302,7 +317,7 @@ export const startPriceFeeds = async (
{
options: {
priceFeedOptions: {
AGORIC_INSTANCE_NAME: `${inBrandName}-${outBrandName} price feed`,
AGORIC_INSTANCE_NAME: instanceNameFor(inBrandName, outBrandName),
contractTerms: {
minSubmissionCount: 2,
minSubmissionValue: 1,
Expand Down
2 changes: 2 additions & 0 deletions packages/inter-protocol/src/vaultFactory/vaultDirector.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ const prepareVaultDirector = (
metrics: makeRecorderTopic('Vault Factory metrics', metricsKit),
});

/** @param {(vm: VaultManager) => void} fn */
const allManagersDo = fn => {
for (const managerIndex of collateralManagers.values()) {
const vm = vaultManagers.get(managerIndex).self;
Expand Down Expand Up @@ -424,6 +425,7 @@ const prepareVaultDirector = (

makeLiquidationWaker() {
return makeWaker('liquidationWaker', _timestamp => {
// XXX floating promise
allManagersDo(vm => vm.liquidateVaults(auctioneer));
});
},
Expand Down
22 changes: 15 additions & 7 deletions packages/inter-protocol/src/vaultFactory/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ export const prepareVaultManagerKit = (
// throw. See https://github.com/Agoric/agoric-sdk/issues/4317
void observeNotifier(quoteNotifier, {
updateState(value) {
trace('vaultManager got new collateral quote', value);
trace('storing new quote', value.quoteAmount.value);
ephemera.storedCollateralQuote = value;
},
fail(reason) {
Expand Down Expand Up @@ -1036,7 +1036,7 @@ export const prepareVaultManagerKit = (
if (!storedCollateralQuote)
throw Fail`lockOraclePrices called before a collateral quote was available`;
trace(
`lockPrice`,
`lockOraclePrices`,
getAmountIn(storedCollateralQuote),
getAmountOut(storedCollateralQuote),
);
Expand All @@ -1046,7 +1046,7 @@ export const prepareVaultManagerKit = (
return storedCollateralQuote;
},
/**
* @param {AuctioneerPublicFacet} auctionPF
* @param {ERef<AuctioneerPublicFacet>} auctionPF
*/
async liquidateVaults(auctionPF) {
const { state, facets } = this;
Expand All @@ -1060,11 +1060,19 @@ export const prepareVaultManagerKit = (
} = state;
trace(collateralBrand, 'considering liquidation');

if (!lockedQuote) {
// By design, the first cycle of auction may call this before a quote is locked
// because the schedule is global at the vaultDirector level, and if a manager
// starts after the price lock time there's nothing to be done.
// NB: this message should not log repeatedly.
console.error(
'Skipping liquidation because no quote is locked yet (may happen with new manager)',
);
return;
}

const { prioritizedVaults } = collateralEphemera(collateralBrand);
assert(factoryPowers && prioritizedVaults && zcf);
lockedQuote ||
Fail`Must have locked a quote before liquidating vaults.`;
assert(lockedQuote); // redundant with previous line
prioritizedVaults || Fail`prioritizedVaults missing from ephemera`;

const liqMargin = self.getGovernedParams().getLiquidationMargin();

Expand Down
20 changes: 16 additions & 4 deletions packages/inter-protocol/test/smartWallet/contexts.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import {
import { heapZone } from '@agoric/zone';
import { E } from '@endo/far';
import path from 'path';
import { createPriceFeed } from '../../src/proposals/price-feed-proposal.js';
import {
createPriceFeed,
instanceNameFor,
} from '../../src/proposals/price-feed-proposal.js';
import { withAmountUtils } from '../supports.js';

// referenced by TS
Expand Down Expand Up @@ -42,7 +45,8 @@ export const makeDefaultTestContext = async (t, makeSpace) => {

const bundleCache = await unsafeMakeBundleCache('bundles/');

const { consume, produce } = await makeSpace(log, bundleCache);
// @ts-expect-error xxx
const { consume, produce, instance } = await makeSpace(log, bundleCache);
const { agoricNames, zoe } = consume;

await produceDiagnostics({ produce });
Expand Down Expand Up @@ -155,12 +159,20 @@ export const makeDefaultTestContext = async (t, makeSpace) => {
const paInstallation = E(zoe).install(paBundle);
await E(installAdmin).update('priceAggregator', paInstallation);

const mockPriceAuthorityAdmin = /** @type {any} */ ({
registerPriceAuthority() {
// noop mock
},
});
produce.priceAuthorityAdmin.resolve(mockPriceAuthorityAdmin);

await createPriceFeed(
{ consume, produce },
// @ts-expect-error xxx
{ consume, produce, instance },
{
options: {
priceFeedOptions: {
AGORIC_INSTANCE_NAME: `${inBrandName}-${outBrandName} price feed`,
AGORIC_INSTANCE_NAME: instanceNameFor(inBrandName, outBrandName),
contractTerms: {
minSubmissionCount: 2,
minSubmissionValue: 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ import '@agoric/vats/src/core/types.js';
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import { NonNullish } from '@agoric/assert';
import { coalesceUpdates } from '@agoric/smart-wallet/src/utils.js';
import { AssetKind, makeIssuerKit } from '@agoric/ertp';
import { zip } from '@agoric/internal';
import { eventLoopIteration } from '@agoric/internal/src/testing-utils.js';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { coalesceUpdates } from '@agoric/smart-wallet/src/utils.js';
import { TimeMath } from '@agoric/time';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';
import { E } from '@endo/far';
import { zip } from '@agoric/internal';
import { AssetKind, makeIssuerKit } from '@agoric/ertp';
import { buildRootObject } from './boot-psm.js';
import { INVITATION_MAKERS_DESC as EC_INVITATION_MAKERS_DESC } from '../../src/econCommitteeCharter.js';
import { INVITATION_MAKERS_DESC as ORACLE_INVITATION_MAKERS_DESC } from '../../src/price/fluxAggregatorKit.js';
import { instanceNameFor } from '../../src/proposals/price-feed-proposal.js';
import { headValue } from '../supports.js';
import { buildRootObject } from './boot-psm.js';
import {
currentPurseBalance,
importBootTestUtils,
Expand All @@ -22,9 +23,10 @@ import {
} from './contexts.js';

/**
* @type {import('ava').TestFn<Awaited<ReturnType<makeDefaultTestContext>>
* & {consume: import('@agoric/inter-protocol/src/proposals/econ-behaviors.js').EconomyBootstrapPowers['consume']}>
* }
* @typedef {Awaited<ReturnType<typeof makeDefaultTestContext>>
& { consume: import('@agoric/inter-protocol/src/proposals/econ-behaviors.js').EconomyBootstrapPowers['consume'] } } TestContext */
/**
* @type {import('ava').TestFn<TestContext>}
*/
const test = anyTest;

Expand Down Expand Up @@ -104,7 +106,7 @@ test.before(async t => {
});

/**
* @param {import('ava').ExecutionContext<*>} t
* @param {import('ava').ExecutionContext<TestContext>} t
* @param {string[]} oracleAddresses
*/
const setupFeedWithWallets = async (t, oracleAddresses) => {
Expand All @@ -121,7 +123,7 @@ const setupFeedWithWallets = async (t, oracleAddresses) => {
/** @type {import('@agoric/zoe/src/zoeService/utils.js').Instance<import('@agoric/inter-protocol/src/price/fluxAggregatorContract.js').prepare>} */
const governedPriceAggregator = await E(agoricNames).lookup(
'instance',
'ATOM-USD price feed',
instanceNameFor('ATOM', 'USD'),
);

return { oracleWallets, governedPriceAggregator };
Expand Down Expand Up @@ -463,7 +465,10 @@ test.serial('govern oracles list', async t => {
t.is(usedInvitations.get('acceptVoterOID')?.value[0].description, 'Voter0');
}

const feed = await E(agoricNames).lookup('instance', 'ATOM-USD price feed');
const feed = await E(agoricNames).lookup(
'instance',
instanceNameFor('ATOM', 'USD'),
);
t.assert(feed);

// Call for a vote to addOracles ////////////////////////////////
Expand Down
Loading