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

feat: CosmosChainInfo includes pfmEnabled: bool #10329

Merged
merged 12 commits into from
Nov 26, 2024
2 changes: 1 addition & 1 deletion a3p-integration/proposals/z:acceptance/wallet.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ test.serial(`send invitation via namesByAddress`, async t => {
});

// FIXME https://github.com/Agoric/agoric-sdk/issues/10565
test.failing('exitOffer tool reclaims stuck payment', async t => {
test.skip('exitOffer tool reclaims stuck payment', async t => {
const istBalanceBefore = await getBalances([GOV1ADDR], 'uist');
t.log('istBalanceBefore', istBalanceBefore);

Expand Down
35 changes: 33 additions & 2 deletions multichain-testing/test/send-anywhere.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,53 @@ import { createWallet } from '../tools/wallet.js';
import { AmountMath } from '@agoric/ertp';
import { makeQueryClient } from '../tools/query.js';
import type { Amount } from '@agoric/ertp/src/types.js';
import chainInfo from '../starship-chain-info.js';
import { denomHash, withChainCapabilities } from '@agoric/orchestration';

const test = anyTest as TestFn<SetupContextWithWallets>;

const accounts = ['osmosis1', 'osmosis2', 'cosmoshub1', 'cosmoshub2'];

const contractName = 'sendAnywhere';
const contractBuilder =
'../packages/builders/scripts/testing/start-send-anywhere.js';
'../packages/builders/scripts/testing/init-send-anywhere.js';

test.before(async t => {
const { deleteTestKeys, setupTestKeys, ...rest } = await commonSetup(t);
deleteTestKeys(accounts).catch();
const wallets = await setupTestKeys(accounts);
t.context = { ...rest, wallets, deleteTestKeys };
const { startContract } = rest;
await startContract(contractName, contractBuilder);

const assetInfo = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a helper for this comes to mind:

const assetInfo = Object.fromEntries([
  assetOn('uosmo', 'osmosis'),
  assetOn('uosmo', 'osmosis', 'agoric', chainInfo),
  assetOn('uatom', 'cosmoshub'),
  assetOn('uatom', 'cosmoshub', 'agoric', 'chainInfo),
])

uosmo: {
baseName: 'osmosis',
chainName: 'osmosis',
baseDenom: 'uosmo',
},
[`ibc/${denomHash({ denom: 'uosmo', channelId: chainInfo.agoric.connections['osmosislocal'].transferChannel.channelId })}`]:
{
baseName: 'osmosis',
chainName: 'agoric',
baseDenom: 'uosmo',
},
uatom: {
baseName: 'cosmoshub',
chainName: 'cosmoshub',
baseDenom: 'uatom',
},
[`ibc/${denomHash({ denom: 'uatom', channelId: chainInfo.agoric.connections['gaialocal'].transferChannel.channelId })}`]:
{
baseName: 'cosmoshub',
chainName: 'agoric',
baseDenom: 'uatom',
},
Comment on lines +48 to +53
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't connect the relevant brand.

};

await startContract(contractName, contractBuilder, {
chainInfo: JSON.stringify(withChainCapabilities(chainInfo)),
assetInfo: JSON.stringify(assetInfo),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using LegibleCapData would let us include brands in assetInfo.

/**
* The smallCaps body is a string, which simplifies some usage.
* But it's hard to read and write.
*
* The parsed structure makes a convenient notation for configuration etc.

});
});

test.after(async t => {
Expand Down
3 changes: 2 additions & 1 deletion multichain-testing/test/support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const commonSetup = async (t: ExecutionContext) => {
const startContract = async (
contractName: string,
contractBuilder: string,
builderOpts?: Record<string, string>,
) => {
const { vstorageClient } = tools;
const instances = Object.fromEntries(
Expand All @@ -98,7 +99,7 @@ export const commonSetup = async (t: ExecutionContext) => {
return t.log('Contract found. Skipping installation...');
}
t.log('bundle and install contract', contractName);
await deployBuilder(contractBuilder);
await deployBuilder(contractBuilder, builderOpts);
await retryUntilCondition(
() => vstorageClient.queryData(`published.agoricNames.instance`),
res => contractName in Object.fromEntries(res),
Expand Down
13 changes: 10 additions & 3 deletions multichain-testing/tools/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { createRequire } from 'module';
import type { AgdTools } from './agd-tools.js';
import type { CoreEvalPlan } from '@agoric/deploy-script-support/src/writeCoreEvalParts.js';
import { flags } from './agd-lib.js';

const nodeRequire = createRequire(import.meta.url);

Expand All @@ -10,10 +11,16 @@ export const makeDeployBuilder = (
readJSON: typeof import('fs-extra').readJSON,
execa: typeof import('execa').execa,
) =>
async function deployBuilder(builder: string) {
async function deployBuilder(
builder: string,
builderOpts?: Record<string, string>,
) {
console.log(`building plan: ${builder}`);
// build the plan
const { stdout } = await execa`agoric run ${builder}`;
const args = ['run', builder];
if (builderOpts) {
args.push(...flags(builderOpts));
}
const { stdout } = await execa('agoric', args);
const match = stdout.match(/ (?<name>[-\w]+)-permit.json/);
if (!(match && match.groups)) {
throw Error('no permit found');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ test.serial('send-anywhere', async t => {

t.log('start send-anywhere');
await evalProposal(
buildProposal('@agoric/builders/scripts/testing/start-send-anywhere.js'),
buildProposal('@agoric/builders/scripts/testing/init-send-anywhere.js'),
);

t.log('making offer');
Expand Down
84 changes: 82 additions & 2 deletions packages/builders/scripts/fast-usdc/init-fast-usdc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ 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 {
multiplyBy,
parseRatio,
Expand All @@ -17,12 +19,35 @@ 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} from '@agoric/orchestration';
*/

const { keys } = Object;

const defaultAssetInfo = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again:

const defaultAssetInfo = Object.fromEntries([
  assetOn('uusdc', 'noble'),
  assetOn('uusdc', 'noble', 'agoric', chainInfo),
  assetOn('uusdc', 'noble', 'osmosis', chainInfo),
]);

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',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What consumes brandKey? What's the type of defaultAssetInfo?

brandKey is another example of the sort of thing that LegibleCapData is designed to address.

For background, see

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good feedback. I went with brandKey since I'm not well versed in this area.

I created a ticket to track the removal of brandKey in favor of this approach: #10580

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I think it's an entirely internal interface, so it's not a big deal.

},
[`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'>>}
* @type {Record<string, Pick<FastUSDCConfig, 'oracles' | 'feedPolicy' | 'chainInfo' | 'assetInfo' >>}
*
* TODO: determine OCW operator addresses
* meanwhile, use price oracle addresses (from updatePriceFeeds.js).
Expand All @@ -47,6 +72,10 @@ const configurations = {
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo)
),
assetInfo: defaultAssetInfo,
},
MAINNET: {
oracles: {
Expand All @@ -69,6 +98,10 @@ const configurations = {
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo)
),
assetInfo: defaultAssetInfo,
},
DEVNET: {
oracles: {
Expand All @@ -90,6 +123,10 @@ const configurations = {
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo) // TODO: use devnet values
),
assetInfo: defaultAssetInfo, // TODO: use emerynet values
},
EMERYNET: {
oracles: {
Expand All @@ -108,6 +145,10 @@ const configurations = {
},
},
},
chainInfo: /** @type {Record<string, CosmosChainInfo & Passable>} */ (
withChainCapabilities(fetchedChainInfo) // TODO: use emerynet values
),
assetInfo: defaultAssetInfo, // TODO: use emerynet values
},
};

Expand All @@ -124,11 +165,17 @@ const options = {
default:
'ibc/FE98AAD68F02F03565E9FA39A5E627946699B2B07115889ED812D8BA639576A9',
},
chainInfo: { type: 'string' },
assetInfo: { type: 'string' },
};
const oraclesUsage = 'use --oracle name:address ...';

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

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

/**
* @typedef {{
* flatFee: string;
Expand All @@ -139,6 +186,8 @@ const feedPolicyUsage = 'use --feedPolicy <policy> ...';
* oracle?: string[];
* usdcDenom: string;
* feedPolicy?: string;
* chainInfo: string;
* assetInfo: string;
* }} FastUSDCOpts
*/

Expand Down Expand Up @@ -180,7 +229,15 @@ export default async (homeP, endowments) => {
/** @type {{ values: FastUSDCOpts }} */
// @ts-expect-error ensured by options
const {
values: { oracle: oracleArgs, net, usdcDenom, feedPolicy, ...fees },
values: {
oracle: oracleArgs,
net,
usdcDenom,
feedPolicy,
chainInfo,
assetInfo,
...fees
},
} = parseArgs({ args: scriptArgs, options });

const parseFeedPolicy = () => {
Expand Down Expand Up @@ -226,6 +283,27 @@ export default async (homeP, endowments) => {
};
};

const parseChainInfo = () => {
if (net) {
if (!(net in configurations)) {
throw Error(`${net} not in ${keys(configurations)}`);
}
return configurations[net].chainInfo;
}
if (!chainInfo) throw Error(chainInfoUsage);
return JSON.parse(chainInfo);
};
const parseAssetInfo = () => {
if (net) {
if (!(net in configurations)) {
throw Error(`${net} not in ${keys(configurations)}`);
}
return configurations[net].assetInfo;
}
if (!assetInfo) throw Error(assetInfoUsage);
return JSON.parse(assetInfo);
};

/** @type {FastUSDCConfig} */
const config = harden({
oracles: parseOracleArgs(),
Expand All @@ -234,6 +312,8 @@ export default async (homeP, endowments) => {
},
feeConfig: parseFeeConfigArgs(),
feedPolicy: parseFeedPolicy(),
chainInfo: parseChainInfo(),
assetInfo: parseAssetInfo(),
});

await writeCoreEval('start-fast-usdc', utils =>
Expand Down
67 changes: 67 additions & 0 deletions packages/builders/scripts/testing/init-send-anywhere.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { makeHelpers } from '@agoric/deploy-script-support';
import {
getManifest,
startSendAnywhere,
} from '@agoric/orchestration/src/proposals/start-send-anywhere.js';
import { parseArgs } from 'node:util';

/**
* @import {ParseArgsConfig} from 'node:util'
*/

/** @type {ParseArgsConfig['options']} */
const parserOpts = {
chainInfo: { type: 'string' },
assetInfo: { type: 'string' },
};

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */
export const defaultProposalBuilder = async (
{ publishRef, install },
options,
) =>
harden({
sourceSpec: '@agoric/orchestration/src/proposals/start-send-anywhere.js',
getManifestCall: [
getManifest.name,
{
installationRef: publishRef(
install(
'@agoric/orchestration/src/examples/send-anywhere.contract.js',
),
),
options,
},
],
});

/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').DeployScriptFunction} */
export default async (homeP, endowments) => {
const { scriptArgs } = endowments;

const {
values: { chainInfo, assetInfo },
} = parseArgs({
args: scriptArgs,
options: parserOpts,
});

const parseChainInfo = () => {
if (typeof chainInfo !== 'string') return undefined;
return JSON.parse(chainInfo);
};
const parseAssetInfo = () => {
if (typeof assetInfo !== 'string') return undefined;
return JSON.parse(assetInfo);
};
const opts = harden({
chainInfo: parseChainInfo(),
assetInfo: parseAssetInfo(),
});

const { writeCoreEval } = await makeHelpers(homeP, endowments);

await writeCoreEval(startSendAnywhere.name, utils =>
defaultProposalBuilder(utils, opts),
);
};
Loading
Loading