Skip to content

Commit

Permalink
feat(fast-usdc): core-eval to change fastUsdc.feedPolicy (#10609)
Browse files Browse the repository at this point in the history
closes: #10507

## Description

 - core-eval to update fast-usdc feed policy
 - policy update support in init-fast-usdc builder script
 - some refactoring to make stuff available to the 2nd core-eval script and to the init-fast-usdc builder

### Testing Considerations

1 happy-path test:

 - test(boot): core-eval to change fastUsdc.feedPolicy

Aside from option parsing in the builder, validation with patterns makes for essentially straight-line code.

### Security / Documentation Considerations

The whole feedPolicy, for all chains, is updated at once. If the goal is to change a property for just 1 chain, it's important to keep the properties in tact for all the other chains.

### Scaling Considerations

none

### Upgrade Considerations

There has been some discussion of versions for `feedPolicy`. This PR doesn't add anything in that area. The block height of the vstorage write can be used as one form of version number, though that isn't available to the contract, so it wouldn't let the contract detect OCW reports based on outdated policies.
  • Loading branch information
mergify[bot] authored Dec 8, 2024
2 parents a66f6a1 + 8ded3d8 commit 1645d7e
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 160 deletions.
1 change: 1 addition & 0 deletions packages/boot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@agoric/cosmic-proto": "^0.4.0",
"@agoric/cosmic-swingset": "^0.41.3",
"@agoric/ertp": "^0.16.2",
"@agoric/fast-usdc": "0.1.0",
"@agoric/inter-protocol": "^0.16.1",
"@agoric/internal": "^0.3.2",
"@agoric/kmarshal": "^0.1.0",
Expand Down
10 changes: 5 additions & 5 deletions packages/boot/test/fast-usdc/fast-usdc.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js';

import type { TestFn } from 'ava';
import { configurations } from '@agoric/fast-usdc/src/utils/deploy-config.js';
import { MockCctpTxEvidences } from '@agoric/fast-usdc/test/fixtures.js';
import { documentStorageSchema } from '@agoric/governance/tools/storageDoc.js';
import { Fail } from '@endo/errors';
Expand Down Expand Up @@ -64,11 +65,10 @@ test.serial(
walletFactoryDriver: wd,
} = t.context;

const [watcherWallet] = await Promise.all([
wd.provideSmartWallet('agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8'),
wd.provideSmartWallet('agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr'),
wd.provideSmartWallet('agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj'),
]);
const { oracles } = configurations.MAINNET;
const [watcherWallet] = await Promise.all(
Object.values(oracles).map(addr => wd.provideSmartWallet(addr)),
);

// inbound `startChannelOpenInit` responses immediately.
// needed since the Fusdc StartFn relies on an ICA being created
Expand Down
61 changes: 61 additions & 0 deletions packages/builders/scripts/fast-usdc/fast-usdc-update.build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { parseArgs } from 'node:util';
import { getManifestForUpdateFastUsdcPolicy } from '@agoric/fast-usdc/src/fast-usdc-policy.core.js';
import { toExternalConfig } from '@agoric/fast-usdc/src/utils/config-marshal.js';
import { FeedPolicyShape } from '@agoric/fast-usdc/src/type-guards.js';
import { makeHelpers } from '@agoric/deploy-script-support';

/**
* @import {CoreEvalBuilder, DeployScriptFunction} from '@agoric/deploy-script-support/src/externalTypes.js'
* @import {ParseArgsConfig} from 'node:util'
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/types.js'
*/

/** @type {ParseArgsConfig['options']} */
const options = {
feedPolicy: { type: 'string' },
};
const feedPolicyUsage = 'use --feedPolicy <policy> ...';

/**
* @typedef {{
* feedPolicy?: string;
* }} FastUSDCUpdateOpts
*/

/** @type {CoreEvalBuilder} */
export const updateProposalBuilder = async (
_utils,
/** @type {FastUSDCConfig} */ config,
) => {
return harden({
sourceSpec: '@agoric/fast-usdc/src/fast-usdc-policy.core.js',
/** @type {[string, Parameters<typeof getManifestForUpdateFastUsdcPolicy>[1]]} */
getManifestCall: [
getManifestForUpdateFastUsdcPolicy.name,
{
options: toExternalConfig(
config,
{},
harden({ feedPolicy: FeedPolicyShape }),
),
},
],
});
};

/** @type {DeployScriptFunction} */
export default async (homeP, endowments) => {
const { writeCoreEval } = await makeHelpers(homeP, endowments);
const {
values: { feedPolicy },
} = parseArgs({ args: endowments.scriptArgs, options });

const parseFeedPolicy = () => {
if (typeof feedPolicy !== 'string') throw Error(feedPolicyUsage);
return JSON.parse(feedPolicy);
};
const config = harden({ feedPolicy: parseFeedPolicy() });
await writeCoreEval('eval-fast-usdc-policy-update', utils =>
updateProposalBuilder(utils, config),
);
};
145 changes: 5 additions & 140 deletions packages/builders/scripts/fast-usdc/init-fast-usdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import {
getManifestForFastUSDC,
} from '@agoric/fast-usdc/src/fast-usdc.start.js';
import { toExternalConfig } from '@agoric/fast-usdc/src/utils/config-marshal.js';
import { denomHash, withChainCapabilities } from '@agoric/orchestration';
import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js';
import { configurations } from '@agoric/fast-usdc/src/utils/deploy-config.js';
import {
multiplyBy,
parseRatio,
Expand All @@ -18,146 +17,11 @@ import { parseArgs } from 'node:util';
/**
* @import {CoreEvalBuilder, DeployScriptFunction} from '@agoric/deploy-script-support/src/externalTypes.js'
* @import {ParseArgsConfig} from 'node:util'
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/fast-usdc.start.js'
* @import {Passable} from '@endo/marshal';
* @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration';
* @import {FastUSDCConfig} from '@agoric/fast-usdc/src/types.js'
*/

const { keys } = Object;

/** @type {[Denom, DenomDetail & { brandKey?: string}][]} */
const defaultAssetInfo = [
[
'uusdc',
{
baseName: 'noble',
chainName: 'noble',
baseDenom: 'uusdc',
},
],
[
`ibc/${denomHash({ denom: 'uusdc', channelId: fetchedChainInfo.agoric.connections['noble-1'].transferChannel.channelId })}`,
{
baseName: 'noble',
chainName: 'agoric',
baseDenom: 'uusdc',
brandKey: 'USDC',
},
],
[
`ibc/${denomHash({ denom: 'uusdc', channelId: fetchedChainInfo.osmosis.connections['noble-1'].transferChannel.channelId })}`,
{
baseName: 'noble',
chainName: 'osmosis',
baseDenom: 'uusdc',
},
],
];

/**
* @type {Record<string, Pick<FastUSDCConfig, 'oracles' | 'feedPolicy' | 'chainInfo' | 'assetInfo' >>}
*
* TODO: determine OCW operator addresses
* meanwhile, use price oracle addresses (from updatePriceFeeds.js).
*/
const configurations = {
A3P_INTEGRATION: {
oracles: {
gov1: 'agoric1ee9hr0jyrxhy999y755mp862ljgycmwyp4pl7q',
gov2: 'agoric1wrfh296eu2z34p6pah7q04jjuyj3mxu9v98277',
gov3: 'agoric1ydzxwh6f893jvpaslmaz6l8j2ulup9a7x8qvvq',
},
feedPolicy: {
nobleAgoricChannelId: 'TODO',
nobleDomainId: 4,
chainPolicies: {
Arbitrum: {
cctpTokenMessengerAddress:
'0x19330d10D9Cc8751218eaf51E8885D058642E08A',
chainId: 42161,
confirmations: 2,
nobleContractAddress: '0x19330d10D9Cc8751218eaf51E8885D058642E08A',
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo)
),
assetInfo: defaultAssetInfo,
},
MAINNET: {
oracles: {
'01node': 'agoric19uscwxdac6cf6z7d5e26e0jm0lgwstc47cpll8',
'Simply Staking': 'agoric1krunjcqfrf7la48zrvdfeeqtls5r00ep68mzkr',
P2P: 'agoric1n4fcxsnkxe4gj6e24naec99hzmc4pjfdccy5nj',
},
feedPolicy: {
nobleAgoricChannelId: 'channel-21',
nobleDomainId: 4,
chainPolicies: {
Arbitrum: {
cctpTokenMessengerAddress:
'0x19330d10D9Cc8751218eaf51E8885D058642E08A',
chainId: 42161,
confirmations: 2,
nobleContractAddress: '0x19330d10D9Cc8751218eaf51E8885D058642E08A',
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo)
),
assetInfo: defaultAssetInfo,
},
DEVNET: {
oracles: {
DSRV: 'agoric1lw4e4aas9q84tq0q92j85rwjjjapf8dmnllnft',
Stakin: 'agoric1zj6vrrrjq4gsyr9lw7dplv4vyejg3p8j2urm82',
'01node': 'agoric1ra0g6crtsy6r3qnpu7ruvm7qd4wjnznyzg5nu4',
'Simply Staking': 'agoric1qj07c7vfk3knqdral0sej7fa6eavkdn8vd8etf',
P2P: 'agoric10vjkvkmpp9e356xeh6qqlhrny2htyzp8hf88fk',
},
feedPolicy: {
nobleAgoricChannelId: 'TODO',
nobleDomainId: 4,
chainPolicies: {
Arbitrum: {
cctpTokenMessengerAddress: '0xTODO',
chainId: 421614,
confirmations: 2,
nobleContractAddress: '0xTODO',
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo) // TODO: use devnet values
),
assetInfo: defaultAssetInfo, // TODO: use emerynet values
},
EMERYNET: {
oracles: {
gov1: 'agoric1ldmtatp24qlllgxmrsjzcpe20fvlkp448zcuce',
gov2: 'agoric140dmkrz2e42ergjj7gyvejhzmjzurvqeq82ang',
},
feedPolicy: {
nobleAgoricChannelId: 'TODO',
nobleDomainId: 4,
chainPolicies: {
Arbitrum: {
cctpTokenMessengerAddress: '0xTODO',
chainId: 421614,
confirmations: 2,
nobleContractAddress: '0xTODO',
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo) // TODO: use emerynet values
),
assetInfo: defaultAssetInfo, // TODO: use emerynet values
},
};

/** @type {ParseArgsConfig['options']} */
const options = {
flatFee: { type: 'string', default: '0.01' },
Expand All @@ -166,6 +30,7 @@ const options = {
contractRate: { type: 'string', default: '0.2' },
net: { type: 'string' },
oracle: { type: 'string', multiple: true },
feedPolicy: { type: 'string' },
usdcDenom: {
type: 'string',
default:
Expand All @@ -178,9 +43,9 @@ const oraclesUsage = 'use --oracle name:address ...';

const feedPolicyUsage = 'use --feedPolicy <policy> ...';

const chainInfoUsage = 'use --chainInfo chainName:CosmosChainInfo ...';
const chainInfoUsage = 'use --chainInfo {chainName:CosmosChainInfo, ...}';
const assetInfoUsage =
'use --assetInfo denom:DenomInfo & {brandKey?: string} ...';
'use --assetInfo { denom:DenomInfo & {brandKey?: string} ... }';

/**
* @typedef {{
Expand Down
75 changes: 75 additions & 0 deletions packages/fast-usdc/src/fast-usdc-policy.core.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/** @file core-eval to publish update to Fast USDC feedPolicy */

import { E } from '@endo/far';
import { fromExternalConfig } from './utils/config-marshal.js';
import { FeedPolicyShape } from './type-guards.js';

/**
* @import {Passable} from '@endo/pass-style'
* @import {BootstrapManifest} from '@agoric/vats/src/core/lib-boot.js'
* @import {LegibleCapData} from './utils/config-marshal.js'
* @import {FeedPolicy} from './types.js'
*/

const contractName = 'fastUsdc';
const FEED_POLICY = 'feedPolicy';

/**
* XXX copied from fast-usdc.start.js
*
* @param {ERef<StorageNode>} node
* @param {FeedPolicy} policy
*/
const publishFeedPolicy = async (node, policy) => {
const feedPolicy = E(node).makeChildNode(FEED_POLICY);
await E(feedPolicy).setValue(JSON.stringify(policy));
};

/**
* @param {BootstrapPowers &
* { consume: { chainStorage: Promise<StorageNode> }}
* } powers
* @param {{ options: LegibleCapData<{feedPolicy: FeedPolicy & Passable}> }} config
*/
export const updateFastUsdcPolicy = async (
{ consume: { agoricNames, chainStorage } },
config,
) => {
/** @type {Issuer<'nat'>} */
const USDCissuer = await E(agoricNames).lookup('issuer', 'USDC');
const brands = harden({
USDC: await E(USDCissuer).getBrand(),
});
const { feedPolicy } = fromExternalConfig(
config.options,
brands,
harden({ feedPolicy: FeedPolicyShape }),
);

const storageNode = await E(chainStorage).makeChildNode(contractName);

await publishFeedPolicy(storageNode, feedPolicy);
};

/**
* @param {unknown} _utils
* @param {{
* options: LegibleCapData<{feedPolicy: FeedPolicy & Passable}>;
* }} param1
*/
export const getManifestForUpdateFastUsdcPolicy = (_utils, { options }) => {
return {
/** @type {BootstrapManifest} */
manifest: {
[updateFastUsdcPolicy.name]: {
consume: {
chainStorage: true,

// widely shared: name services
agoricNames: true,
},
},
},
options,
};
};
16 changes: 2 additions & 14 deletions packages/fast-usdc/src/fast-usdc.start.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,19 @@ import { fromExternalConfig } from './utils/config-marshal.js';
/**
* @import {DepositFacet} from '@agoric/ertp/src/types.js'
* @import {TypedPattern} from '@agoric/internal'
* @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration';
* @import {Instance, StartParams} from '@agoric/zoe/src/zoeService/utils'
* @import {Board} from '@agoric/vats'
* @import {ManifestBundleRef} from '@agoric/deploy-script-support/src/externalTypes.js'
* @import {BootstrapManifest} from '@agoric/vats/src/core/lib-boot.js'
* @import {Passable} from '@endo/marshal';
* @import {LegibleCapData} from './utils/config-marshal.js'
* @import {FastUsdcSF, FastUsdcTerms} from './fast-usdc.contract.js'
* @import {FeeConfig, FeedPolicy} from './types.js'
* @import {FastUsdcSF} from './fast-usdc.contract.js'
* @import {FeedPolicy, FastUSDCConfig} from './types.js'
*/

const trace = makeTracer('FUSD-Start', true);

const contractName = 'fastUsdc';

/**
* @typedef {{
* terms: FastUsdcTerms;
* oracles: Record<string, string>;
* feeConfig: FeeConfig;
* feedPolicy: FeedPolicy & Passable;
* chainInfo: Record<string, CosmosChainInfo & Passable>;
* assetInfo: [Denom, DenomDetail & {brandKey?: string}][];
* }} FastUSDCConfig
*/
/** @type {TypedPattern<FastUSDCConfig>} */
export const FastUSDCConfigShape = M.splitRecord({
terms: FastUSDCTermsShape,
Expand Down
Loading

0 comments on commit 1645d7e

Please sign in to comment.