Skip to content

Commit

Permalink
feat(orchestration): add queryBalance to stakeAtom
Browse files Browse the repository at this point in the history
- refs: #9042
  • Loading branch information
0xpatrickdev committed Apr 30, 2024
1 parent c8aa0e4 commit 46c89ab
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 17 deletions.
11 changes: 11 additions & 0 deletions packages/boot/test/bootstrapTests/test-orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ test.serial('stakeAtom - repl-style', async t => {

const res = await EV(account).delegate('cosmosvaloper1test', atomAmount);
t.is(res, 'Success', 'delegate returns Success');

const queryRes = await EV(account).queryBalance();
t.deepEqual(queryRes, { amount: '0', denom: 'uatom' });

const queryUnknownDenom =
await EV(account).queryBalance('some-invalid-denom');
t.deepEqual(
queryUnknownDenom,
{ amount: '0', denom: 'some-invalid-denom' },
'queryBalance for unknown denom returns amount: 0',
);
});

test.serial('stakeAtom - smart wallet', async t => {
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
12 changes: 11 additions & 1 deletion packages/orchestration/src/examples/stakeAtom.contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const trace = makeTracer('StakeAtom');
* @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,
);
// TODO #9063 reference existing QueryConn from `Chain` object versus creating
// a new one for every user
const queryConnection = await E(orchestration).createQueryConnection(
controllerConnectionId,
);
const accountAddress = await E(account).getAccountAddress();
trace('account address', accountAddress);
const { holder, invitationMakers } = makeStakingAccountKit(
account,
storageNode,
accountAddress,
queryConnection,
bondDenom,
);
return {
publicSubscribers: holder.getPublicTopics(),
Expand Down
90 changes: 76 additions & 14 deletions packages/orchestration/src/exos/stakingAccountKit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,25 @@ import {
MsgDelegate,
MsgDelegateResponse,
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
import {
QueryBalanceRequest,
QueryBalanceResponse,
} from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any';
import { RequestQuery } from '@agoric/cosmic-proto/tendermint/abci/types.js';
import { AmountShape } from '@agoric/ertp';
import { makeTracer } from '@agoric/internal';
import { UnguardedHelperI } from '@agoric/internal/src/typeGuards.js';
import { M, prepareExoClassKit } from '@agoric/vat-data';
import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
import { decodeBase64 } from '@endo/base64';
import { E } from '@endo/far';
import { Any } from '@agoric/cosmic-proto/google/protobuf/any';

/**
* @import { ChainAccount, ChainAddress } from '../types.js';
* @import { ChainAccountKit, ChainAddress, CosmosValidatorAddress, QueryConnection } from '../types.js';
* @import { RecorderKit, MakeRecorderKit } from '@agoric/zoe/src/contractSupport/recorder.js';
* @import { Baggage } from '@agoric/swingset-liveslots';
* @import {AnyJson} from '@agoric/cosmic-proto';
* @import { AnyJson, RequestQueryJson } from '@agoric/cosmic-proto';
*/

const trace = makeTracer('StakingAccountHolder');
Expand All @@ -31,17 +36,23 @@ const { Fail } = assert;
/**
* @typedef {{
* topicKit: RecorderKit<StakingAccountNotification>;
* account: ChainAccount;
* chainAddress: string;
* account: ChainAccountKit['account'];
* chainAddress: ChainAddress['address'];
* queryConnection: QueryConnection;
* bondDenom: string;
* }} State
*/

const BalanceShape = { amount: M.string(), denom: M.string() };

const HolderI = M.interface('holder', {
getPublicTopics: M.call().returns(TopicsRecordShape),
makeDelegateInvitation: M.call(M.string(), AmountShape).returns(M.promise()),
makeCloseAccountInvitation: M.call().returns(M.promise()),
makeTransferAccountInvitation: M.call().returns(M.promise()),
delegate: M.callWhen(M.string(), AmountShape).returns(M.string()),
queryBalance: M.callWhen().optional(M.string()).returns(BalanceShape),
getAccountAddress: M.call().returns(M.string()),
});

/** @type {{ [name: string]: [description: string, valueShape: Pattern] }} */
Expand Down Expand Up @@ -69,16 +80,18 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
}),
},
/**
* @param {ChainAccount} account
* @param {ChainAccountKit['account']} account
* @param {StorageNode} storageNode
* @param {string} chainAddress
* @param {ChainAddress['address']} chainAddress
* @param {QueryConnection} queryConnection
* @param {string} bondDenom e.g. 'uatom'
* @returns {State}
*/
(account, storageNode, chainAddress) => {
(account, storageNode, chainAddress, queryConnection, bondDenom) => {
// must be the fully synchronous maker because the kit is held in durable state
const topicKit = makeRecorderKit(storageNode, PUBLIC_TOPICS.account[1]);

return { account, chainAddress, topicKit };
return { account, chainAddress, topicKit, queryConnection, bondDenom };
},
{
helper: {
Expand All @@ -95,17 +108,49 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
},
// TODO move this beneath the Orchestration abstraction,
// to the OrchestrationAccount provided by createAccount()
/**
* @param {string} [denom] - defaults to bondDenom
* @returns {Promise<{ amount: string; denom: string; }>}
*/
async queryBalance(denom) {
const { chainAddress, queryConnection, bondDenom } = this.state;

denom ||= bondDenom;

const [result] = await E(queryConnection).query([
/** @type {RequestQueryJson} */ (
RequestQuery.toJSON(
RequestQuery.fromPartial({
path: '/cosmos.bank.v1beta1.Query/Balance',
data: QueryBalanceRequest.encode(
QueryBalanceRequest.fromPartial({
address: chainAddress,
denom,
}),
).finish(),
}),
)
),
]);
if (!result?.key) throw Fail`Error parsing result ${result}`;
const { balance } = QueryBalanceResponse.decode(
decodeBase64(result.key),
);
if (!balance) throw Fail`Error parsing result ${result}`;
// TODO, return Amount? cast amount to bigint? #9211
return balance;
},
/**
* _Assumes users has already sent funds to their ICA, until #9193
* @param {string} validatorAddress
* @param {CosmosValidatorAddress} validatorAddress
* @param {Amount<'nat'>} ertpAmount
*/
async delegate(validatorAddress, ertpAmount) {
// FIXME get values from proposal or args
// FIXME get values from proposal or args #9211
// FIXME brand handling and amount scaling
const amount = {
amount: String(ertpAmount.value),
denom: 'uatom',
denom: this.state.bondDenom, // TODO use ertpAmount.brand #9211
};

const account = this.facets.helper.owned();
Expand All @@ -116,6 +161,7 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
Any.toJSON(
MsgDelegate.toProtoMsg({
delegatorAddress,
// @ts-expect-error Type 'CosmosValidatorAddress' is not assignable to type 'string'.
validatorAddress,
amount,
}),
Expand All @@ -134,6 +180,10 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
},
},
invitationMakers: {
/**
* @param {CosmosValidatorAddress} validatorAddress
* @param {Amount<'nat'>} amount
*/
Delegate(validatorAddress, amount) {
return this.facets.holder.makeDelegateInvitation(
validatorAddress,
Expand All @@ -160,16 +210,28 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
},
/**
*
* @param {string} validatorAddress
* @param {CosmosValidatorAddress} validatorAddress
* @param {Amount<'nat'>} ertpAmount
*/
async delegate(validatorAddress, ertpAmount) {
trace('delegate', validatorAddress, ertpAmount);
return this.facets.helper.delegate(validatorAddress, ertpAmount);
},
getAccountAddress() {
return this.state.chainAddress;
},
/**
* @param {string} [denom] - defaults to bondDenom
* @returns {Promise<{ amount: string; denom: string; }>}
*/
async queryBalance(denom) {
denom ||= this.state.bondDenom;
trace('queryBalance', this.state.chainAddress, denom);
return this.facets.helper.queryBalance(denom);
},
/**
*
* @param {string} validatorAddress
* @param {CosmosValidatorAddress} validatorAddress
* @param {Amount<'nat'>} ertpAmount
*/
makeDelegateInvitation(validatorAddress, ertpAmount) {
Expand Down
9 changes: 7 additions & 2 deletions packages/orchestration/src/proposals/start-stakeAtom.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ export const startStakeAtom = async (
produce: { stakeAtom: produceInstance },
},
},
{ options: { hostConnectionId, controllerConnectionId } },
{ options: { hostConnectionId, controllerConnectionId, bondDenom } },
) => {
const VSTORAGE_PATH = 'stakeAtom';
trace('startStakeAtom', { hostConnectionId, controllerConnectionId });
trace('startStakeAtom', {
hostConnectionId,
controllerConnectionId,
bondDenom,
});
await null;

const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH);
Expand All @@ -46,6 +50,7 @@ export const startStakeAtom = async (
terms: {
hostConnectionId,
controllerConnectionId,
bondDenom,
},
privateArgs: {
orchestration: await orchestration,
Expand Down

0 comments on commit 46c89ab

Please sign in to comment.