Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(localchain): add transfer method to LocalChainAccountKit holder #9380

Merged
merged 4 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/cosmic-proto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
"types": "./dist/codegen/ibc/applications/interchain_accounts/v1/packet.d.ts",
"default": "./dist/codegen/ibc/applications/interchain_accounts/v1/packet.js"
},
"./ibc/core/channel/v1/channel.js": {
"types": "./dist/codegen/ibc/core/channel/v1/channel.d.ts",
"default": "./dist/codegen/ibc/core/channel/v1/channel.js"
},
"./ibc/core/connection/v1/connection.js": {
"types": "./dist/codegen/ibc/core/connection/v1/connection.d.ts",
"default": "./dist/codegen/ibc/core/connection/v1/connection.js"
},
"./icq/*.js": {
"types": "./dist/codegen/icq/*.d.ts",
"default": "./dist/codegen/icq/v1/*.js"
Expand Down
7 changes: 7 additions & 0 deletions packages/cosmic-proto/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import type {
} from './codegen/cosmos/staking/v1beta1/tx.js';
import { RequestQuery } from './codegen/tendermint/abci/types.js';
import type { Any } from './codegen/google/protobuf/any.js';
import {
MsgTransfer,
MsgTransferResponse,
} from './codegen/ibc/applications/transfer/v1/tx.js';

/**
* The result of Any.toJSON(). The type in cosms-types says it returns
Expand All @@ -28,13 +32,16 @@ export type Proto3Shape = {
'/cosmos.bank.v1beta1.QueryAllBalancesResponse': QueryAllBalancesResponse;
'/cosmos.staking.v1beta1.MsgDelegate': MsgDelegate;
'/cosmos.staking.v1beta1.MsgDelegateResponse': MsgDelegateResponse;
'/ibc.applications.transfer.v1.MsgTransfer': MsgTransfer;
'/ibc.applications.transfer.v1.MsgTransferResponse': MsgTransferResponse;
Comment on lines +35 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this for?

Copy link
Member Author

@0xpatrickdev 0xpatrickdev May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typed return values for LocalChainAccount['executeTx']. Here's a corresponding test:
https://github.com/Agoric/agoric-sdk/pull/9351/files#diff-5029634754ca0f3ec74d84e0f67e99fe74eaf5007b291a7ba337e04e64c8f9c7

};

// Often s/Request$/Response/ but not always
type ResponseMap = {
'/cosmos.bank.v1beta1.MsgSend': '/cosmos.bank.v1beta1.MsgSendResponse';
'/cosmos.bank.v1beta1.QueryAllBalancesRequest': '/cosmos.bank.v1beta1.QueryAllBalancesResponse';
'/cosmos.staking.v1beta1.MsgDelegate': '/cosmos.staking.v1beta1.MsgDelegateResponse';
'/ibc.applications.transfer.v1.MsgTransfer': '/ibc.applications.transfer.v1.MsgTransferResponse';
};

/**
Expand Down
54 changes: 40 additions & 14 deletions packages/orchestration/src/cosmos-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import type {
LocalIbcAddress,
RemoteIbcAddress,
} from '@agoric/vats/tools/ibc-utils.js';
import { MsgTransfer } from '@agoric/cosmic-proto/ibc/applications/transfer/v1/tx.js';
import type { State as IBCConnectionState } from '@agoric/cosmic-proto/ibc/core/connection/v1/connection.js';
import type {
Order,
State as IBCChannelState,
} from '@agoric/cosmic-proto/ibc/core/channel/v1/channel.js';
import { IBCChannelID, IBCConnectionID } from '@agoric/vats';
import { MapStore } from '@agoric/store';
import type { AmountArg, ChainAddress, DenomAmount } from './types.js';

/** A helper type for type extensions. */
Expand All @@ -29,25 +37,37 @@ export type CosmosValidatorAddress = ChainAddress & {
addressEncoding: 'bech32';
};

/** Represents an IBC Connection between two chains, which can contain multiple Channels. */
export type IBCConnectionInfo = {
id: IBCConnectionID; // e.g. connection-0
client_id: string; // '07-tendermint-0'
state: IBCConnectionState;
counterparty: {
client_id: string;
connection_id: IBCConnectionID;
prefix: {
key_prefix: string;
};
};
versions: { identifier: string; features: string[] }[];
delay_period: bigint;
transferChannel: {
portId: string;
channelId: IBCChannelID;
counterPartyPortId: string;
counterPartyChannelId: IBCChannelID;
ordering: Order;
state: IBCChannelState;
version: string; // e.eg. 'ics20-1'
};
};

/**
* Info for a Cosmos-based chain.
*/
export type CosmosChainInfo = {
chainId: string;
ibcConnectionInfo: {
id: string; // e.g. connection-0
client_id: string; // '07-tendermint-0'
state: 'OPEN' | 'TRYOPEN' | 'INIT' | 'CLOSED';
counterparty: {
client_id: string;
connection_id: string;
prefix: {
key_prefix: string;
};
};
versions: { identifier: string; features: string[] }[];
delay_period: bigint;
};
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mutable... hm... 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would you expect to see instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure... it's an adjustment to my mental model, and I'm not sure where I might have assumed that ChainInfo is immutable. I just need to be aware of it going forward, I suppose.

(technically, this still is immutable; the value of the connections property doesn't change. it's always the same MapStore. It's just the contents of that MapStore that may change.)

One impact is the contents of connections aren't included when a CosmosChainInfo is marshaled to vstorage as part of agoricNames.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when do we use chainId vs wellKnownName?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question, I am also wondering the same. The transfer parameters imply we should be able to get there from chainId or chainId -> wellKnownName. https://github.com/Agoric/agoric-sdk/pull/9380/files#r1610510589

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think solving this can wait for #9063

icaEnabled: boolean;
icqEnabled: boolean;
pfmEnabled: boolean;
Expand Down Expand Up @@ -197,3 +217,9 @@ export interface IcaAccount {
export type LiquidStakingMethods = {
liquidStake: (amount: AmountArg) => Promise<void>;
};

export type IBCMsgTransferOptions = {
timeoutHeight?: MsgTransfer['timeoutHeight'];
timeoutTimestamp?: MsgTransfer['timeoutTimestamp'];
memo?: string;
};
15 changes: 9 additions & 6 deletions packages/orchestration/src/examples/stakeAtom.contract.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/**
* @file Example contract that uses orchestration
*/

import { makeTracer, StorageNodeShape } from '@agoric/internal';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { V as E } from '@agoric/vow/vat.js';
import { M } from '@endo/patterns';
import { prepareRecorderKitMakers } 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';
import { prepareStakingAccountKit } from '../exos/stakingAccountKit.js';

const trace = makeTracer('StakeAtom');
Expand Down Expand Up @@ -61,7 +63,7 @@ export const start = async (zcf, privateArgs, baggage) => {
zcf,
);

async function makeAccount() {
async function makeAccountKit() {
const account = await E(orchestration).makeAccount(
hostConnectionId,
controllerConnectionId,
Expand Down Expand Up @@ -94,19 +96,20 @@ export const start = async (zcf, privateArgs, baggage) => {
'StakeAtom',
M.interface('StakeAtomI', {
makeAccount: M.callWhen().returns(M.remotable('ChainAccount')),
makeAcountInvitationMaker: M.call().returns(M.promise()),
makeAcountInvitationMaker: M.callWhen().returns(InvitationShape),
}),
{
async makeAccount() {
trace('makeAccount');
return makeAccount().then(({ account }) => account);
const { account } = await makeAccountKit();
return account;
},
makeAcountInvitationMaker() {
trace('makeCreateAccountInvitation');
return zcf.makeInvitation(
async seat => {
seat.exit();
return makeAccount();
return makeAccountKit();
},
'wantStakingAccount',
undefined,
Expand Down
119 changes: 80 additions & 39 deletions packages/orchestration/src/examples/stakeBld.contract.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
/**
* @file Stake BLD contract
*
*/

import { makeTracer } from '@agoric/internal';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { withdrawFromSeat } from '@agoric/zoe/src/contractSupport/zoeHelpers.js';
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
import { makeDurableZone } from '@agoric/zone/durable.js';
import { M } from '@endo/patterns';
import { E } from '@endo/far';
import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport/recorder.js';
import { atomicTransfer } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
import { prepareLocalchainAccountKit } from '../exos/localchainAccountKit.js';
import { deeplyFulfilled } from '@endo/marshal';
import { M } from '@endo/patterns';
import { prepareLocalChainAccountKit } from '../exos/local-chain-account-kit.js';
import { prepareMockChainInfo } from '../utils/mockChainInfo.js';

/**
* @import {TimerBrand, TimerService} from '@agoric/time';
*/

const trace = makeTracer('StakeBld');

Expand All @@ -20,12 +25,15 @@ const trace = makeTracer('StakeBld');
* localchain: import('@agoric/vats/src/localchain.js').LocalChain;
* marshaller: Marshaller;
* storageNode: StorageNode;
* timerService: TimerService;
* timerBrand: TimerBrand;
* }} privateArgs
* @param {import("@agoric/vat-data").Baggage} baggage
*/
export const start = async (zcf, privateArgs, baggage) => {
const { BLD } = zcf.getTerms().brands;
const BLD = zcf.getTerms().brands.In;

// XXX is this safe to call before prepare statements are completed?
Copy link
Member

@turadg turadg May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope, it's a remote call and those can't happen in the first crank of upgrade. but we can tackle that when this gets an upgrade test

const bldAmountShape = await E(BLD).getAmountShape();

const zone = makeDurableZone(baggage);
Expand All @@ -34,48 +42,81 @@ export const start = async (zcf, privateArgs, baggage) => {
baggage,
privateArgs.marshaller,
);
const makeLocalchainAccountKit = prepareLocalchainAccountKit(

// Mocked until #8879
// Would expect this to be instantiated elsewhere, and passed in as a reference
const agoricChainInfo = prepareMockChainInfo(zone);

const makeLocalChainAccountKit = prepareLocalChainAccountKit(
baggage,
makeRecorderKit,
zcf,
privateArgs.timerService,
privateArgs.timerBrand,
agoricChainInfo,
);

const publicFacet = zone.exo('StakeBld', undefined, {
makeStakeBldInvitation() {
return zcf.makeInvitation(
async seat => {
const { give } = seat.getProposal();
trace('makeStakeBldInvitation', give);
// XXX type appears local but it's remote
const account = await E(privateArgs.localchain).makeAccount();
const lcaSeatKit = zcf.makeEmptySeatKit();
atomicTransfer(zcf, seat, lcaSeatKit.zcfSeat, give);
seat.exit();
trace('makeStakeBldInvitation tryExit lca userSeat');
await E(lcaSeatKit.userSeat).tryExit();
trace('awaiting payouts');
const payouts = await E(lcaSeatKit.userSeat).getPayouts();
const { holder, invitationMakers } = makeLocalchainAccountKit(
account,
privateArgs.storageNode,
);
trace('awaiting deposit');
await E(account).deposit(await payouts.In);
async function makeLocalAccountKit() {
const account = await E(privateArgs.localchain).makeAccount();
const address = await E(account).getAddress();
return makeLocalChainAccountKit({
account,
address,
storageNode: privateArgs.storageNode,
});
}

return {
const publicFacet = zone.exo(
'StakeBld',
M.interface('StakeBldI', {
makeAccount: M.callWhen().returns(M.remotable('LocalChainAccountHolder')),
makeAcountInvitationMaker: M.callWhen().returns(InvitationShape),
makeStakeBldInvitation: M.callWhen().returns(InvitationShape),
}),
{
makeStakeBldInvitation() {
return zcf.makeInvitation(
async seat => {
const { give } = seat.getProposal();
trace('makeStakeBldInvitation', give);
const { holder, invitationMakers } = await makeLocalAccountKit();
const { In } = await deeplyFulfilled(
withdrawFromSeat(zcf, seat, give),
);
await E(holder).deposit(In);
seat.exit();
return harden({
publicSubscribers: holder.getPublicTopics(),
invitationMakers,
account: holder,
});
},
'wantStake',
undefined,
M.splitRecord({
give: { In: bldAmountShape },
}),
);
},
async makeAccount() {
trace('makeAccount');
const { holder } = await makeLocalAccountKit();
return holder;
},
makeAcountInvitationMaker() {
trace('makeCreateAccountInvitation');
return zcf.makeInvitation(async seat => {
seat.exit();
const { holder, invitationMakers } = await makeLocalAccountKit();
return harden({
publicSubscribers: holder.getPublicTopics(),
invitationMakers,
account: holder,
};
},
'wantStake',
undefined,
M.splitRecord({
give: { In: bldAmountShape },
}),
);
});
}, 'wantLocalChainAccount');
},
},
});
);

return { publicFacet };
};
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const start = async (zcf, privateArgs) => {
// deposit funds from user seat to LocalChainAccount
const payments = await withdrawFromSeat(zcf, seat, give);
await deeplyFulfilled(objectMap(payments, localAccount.deposit));
seat.exit();

// build swap instructions with orcUtils library
const transferMsg = orcUtils.makeOsmosisSwap({
Expand Down
1 change: 0 additions & 1 deletion packages/orchestration/src/exos/chainAccountKit.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/** @file ChainAccount exo */

import { NonNullish } from '@agoric/assert';
import { makeTracer } from '@agoric/internal';
import { V as E } from '@agoric/vow/vat.js';
Expand Down
Loading
Loading