Skip to content

Commit

Permalink
feat(orchestration): stakeAtom query balance
Browse files Browse the repository at this point in the history
  • Loading branch information
0xpatrickdev committed May 3, 2024
1 parent 79b5d0f commit 9f0ae09
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 53 deletions.
33 changes: 29 additions & 4 deletions packages/boot/test/bootstrapTests/test-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { AmountMath } from '@agoric/ertp';
import type { start as stakeBldStart } from '@agoric/orchestration/src/examples/stakeBld.contract.js';
import type { Instance } from '@agoric/zoe/src/zoeService/utils.js';
import { M, matches } from '@endo/patterns';
import type { CosmosValidatorAddress } from '@agoric/orchestration';
import { makeWalletFactoryContext } from './walletFactory.ts';

type DefaultTestContext = Awaited<ReturnType<typeof makeWalletFactoryContext>>;
Expand Down Expand Up @@ -124,8 +125,21 @@ test.serial('stakeAtom - repl-style', async t => {
const atomBrand = await EV(agoricNames).lookup('brand', 'ATOM');
const atomAmount = AmountMath.make(atomBrand, 10n);

await t.notThrowsAsync(
EV(account).delegate('cosmosvaloper1test', atomAmount),
const validatorAddress: CosmosValidatorAddress = {
address: 'cosmosvaloper1test',
chainId: 'gaiatest',
addressEncoding: 'bech32',
};
await t.notThrowsAsync(EV(account).delegate(validatorAddress, atomAmount));

const queryRes = await EV(account).getBalance();
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',
);
});

