From a74b6a27d5108c5e014d546b86c695e3fc8bf2e5 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Tue, 11 Jun 2024 09:34:52 -0400 Subject: [PATCH 1/6] fix(orchestration): makeAccount never resolves when icqEnabled: false --- .../scripts/orchestration/init-stakeAtom.js | 2 ++ .../src/examples/stakeIca.contract.js | 22 +++++++++------ .../src/exos/cosmosOrchestrationAccount.js | 5 ++-- .../src/proposals/start-stakeAtom.js | 2 ++ .../test/examples/stake-atom.contract.test.ts | 28 +++++++++++++------ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/packages/builders/scripts/orchestration/init-stakeAtom.js b/packages/builders/scripts/orchestration/init-stakeAtom.js index 68166a7601d..94f5ab4aea2 100644 --- a/packages/builders/scripts/orchestration/init-stakeAtom.js +++ b/packages/builders/scripts/orchestration/init-stakeAtom.js @@ -9,6 +9,7 @@ export const defaultProposalBuilder = async ( hostConnectionId = 'connection-1', controllerConnectionId = 'connection-0', bondDenom = 'uatom', + icqEnabled = true, } = options; return harden({ sourceSpec: '@agoric/orchestration/src/proposals/start-stakeAtom.js', @@ -23,6 +24,7 @@ export const defaultProposalBuilder = async ( hostConnectionId, controllerConnectionId, bondDenom, + icqEnabled, }, ], }); diff --git a/packages/orchestration/src/examples/stakeIca.contract.js b/packages/orchestration/src/examples/stakeIca.contract.js index 8f4562220a4..cc0a6731067 100644 --- a/packages/orchestration/src/examples/stakeIca.contract.js +++ b/packages/orchestration/src/examples/stakeIca.contract.js @@ -24,6 +24,7 @@ export const meta = harden({ hostConnectionId: M.string(), controllerConnectionId: M.string(), bondDenom: M.string(), + icqEnabled: M.boolean(), }, privateArgsShape: { orchestration: M.remotable('orchestration'), @@ -40,6 +41,7 @@ export const privateArgsShape = meta.privateArgsShape; * hostConnectionId: IBCConnectionID; * controllerConnectionId: IBCConnectionID; * bondDenom: string; + * icqEnabled: boolean; * }} StakeIcaTerms */ @@ -54,8 +56,13 @@ export const privateArgsShape = meta.privateArgsShape; * @param {Baggage} baggage */ export const start = async (zcf, privateArgs, baggage) => { - const { chainId, hostConnectionId, controllerConnectionId, bondDenom } = - zcf.getTerms(); + const { + chainId, + hostConnectionId, + controllerConnectionId, + bondDenom, + icqEnabled, + } = zcf.getTerms(); const { orchestration, marshaller, storageNode, timer } = privateArgs; const zone = makeDurableZone(baggage); @@ -74,12 +81,11 @@ export const start = async (zcf, privateArgs, baggage) => { hostConnectionId, controllerConnectionId, ); - // TODO https://github.com/Agoric/agoric-sdk/issues/9326 - // Should not fail if host does not have `async-icq` module; - // communicate to OrchestrationAccount that it can't send queries - const icqConnection = await E(orchestration).provideICQConnection( - controllerConnectionId, - ); + // TODO permissionless queries https://github.com/Agoric/agoric-sdk/issues/9326 + const icqConnection = icqEnabled + ? await E(orchestration).provideICQConnection(controllerConnectionId) + : undefined; + const accountAddress = await E(account).getAddress(); trace('account address', accountAddress); const holder = makeCosmosOrchestrationAccount(accountAddress, bondDenom, { diff --git a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js index f1c10cb24ac..6ef7ea2ec8a 100644 --- a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js +++ b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js @@ -60,7 +60,7 @@ const { Fail } = assert; * topicKit: RecorderKit; * account: IcaAccount; * chainAddress: ChainAddress; - * icqConnection: ICQConnection; + * icqConnection: ICQConnection | undefined; * bondDenom: string; * timer: Remote; * }} State @@ -149,7 +149,7 @@ export const prepareCosmosOrchestrationAccountKit = ( * @param {object} io * @param {IcaAccount} io.account * @param {Remote} io.storageNode - * @param {ICQConnection} io.icqConnection + * @param {ICQConnection | undefined} io.icqConnection * @param {Remote} io.timer * @returns {State} */ @@ -363,6 +363,7 @@ export const prepareCosmosOrchestrationAccountKit = ( */ async getBalance(denom) { const { chainAddress, icqConnection } = this.state; + if (!icqConnection) throw Error('Queries not enabled.'); // TODO #9211 lookup denom from brand assert.typeof(denom, 'string'); diff --git a/packages/orchestration/src/proposals/start-stakeAtom.js b/packages/orchestration/src/proposals/start-stakeAtom.js index 228b4ccc9bc..2648f810beb 100644 --- a/packages/orchestration/src/proposals/start-stakeAtom.js +++ b/packages/orchestration/src/proposals/start-stakeAtom.js @@ -65,6 +65,8 @@ export const startStakeAtom = async ({ hostConnectionId: connectionInfo.id, controllerConnectionId: connectionInfo.counterparty.connection_id, bondDenom: cosmoshub.stakingTokens[0].denom, + // @ts-expect-error icqEnabled exists on CosmosChainInfo type + icqEnabled: !!cosmoshub.icqEnabled, }, privateArgs: { orchestration: await orchestration, diff --git a/packages/orchestration/test/examples/stake-atom.contract.test.ts b/packages/orchestration/test/examples/stake-atom.contract.test.ts index 31f8478b03e..3dea9945d04 100644 --- a/packages/orchestration/test/examples/stake-atom.contract.test.ts +++ b/packages/orchestration/test/examples/stake-atom.contract.test.ts @@ -1,11 +1,11 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; -import { AmountMath } from '@agoric/ertp'; import { setUpZoeForTest } from '@agoric/zoe/tools/setup-zoe.js'; import { E } from '@endo/far'; import path from 'path'; import type { Installation } from '@agoric/zoe/src/zoeService/utils.js'; import { commonSetup } from '../supports.js'; +import { type StakeAtomTerms } from '../../src/examples/stakeAtom.contract.js'; const dirname = path.dirname(new URL(import.meta.url).pathname); @@ -18,7 +18,13 @@ const startContract = async ({ timer, marshaller, storage, - bld, + issuerKeywordRecord, + terms = { + hostConnectionId: 'connection-1', + controllerConnectionId: 'connection-2', + bondDenom: 'uatom', + icqEnabled: false, + } as StakeAtomTerms, }) => { const { zoe, bundleAndInstall } = await setUpZoeForTest(); const installation: Installation = @@ -43,21 +49,23 @@ const startContract = async ({ return { publicFacet, zoe }; }; -test('makeAccount, deposit, withdraw', async t => { +test('makeAccount, getAddress, getBalances, getBalance', async t => { const { bootstrap, brands: { ist }, utils, } = await commonSetup(t); - const { publicFacet } = await startContract({ ...bootstrap, bld: ist }); + const { publicFacet } = await startContract({ + ...bootstrap, + issuerKeywordRecord: { In: ist.issuer }, + }); t.log('make an ICA account'); const account = await E(publicFacet).makeAccount(); t.truthy(account, 'account is returned'); - const address = await E(account).getAddress(); - // XXX address.address is weird - // t.regex(address.address, /agoric1/); - t.like(address, { chainId: 'cosmoshub-4', addressEncoding: 'bech32' }); + const chainAddress = await E(account).getAddress(); + // t.regex(address.address, /cosmos1/); + t.like(chainAddress, { chainId: 'cosmoshub-4', addressEncoding: 'bech32' }); t.log('deposit 100 bld to account'); await E(account).deposit(await utils.pourPayment(ist.units(100))); @@ -65,4 +73,8 @@ test('makeAccount, deposit, withdraw', async t => { await t.throwsAsync(E(account).getBalances(), { message: 'not yet implemented', }); + + await t.throwsAsync(E(account).getBalance('uatom'), { + message: 'Queries not enabled.', + }); }); From c2021ba0e59de5942c1286b2db70f8becb201730 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Tue, 11 Jun 2024 11:01:44 -0400 Subject: [PATCH 2/6] feat(stakeAtom): publish address to vstorage --- .../test/bootstrapTests/orchestration.test.ts | 12 +++- .../src/examples/sendAnywhere.contract.js | 1 + .../src/examples/stakeIca.contract.js | 14 +++- .../src/examples/swapExample.contract.js | 9 ++- .../src/examples/unbondExample.contract.js | 1 + .../src/exos/cosmosOrchestrationAccount.js | 3 + packages/orchestration/src/facade.js | 12 +++- .../test/examples/stake-atom.contract.test.ts | 65 ++++++++++++++++--- packages/orchestration/test/facade.test.ts | 6 ++ .../orchestration/test/staking-ops.test.ts | 43 ++++++++++-- 10 files changed, 140 insertions(+), 26 deletions(-) diff --git a/packages/boot/test/bootstrapTests/orchestration.test.ts b/packages/boot/test/bootstrapTests/orchestration.test.ts index d5fc69792bb..1d468e8abc9 100644 --- a/packages/boot/test/bootstrapTests/orchestration.test.ts +++ b/packages/boot/test/bootstrapTests/orchestration.test.ts @@ -123,7 +123,7 @@ test.serial('stakeAtom - repl-style', async t => { }); test.serial('stakeAtom - smart wallet', async t => { - const { agoricNamesRemotes } = t.context; + const { agoricNamesRemotes, readLatest } = t.context; const wd = await t.context.walletFactoryDriver.provideSmartWallet( 'agoric1testStakAtom', @@ -140,12 +140,20 @@ test.serial('stakeAtom - smart wallet', async t => { }); t.like(wd.getCurrentWalletRecord(), { offerToPublicSubscriberPaths: [ - ['request-account', { account: 'published.stakeAtom' }], + [ + 'request-account', + { + account: 'published.stakeAtom.accounts.cosmos1test', + }, + ], ], }); t.like(wd.getLatestUpdateRecord(), { status: { id: 'request-account', numWantsSatisfied: 1 }, }); + t.like(readLatest('published.stakeAtom.accounts.cosmos1test'), { + sequence: 0n, + }); const { ATOM } = agoricNamesRemotes.brand; ATOM || Fail`ATOM missing from agoricNames`; diff --git a/packages/orchestration/src/examples/sendAnywhere.contract.js b/packages/orchestration/src/examples/sendAnywhere.contract.js index ed49472cee2..301f6da3f2d 100644 --- a/packages/orchestration/src/examples/sendAnywhere.contract.js +++ b/packages/orchestration/src/examples/sendAnywhere.contract.js @@ -68,6 +68,7 @@ export const start = async (zcf, privateArgs, baggage) => { zone, chainHub, makeLocalChainAccountKit, + makeRecorderKit, ...orchPowers, }); diff --git a/packages/orchestration/src/examples/stakeIca.contract.js b/packages/orchestration/src/examples/stakeIca.contract.js index cc0a6731067..5c5b14ffb1c 100644 --- a/packages/orchestration/src/examples/stakeIca.contract.js +++ b/packages/orchestration/src/examples/stakeIca.contract.js @@ -3,7 +3,10 @@ import { makeTracer, StorageNodeShape } from '@agoric/internal'; import { TimerServiceShape } from '@agoric/time'; import { V as E } from '@agoric/vow/vat.js'; -import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport'; +import { + prepareRecorderKitMakers, + provideAll, +} from '@agoric/zoe/src/contractSupport'; import { InvitationShape } from '@agoric/zoe/src/typeGuards.js'; import { makeDurableZone } from '@agoric/zone/durable.js'; import { M } from '@endo/patterns'; @@ -67,6 +70,10 @@ export const start = async (zcf, privateArgs, baggage) => { const zone = makeDurableZone(baggage); + const { accountsStorageNode } = await provideAll(baggage, { + accountsStorageNode: () => E(storageNode).makeChildNode('accounts'), + }); + const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller); const makeCosmosOrchestrationAccount = prepareCosmosOrchestrationAccount( @@ -88,9 +95,12 @@ export const start = async (zcf, privateArgs, baggage) => { const accountAddress = await E(account).getAddress(); trace('account address', accountAddress); + const accountNode = await E(accountsStorageNode).makeChildNode( + accountAddress.address, + ); const holder = makeCosmosOrchestrationAccount(accountAddress, bondDenom, { account, - storageNode, + storageNode: accountNode, icqConnection, timer, }); diff --git a/packages/orchestration/src/examples/swapExample.contract.js b/packages/orchestration/src/examples/swapExample.contract.js index a7ba4c8db2e..9b36e37d91a 100644 --- a/packages/orchestration/src/examples/swapExample.contract.js +++ b/packages/orchestration/src/examples/swapExample.contract.js @@ -2,9 +2,10 @@ import { StorageNodeShape } from '@agoric/internal'; import { TimerServiceShape } from '@agoric/time'; import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js'; import { makeDurableZone } from '@agoric/zone/durable.js'; -import { Far } from '@endo/far'; +import { E, Far } from '@endo/far'; import { deeplyFulfilled } from '@endo/marshal'; import { M, objectMap } from '@endo/patterns'; +import { provideAll } from '@agoric/zoe/src/contractSupport'; import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js'; import { makeOrchestrationFacade } from '../facade.js'; import { orcUtils } from '../utils/orc.js'; @@ -79,16 +80,20 @@ export const start = async (zcf, privateArgs, baggage) => { timerService, chainHub, ); + const { accountsStorageNode } = await provideAll(baggage, { + accountsStorageNode: () => E(storageNode).makeChildNode('accounts'), + }); const { orchestrate } = makeOrchestrationFacade({ localchain, orchestrationService, - storageNode, + storageNode: accountsStorageNode, timerService, zcf, zone, chainHub, makeLocalChainAccountKit, + makeRecorderKit, }); /** deprecated historical example */ diff --git a/packages/orchestration/src/examples/unbondExample.contract.js b/packages/orchestration/src/examples/unbondExample.contract.js index 7bd5d2053e6..8f707e2f4ac 100644 --- a/packages/orchestration/src/examples/unbondExample.contract.js +++ b/packages/orchestration/src/examples/unbondExample.contract.js @@ -57,6 +57,7 @@ export const start = async (zcf, privateArgs, baggage) => { zone, chainHub: makeChainHub(agoricNames), makeLocalChainAccountKit, + makeRecorderKit, }); /** @type {OfferHandler} */ diff --git a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js index 6ef7ea2ec8a..136489ac752 100644 --- a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js +++ b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js @@ -158,6 +158,9 @@ export const prepareCosmosOrchestrationAccountKit = ( // must be the fully synchronous maker because the kit is held in durable state // @ts-expect-error XXX Patterns const topicKit = makeRecorderKit(storageNode, PUBLIC_TOPICS.account[1]); + // TODO https://github.com/Agoric/agoric-sdk/issues/9066 + // update sequence after successful executeEncodedTx, _if we want sequence in `vstorage`_ + void E(topicKit.recorder).write(harden({ sequence: 0n })); return { chainAddress, bondDenom, topicKit, ...rest }; }, diff --git a/packages/orchestration/src/facade.js b/packages/orchestration/src/facade.js index b2e3beb3384..9567b974db9 100644 --- a/packages/orchestration/src/facade.js +++ b/packages/orchestration/src/facade.js @@ -8,6 +8,7 @@ import { prepareCosmosOrchestrationAccount } from './exos/cosmosOrchestrationAcc * @import {TimerService} from '@agoric/time'; * @import {IBCConnectionID} from '@agoric/vats'; * @import {LocalChain} from '@agoric/vats/src/localchain.js'; + * @import {RecorderKit, MakeRecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js'. * @import {Remote} from '@agoric/internal'; * @import {OrchestrationService} from './service.js'; * @import {Chain, ChainInfo, CosmosChainInfo, IBCConnectionInfo, OrchestrationAccount, Orchestrator} from './types.js'; @@ -95,6 +96,8 @@ const makeLocalChainFacade = ( * @param {IBCConnectionInfo} connectionInfo * @param {object} io * @param {Remote} io.orchestration + * @param {MakeRecorderKit} io.makeRecorderKit + * @param {Remote} io.storageNode * @param {Remote} io.timer * @param {ZCF} io.zcf * @param {Zone} io.zone @@ -103,9 +106,8 @@ const makeLocalChainFacade = ( const makeRemoteChainFacade = ( chainInfo, connectionInfo, - { orchestration, timer, zcf, zone }, + { orchestration, makeRecorderKit, storageNode, timer, zcf, zone }, ) => { - const makeRecorderKit = () => anyVal; const makeCosmosOrchestrationAccount = prepareCosmosOrchestrationAccount( zone.subZone(chainInfo.chainId), makeRecorderKit, @@ -133,7 +135,7 @@ const makeRemoteChainFacade = ( // @ts-expect-error XXX dynamic method availability return makeCosmosOrchestrationAccount(address, bondDenom, { account: icaAccount, - storageNode: anyVal, + storageNode, icqConnection: anyVal, timer, }); @@ -153,6 +155,7 @@ const makeRemoteChainFacade = ( * makeLocalChainAccountKit: ReturnType< * typeof import('./exos/local-chain-account-kit.js').prepareLocalChainAccountKit * >; + * makeRecorderKit: MakeRecorderKit; * }} powers */ export const makeOrchestrationFacade = ({ @@ -164,6 +167,7 @@ export const makeOrchestrationFacade = ({ localchain, chainHub, makeLocalChainAccountKit, + makeRecorderKit, }) => { console.log('makeOrchestrationFacade got', { zone, @@ -205,6 +209,8 @@ export const makeOrchestrationFacade = ({ return makeRemoteChainFacade(remoteChainInfo, connectionInfo, { orchestration: orchestrationService, + makeRecorderKit, + storageNode, timer: timerService, zcf, zone, diff --git a/packages/orchestration/test/examples/stake-atom.contract.test.ts b/packages/orchestration/test/examples/stake-atom.contract.test.ts index 3dea9945d04..53570a3e9f9 100644 --- a/packages/orchestration/test/examples/stake-atom.contract.test.ts +++ b/packages/orchestration/test/examples/stake-atom.contract.test.ts @@ -3,9 +3,10 @@ import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import { setUpZoeForTest } from '@agoric/zoe/tools/setup-zoe.js'; import { E } from '@endo/far'; import path from 'path'; +import { makeNotifierFromSubscriber } from '@agoric/notifier'; import type { Installation } from '@agoric/zoe/src/zoeService/utils.js'; import { commonSetup } from '../supports.js'; -import { type StakeAtomTerms } from '../../src/examples/stakeAtom.contract.js'; +import { type StakeIcaTerms } from '../../src/examples/stakeIca.contract.js'; const dirname = path.dirname(new URL(import.meta.url).pathname); @@ -20,11 +21,12 @@ const startContract = async ({ storage, issuerKeywordRecord, terms = { + chainId: 'cosmoshub-4', hostConnectionId: 'connection-1', controllerConnectionId: 'connection-2', bondDenom: 'uatom', icqEnabled: false, - } as StakeAtomTerms, + } as StakeIcaTerms, }) => { const { zoe, bundleAndInstall } = await setUpZoeForTest(); const installation: Installation = @@ -32,17 +34,12 @@ const startContract = async ({ const { publicFacet } = await E(zoe).startInstance( installation, - { In: bld.issuer }, - { - chainId: 'cosmoshub-4', - hostConnectionId: 'connection-1', - controllerConnectionId: 'connection-2', - bondDenom: 'uatom', - }, + issuerKeywordRecord, + terms, { marshaller, orchestration, - storageNode: storage.rootNode, + storageNode: storage.rootNode.makeChildNode('stakeAtom'), timer, }, ); @@ -78,3 +75,51 @@ test('makeAccount, getAddress, getBalances, getBalance', async t => { message: 'Queries not enabled.', }); }); + +test('makeAccountInvitationMaker', async t => { + const { + bootstrap, + brands: { ist }, + } = await commonSetup(t); + const { publicFacet, zoe } = await startContract({ + ...bootstrap, + issuerKeywordRecord: { In: ist.issuer }, + }); + const inv = await E(publicFacet).makeAccountInvitationMaker(); + t.log('make an offer for ICA account'); + t.log('inv', inv); + + const seat = await E(zoe).offer(inv); + t.log('seat', seat); + const offerResult = await E(seat).getOfferResult(); + + t.like(offerResult, { + publicSubscribers: { + account: { + description: 'Staking Account holder status', + }, + }, + }); + + const accountNotifier = makeNotifierFromSubscriber( + offerResult.publicSubscribers.account.subscriber, + ); + const storageUpdate = await E(accountNotifier).getUpdateSince(); + t.deepEqual(storageUpdate, { + updateCount: 1n, + value: { + sequence: 0n, + }, + }); + + // FIXME mock remoteAddress in ibc bridge + const storagePath = + 'mockChainStorageRoot.stakeAtom.accounts.UNPARSABLE_CHAIN_ADDRESS'; + const vstorageEntry = bootstrap.storage.data.get(storagePath); + if (typeof vstorageEntry !== 'string') { + t.fail('vstorageEntry not found'); + } else { + t.log(storagePath, vstorageEntry); + t.regex(vstorageEntry, /sequence/); + } +}); diff --git a/packages/orchestration/test/facade.test.ts b/packages/orchestration/test/facade.test.ts index 28ede33b678..e68a3cdd54f 100644 --- a/packages/orchestration/test/facade.test.ts +++ b/packages/orchestration/test/facade.test.ts @@ -1,6 +1,7 @@ import { test as anyTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import { setupZCFTest } from '@agoric/zoe/test/unitTests/zcf/setupZcfTest.js'; +import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js'; import type { CosmosChainInfo, IBCConnectionInfo } from '../src/cosmos-api.js'; import { makeOrchestrationFacade } from '../src/facade.js'; import type { Chain } from '../src/orchestration-api.js'; @@ -48,6 +49,10 @@ test('chain info', async t => { const { zcf } = await setupZCFTest(); const chainHub = makeChainHub(facadeServices.agoricNames); + const { makeRecorderKit } = prepareRecorderKitMakers( + zone.mapStore('recorder'), + bootstrap.marshaller, + ); const { orchestrate } = makeOrchestrationFacade({ ...facadeServices, @@ -56,6 +61,7 @@ test('chain info', async t => { zone, chainHub, makeLocalChainAccountKit, + makeRecorderKit, }); chainHub.registerChain('mock', mockChainInfo); diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index 4059ac372f8..b1c2058b115 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -8,6 +8,7 @@ import { MsgUndelegateResponse, } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js'; import { makeScalarBigMapStore, type Baggage } from '@agoric/vat-data'; +import { makeFakeBoard } from '@agoric/vats/tools/board-utils.js'; import { decodeBase64 } from '@endo/base64'; import { E, Far } from '@endo/far'; import { buildZoeManualTimer } from '@agoric/zoe/tools/manualTimer.js'; @@ -16,6 +17,9 @@ import type { Coin } from '@agoric/cosmic-proto/cosmos/base/v1beta1/coin.js'; import type { TimestampRecord, TimestampValue } from '@agoric/time'; import type { AnyJson } from '@agoric/cosmic-proto'; import { makeDurableZone } from '@agoric/zone/durable.js'; +import { makeFakeStorageKit } from '@agoric/internal/src/storage-test-utils.js'; +import { makeNotifierFromSubscriber } from '@agoric/notifier'; +import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js'; import { prepareCosmosOrchestrationAccountKit, trivialDelegateResponse, @@ -174,17 +178,16 @@ const makeScenario = () => { return { zcf, zoe }; }; - const makeRecorderKit = () => harden({}) as any; - const baggage = makeScalarBigMapStore('b1') as Baggage; const zone = makeDurableZone(baggage); + const marshaller = makeFakeBoard().getReadonlyMarshaller(); + const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller); const { delegations, startTime } = configStaking; - // TODO: when we write to chainStorage, test it. - // const { rootNode } = makeFakeStorageKit('mockChainStorageRoot'); - - const storageNode = Far('StorageNode', {}) as unknown as StorageNode; + const { rootNode } = makeFakeStorageKit('mockChainStorageRoot', { + sequence: false, + }); const icqConnection = Far('ICQConnection', {}) as ICQConnection; @@ -197,13 +200,39 @@ const makeScenario = () => { zone, makeRecorderKit, ...mockAccount(undefined, delegations), - storageNode, + storageNode: rootNode, timer, icqConnection, ...mockZCF(), }; }; +test('makeAccount() writes to storage', async t => { + const s = makeScenario(); + const { account, calls, timer } = s; + const { makeRecorderKit, storageNode, zcf, icqConnection, zone } = s; + const make = prepareCosmosOrchestrationAccountKit(zone, makeRecorderKit, zcf); + + // Higher fidelity tests below use invitationMakers. + const { holder } = make(account.getAddress(), 'uatom', { + account, + storageNode, + icqConnection, + timer, + }); + const { publicSubscribers } = holder.asContinuingOffer(); + const accountNotifier = makeNotifierFromSubscriber( + publicSubscribers.account.subscriber, + ); + const storageUpdate = await E(accountNotifier).getUpdateSince(); + t.deepEqual(storageUpdate, { + updateCount: 1n, + value: { + sequence: 0n, + }, + }); +}); + test('withdrawRewards() on StakingAccountHolder formats message correctly', async t => { const s = makeScenario(); const { account, calls, timer } = s; From b83745f1075b7748c7b4b339238c2ee5f759761c Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Wed, 12 Jun 2024 16:41:12 -0400 Subject: [PATCH 3/6] fix(chainInfo): ensure icqEnabled is present - given the ChainInfo type is inferred from KnownChains, ensure the icqEnabled key is present on all cosmos chains to facilitate TS types --- packages/orchestration/src/proposals/start-stakeAtom.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/orchestration/src/proposals/start-stakeAtom.js b/packages/orchestration/src/proposals/start-stakeAtom.js index 2648f810beb..cde690f509a 100644 --- a/packages/orchestration/src/proposals/start-stakeAtom.js +++ b/packages/orchestration/src/proposals/start-stakeAtom.js @@ -65,8 +65,7 @@ export const startStakeAtom = async ({ hostConnectionId: connectionInfo.id, controllerConnectionId: connectionInfo.counterparty.connection_id, bondDenom: cosmoshub.stakingTokens[0].denom, - // @ts-expect-error icqEnabled exists on CosmosChainInfo type - icqEnabled: !!cosmoshub.icqEnabled, + icqEnabled: cosmoshub.icqEnabled, }, privateArgs: { orchestration: await orchestration, From b6df6c230a902288f11f6217dbd1ca9701a9a8b6 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Wed, 12 Jun 2024 17:19:01 -0400 Subject: [PATCH 4/6] feat(orchestration): add init-stakeOsmo.js to support .query tests - a better approach seems to be refactoring start-stakeAtom.js to start-stakeIca.js with parameters. buildProposal doesn't seem to accept options so punted for now - TODO: register OSMO in bank --- .../test/bootstrapTests/orchestration.test.ts | 52 +++++++-- .../scripts/orchestration/init-stakeAtom.js | 15 +-- .../scripts/orchestration/init-stakeOsmo.js | 23 ++++ .../src/examples/stakeIca.contract.js | 2 +- .../orchestration/src/exos/chainAccountKit.js | 2 + .../src/exos/cosmosOrchestrationAccount.js | 9 +- .../src/proposals/start-stakeAtom.js | 7 +- .../src/proposals/start-stakeOsmo.js | 109 ++++++++++++++++++ .../test/examples/stake-atom.contract.test.ts | 35 ++---- .../orchestration/test/staking-ops.test.ts | 9 +- .../vm-config/decentral-devnet-config.json | 13 --- 11 files changed, 196 insertions(+), 80 deletions(-) create mode 100644 packages/builders/scripts/orchestration/init-stakeOsmo.js create mode 100644 packages/orchestration/src/proposals/start-stakeOsmo.js diff --git a/packages/boot/test/bootstrapTests/orchestration.test.ts b/packages/boot/test/bootstrapTests/orchestration.test.ts index 1d468e8abc9..4bdc1fc7f4b 100644 --- a/packages/boot/test/bootstrapTests/orchestration.test.ts +++ b/packages/boot/test/bootstrapTests/orchestration.test.ts @@ -72,6 +72,42 @@ test.serial('config', async t => { } }); +test.serial('stakeOsmo - queries', async t => { + const { + buildProposal, + evalProposal, + runUtils: { EV }, + } = t.context; + await evalProposal( + buildProposal('@agoric/builders/scripts/orchestration/init-stakeOsmo.js'), + ); + + const agoricNames = await EV.vat('bootstrap').consumeItem('agoricNames'); + const instance: Instance = await EV(agoricNames).lookup( + 'instance', + 'stakeOsmo', + ); + t.truthy(instance, 'stakeOsmo instance is available'); + + const zoe: ZoeService = await EV.vat('bootstrap').consumeItem('zoe'); + const publicFacet = await EV(zoe).getPublicFacet(instance); + t.truthy(publicFacet, 'stakeOsmo publicFacet is available'); + + const account = await EV(publicFacet).makeAccount(); + 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 queryUnknownDenom = await EV(account).getBalance('some-invalid-denom'); + t.deepEqual( + queryUnknownDenom, + { value: 0n, denom: 'some-invalid-denom' }, + 'getBalance for unknown denom returns value: 0n', + ); +}); + test.serial('stakeAtom - repl-style', async t => { const { buildProposal, @@ -111,15 +147,9 @@ test.serial('stakeAtom - repl-style', async t => { }; await t.notThrowsAsync(EV(account).delegate(validatorAddress, atomAmount)); - const queryRes = await EV(account).getBalance('uatom'); - t.deepEqual(queryRes, { value: 0n, denom: 'uatom' }); - - const queryUnknownDenom = await EV(account).getBalance('some-invalid-denom'); - t.deepEqual( - queryUnknownDenom, - { value: 0n, denom: 'some-invalid-denom' }, - 'getBalance for unknown denom returns value: 0n', - ); + await t.throwsAsync(EV(account).getBalance('uatom'), { + message: 'Queries not available for chain "cosmoshub-4"', + }); }); test.serial('stakeAtom - smart wallet', async t => { @@ -151,9 +181,7 @@ test.serial('stakeAtom - smart wallet', async t => { t.like(wd.getLatestUpdateRecord(), { status: { id: 'request-account', numWantsSatisfied: 1 }, }); - t.like(readLatest('published.stakeAtom.accounts.cosmos1test'), { - sequence: 0n, - }); + t.is(readLatest('published.stakeAtom.accounts.cosmos1test'), ''); const { ATOM } = agoricNamesRemotes.brand; ATOM || Fail`ATOM missing from agoricNames`; diff --git a/packages/builders/scripts/orchestration/init-stakeAtom.js b/packages/builders/scripts/orchestration/init-stakeAtom.js index 94f5ab4aea2..da1ea661073 100644 --- a/packages/builders/scripts/orchestration/init-stakeAtom.js +++ b/packages/builders/scripts/orchestration/init-stakeAtom.js @@ -1,16 +1,7 @@ import { makeHelpers } from '@agoric/deploy-script-support'; /** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ -export const defaultProposalBuilder = async ( - { publishRef, install }, - options = {}, -) => { - const { - hostConnectionId = 'connection-1', - controllerConnectionId = 'connection-0', - bondDenom = 'uatom', - icqEnabled = true, - } = options; +export const defaultProposalBuilder = async ({ publishRef, install }) => { return harden({ sourceSpec: '@agoric/orchestration/src/proposals/start-stakeAtom.js', getManifestCall: [ @@ -21,10 +12,6 @@ export const defaultProposalBuilder = async ( install('@agoric/orchestration/src/examples/stakeIca.contract.js'), ), }, - hostConnectionId, - controllerConnectionId, - bondDenom, - icqEnabled, }, ], }); diff --git a/packages/builders/scripts/orchestration/init-stakeOsmo.js b/packages/builders/scripts/orchestration/init-stakeOsmo.js new file mode 100644 index 00000000000..7374d3d59a6 --- /dev/null +++ b/packages/builders/scripts/orchestration/init-stakeOsmo.js @@ -0,0 +1,23 @@ +import { makeHelpers } from '@agoric/deploy-script-support'; + +/** @type {import('@agoric/deploy-script-support/src/externalTypes.js').CoreEvalBuilder} */ +export const defaultProposalBuilder = async ({ publishRef, install }) => { + return harden({ + sourceSpec: '@agoric/orchestration/src/proposals/start-stakeOsmo.js', + getManifestCall: [ + 'getManifestForStakeOsmo', + { + installKeys: { + stakeIca: publishRef( + install('@agoric/orchestration/src/examples/stakeIca.contract.js'), + ), + }, + }, + ], + }); +}; + +export default async (homeP, endowments) => { + const { writeCoreEval } = await makeHelpers(homeP, endowments); + await writeCoreEval('start-stakeOsmo', defaultProposalBuilder); +}; diff --git a/packages/orchestration/src/examples/stakeIca.contract.js b/packages/orchestration/src/examples/stakeIca.contract.js index 5c5b14ffb1c..40fc24c08e1 100644 --- a/packages/orchestration/src/examples/stakeIca.contract.js +++ b/packages/orchestration/src/examples/stakeIca.contract.js @@ -137,4 +137,4 @@ export const start = async (zcf, privateArgs, baggage) => { return { publicFacet }; }; -/** @typedef {typeof start} StakeAtomSF */ +/** @typedef {typeof start} StakeIcaSF */ diff --git a/packages/orchestration/src/exos/chainAccountKit.js b/packages/orchestration/src/exos/chainAccountKit.js index 35d20c5583d..187b661ea8e 100644 --- a/packages/orchestration/src/exos/chainAccountKit.js +++ b/packages/orchestration/src/exos/chainAccountKit.js @@ -160,6 +160,8 @@ export const prepareChainAccountKit = zone => this.state.remoteAddress = remoteAddr; this.state.localAddress = localAddr; this.state.chainAddress = harden({ + // FIXME need a fallback value like icacontroller-1-connection-1 if this fails + // https://github.com/Agoric/agoric-sdk/issues/9066 address: findAddressField(remoteAddr) || UNPARSABLE_CHAIN_ADDRESS, chainId: this.state.chainId, addressEncoding: 'bech32', diff --git a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js index 136489ac752..968b61335b7 100644 --- a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js +++ b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js @@ -158,9 +158,8 @@ export const prepareCosmosOrchestrationAccountKit = ( // must be the fully synchronous maker because the kit is held in durable state // @ts-expect-error XXX Patterns const topicKit = makeRecorderKit(storageNode, PUBLIC_TOPICS.account[1]); - // TODO https://github.com/Agoric/agoric-sdk/issues/9066 - // update sequence after successful executeEncodedTx, _if we want sequence in `vstorage`_ - void E(topicKit.recorder).write(harden({ sequence: 0n })); + // TODO determine what goes in vstorage https://github.com/Agoric/agoric-sdk/issues/9066 + void E(topicKit.recorder).write(''); return { chainAddress, bondDenom, topicKit, ...rest }; }, @@ -366,7 +365,9 @@ export const prepareCosmosOrchestrationAccountKit = ( */ async getBalance(denom) { const { chainAddress, icqConnection } = this.state; - if (!icqConnection) throw Error('Queries not enabled.'); + if (!icqConnection) { + throw Fail`Queries not available for chain ${chainAddress.chainId}`; + } // TODO #9211 lookup denom from brand assert.typeof(denom, 'string'); diff --git a/packages/orchestration/src/proposals/start-stakeAtom.js b/packages/orchestration/src/proposals/start-stakeAtom.js index cde690f509a..f0150404470 100644 --- a/packages/orchestration/src/proposals/start-stakeAtom.js +++ b/packages/orchestration/src/proposals/start-stakeAtom.js @@ -5,7 +5,7 @@ import { makeChainHub } from '../utils/chainHub.js'; /** * @import {IBCConnectionID} from '@agoric/vats'; - * @import {StakeAtomSF, StakeIcaTerms} from '../examples/stakeIca.contract'; + * @import {StakeIcaSF, StakeIcaTerms} from '../examples/stakeIca.contract'; */ const trace = makeTracer('StartStakeAtom', true); @@ -43,8 +43,6 @@ export const startStakeAtom = async ({ const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH); const marshaller = await E(board).getPublishingMarshaller(); - const atomIssuer = await E(agoricNames).lookup('issuer', 'ATOM'); - trace('ATOM Issuer', atomIssuer); const chainHub = makeChainHub(await agoricNames); @@ -55,11 +53,10 @@ export const startStakeAtom = async ({ cosmoshub.chainId, ); - /** @type {StartUpgradableOpts} */ + /** @type {StartUpgradableOpts} */ const startOpts = { label: 'stakeAtom', installation: stakeIca, - issuerKeywordRecord: harden({ ATOM: atomIssuer }), terms: { chainId: cosmoshub.chainId, hostConnectionId: connectionInfo.id, diff --git a/packages/orchestration/src/proposals/start-stakeOsmo.js b/packages/orchestration/src/proposals/start-stakeOsmo.js new file mode 100644 index 00000000000..96c8ea69fbe --- /dev/null +++ b/packages/orchestration/src/proposals/start-stakeOsmo.js @@ -0,0 +1,109 @@ +import { makeTracer } from '@agoric/internal'; +import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js'; +import { E } from '@endo/far'; +import { makeChainHub } from '../utils/chainHub.js'; + +/** + * @import {IBCConnectionID} from '@agoric/vats'; + * @import {StakeIcaSF} from '../examples/stakeIca.contract'; + */ + +const trace = makeTracer('StartStakeOsmo', true); + +/** + * @param {BootstrapPowers & { + * installation: { + * consume: { + * stakeIca: Installation< + * import('../examples/stakeIca.contract.js').start + * >; + * }; + * }; + * }} powers + */ +export const startStakeOsmo = async ({ + consume: { + agoricNames, + board, + chainStorage, + chainTimerService, + orchestration, + startUpgradable, + }, + installation: { + consume: { stakeIca }, + }, + instance: { + // @ts-expect-error stakeOsmo not typed + produce: { stakeOsmo: produceInstance }, + }, +}) => { + const VSTORAGE_PATH = 'stakeOsmo'; + trace('startStakeOsmo'); + await null; + + const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH); + const marshaller = await E(board).getPublishingMarshaller(); + + const chainHub = makeChainHub(await agoricNames); + + const agoric = await chainHub.getChainInfo('agoric'); + const osmosis = await chainHub.getChainInfo('osmosis'); + const connectionInfo = await chainHub.getConnectionInfo( + agoric.chainId, + osmosis.chainId, + ); + + /** @type {StartUpgradableOpts} */ + const startOpts = { + label: 'stakeOsmo', + installation: stakeIca, + terms: { + chainId: osmosis.chainId, + hostConnectionId: connectionInfo.id, + controllerConnectionId: connectionInfo.counterparty.connection_id, + bondDenom: osmosis.stakingTokens[0].denom, + icqEnabled: osmosis.icqEnabled, + }, + privateArgs: { + orchestration: await orchestration, + storageNode, + marshaller, + timer: await chainTimerService, + }, + }; + + const { instance } = await E(startUpgradable)(startOpts); + produceInstance.resolve(instance); +}; +harden(startStakeOsmo); + +export const getManifestForStakeOsmo = ( + { restoreRef }, + { installKeys, ...options }, +) => { + return { + manifest: { + [startStakeOsmo.name]: { + consume: { + agoricNames: true, + board: true, + chainStorage: true, + chainTimerService: true, + orchestration: true, + startUpgradable: true, + }, + installation: { + consume: { stakeIca: true }, + }, + instance: { + produce: { stakeOsmo: true }, + }, + }, + }, + installations: { + stakeIca: restoreRef(installKeys.stakeIca), + }, + options, + }; +}; diff --git a/packages/orchestration/test/examples/stake-atom.contract.test.ts b/packages/orchestration/test/examples/stake-atom.contract.test.ts index 53570a3e9f9..5df7dc624de 100644 --- a/packages/orchestration/test/examples/stake-atom.contract.test.ts +++ b/packages/orchestration/test/examples/stake-atom.contract.test.ts @@ -19,7 +19,7 @@ const startContract = async ({ timer, marshaller, storage, - issuerKeywordRecord, + issuerKeywordRecord = undefined, terms = { chainId: 'cosmoshub-4', hostConnectionId: 'connection-1', @@ -52,15 +52,13 @@ test('makeAccount, getAddress, getBalances, getBalance', async t => { brands: { ist }, utils, } = await commonSetup(t); - const { publicFacet } = await startContract({ - ...bootstrap, - issuerKeywordRecord: { In: ist.issuer }, - }); + const { publicFacet } = await startContract(bootstrap); t.log('make an ICA account'); const account = await E(publicFacet).makeAccount(); t.truthy(account, 'account is returned'); const chainAddress = await E(account).getAddress(); + // FIXME mock remoteAddress in ibc bridge. Currently UNPARSABLE_CHAIN_ADDRESS // t.regex(address.address, /cosmos1/); t.like(chainAddress, { chainId: 'cosmoshub-4', addressEncoding: 'bech32' }); @@ -72,25 +70,17 @@ test('makeAccount, getAddress, getBalances, getBalance', async t => { }); await t.throwsAsync(E(account).getBalance('uatom'), { - message: 'Queries not enabled.', + message: 'Queries not available for chain "cosmoshub-4"', }); }); test('makeAccountInvitationMaker', async t => { - const { - bootstrap, - brands: { ist }, - } = await commonSetup(t); - const { publicFacet, zoe } = await startContract({ - ...bootstrap, - issuerKeywordRecord: { In: ist.issuer }, - }); + const { bootstrap } = await commonSetup(t); + const { publicFacet, zoe } = await startContract(bootstrap); const inv = await E(publicFacet).makeAccountInvitationMaker(); t.log('make an offer for ICA account'); - t.log('inv', inv); const seat = await E(zoe).offer(inv); - t.log('seat', seat); const offerResult = await E(seat).getOfferResult(); t.like(offerResult, { @@ -107,19 +97,14 @@ test('makeAccountInvitationMaker', async t => { const storageUpdate = await E(accountNotifier).getUpdateSince(); t.deepEqual(storageUpdate, { updateCount: 1n, - value: { - sequence: 0n, - }, + value: '', }); // FIXME mock remoteAddress in ibc bridge const storagePath = 'mockChainStorageRoot.stakeAtom.accounts.UNPARSABLE_CHAIN_ADDRESS'; const vstorageEntry = bootstrap.storage.data.get(storagePath); - if (typeof vstorageEntry !== 'string') { - t.fail('vstorageEntry not found'); - } else { - t.log(storagePath, vstorageEntry); - t.regex(vstorageEntry, /sequence/); - } + t.truthy(vstorageEntry, 'vstorage account entry created'); + t.log(storagePath, vstorageEntry); + t.is(bootstrap.marshaller.fromCapData(JSON.parse(vstorageEntry!)), ''); }); diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index b1c2058b115..fbc873e52d6 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -185,7 +185,7 @@ const makeScenario = () => { const { delegations, startTime } = configStaking; - const { rootNode } = makeFakeStorageKit('mockChainStorageRoot', { + const { rootNode } = makeFakeStorageKit('stakingOpsTest', { sequence: false, }); @@ -209,11 +209,10 @@ const makeScenario = () => { test('makeAccount() writes to storage', async t => { const s = makeScenario(); - const { account, calls, timer } = s; + const { account, timer } = s; const { makeRecorderKit, storageNode, zcf, icqConnection, zone } = s; const make = prepareCosmosOrchestrationAccountKit(zone, makeRecorderKit, zcf); - // Higher fidelity tests below use invitationMakers. const { holder } = make(account.getAddress(), 'uatom', { account, storageNode, @@ -227,9 +226,7 @@ test('makeAccount() writes to storage', async t => { const storageUpdate = await E(accountNotifier).getUpdateSince(); t.deepEqual(storageUpdate, { updateCount: 1n, - value: { - sequence: 0n, - }, + value: '', }); }); diff --git a/packages/vm-config/decentral-devnet-config.json b/packages/vm-config/decentral-devnet-config.json index e93ba27a609..232420684cc 100644 --- a/packages/vm-config/decentral-devnet-config.json +++ b/packages/vm-config/decentral-devnet-config.json @@ -165,19 +165,6 @@ } ] } - ], - [ - { - "module": "@agoric/builders/scripts/orchestration/init-stakeAtom.js", - "entrypoint": "defaultProposalBuilder", - "args": [ - { - "hostConnectionId": "connection-1", - "controllerConnectionId": "connection-0", - "bondDenom": "uatom" - } - ] - } ] ] }, From 71403dd0fc2e6cc0082c4e099a8a74797a810cc8 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Thu, 13 Jun 2024 16:48:44 -0400 Subject: [PATCH 5/6] fix(cosmosOrchestrationAccount): use AmountArg consistently --- .../src/exos/cosmosOrchestrationAccount.js | 13 +++++++------ packages/orchestration/test/staking-ops.test.ts | 7 +++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js index 968b61335b7..c69f01b6b27 100644 --- a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js +++ b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js @@ -16,7 +16,6 @@ import { MsgUndelegateResponse, } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js'; import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js'; -import { AmountShape } from '@agoric/ertp'; import { makeTracer } from '@agoric/internal'; import { M } from '@agoric/vat-data'; import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js'; @@ -75,11 +74,13 @@ export const IcaAccountHolderI = M.interface('IcaAccountHolder', { holder: M.any(), }), getPublicTopics: M.call().returns(TopicsRecordShape), - delegate: M.callWhen(ChainAddressShape, AmountShape).returns(M.undefined()), + delegate: M.callWhen(ChainAddressShape, AmountArgShape).returns( + M.undefined(), + ), redelegate: M.callWhen( ChainAddressShape, ChainAddressShape, - AmountShape, + AmountArgShape, ).returns(M.undefined()), withdrawReward: M.callWhen(ChainAddressShape).returns( M.arrayOf(ChainAmountShape), @@ -123,11 +124,11 @@ export const prepareCosmosOrchestrationAccountKit = ( helper: M.interface('helper', { owned: M.call().returns(M.remotable()), getUpdater: M.call().returns(M.remotable()), - amountToCoin: M.call(AmountShape).returns(M.record()), + amountToCoin: M.call(AmountArgShape).returns(M.record()), }), holder: IcaAccountHolderI, invitationMakers: M.interface('invitationMakers', { - Delegate: M.callWhen(ChainAddressShape, AmountShape).returns( + Delegate: M.callWhen(ChainAddressShape, AmountArgShape).returns( InvitationShape, ), Redelegate: M.callWhen( @@ -197,7 +198,7 @@ export const prepareCosmosOrchestrationAccountKit = ( invitationMakers: { /** * @param {CosmosValidatorAddress} validator - * @param {Amount<'nat'>} amount + * @param {AmountArg} amount */ Delegate(validator, amount) { trace('Delegate', validator, amount); diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index fbc873e52d6..5c07ade3e19 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -274,8 +274,11 @@ test(`delegate; redelegate using invitationMakers`, async t => { const { validator, delegations } = configStaking; { const value = BigInt(Object.values(delegations)[0].amount); - const anAmount = { brand: aBrand, value }; - const toDelegate = await E(invitationMakers).Delegate(validator, anAmount); + const anAmountArg = { denom: 'uatom', value }; + const toDelegate = await E(invitationMakers).Delegate( + validator, + anAmountArg, + ); const seat = E(zoe).offer(toDelegate); const result = await E(seat).getOfferResult(); From 11aea6561865a8ac17c449a6fcf226dcd52e7c22 Mon Sep 17 00:00:00 2001 From: 0xPatrick Date: Fri, 14 Jun 2024 15:53:03 -0400 Subject: [PATCH 6/6] fix(undelegate): `delegatorAddress` should not be user supplied --- packages/orchestration/src/cosmos-api.ts | 4 +++- packages/orchestration/src/exos/cosmosOrchestrationAccount.js | 4 ++-- packages/orchestration/src/typeGuards.js | 1 - packages/orchestration/test/staking-ops.test.ts | 1 - 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/orchestration/src/cosmos-api.ts b/packages/orchestration/src/cosmos-api.ts index 2eefdd7b9f8..08a95916ccd 100644 --- a/packages/orchestration/src/cosmos-api.ts +++ b/packages/orchestration/src/cosmos-api.ts @@ -151,7 +151,9 @@ export interface StakingAccountActions { * The unbonding time is padded by 10 minutes to account for clock skew. * @param delegations - the delegation to undelegate */ - undelegate: (delegations: Delegation[]) => Promise; + undelegate: ( + delegations: Omit[], + ) => Promise; /** * Withdraw rewards from all validators. The promise settles when the rewards are withdrawn. diff --git a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js index c69f01b6b27..529f35c77d4 100644 --- a/packages/orchestration/src/exos/cosmosOrchestrationAccount.js +++ b/packages/orchestration/src/exos/cosmosOrchestrationAccount.js @@ -234,7 +234,7 @@ export const prepareCosmosOrchestrationAccountKit = ( return this.facets.holder.withdrawReward(validator); }, 'WithdrawReward'); }, - /** @param {Delegation[]} delegations */ + /** @param {Omit[]} delegations */ Undelegate(delegations) { trace('Undelegate', delegations); @@ -407,7 +407,7 @@ export const prepareCosmosOrchestrationAccountKit = ( throw assert.error('Not implemented'); }, - /** @param {Delegation[]} delegations */ + /** @param {Omit[]} delegations */ async undelegate(delegations) { trace('undelegate', delegations); const { helper } = this.facets; diff --git a/packages/orchestration/src/typeGuards.js b/packages/orchestration/src/typeGuards.js index 6cc02a4dbad..e3a836cf2dc 100644 --- a/packages/orchestration/src/typeGuards.js +++ b/packages/orchestration/src/typeGuards.js @@ -25,7 +25,6 @@ export const ChainAmountShape = harden({ denom: M.string(), value: M.nat() }); export const AmountArgShape = M.or(AmountShape, ChainAmountShape); export const DelegationShape = harden({ - delegatorAddress: M.string(), validatorAddress: M.string(), shares: M.string(), // TODO: bigint? }); diff --git a/packages/orchestration/test/staking-ops.test.ts b/packages/orchestration/test/staking-ops.test.ts index 5c07ade3e19..f6eb1bd8e9a 100644 --- a/packages/orchestration/test/staking-ops.test.ts +++ b/packages/orchestration/test/staking-ops.test.ts @@ -378,7 +378,6 @@ test(`undelegate waits for unbonding period`, async t => { const value = BigInt(Object.values(delegations)[0].amount); const anAmount = { brand: Far('Token'), value } as Amount<'nat'>; const delegation = { - delegatorAddress: account.getAddress().address, shares: `${anAmount.value}`, validatorAddress: validator.address, };