diff --git a/packages/boot/test/orchestration/axelar-gmp-upgrade.test.ts b/packages/boot/test/orchestration/axelar-gmp-upgrade.test.ts new file mode 100644 index 00000000000..1174bcbb5d1 --- /dev/null +++ b/packages/boot/test/orchestration/axelar-gmp-upgrade.test.ts @@ -0,0 +1,100 @@ +/** @file Bootstrap test of restarting contracts using orchestration */ +import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import type { TestFn } from 'ava'; +import { withChainCapabilities } from '@agoric/orchestration'; +import { + makeWalletFactoryContext, + type WalletFactoryTestContext, +} from '../bootstrapTests/walletFactory.js'; +import { minimalChainInfos } from '../tools/chainInfo.js'; + +const test: TestFn = anyTest; +test.before(async t => { + t.context = await makeWalletFactoryContext( + t, + '@agoric/vm-config/decentral-itest-orchestration-config.json', + ); +}); +test.after.always(t => t.context.shutdown?.()); + +/** + * This test core-evals an installation of the axelarGmp contract that + * initiates an IBC Transfer. + */ +test('start axelarGmp and send an offer', async t => { + const { + walletFactoryDriver, + bridgeUtils: { runInbound }, + buildProposal, + evalProposal, + storage, + } = t.context; + + const { IST } = t.context.agoricNamesRemotes.brand; + + t.log('start axelarGmp'); + await evalProposal( + buildProposal('@agoric/builders/scripts/testing/init-axelar-gmp.js', [ + '--chainInfo', + JSON.stringify(withChainCapabilities(minimalChainInfos)), + '--assetInfo', + JSON.stringify([ + [ + 'uist', + { + baseDenom: 'uist', + baseName: 'agoric', + chainName: 'agoric', + }, + ], + ]), + ]), + ); + + t.log('making offer'); + const wallet = await walletFactoryDriver.provideSmartWallet('agoric1test'); + // no money in wallet to actually send + const zero = { brand: IST, value: 0n }; + // send because it won't resolve + await wallet.sendOffer({ + id: 'invokeEVMContract', + invitationSpec: { + source: 'agoricContract', + instancePath: ['axelarGmp'], + callPipe: [['makeSendInvitation']], + }, + proposal: { + // @ts-expect-error XXX BoardRemote + give: { Send: zero }, + }, + offerArgs: { + destAddr: '0x20E68F6c276AC6E297aC46c84Ab260928276691D', + type: 1, + destinationEVMChain: 'ethereum', + gasAmount: 33, + contractInvocationDetails: { + evmContractAddress: 'anyContract', + functionSelector: 'fnSelector', + encodedArgs: 'args', + nonce: 25, + deadline: 3444, + }, + }, + }); + + const getLogged = () => + JSON.parse(storage.data.get('published.axelarGmp.log')!).values; + + // This log shows the flow started, but didn't get past the IBC Transfer settlement + t.deepEqual(getLogged(), [ + 'offer args', + 'evmContractAddress:anyContract', + 'functionSelector:fnSelector', + 'encodedArgs:args', + 'nonce:25', + 'got info for denoms: ibc/FE98AAD68F02F03565E9FA39A5E627946699B2B07115889ED812D8BA639576A9, ibc/toyatom, ibc/toyusdc, ubld, uist', + 'got info for chain: osmosis-1', + 'completed transfer to localAccount', + 'payload received', + ]); +}); diff --git a/packages/orchestration/src/examples/axelar.flows.js b/packages/orchestration/src/examples/axelar.flows.js index e0bfe384138..0d8d41381c5 100644 --- a/packages/orchestration/src/examples/axelar.flows.js +++ b/packages/orchestration/src/examples/axelar.flows.js @@ -1,6 +1,5 @@ import { NonNullish } from '@agoric/internal'; import { makeError, q } from '@endo/errors'; -import { M, mustMatch } from '@endo/patterns'; import { ethers } from 'ethers'; /** @@ -36,6 +35,7 @@ const channels = { * contract method. * @param {string} params.encodedArgs - The ABI-encoded arguments for the * contract method. + * @param {number} params.deadline * @param {number} params.nonce - A unique identifier for the transaction. * @returns {number[]} The encoded contract call payload as an array of numbers. */ @@ -43,10 +43,11 @@ const getContractInvocationPayload = ({ evmContractAddress, functionSelector, encodedArgs, + deadline, nonce, }) => { const LOGIC_CALL_MSG_ID = 0; - const deadline = Math.floor(Date.now() / 1000) + 3600; + const abiCoder = new ethers.utils.AbiCoder(); const payload = abiCoder.encode( @@ -83,6 +84,7 @@ const getContractInvocationPayload = ({ * functionSelector: string; * encodedArgs: string; * nonce: number; + * deadline: number; * }; * }} offerArgs */ @@ -92,16 +94,6 @@ export const sendIt = async ( seat, offerArgs, ) => { - mustMatch( - offerArgs, - harden({ - chainName: M.scalar(), - destAddr: M.string(), - type: M.number(), - destinationEVMChain: M.string(), - gasAmount: M.number(), - }), - ); const { destAddr, type, @@ -110,9 +102,15 @@ export const sendIt = async ( contractInvocationDetails, } = offerArgs; - const { evmContractAddress, functionSelector, encodedArgs, nonce } = + const { evmContractAddress, functionSelector, encodedArgs, nonce, deadline } = contractInvocationDetails; + void log(`offer args`); + void log(`evmContractAddress:${evmContractAddress}`); + void log(`functionSelector:${functionSelector}`); + void log(`encodedArgs:${encodedArgs}`); + void log(`nonce:${nonce}`); + const { give } = seat.getProposal(); const [[_kw, amt]] = entries(give); @@ -128,7 +126,7 @@ export const sendIt = async ( const info = await osmosisChain.getChainInfo(); const { chainId } = info; assert(typeof chainId === 'string', 'bad chainId'); - void log(`got info for chain: ${osmosisChain} ${chainId}`); + void log(`got info for chain: ${chainId}`); /** * @type {any} XXX methods returning vows @@ -146,9 +144,12 @@ export const sendIt = async ( functionSelector, encodedArgs, nonce, + deadline, }) : null; + void log(`payload received`); + const memoToAxelar = { destination_chain: destinationEVMChain, destination_address: destAddr, diff --git a/packages/orchestration/src/proposals/start-axelar-gmp.js b/packages/orchestration/src/proposals/start-axelar-gmp.js index e0d6480e090..83c84cbbcc7 100644 --- a/packages/orchestration/src/proposals/start-axelar-gmp.js +++ b/packages/orchestration/src/proposals/start-axelar-gmp.js @@ -13,7 +13,7 @@ import { E } from '@endo/far'; * @import {start as StartFn} from '@agoric/orchestration/src/examples/axelar-gmp.contract.js'; */ -const trace = makeTracer('StartAxelarGMP', true); +const trace = makeTracer('start axelarGmp', true); /** * @param {BootstrapPowers & { @@ -74,9 +74,7 @@ export const startAxelarGmp = async ( localchain, marshaller, orchestrationService: cosmosInterchainService, - storageNode: E(NonNullish(await chainStorage)).makeChildNode( - 'axelar-gmp', - ), + storageNode: E(NonNullish(await chainStorage)).makeChildNode('axelarGmp'), timerService: chainTimerService, chainInfo, assetInfo,