From 99b1704f9d3b1f72e14780b4e17c6a119a3e8817 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Thu, 8 Aug 2024 20:33:16 -0400 Subject: [PATCH] feat: amountToCoin accepts any denom - still does not support brand lookups (see #9211) - throws error if a brand is provided instead of warning in the console - moves bondDenom check to delegate method to preserve functionality - also includes this helper on LocalOrchestrationAccount --- .../test/bootstrapTests/orchestration.test.ts | 40 ++++++++++++++----- .../orchestration/restart-contracts.test.ts | 3 +- .../src/exos/cosmos-orchestration-account.js | 19 ++++----- .../src/exos/local-orchestration-account.js | 22 ++++++++++ .../orchestration/test/staking-ops.test.ts | 2 +- 5 files changed, 66 insertions(+), 20 deletions(-) diff --git a/packages/boot/test/bootstrapTests/orchestration.test.ts b/packages/boot/test/bootstrapTests/orchestration.test.ts index 34100dfb7b0..9dc03af6fd4 100644 --- a/packages/boot/test/bootstrapTests/orchestration.test.ts +++ b/packages/boot/test/bootstrapTests/orchestration.test.ts @@ -19,6 +19,8 @@ const validatorAddress: CosmosValidatorAddress = { encoding: 'bech32', }; +const ATOM_DENOM = 'uatom'; + test.before(async t => { t.context = await makeWalletFactoryContext( t, @@ -43,7 +45,7 @@ test.serial('config', async t => { const cosmosChainInfo = await EV(agoricNames).lookup('chain', 'cosmoshub'); t.like(cosmosChainInfo, { chainId: 'cosmoshub-4', - stakingTokens: [{ denom: 'uatom' }], + stakingTokens: [{ denom: ATOM_DENOM }], }); t.deepEqual( readLatest(`published.agoricNames.chain.cosmoshub`), @@ -120,8 +122,8 @@ test.skip('stakeOsmo - queries', async t => { t.log('account', account); t.truthy(account, 'makeAccount returns an account on OSMO connection'); - const queryRes = await EV(account).getBalance('uatom'); - t.deepEqual(queryRes, { value: 0n, denom: 'uatom' }); + const queryRes = await EV(account).getBalance(ATOM_DENOM); + t.deepEqual(queryRes, { value: 0n, denom: ATOM_DENOM }); const queryUnknownDenom = await EV(account).getBalance('some-invalid-denom'); t.deepEqual( @@ -184,7 +186,7 @@ test.serial('stakeAtom - smart wallet', async t => { source: 'continuing', previousOffer: 'request-account', invitationMakerName: 'Delegate', - invitationArgs: [validatorAddress, { brand: ATOM, value: 10n }], + invitationArgs: [validatorAddress, { denom: ATOM_DENOM, value: 10n }], }, proposal: {}, }); @@ -206,7 +208,10 @@ test.serial('stakeAtom - smart wallet', async t => { source: 'continuing', previousOffer: 'request-account', invitationMakerName: 'Delegate', - invitationArgs: [validatorAddressFail, { brand: ATOM, value: 10n }], + invitationArgs: [ + validatorAddressFail, + { denom: ATOM_DENOM, value: 10n }, + ], }, proposal: {}, }), @@ -215,6 +220,24 @@ test.serial('stakeAtom - smart wallet', async t => { }, 'delegate fails with invalid validator', ); + + // This will trigger the immediate ack of the mock bridge + await t.throwsAsync( + wd.executeOffer({ + id: 'request-delegate-brand', + invitationSpec: { + source: 'continuing', + previousOffer: 'request-account', + invitationMakerName: 'Delegate', + invitationArgs: [validatorAddress, { brand: ATOM, value: 10n }], + }, + proposal: {}, + }), + { + message: 'Brands not currently supported.', + }, + 'brands not currently supported', + ); }); test.todo('undelegate wallet offer'); @@ -393,8 +416,7 @@ test.serial('basic-flows - portfolio holder', async t => { // XXX this overrides a previous account, since mocks only provide one address t.is(readLatest('published.basicFlows.agoric1mockVlocalchainAddress'), ''); - const { ATOM, BLD } = agoricNamesRemotes.brand; - ATOM || Fail`ATOM missing from agoricNames`; + const { BLD } = agoricNamesRemotes.brand; BLD || Fail`BLD missing from agoricNames`; await wd.sendOffer({ @@ -406,7 +428,7 @@ test.serial('basic-flows - portfolio holder', async t => { invitationArgs: [ 'cosmoshub', 'Delegate', - [validatorAddress, { brand: ATOM, value: 10n }], + [validatorAddress, { denom: ATOM_DENOM, value: 10n }], ], }, proposal: {}, @@ -444,7 +466,7 @@ test.serial('basic-flows - portfolio holder', async t => { invitationArgs: [ 'cosmoshub', 'Delegate', - [validatorAddress, { brand: ATOM, value: 504n }], + [validatorAddress, { denom: ATOM_DENOM, value: 504n }], ], }, proposal: {}, diff --git a/packages/boot/test/orchestration/restart-contracts.test.ts b/packages/boot/test/orchestration/restart-contracts.test.ts index 730a5f8b565..d72221779d9 100644 --- a/packages/boot/test/orchestration/restart-contracts.test.ts +++ b/packages/boot/test/orchestration/restart-contracts.test.ts @@ -101,6 +101,7 @@ const validatorAddress: CosmosValidatorAddress = { chainId: 'gaiatest', encoding: 'bech32', }; +const ATOM_DENOM = 'uatom'; // check for key because the value will be 'undefined' when the result is provided // TODO should it be something truthy? @@ -152,7 +153,7 @@ test.serial('stakeAtom', async t => { source: 'continuing', previousOffer: 'request-account', invitationMakerName: 'Delegate', - invitationArgs: [validatorAddress, { brand: ATOM, value: 10n }], + invitationArgs: [validatorAddress, { denom: ATOM_DENOM, value: 10n }], }, proposal: {}, }); diff --git a/packages/orchestration/src/exos/cosmos-orchestration-account.js b/packages/orchestration/src/exos/cosmos-orchestration-account.js index 08bac4861d9..31b29fbd770 100644 --- a/packages/orchestration/src/exos/cosmos-orchestration-account.js +++ b/packages/orchestration/src/exos/cosmos-orchestration-account.js @@ -184,15 +184,13 @@ export const prepareCosmosOrchestrationAccountKit = ( * @returns {Coin} */ amountToCoin(amount) { - const { bondDenom } = this.state; - if ('denom' in amount) { - assert.equal(amount.denom, bondDenom); - } else { - trace('TODO: handle brand', amount); - // FIXME(#9211) brand handling + if (!('denom' in amount)) { + // FIXME(#9211) look up values from brands + trace('TODO #9211: handle brand', amount); + throw Fail`Brands not currently supported.`; } return harden({ - denom: bondDenom, + denom: amount.denom, amount: String(amount.value), }); }, @@ -358,14 +356,17 @@ export const prepareCosmosOrchestrationAccountKit = ( return asVow(() => { trace('delegate', validator, amount); const { helper } = this.facets; - const { chainAddress } = this.state; + const { chainAddress, bondDenom } = this.state; + + const amountAsCoin = helper.amountToCoin(amount); + assert.equal(amountAsCoin.denom, bondDenom); const results = E(helper.owned()).executeEncodedTx([ Any.toJSON( MsgDelegate.toProtoMsg({ delegatorAddress: chainAddress.value, validatorAddress: validator.value, - amount: helper.amountToCoin(amount), + amount: amountAsCoin, }), ), ]); diff --git a/packages/orchestration/src/exos/local-orchestration-account.js b/packages/orchestration/src/exos/local-orchestration-account.js index f5b90894ef8..dbf2170d7f4 100644 --- a/packages/orchestration/src/exos/local-orchestration-account.js +++ b/packages/orchestration/src/exos/local-orchestration-account.js @@ -8,6 +8,7 @@ import { VowShape } from '@agoric/vow'; import { E } from '@endo/far'; import { + AmountArgShape, ChainAddressShape, DenomAmountShape, DenomShape, @@ -32,6 +33,7 @@ import { prepareIBCTools } from './ibc-packet.js'; * @import {TimerService, TimestampRecord} from '@agoric/time'; * @import {PromiseVow, EVow, Vow, VowTools} from '@agoric/vow'; * @import {TypedJson, JsonSafe, ResponseTo} from '@agoric/cosmic-proto'; + * @import {Coin} from '@agoric/cosmic-proto/cosmos/base/v1beta1/coin.js'; * @import {Matcher, Pattern} from '@endo/patterns'; * @import {ChainHub} from './chain-hub.js'; * @import {PacketTools} from './packet-tools.js'; @@ -108,6 +110,9 @@ export const prepareLocalOrchestrationAccountKit = ( const makeLocalOrchestrationAccountKit = zone.exoClassKit( 'Local Orchestration Account Kit', { + helper: M.interface('helper', { + amountToCoin: M.call(AmountArgShape).returns(M.record()), + }), holder: HolderI, undelegateWatcher: M.interface('undelegateWatcher', { onFulfilled: M.call([ @@ -166,6 +171,23 @@ export const prepareLocalOrchestrationAccountKit = ( return { account, address, topicKit, packetTools }; }, { + helper: { + /** + * @param {AmountArg} amount + * @returns {Coin} + */ + amountToCoin(amount) { + if (!('denom' in amount)) { + // FIXME(#9211) look up values from brands + trace('TODO #9211: handle brand', amount); + throw Fail`Brands not currently supported.`; + } + return harden({ + denom: amount.denom, + amount: String(amount.value), + }); + }, + }, invitationMakers: { /** * @param {string} validatorAddress diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index 040f7ff5e74..7dcc4bf67ca 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -341,7 +341,7 @@ test(`delegate; redelegate using invitationMakers`, async t => { { const { validator: dst } = configRedelegate; const value = BigInt(Object.values(configRedelegate.delegations)[0].amount); - const anAmount = { brand: aBrand, value }; + const anAmount = { denom: 'uatom', value }; const toRedelegate = await E(invitationMakers).Redelegate( validator, dst,