Expand Down Expand Up @@ -156,6 +170,11 @@ test.serial('stakeAtom - smart wallet', async t => {

const { ATOM } = agoricNamesRemotes.brand;
ATOM || Fail`ATOM missing from agoricNames`;
const validatorAddress: CosmosValidatorAddress = {
address: 'cosmosvaloper1test',
chainId: 'gaiatest',
addressEncoding: 'bech32',
};

await t.notThrowsAsync(
wd.executeOffer({
Expand All @@ -164,7 +183,7 @@ test.serial('stakeAtom - smart wallet', async t => {
source: 'continuing',
previousOffer: 'request-account',
invitationMakerName: 'Delegate',
invitationArgs: ['cosmosvaloper1test', { brand: ATOM, value: 10n }],
invitationArgs: [validatorAddress, { brand: ATOM, value: 10n }],
},
proposal: {},
}),
Expand All @@ -173,14 +192,20 @@ test.serial('stakeAtom - smart wallet', async t => {
status: { id: 'request-delegate-success', numWantsSatisfied: 1 },
});

const validatorAddressFail: CosmosValidatorAddress = {
address: 'cosmosvaloper1fail',
chainId: 'gaiatest',
addressEncoding: 'bech32',
};

await t.throwsAsync(
wd.executeOffer({
id: 'request-delegate-fail',
invitationSpec: {
source: 'continuing',
previousOffer: 'request-account',
invitationMakerName: 'Delegate',
invitationArgs: ['cosmosvaloper1fail', { brand: ATOM, value: 10n }],
invitationArgs: [validatorAddressFail, { brand: ATOM, value: 10n }],
},
proposal: {},
}),
Expand Down
2 changes: 2 additions & 0 deletions packages/builders/scripts/orchestration/init-stakeAtom.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const defaultProposalBuilder = async (
const {
hostConnectionId = 'connection-1',
controllerConnectionId = 'connection-0',
bondDenom = 'uatom',
} = options;
return harden({
sourceSpec: '@agoric/orchestration/src/proposals/start-stakeAtom.js',
Expand All @@ -21,6 +22,7 @@ export const defaultProposalBuilder = async (
},
hostConnectionId,
controllerConnectionId,
bondDenom,
},
],
});
Expand Down
7 changes: 4 additions & 3 deletions packages/cosmic-proto/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,17 @@ export type JsonSafe<T> = {
*/
export type RequestQueryJson = JsonSafe<RequestQuery>;

const QUERY_REQ_TYPEURL_RE = /^\/(\w+(?:\.\w+)*)\.Query(\w+)Request$/;
const QUERY_REQ_TYPEURL_RE =
/^\/(?<serviceName>\w+(?:\.\w+)*)\.Query(?<methodName>\w+)Request$/;

export const typeUrlToGrpcPath = (typeUrl: Any['typeUrl']) => {
const match = typeUrl.match(QUERY_REQ_TYPEURL_RE);
if (!match) {
if (!(match && match.groups)) {
throw new TypeError(
`Invalid typeUrl: ${typeUrl}. Must be a Query Request.`,
);
}
const [, serviceName, methodName] = match;
const { serviceName, methodName } = match.groups;
return `/${serviceName}.Query/${methodName}`;
};

Expand Down
2 changes: 2 additions & 0 deletions packages/cosmic-proto/test/snapshots/test-exports.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Generated by [AVA](https://avajs.dev).
'readInt32',
'readUInt32',
'tendermint',
'toRequestQueryJson',
'typeUrlToGrpcPath',
'typedJson',
'uInt64ToString',
'utf8Length',
Expand Down
Binary file modified packages/cosmic-proto/test/snapshots/test-exports.js.snap
Binary file not shown.
20 changes: 15 additions & 5 deletions packages/orchestration/src/examples/stakeAtom.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ import { prepareStakingAccountKit } from '../exos/stakingAccountKit.js';

const trace = makeTracer('StakeAtom');
/**
* @import { OrchestrationService } from '../service.js'
* @import { Baggage } from '@agoric/vat-data';
* @import { IBCConnectionID } from '@agoric/vats';
* @import { ICQConnection, OrchestrationService } from '../types.js';
*/

/**
* @typedef {{
* hostConnectionId: IBCConnectionID;
* controllerConnectionId: IBCConnectionID;
* bondDenom: string;
* }} StakeAtomTerms
*/

Expand All @@ -34,7 +35,9 @@ const trace = makeTracer('StakeAtom');
* @param {Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
const { hostConnectionId, controllerConnectionId } = zcf.getTerms();
// TODO #9063 this roughly matches what we'll get from Chain<C>.getChainInfo()
const { hostConnectionId, controllerConnectionId, bondDenom } =
zcf.getTerms();
const { orchestration, marshaller, storageNode } = privateArgs;

const zone = makeDurableZone(baggage);
Expand All @@ -52,12 +55,19 @@ export const start = async (zcf, privateArgs, baggage) => {
hostConnectionId,
controllerConnectionId,
);
const address = await E(account).getAddress();
trace('chain address', address);
// #9212 TODO do 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,
);
const accountAddress = await E(account).getAddress();
trace('account address', accountAddress);
const { holder, invitationMakers } = makeStakingAccountKit(
account,
storageNode,
address,
accountAddress,
icqConnection,
bondDenom,
);
return {
publicSubscribers: holder.getPublicTopics(),
Expand Down
15 changes: 14 additions & 1 deletion packages/orchestration/src/exos/icqConnectionKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,20 @@ export const ICQConnectionI = M.interface('ICQConnection', {
* }} ICQConnectionKitState
*/

/** @param {Zone} zone */
/**
* Prepares an ICQ Connection Kit based on the {@link https://github.com/cosmos/ibc-apps/blob/e9b46e4bf0ad0a66cf6bc53b5e5496f6e2b4b02b/modules/async-icq/README.md | `icq/v1` IBC application protocol}.
*
* `icq/v1`, also referred to as `async-icq`, is a protocol for asynchronous queries
* between IBC-enabled chains. It allows a chain to send queries to another chain
* and receive responses asynchronously.
*
* The ICQ connection kit provides the necessary functionality to establish and manage
* an ICQ connection between two chains. It includes methods for retrieving the local
* and remote addresses of the connection, as well as sending queries and handling
* connection events.
*
* @param {Zone} zone
*/
export const prepareICQConnectionKit = zone =>
zone.exoClassKit(
'ICQConnectionKit',
Expand Down
Loading

0 comments on commit 9f0ae09

Please sign in to comment.