diff --git a/packages/boot/test/bootstrapTests/orchestration.test.ts b/packages/boot/test/bootstrapTests/orchestration.test.ts index ec95a0c95ef7..b7ec1756b9a0 100644 --- a/packages/boot/test/bootstrapTests/orchestration.test.ts +++ b/packages/boot/test/bootstrapTests/orchestration.test.ts @@ -266,7 +266,9 @@ test.serial('stakeAtom - smart wallet', async t => { proposal: {}, }), { - message: 'No denom for brand [object Alleged: ATOM brand]', + // TODO #10449 + message: + "'amountToCoin' not working for \"[Alleged: ATOM brand]\" until #10449; use 'DenomAmount' for now", }, ); }); diff --git a/packages/orchestration/src/examples/staking-combinations.contract.js b/packages/orchestration/src/examples/staking-combinations.contract.js index b38da6633d6f..209fe7dc7870 100644 --- a/packages/orchestration/src/examples/staking-combinations.contract.js +++ b/packages/orchestration/src/examples/staking-combinations.contract.js @@ -131,6 +131,7 @@ const contract = async ( ); const orchFns = orchestrateAll(flows, { + chainHub, sharedLocalAccountP, makeCombineInvitationMakers, makeExtraInvitationMaker, diff --git a/packages/orchestration/src/examples/staking-combinations.flows.js b/packages/orchestration/src/examples/staking-combinations.flows.js index 23b1583ce587..794d13099f85 100644 --- a/packages/orchestration/src/examples/staking-combinations.flows.js +++ b/packages/orchestration/src/examples/staking-combinations.flows.js @@ -1,6 +1,6 @@ /** * @import {GuestInterface} from '@agoric/async-flow'; - * @import {Orchestrator, OrchestrationFlow, AmountArg, CosmosValidatorAddress, ChainAddress, LocalAccountMethods, OrchestrationAccountI} from '../types.js' + * @import {Orchestrator, OrchestrationFlow, AmountArg, CosmosValidatorAddress, ChainAddress, LocalAccountMethods, OrchestrationAccountI, ChainHub} from '../types.js' * @import {ContinuingOfferResult, InvitationMakers} from '@agoric/smart-wallet/src/types.js'; * @import {LocalOrchestrationAccountKit} from '../exos/local-orchestration-account.js'; * @import {MakeCombineInvitationMakers} from '../exos/combine-invitation-makers.js'; @@ -9,7 +9,7 @@ */ import { mustMatch } from '@endo/patterns'; -import { makeError, q } from '@endo/errors'; +import { Fail, makeError, q } from '@endo/errors'; import { makeTracer } from '@agoric/internal'; import { ChainAddressShape } from '../typeGuards.js'; @@ -48,6 +48,7 @@ harden(makeAccount); * @satisfies {OrchestrationFlow} * @param {Orchestrator} orch * @param {object} ctx + * @param {GuestInterface} ctx.chainHub * @param {Promise>} ctx.sharedLocalAccountP * @param {GuestInterface} ctx.zoeTools * @param {GuestInterface} account @@ -57,7 +58,7 @@ harden(makeAccount); */ export const depositAndDelegate = async ( orch, - { sharedLocalAccountP, zoeTools }, + { chainHub, sharedLocalAccountP, zoeTools }, account, seat, validator, @@ -84,7 +85,11 @@ export const depositAndDelegate = async ( throw errMsg; } seat.exit(); - await account.delegate(validator, give.Stake); + const denom = chainHub.getDenom(give.Stake.brand); + if (!denom) throw Fail`unknown brand ${q(give.Stake.brand)}`; + // TODO #10449 amountToCoin accepts brands + const denomAmount = harden({ denom, value: give.Stake.value }); + await account.delegate(validator, denomAmount); }; harden(depositAndDelegate); diff --git a/packages/orchestration/src/exos/cosmos-orchestration-account.js b/packages/orchestration/src/exos/cosmos-orchestration-account.js index cc33d39c448f..843bb10fbb5e 100644 --- a/packages/orchestration/src/exos/cosmos-orchestration-account.js +++ b/packages/orchestration/src/exos/cosmos-orchestration-account.js @@ -340,6 +340,8 @@ export const prepareCosmosOrchestrationAccountKit = ( * @returns {Coin} */ amountToCoin(amount) { + !('brand' in amount) || + Fail`'amountToCoin' not working for ${q(amount.brand)} until #10449; use 'DenomAmount' for now`; return coerceCoin(chainHub, amount); }, }, diff --git a/packages/orchestration/test/exos/cosmos-orchestration-account.test.ts b/packages/orchestration/test/exos/cosmos-orchestration-account.test.ts index f88664fad1d3..1cfb26d7a2f8 100644 --- a/packages/orchestration/test/exos/cosmos-orchestration-account.test.ts +++ b/packages/orchestration/test/exos/cosmos-orchestration-account.test.ts @@ -14,6 +14,10 @@ import { QueryBalanceRequest, QueryBalanceResponse, } from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js'; +import { + MsgSend, + MsgSendResponse, +} from '@agoric/cosmic-proto/cosmos/bank/v1beta1/tx.js'; import { Coin } from '@agoric/cosmic-proto/cosmos/base/v1beta1/coin.js'; import { QueryDelegationRequest, @@ -38,6 +42,8 @@ import { MsgDelegate, MsgDelegateResponse, } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js'; +import { withAmountUtils } from '@agoric/zoe/tools/test-utils.js'; +import { makeIssuerKit } from '@agoric/ertp'; import { decodeBase64 } from '@endo/base64'; import { commonSetup } from '../supports.js'; import type { @@ -55,6 +61,8 @@ import { } from '../../tools/ibc-mocks.js'; import type { CosmosValidatorAddress } from '../../src/cosmos-api.js'; import { protoMsgMocks } from '../ibc-mocks.js'; +import fetchedChainInfo from '../../src/fetched-chain-info.js'; +import { assetOn } from '../../src/utils/asset.js'; type TestContext = Awaited>; @@ -64,9 +72,18 @@ test.beforeEach(async t => { t.context = await commonSetup(t); }); +const [uistOnCosmos] = assetOn( + 'uist', + 'agoric', + undefined, + 'cosmoshub', + fetchedChainInfo, +); + test('send (to addr on same chain)', async t => { const { brands: { ist }, + mocks: { ibcBridge }, utils: { inspectDibcBridge, populateChainHub }, } = t.context; populateChainHub(); @@ -89,6 +106,42 @@ test('send (to addr on same chain)', async t => { undefined, ); + // register handler for ist bank send + ibcBridge.addMockAck( + buildTxPacketString([ + MsgSend.toProtoMsg({ + fromAddress: 'cosmos1test', + toAddress: 'cosmos99test', + amount: [ + { + denom: uistOnCosmos, + // denom: 'ibc/uisthash', + amount: '10', + }, + ], + }), + ]), + buildMsgResponseString(MsgSendResponse, {}), + ); + + t.is( + await E(account).send(toAddress, { + denom: uistOnCosmos, + value: 10n, + } as AmountArg), + undefined, + 'send accepts Amount', + ); + + await t.throwsAsync( + () => E(account).send(toAddress, ist.make(10n) as AmountArg), + { + message: + "'amountToCoin' not working for \"[Alleged: IST brand]\" until #10449; use 'DenomAmount' for now", + }, + 'TODO #10449 amountToCoin for CosmosOrchestrationAccount', + ); + // simulate timeout error await t.throwsAsync( E(account).send(toAddress, { value: 504n, denom: 'uatom' } as AmountArg), @@ -96,10 +149,17 @@ test('send (to addr on same chain)', async t => { { message: 'ABCI code: 5: error handling packet: see events for details' }, ); - // IST not registered - await t.throwsAsync(E(account).send(toAddress, ist.make(10n) as AmountArg), { - message: 'No denom for brand [object Alleged: IST brand]', - }); + // MOO brand not registered + const moolah = withAmountUtils(makeIssuerKit('MOO')); + await t.throwsAsync( + E(account).send(toAddress, moolah.make(10n) as AmountArg), + { + // TODO #10449 + // message: 'No denom for brand [object Alleged: MOO brand]', + message: + "'amountToCoin' not working for \"[Alleged: MOO brand]\" until #10449; use 'DenomAmount' for now", + }, + ); // multi-send (sendAll) t.is( @@ -111,11 +171,10 @@ test('send (to addr on same chain)', async t => { ); const { bridgeDowncalls } = await inspectDibcBridge(); - t.is( bridgeDowncalls.filter(d => d.method === 'sendPacket').length, - 3, - 'sent 2 successful txs and 1 failed. 1 rejected before sending', + 4, + 'sent 3 successful txs and 1 failed. 1 rejected before sending', ); }); @@ -186,6 +245,14 @@ test('transfer', async t => { }, }); + const umooTransfer = toTransferTxPacket({ + ...mockIbcTransfer, + token: { + denom: 'umoo', + amount: '10', + }, + }); + return { [defaultTransfer]: transferResp, [customTimeoutHeight]: transferResp, @@ -193,6 +260,7 @@ test('transfer', async t => { [customTimeout]: transferResp, [customMemo]: transferResp, [uistTransfer]: transferResp, + [umooTransfer]: transferResp, }; }; ibcBridge.setMockAck(buildMocks()); @@ -309,18 +377,26 @@ test('transfer', async t => { }, ); + const moolah = withAmountUtils(makeIssuerKit('MOO')); t.log('transfer throws if asset is not in its chainHub'); - await t.throwsAsync(E(account).transfer(mockDestination, ist.make(10n)), { - message: 'No denom for brand [object Alleged: IST brand]', + await t.throwsAsync(E(account).transfer(mockDestination, moolah.make(10n)), { + // TODO #10449 + // message: 'No denom for brand [object Alleged: MOO brand]', + message: + "'amountToCoin' not working for \"[Alleged: MOO brand]\" until #10449; use 'DenomAmount' for now", }); - chainHub.registerAsset('uist', { - baseDenom: 'uist', + chainHub.registerAsset('umoo', { + baseDenom: 'umoo', baseName: 'agoric', - brand: ist.brand, + brand: moolah.brand, chainName: 'agoric', }); - // uses uistTransfer mock above - await E(account).transfer(mockDestination, ist.make(10n)); + // uses umooTransfer mock above + await E(account).transfer( + mockDestination, + // moolah.make(10n), // TODO #10449 restore + { denom: 'umoo', value: 10n }, + ); t.log('transfer timeout error recieved and handled from the bridge'); await t.throwsAsync( diff --git a/packages/orchestration/test/supports.ts b/packages/orchestration/test/supports.ts index 0454f493eb34..2d8a85cb0569 100644 --- a/packages/orchestration/test/supports.ts +++ b/packages/orchestration/test/supports.ts @@ -178,6 +178,7 @@ export const commonSetup = async (t: ExecutionContext) => { const commonAssetInfo = harden([ assetOn('ubld', 'agoric', bldSansMint.brand), assetOn('uist', 'agoric', istSansMint.brand), + assetOn('uist', 'agoric', undefined, 'cosmoshub', chainInfoWithCaps), assetOn('uusdc', 'noble', undefined, 'agoric', chainInfoWithCaps), assetOn('uatom', 'cosmoshub', undefined, 'agoric', chainInfoWithCaps), assetOn('uusdc', 'noble', undefined, 'dydx', chainInfoWithCaps),