From 9f21c28c484a24ec860c795fc65c95289cb58ef1 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Fri, 29 Nov 2024 17:16:32 -0500 Subject: [PATCH] feat: basic-flows.contract.js seeds `chainHub` - basic-flows.contract.js is provided with `chainInfo` and `assetInfo` in `privateArgs` via builder options - needed for tests that use `localAccount.transfer()`, now reliant on asset info, to pass --- .../test/bootstrapTests/orchestration.test.ts | 90 ++++++++++++++++--- .../scripts/orchestration/init-basic-flows.js | 44 ++++++++- .../src/examples/basic-flows.contract.js | 17 +++- .../src/proposals/start-basic-flows.js | 63 ++++++++----- 4 files changed, 178 insertions(+), 36 deletions(-) diff --git a/packages/boot/test/bootstrapTests/orchestration.test.ts b/packages/boot/test/bootstrapTests/orchestration.test.ts index caf92697f4d..e4d59d4e02d 100644 --- a/packages/boot/test/bootstrapTests/orchestration.test.ts +++ b/packages/boot/test/bootstrapTests/orchestration.test.ts @@ -5,11 +5,17 @@ import { defaultMarshaller, documentStorageSchema, } from '@agoric/internal/src/storage-test-utils.js'; -import type { CosmosValidatorAddress } from '@agoric/orchestration'; +import { + withChainCapabilities, + type CosmosValidatorAddress, +} from '@agoric/orchestration'; import type { start as startStakeIca } from '@agoric/orchestration/src/examples/stake-ica.contract.js'; import type { Instance } from '@agoric/zoe/src/zoeService/utils.js'; import type { TestFn } from 'ava'; import { SIMULATED_ERRORS } from '@agoric/vats/tools/fake-bridge.js'; +import fetchedChainInfo from '@agoric/orchestration/src/fetched-chain-info.js'; +import { buildVTransferEvent } from '@agoric/orchestration/tools/ibc-mocks.js'; +import { BridgeId } from '@agoric/internal'; import { makeWalletFactoryContext, type WalletFactoryTestContext, @@ -290,11 +296,38 @@ test.serial('basic-flows', async t => { evalProposal, agoricNamesRemotes, readPublished, - bridgeUtils: { flushInboundQueue }, + bridgeUtils: { flushInboundQueue, runInbound }, } = t.context; await evalProposal( - buildProposal('@agoric/builders/scripts/orchestration/init-basic-flows.js'), + buildProposal( + '@agoric/builders/scripts/orchestration/init-basic-flows.js', + [ + '--chainInfo', + JSON.stringify(withChainCapabilities(fetchedChainInfo)), + '--assetInfo', + JSON.stringify([ + [ + 'ibc/uusdconagoric', + { + chainName: 'agoric', + baseName: 'noble', + baseDenom: 'uusdc', + }, + ], + // not tested until #10006. consider renaming to ibc/uusdconcosmos + // and updating boot/tools/ibc/mocks.ts + [ + 'ibc/uusdchash', + { + chainName: 'cosmoshub', + baseName: 'noble', + baseDenom: 'uusdc', + }, + ], + ]), + ], + ), ); const wd = @@ -416,7 +449,7 @@ test.serial('basic-flows', async t => { }, proposal: {}, offerArgs: { - amount: { denom: 'ibc/uusdchash', value: 10n }, + amount: { denom: 'ibc/uusdconagoric', value: 10n }, destination: { chainId: 'noble-1', value: 'noble1test', @@ -424,12 +457,29 @@ test.serial('basic-flows', async t => { }, }, }); - t.like(wd.getLatestUpdateRecord(), { - status: { - id: 'transfer-to-noble-from-agoric', - error: undefined, - }, + + await runInbound( + BridgeId.VTRANSFER, + buildVTransferEvent({ + sourceChannel: 'channel-62', + sequence: '1', + }), + ); + + const latestOfferStatus = () => { + const curr = wd.getLatestUpdateRecord(); + if (curr.updated === 'offerStatus') { + return curr.status; + } + throw new Error('expected updated to be "offerStatus"'); + }; + + const offerResult = latestOfferStatus(); + t.like(offerResult, { + id: 'transfer-to-noble-from-agoric', + error: undefined, }); + t.true('result' in offerResult, 'transfer vow settled'); await t.throwsAsync( wd.executeOffer({ @@ -441,7 +491,7 @@ test.serial('basic-flows', async t => { }, proposal: {}, offerArgs: { - amount: { denom: 'ibc/uusdchash', value: SIMULATED_ERRORS.TIMEOUT }, + amount: { denom: 'ibc/uusdconagoric', value: SIMULATED_ERRORS.TIMEOUT }, destination: { chainId: 'noble-1', value: 'noble1test', @@ -472,7 +522,25 @@ test.serial('basic-flows - portfolio holder', async t => { } = t.context; await evalProposal( - buildProposal('@agoric/builders/scripts/orchestration/init-basic-flows.js'), + buildProposal( + '@agoric/builders/scripts/orchestration/init-basic-flows.js', + [ + '--chainInfo', + JSON.stringify(withChainCapabilities(fetchedChainInfo)), + '--assetInfo', + JSON.stringify([ + [ + 'ubld', + { + baseDenom: 'ubld', + baseName: 'agoric', + chainName: 'agoric', + brandKey: 'BLD', + }, + ], + ]), + ], + ), ); const wd = diff --git a/packages/builders/scripts/orchestration/init-basic-flows.js b/packages/builders/scripts/orchestration/init-basic-flows.js index ab2de229cf3..430df76140a 100644 --- a/packages/builders/scripts/orchestration/init-basic-flows.js +++ b/packages/builders/scripts/orchestration/init-basic-flows.js @@ -1,8 +1,22 @@ import { makeHelpers } from '@agoric/deploy-script-support'; import { startBasicFlows } from '@agoric/orchestration/src/proposals/start-basic-flows.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 }) => { +export const defaultProposalBuilder = async ( + { publishRef, install }, + options, +) => { return harden({ sourceSpec: '@agoric/orchestration/src/proposals/start-basic-flows.js', getManifestCall: [ @@ -15,6 +29,7 @@ export const defaultProposalBuilder = async ({ publishRef, install }) => { ), ), }, + options, }, ], }); @@ -22,6 +37,31 @@ export const defaultProposalBuilder = async ({ publishRef, install }) => { /** @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(startBasicFlows.name, defaultProposalBuilder); + + await writeCoreEval(startBasicFlows.name, utils => + defaultProposalBuilder(utils, opts), + ); }; diff --git a/packages/orchestration/src/examples/basic-flows.contract.js b/packages/orchestration/src/examples/basic-flows.contract.js index 60f58cadd1f..ab8387d2ec8 100644 --- a/packages/orchestration/src/examples/basic-flows.contract.js +++ b/packages/orchestration/src/examples/basic-flows.contract.js @@ -6,10 +6,12 @@ import { InvitationShape } from '@agoric/zoe/src/typeGuards.js'; import { M } from '@endo/patterns'; import { preparePortfolioHolder } from '../exos/portfolio-holder-kit.js'; import { withOrchestration } from '../utils/start-helper.js'; +import { registerChainsAndAssets } from '../utils/chain-hub-helper.js'; import * as flows from './basic-flows.flows.js'; /** * @import {Zone} from '@agoric/zone'; + * @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration'; * @import {OrchestrationPowers, OrchestrationTools} from '../utils/start-helper.js'; */ @@ -17,15 +19,17 @@ import * as flows from './basic-flows.flows.js'; * @param {ZCF} zcf * @param {OrchestrationPowers & { * marshaller: Marshaller; - * }} _privateArgs + * chainInfo?: Record; + * assetInfo?: [Denom, DenomDetail & { brandKey?: string }][]; + * }} privateArgs * @param {Zone} zone * @param {OrchestrationTools} tools */ const contract = async ( zcf, - _privateArgs, + privateArgs, zone, - { orchestrateAll, vowTools }, + { chainHub, orchestrateAll, vowTools }, ) => { const makePortfolioHolder = preparePortfolioHolder( zone.subZone('portfolio'), @@ -56,6 +60,13 @@ const contract = async ( }, ); + registerChainsAndAssets( + chainHub, + zcf.getTerms().brands, + privateArgs.chainInfo, + privateArgs.assetInfo, + ); + return { publicFacet }; }; diff --git a/packages/orchestration/src/proposals/start-basic-flows.js b/packages/orchestration/src/proposals/start-basic-flows.js index e19b242ff90..3c66cd7736c 100644 --- a/packages/orchestration/src/proposals/start-basic-flows.js +++ b/packages/orchestration/src/proposals/start-basic-flows.js @@ -6,6 +6,7 @@ import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; import { E } from '@endo/far'; /** + * @import {CosmosChainInfo, Denom, DenomDetail} from '@agoric/orchestration'; * @import {BasicFlowsSF} from '../examples/basic-flows.contract.js'; */ @@ -15,30 +16,43 @@ const contractName = 'basicFlows'; /** * See `@agoric/builders/builders/scripts/orchestration/init-basic-flows.js` for * the accompanying proposal builder. Run `agoric run - * packages/builders/scripts/orchestration/init-basic-flows.js` to build the + * packages/builders/scripts/orchestration/init-basic-flows.js --chainInfo + * 'chainName:CosmosChainInfo' --assetInfo 'denom:DenomDetail'` to build the * contract and proposal files. * * @param {BootstrapPowers} powers + * @param {{ + * options: { + * chainInfo: Record; + * assetInfo: [Denom, DenomDetail & { brandKey?: string }][]; + * }; + * }} config */ -export const startBasicFlows = async ({ - consume: { - agoricNames, - board, - chainStorage, - chainTimerService, - cosmosInterchainService, - localchain, - startUpgradable, - }, - installation: { - // @ts-expect-error not a WellKnownName - consume: { [contractName]: installation }, - }, - instance: { - // @ts-expect-error not a WellKnownName - produce: { [contractName]: produceInstance }, +export const startBasicFlows = async ( + { + consume: { + agoricNames, + board, + chainStorage, + chainTimerService, + cosmosInterchainService, + localchain, + startUpgradable, + }, + installation: { + // @ts-expect-error not a WellKnownName + consume: { [contractName]: installation }, + }, + instance: { + // @ts-expect-error not a WellKnownName + produce: { [contractName]: produceInstance }, + }, + issuer: { + consume: { BLD, IST }, + }, }, -}) => { + { options: { chainInfo, assetInfo } }, +) => { trace(`start ${contractName}`); await null; @@ -49,6 +63,10 @@ export const startBasicFlows = async ({ const startOpts = { label: 'basicFlows', installation, + issuerKeywordRecord: { + BLD: await BLD, + IST: await IST, + }, terms: undefined, privateArgs: { agoricNames: await agoricNames, @@ -57,6 +75,8 @@ export const startBasicFlows = async ({ storageNode, marshaller, timerService: await chainTimerService, + chainInfo, + assetInfo, }, }; @@ -67,7 +87,7 @@ harden(startBasicFlows); export const getManifestForContract = ( { restoreRef }, - { installKeys, ...options }, + { installKeys, options }, ) => { return { manifest: { @@ -87,6 +107,9 @@ export const getManifestForContract = ( instance: { produce: { [contractName]: true }, }, + issuer: { + consume: { BLD: true, IST: true }, + }, }, }, installations: {