@agoric/orchestration 0.1.1-dev-286302a.0 → 0.1.1-dev-24f7f32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  // eslint-disable-next-line import/export
2
2
  export * from './src/types.js';
3
- export * from './src/utils/address.js';
4
- export * from './src/utils/tx.js';
5
3
  export * from './src/service.js';
4
+ export * from './src/typeGuards.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/orchestration",
3
- "version": "0.1.1-dev-286302a.0+286302a",
3
+ "version": "0.1.1-dev-24f7f32.0+24f7f32",
4
4
  "description": "Chain abstraction for Agoric's orchestration clients",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -29,18 +29,18 @@
29
29
  },
30
30
  "homepage": "https://github.com/Agoric/agoric-sdk#readme",
31
31
  "dependencies": {
32
- "@agoric/assert": "0.6.1-dev-286302a.0+286302a",
33
- "@agoric/cosmic-proto": "0.4.1-dev-286302a.0+286302a",
34
- "@agoric/ertp": "0.16.3-dev-286302a.0+286302a",
35
- "@agoric/internal": "0.3.3-dev-286302a.0+286302a",
36
- "@agoric/network": "0.1.1-dev-286302a.0+286302a",
37
- "@agoric/notifier": "0.6.3-dev-286302a.0+286302a",
38
- "@agoric/store": "0.9.3-dev-286302a.0+286302a",
39
- "@agoric/time": "0.3.3-dev-286302a.0+286302a",
40
- "@agoric/vat-data": "0.5.3-dev-286302a.0+286302a",
41
- "@agoric/vats": "0.15.2-dev-286302a.0+286302a",
42
- "@agoric/zoe": "0.26.3-dev-286302a.0+286302a",
43
- "@agoric/zone": "0.2.3-dev-286302a.0+286302a",
32
+ "@agoric/assert": "0.6.1-dev-24f7f32.0+24f7f32",
33
+ "@agoric/cosmic-proto": "0.4.1-dev-24f7f32.0+24f7f32",
34
+ "@agoric/ertp": "0.16.3-dev-24f7f32.0+24f7f32",
35
+ "@agoric/internal": "0.3.3-dev-24f7f32.0+24f7f32",
36
+ "@agoric/network": "0.1.1-dev-24f7f32.0+24f7f32",
37
+ "@agoric/notifier": "0.6.3-dev-24f7f32.0+24f7f32",
38
+ "@agoric/store": "0.9.3-dev-24f7f32.0+24f7f32",
39
+ "@agoric/time": "0.3.3-dev-24f7f32.0+24f7f32",
40
+ "@agoric/vat-data": "0.5.3-dev-24f7f32.0+24f7f32",
41
+ "@agoric/vats": "0.15.2-dev-24f7f32.0+24f7f32",
42
+ "@agoric/zoe": "0.26.3-dev-24f7f32.0+24f7f32",
43
+ "@agoric/zone": "0.2.3-dev-24f7f32.0+24f7f32",
44
44
  "@endo/base64": "^1.0.4",
45
45
  "@endo/far": "^1.1.1",
46
46
  "@endo/marshal": "^1.4.1",
@@ -82,5 +82,5 @@
82
82
  "typeCoverage": {
83
83
  "atLeast": 96.39
84
84
  },
85
- "gitHead": "286302a192b9eb2e222faa08479f496645bb7b9a"
85
+ "gitHead": "24f7f32495a6eda13fd75d18620ab9f7989b5d2e"
86
86
  }
@@ -11,15 +11,16 @@ import { prepareStakingAccountKit } from '../exos/stakingAccountKit.js';
11
11
 
12
12
  const trace = makeTracer('StakeAtom');
13
13
  /**
14
- * @import { OrchestrationService } from '../service.js'
15
14
  * @import { Baggage } from '@agoric/vat-data';
16
15
  * @import { IBCConnectionID } from '@agoric/vats';
16
+ * @import { ICQConnection, OrchestrationService } from '../types.js';
17
17
  */
18
18
 
19
19
  /**
20
20
  * @typedef {{
21
21
  * hostConnectionId: IBCConnectionID;
22
22
  * controllerConnectionId: IBCConnectionID;
23
+ * bondDenom: string;
23
24
  * }} StakeAtomTerms
24
25
  */
25
26
 
@@ -34,7 +35,9 @@ const trace = makeTracer('StakeAtom');
34
35
  * @param {Baggage} baggage
35
36
  */
36
37
  export const start = async (zcf, privateArgs, baggage) => {
37
- const { hostConnectionId, controllerConnectionId } = zcf.getTerms();
38
+ // TODO #9063 this roughly matches what we'll get from Chain<C>.getChainInfo()
39
+ const { hostConnectionId, controllerConnectionId, bondDenom } =
40
+ zcf.getTerms();
38
41
  const { orchestration, marshaller, storageNode } = privateArgs;
39
42
 
40
43
  const zone = makeDurableZone(baggage);
@@ -52,12 +55,19 @@ export const start = async (zcf, privateArgs, baggage) => {
52
55
  hostConnectionId,
53
56
  controllerConnectionId,
54
57
  );
55
- const address = await E(account).getAddress();
56
- trace('chain address', address);
58
+ // #9212 TODO do not fail if host does not have `async-icq` module;
59
+ // communicate to OrchestrationAccount that it can't send queries
60
+ const icqConnection = await E(orchestration).provideICQConnection(
61
+ controllerConnectionId,
62
+ );
63
+ const accountAddress = await E(account).getAddress();
64
+ trace('account address', accountAddress);
57
65
  const { holder, invitationMakers } = makeStakingAccountKit(
58
66
  account,
59
67
  storageNode,
60
- address,
68
+ accountAddress,
69
+ icqConnection,
70
+ bondDenom,
61
71
  );
62
72
  return {
63
73
  publicSubscribers: holder.getPublicTopics(),
@@ -1,22 +1,27 @@
1
1
  // @ts-check
2
- /** @file Orchestration service */
3
- import { NonNullish } from '@agoric/assert';
4
- import { makeTracer } from '@agoric/internal';
2
+ /** @file ChainAccount exo */
5
3
 
6
4
  // XXX ambient types runtime imports until https://github.com/Agoric/agoric-sdk/issues/6512
7
5
  import '@agoric/network/exported.js';
8
6
 
7
+ import { NonNullish } from '@agoric/assert';
8
+ import { makeTracer } from '@agoric/internal';
9
9
  import { V as E } from '@agoric/vat-data/vow.js';
10
10
  import { M } from '@endo/patterns';
11
11
  import { PaymentShape, PurseShape } from '@agoric/ertp';
12
12
  import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
13
- import { parseAddress } from '../utils/address.js';
14
- import { makeTxPacket, parsePacketAck } from '../utils/tx.js';
13
+ import { findAddressField } from '../utils/address.js';
14
+ import {
15
+ ConnectionHandlerI,
16
+ ChainAddressShape,
17
+ Proto3Shape,
18
+ } from '../typeGuards.js';
19
+ import { makeTxPacket, parseTxPacket } from '../utils/packet.js';
15
20
 
16
21
  /**
22
+ * @import { Zone } from '@agoric/base-zone';
17
23
  * @import { Connection, Port } from '@agoric/network';
18
24
  * @import { Remote } from '@agoric/vow';
19
- * @import { Zone } from '@agoric/base-zone';
20
25
  * @import { AnyJson } from '@agoric/cosmic-proto';
21
26
  * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
22
27
  * @import { LocalIbcAddress, RemoteIbcAddress } from '@agoric/vats/tools/ibc-utils.js';
@@ -24,18 +29,7 @@ import { makeTxPacket, parsePacketAck } from '../utils/tx.js';
24
29
  */
25
30
 
26
31
  const { Fail } = assert;
27
- const trace = makeTracer('ChainAccount');
28
-
29
- export const Proto3Shape = {
30
- typeUrl: M.string(),
31
- value: M.string(),
32
- };
33
-
34
- export const ChainAddressShape = {
35
- address: M.string(),
36
- chainId: M.string(),
37
- addressEncoding: M.string(),
38
- };
32
+ const trace = makeTracer('ChainAccountKit');
39
33
 
40
34
  /** @typedef {'UNPARSABLE_CHAIN_ADDRESS'} UnparsableChainAddress */
41
35
  const UNPARSABLE_CHAIN_ADDRESS = 'UNPARSABLE_CHAIN_ADDRESS';
@@ -55,32 +49,28 @@ export const ChainAccountI = M.interface('ChainAccount', {
55
49
  prepareTransfer: M.callWhen().returns(InvitationShape),
56
50
  });
57
51
 
58
- export const ConnectionHandlerI = M.interface('ConnectionHandler', {
59
- onOpen: M.callWhen(M.any(), M.string(), M.string(), M.any()).returns(M.any()),
60
- onClose: M.callWhen(M.any(), M.any(), M.any()).returns(M.any()),
61
- onReceive: M.callWhen(M.any(), M.string()).returns(M.any()),
62
- });
52
+ /**
53
+ * @typedef {{
54
+ * port: Port;
55
+ * connection: Remote<Connection> | undefined;
56
+ * localAddress: LocalIbcAddress | undefined;
57
+ * requestedRemoteAddress: string;
58
+ * remoteAddress: RemoteIbcAddress | undefined;
59
+ * chainAddress: ChainAddress | undefined;
60
+ * }} State
61
+ */
63
62
 
64
63
  /** @param {Zone} zone */
65
64
  export const prepareChainAccountKit = zone =>
66
65
  zone.exoClassKit(
67
- 'ChainAccount',
66
+ 'ChainAccountKit',
68
67
  { account: ChainAccountI, connectionHandler: ConnectionHandlerI },
69
68
  /**
70
69
  * @param {Port} port
71
70
  * @param {string} requestedRemoteAddress
72
71
  */
73
72
  (port, requestedRemoteAddress) =>
74
- /**
75
- * @type {{
76
- * port: Port;
77
- * connection: Remote<Connection> | undefined;
78
- * localAddress: LocalIbcAddress | undefined;
79
- * requestedRemoteAddress: string;
80
- * remoteAddress: RemoteIbcAddress | undefined;
81
- * chainAddress: ChainAddress | undefined;
82
- * }}
83
- */ (
73
+ /** @type {State} */ (
84
74
  harden({
85
75
  port,
86
76
  connection: undefined,
@@ -131,8 +121,8 @@ export const prepareChainAccountKit = zone =>
131
121
  if (!connection) throw Fail`connection not available`;
132
122
  return E.when(
133
123
  E(connection).send(makeTxPacket(msgs, opts)),
134
- // if parsePacketAck cannot find a `result` key, it throws
135
- ack => parsePacketAck(ack),
124
+ // if parseTxPacket cannot find a `result` key, it throws
125
+ ack => parseTxPacket(ack),
136
126
  );
137
127
  },
138
128
  /**
@@ -175,9 +165,8 @@ export const prepareChainAccountKit = zone =>
175
165
  this.state.connection = connection;
176
166
  this.state.remoteAddress = remoteAddr;
177
167
  this.state.localAddress = localAddr;
178
- // XXX parseAddress currently throws, should it return '' instead?
179
168
  this.state.chainAddress = harden({
180
- address: parseAddress(remoteAddr) || UNPARSABLE_CHAIN_ADDRESS,
169
+ address: findAddressField(remoteAddr) || UNPARSABLE_CHAIN_ADDRESS,
181
170
  // TODO get this from `Chain` object #9063
182
171
  // XXX how do we get a chainId for an unknown chain? seems it may need to be a user supplied arg
183
172
  chainId: 'FIXME',
@@ -0,0 +1,125 @@
1
+ // @ts-check
2
+ /** @file ICQConnection Exo */
3
+ import { NonNullish } from '@agoric/assert';
4
+ import { makeTracer } from '@agoric/internal';
5
+ import { V as E } from '@agoric/vat-data/vow.js';
6
+ import { M } from '@endo/patterns';
7
+ import { makeQueryPacket, parseQueryPacket } from '../utils/packet.js';
8
+ import { ConnectionHandlerI } from '../typeGuards.js';
9
+
10
+ /**
11
+ * @import {Zone} from '@agoric/base-zone';
12
+ * @import {Connection, Port} from '@agoric/network';
13
+ * @import {Remote} from '@agoric/vow';
14
+ * @import {Base64Any, RequestQueryJson} from '@agoric/cosmic-proto';
15
+ * @import {ResponseQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js';
16
+ * @import {LocalIbcAddress, RemoteIbcAddress} from '@agoric/vats/tools/ibc-utils.js';
17
+ */
18
+
19
+ const { Fail } = assert;
20
+ const trace = makeTracer('Orchestration:ICQConnection');
21
+
22
+ export const ICQMsgShape = M.splitRecord(
23
+ { path: M.string(), data: M.string() },
24
+ { height: M.string(), prove: M.boolean() },
25
+ );
26
+
27
+ export const ICQConnectionI = M.interface('ICQConnection', {
28
+ getLocalAddress: M.call().returns(M.string()),
29
+ getRemoteAddress: M.call().returns(M.string()),
30
+ query: M.call(M.arrayOf(ICQMsgShape)).returns(M.promise()),
31
+ });
32
+
33
+ /**
34
+ * @typedef {{
35
+ * port: Port;
36
+ * connection: Remote<Connection> | undefined;
37
+ * localAddress: LocalIbcAddress | undefined;
38
+ * remoteAddress: RemoteIbcAddress | undefined;
39
+ * }} ICQConnectionKitState
40
+ */
41
+
42
+ /**
43
+ * 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}.
44
+ *
45
+ * `icq/v1`, also referred to as `async-icq`, is a protocol for asynchronous queries
46
+ * between IBC-enabled chains. It allows a chain to send queries to another chain
47
+ * and receive responses asynchronously.
48
+ *
49
+ * The ICQ connection kit provides the necessary functionality to establish and manage
50
+ * an ICQ connection between two chains. It includes methods for retrieving the local
51
+ * and remote addresses of the connection, as well as sending queries and handling
52
+ * connection events.
53
+ *
54
+ * @param {Zone} zone
55
+ */
56
+ export const prepareICQConnectionKit = zone =>
57
+ zone.exoClassKit(
58
+ 'ICQConnectionKit',
59
+ { connection: ICQConnectionI, connectionHandler: ConnectionHandlerI },
60
+ /**
61
+ * @param {Port} port
62
+ */
63
+ port =>
64
+ /** @type {ICQConnectionKitState} */ (
65
+ harden({
66
+ port,
67
+ connection: undefined,
68
+ remoteAddress: undefined,
69
+ localAddress: undefined,
70
+ })
71
+ ),
72
+ {
73
+ connection: {
74
+ getLocalAddress() {
75
+ return NonNullish(
76
+ this.state.localAddress,
77
+ 'local address not available',
78
+ );
79
+ },
80
+ getRemoteAddress() {
81
+ return NonNullish(
82
+ this.state.remoteAddress,
83
+ 'remote address not available',
84
+ );
85
+ },
86
+ /**
87
+ * @param {RequestQueryJson[]} msgs
88
+ * @returns {Promise<Base64Any<ResponseQuery>[]>}
89
+ * @throws {Error} if packet fails to send or an error is returned
90
+ */
91
+ query(msgs) {
92
+ const { connection } = this.state;
93
+ if (!connection) throw Fail`connection not available`;
94
+ return E.when(
95
+ E(connection).send(makeQueryPacket(msgs)),
96
+ // if parseTxPacket cannot find a `result` key, it throws
97
+ ack => parseQueryPacket(ack),
98
+ );
99
+ },
100
+ },
101
+ connectionHandler: {
102
+ /**
103
+ * @param {Remote<Connection>} connection
104
+ * @param {LocalIbcAddress} localAddr
105
+ * @param {RemoteIbcAddress} remoteAddr
106
+ */
107
+ async onOpen(connection, localAddr, remoteAddr) {
108
+ trace(`ICQ Channel Opened for ${localAddr} at ${remoteAddr}`);
109
+ this.state.connection = connection;
110
+ this.state.remoteAddress = remoteAddr;
111
+ this.state.localAddress = localAddr;
112
+ },
113
+ async onClose(_connection, reason) {
114
+ trace(`ICQ Channel closed. Reason: ${reason}`);
115
+ },
116
+ async onReceive(connection, bytes) {
117
+ trace(`ICQ Channel onReceive`, connection, bytes);
118
+ return '';
119
+ },
120
+ },
121
+ },
122
+ );
123
+
124
+ /** @typedef {ReturnType<ReturnType<typeof prepareICQConnectionKit>>} ICQConnectionKit */
125
+ /** @typedef {ICQConnectionKit['connection']} ICQConnection */
@@ -8,6 +8,10 @@ import {
8
8
  MsgDelegate,
9
9
  MsgDelegateResponse,
10
10
  } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
11
+ import {
12
+ QueryBalanceRequest,
13
+ QueryBalanceResponse,
14
+ } from '@agoric/cosmic-proto/cosmos/bank/v1beta1/query.js';
11
15
  import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
12
16
  import { AmountShape } from '@agoric/ertp';
13
17
  import { makeTracer } from '@agoric/internal';
@@ -16,11 +20,13 @@ import { M, prepareExoClassKit } from '@agoric/vat-data';
16
20
  import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
17
21
  import { decodeBase64 } from '@endo/base64';
18
22
  import { E } from '@endo/far';
23
+ import { toRequestQueryJson } from '@agoric/cosmic-proto';
24
+ import { ChainAddressShape, CoinShape } from '../typeGuards.js';
19
25
 
20
26
  /**
21
- * @import { ChainAccount, ChainAddress, ChainAmount, CosmosValidatorAddress } from '../types.js';
22
- * @import { RecorderKit, MakeRecorderKit } from '@agoric/zoe/src/contractSupport/recorder.js';
23
- * @import { Baggage } from '@agoric/swingset-liveslots';
27
+ * @import {ChainAccount, ChainAddress, ChainAmount, CosmosValidatorAddress, ICQConnection} from '../types.js';
28
+ * @import {RecorderKit, MakeRecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js';
29
+ * @import {Baggage} from '@agoric/swingset-liveslots';
24
30
  * @import {AnyJson} from '@agoric/cosmic-proto';
25
31
  */
26
32
 
@@ -37,13 +43,17 @@ const { Fail } = assert;
37
43
  * topicKit: RecorderKit<StakingAccountNotification>;
38
44
  * account: ChainAccount;
39
45
  * chainAddress: ChainAddress;
46
+ * icqConnection: ICQConnection;
47
+ * bondDenom: string;
40
48
  * }} State
41
49
  */
42
50
 
43
- const HolderI = M.interface('holder', {
51
+ export const ChainAccountHolderI = M.interface('ChainAccountHolder', {
44
52
  getPublicTopics: M.call().returns(TopicsRecordShape),
45
- delegate: M.callWhen(M.string(), AmountShape).returns(M.record()),
46
- withdrawReward: M.callWhen(M.string()).returns(M.array()),
53
+ getAddress: M.call().returns(ChainAddressShape),
54
+ getBalance: M.callWhen().optional(M.string()).returns(CoinShape),
55
+ delegate: M.callWhen(ChainAddressShape, AmountShape).returns(M.record()),
56
+ withdrawReward: M.callWhen(ChainAddressShape).returns(M.arrayOf(CoinShape)),
47
57
  });
48
58
 
49
59
  /** @type {{ [name: string]: [description: string, valueShape: Pattern] }} */
@@ -89,10 +99,10 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
89
99
  'Staking Account Holder',
90
100
  {
91
101
  helper: UnguardedHelperI,
92
- holder: HolderI,
102
+ holder: ChainAccountHolderI,
93
103
  invitationMakers: M.interface('invitationMakers', {
94
- Delegate: M.call(M.string(), AmountShape).returns(M.promise()),
95
- WithdrawReward: M.call(M.string()).returns(M.promise()),
104
+ Delegate: M.call(ChainAddressShape, AmountShape).returns(M.promise()),
105
+ WithdrawReward: M.call(ChainAddressShape).returns(M.promise()),
96
106
  CloseAccount: M.call().returns(M.promise()),
97
107
  TransferAccount: M.call().returns(M.promise()),
98
108
  }),
@@ -101,13 +111,15 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
101
111
  * @param {ChainAccount} account
102
112
  * @param {StorageNode} storageNode
103
113
  * @param {ChainAddress} chainAddress
114
+ * @param {ICQConnection} icqConnection
115
+ * @param {string} bondDenom e.g. 'uatom'
104
116
  * @returns {State}
105
117
  */
106
- (account, storageNode, chainAddress) => {
118
+ (account, storageNode, chainAddress, icqConnection, bondDenom) => {
107
119
  // must be the fully synchronous maker because the kit is held in durable state
108
120
  const topicKit = makeRecorderKit(storageNode, PUBLIC_TOPICS.account[1]);
109
121
 
110
- return { account, chainAddress, topicKit };
122
+ return { account, chainAddress, topicKit, icqConnection, bondDenom };
111
123
  },
112
124
  {
113
125
  helper: {
@@ -126,24 +138,24 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
126
138
  invitationMakers: {
127
139
  /**
128
140
  *
129
- * @param {string} validatorAddress
141
+ * @param {CosmosValidatorAddress} validator
130
142
  * @param {Amount<'nat'>} amount
131
143
  */
132
- Delegate(validatorAddress, amount) {
133
- trace('Delegate', validatorAddress, amount);
144
+ Delegate(validator, amount) {
145
+ trace('Delegate', validator, amount);
134
146
 
135
147
  return zcf.makeInvitation(async seat => {
136
148
  seat.exit();
137
- return this.facets.holder.delegate(validatorAddress, amount);
149
+ return this.facets.holder.delegate(validator, amount);
138
150
  }, 'Delegate');
139
151
  },
140
- /** @param {string} validatorAddress */
141
- WithdrawReward(validatorAddress) {
142
- trace('WithdrawReward', validatorAddress);
152
+ /** @param {CosmosValidatorAddress} validator */
153
+ WithdrawReward(validator) {
154
+ trace('WithdrawReward', validator);
143
155
 
144
156
  return zcf.makeInvitation(async seat => {
145
157
  seat.exit();
146
- return this.facets.holder.withdrawReward(validatorAddress);
158
+ return this.facets.holder.withdrawReward(validator);
147
159
  }, 'WithdrawReward');
148
160
  },
149
161
  CloseAccount() {
@@ -170,20 +182,23 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
170
182
  },
171
183
  // TODO move this beneath the Orchestration abstraction,
172
184
  // to the OrchestrationAccount provided by makeAccount()
185
+ /** @returns {ChainAddress} */
186
+ getAddress() {
187
+ return this.state.chainAddress;
188
+ },
173
189
  /**
174
190
  * _Assumes users has already sent funds to their ICA, until #9193
175
- * @param {string} validatorAddress
191
+ * @param {CosmosValidatorAddress} validator
176
192
  * @param {Amount<'nat'>} ertpAmount
177
193
  */
178
- async delegate(validatorAddress, ertpAmount) {
179
- trace('delegate', validatorAddress, ertpAmount);
194
+ async delegate(validator, ertpAmount) {
195
+ trace('delegate', validator, ertpAmount);
180
196
 
181
- // FIXME get values from proposal or args
182
- // FIXME brand handling and amount scaling
197
+ // FIXME brand handling and amount scaling #9211
183
198
  trace('TODO: handle brand', ertpAmount);
184
199
  const amount = {
185
200
  amount: String(ertpAmount.value),
186
- denom: 'uatom',
201
+ denom: this.state.bondDenom,
187
202
  };
188
203
 
189
204
  const account = this.facets.helper.owned();
@@ -193,7 +208,7 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
193
208
  toAnyJSON(
194
209
  MsgDelegate.toProtoMsg({
195
210
  delegatorAddress,
196
- validatorAddress,
211
+ validatorAddress: validator.address,
197
212
  amount,
198
213
  }),
199
214
  ),
@@ -204,15 +219,15 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
204
219
  },
205
220
 
206
221
  /**
207
- * @param {string} validatorAddress
222
+ * @param {CosmosValidatorAddress} validator
208
223
  * @returns {Promise<ChainAmount[]>}
209
224
  */
210
- async withdrawReward(validatorAddress) {
225
+ async withdrawReward(validator) {
211
226
  const { chainAddress } = this.state;
212
- assert.typeof(validatorAddress, 'string');
227
+ assert.typeof(validator.address, 'string');
213
228
  const msg = MsgWithdrawDelegatorReward.toProtoMsg({
214
229
  delegatorAddress: chainAddress.address,
215
- validatorAddress,
230
+ validatorAddress: validator.address,
216
231
  });
217
232
  const account = this.facets.helper.owned();
218
233
  const result = await E(account).executeEncodedTx([toAnyJSON(msg)]);
@@ -222,9 +237,35 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
222
237
  );
223
238
  return harden(coins.map(toChainAmount));
224
239
  },
240
+ /**
241
+ * @param {ChainAmount['denom']} [denom] - defaults to bondDenom
242
+ * @returns {Promise<ChainAmount>}
243
+ */
244
+ async getBalance(denom) {
245
+ const { chainAddress, icqConnection, bondDenom } = this.state;
246
+ denom ||= bondDenom;
247
+ assert.typeof(denom, 'string');
248
+
249
+ const [result] = await E(icqConnection).query([
250
+ toRequestQueryJson(
251
+ QueryBalanceRequest.toProtoMsg({
252
+ address: chainAddress.address,
253
+ denom,
254
+ }),
255
+ ),
256
+ ]);
257
+ if (!result?.key) throw Fail`Error parsing result ${result}`;
258
+ const { balance } = QueryBalanceResponse.decode(
259
+ decodeBase64(result.key),
260
+ );
261
+ if (!balance) throw Fail`Result lacked balance key: ${result}`;
262
+ return harden(toChainAmount(balance));
263
+ },
225
264
  },
226
265
  },
227
266
  );
228
267
  return makeStakingAccountKit;
229
268
  };
269
+
230
270
  /** @typedef {ReturnType<ReturnType<typeof prepareStakingAccountKit>>} StakingAccountKit */
271
+ /** @typedef {StakingAccountKit['holder']} StakingAccounHolder */
@@ -2,9 +2,9 @@
2
2
  import { V as E } from '@agoric/vat-data/vow.js';
3
3
 
4
4
  /**
5
- * @import {Connection, Port, PortAllocator} from '@agoric/network';
6
- * @import { OrchestrationService } from '../service.js'
7
- * @import { OrchestrationVat } from '../vat-orchestration.js'
5
+ * @import {PortAllocator} from '@agoric/network';
6
+ * @import {OrchestrationService} from '../service.js'
7
+ * @import {OrchestrationVat} from '../vat-orchestration.js'
8
8
  */
9
9
 
10
10
  /**
@@ -19,8 +19,7 @@ import { V as E } from '@agoric/vat-data/vow.js';
19
19
  * orchestrationVat: Producer<any>;
20
20
  * };
21
21
  * }} powers
22
- * @param {object} options
23
- * @param {{ orchestrationRef: VatSourceRef }} options.options
22
+ * @param {{ options: { orchestrationRef: VatSourceRef }}} options
24
23
  *
25
24
  * @typedef {{
26
25
  * orchestration: ERef<OrchestrationVat>;
@@ -50,7 +49,7 @@ export const setupOrchestrationVat = async (
50
49
 
51
50
  const portAllocator = await portAllocatorP;
52
51
 
53
- const newOrchestrationKit = await E(vats.orchestration).makeOrchestration({
52
+ const newOrchestrationKit = await E(vats.orchestration).makeOrchestrationKit({
54
53
  portAllocator,
55
54
  });
56
55
 
@@ -84,8 +83,8 @@ export const getManifestForOrchestration = (_powers, { orchestrationRef }) => ({
84
83
  },
85
84
  produce: {
86
85
  orchestration: 'orchestration',
87
- orchestrationKit: 'orchestration',
88
- orchestrationVat: 'orchestration',
86
+ orchestrationKit: 'orchestrationKit',
87
+ orchestrationVat: 'orchestrationVat',
89
88
  },
90
89
  },
91
90
  },
@@ -27,10 +27,14 @@ export const startStakeAtom = async (
27
27
  produce: { stakeAtom: produceInstance },
28
28
  },
29
29
  },
30
- { options: { hostConnectionId, controllerConnectionId } },
30
+ { options: { hostConnectionId, controllerConnectionId, bondDenom } },
31
31
  ) => {
32
32
  const VSTORAGE_PATH = 'stakeAtom';
33
- trace('startStakeAtom', { hostConnectionId, controllerConnectionId });
33
+ trace('startStakeAtom', {
34
+ hostConnectionId,
35
+ controllerConnectionId,
36
+ bondDenom,
37
+ });
34
38
  await null;
35
39
 
36
40
  const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH);
@@ -46,6 +50,7 @@ export const startStakeAtom = async (
46
50
  terms: {
47
51
  hostConnectionId,
48
52
  controllerConnectionId,
53
+ bondDenom,
49
54
  },
50
55
  privateArgs: {
51
56
  orchestration: await orchestration,
package/src/service.js CHANGED
@@ -6,14 +6,19 @@ import '@agoric/network/exported.js';
6
6
 
7
7
  import { V as E } from '@agoric/vat-data/vow.js';
8
8
  import { M } from '@endo/patterns';
9
+ import { Shape as NetworkShape } from '@agoric/network';
9
10
  import { prepareChainAccountKit } from './exos/chainAccountKit.js';
10
- import { makeICAConnectionAddress } from './utils/address.js';
11
+ import { prepareICQConnectionKit } from './exos/icqConnectionKit.js';
12
+ import {
13
+ makeICAChannelAddress,
14
+ makeICQChannelAddress,
15
+ } from './utils/address.js';
11
16
 
12
17
  /**
13
- * @import { PortAllocator} from '@agoric/network';
14
- * @import { IBCConnectionID } from '@agoric/vats';
15
18
  * @import { Zone } from '@agoric/base-zone';
16
- * @import { ChainAccount } from './types.js';
19
+ * @import { Port, PortAllocator } from '@agoric/network';
20
+ * @import { IBCConnectionID } from '@agoric/vats';
21
+ * @import { ICQConnection, ChainAccount, ICQConnectionKit } from './types.js';
17
22
  */
18
23
 
19
24
  const { Fail, bare } = assert;
@@ -34,6 +39,10 @@ const { Fail, bare } = assert;
34
39
  * >} PowerStore
35
40
  */
36
41
 
42
+ /**
43
+ * @typedef {MapStore<IBCConnectionID,ICQConnectionKit>} ICQConnectionStore
44
+ */
45
+
37
46
  /**
38
47
  * @template {keyof OrchestrationPowers} K
39
48
  * @param {PowerStore} powers
@@ -48,18 +57,33 @@ export const OrchestrationI = M.interface('Orchestration', {
48
57
  makeAccount: M.callWhen(M.string(), M.string()).returns(
49
58
  M.remotable('ChainAccount'),
50
59
  ),
60
+ provideICQConnection: M.callWhen(M.string()).returns(
61
+ M.remotable('Connection'),
62
+ ),
51
63
  });
52
64
 
65
+ /** @typedef {{ powers: PowerStore; icqConnections: ICQConnectionStore } } OrchestrationState */
66
+
53
67
  /**
54
68
  * @param {Zone} zone
55
69
  * @param {ReturnType<typeof prepareChainAccountKit>} makeChainAccountKit
70
+ * @param {ReturnType<typeof prepareICQConnectionKit>} makeICQConnectionKit
56
71
  */
57
- const prepareOrchestration = (zone, makeChainAccountKit) =>
72
+ const prepareOrchestrationKit = (
73
+ zone,
74
+ makeChainAccountKit,
75
+ makeICQConnectionKit,
76
+ ) =>
58
77
  zone.exoClassKit(
59
78
  'Orchestration',
60
79
  {
61
80
  self: M.interface('OrchestrationSelf', {
62
- bindPort: M.callWhen().returns(M.remotable()),
81
+ allocateICAControllerPort: M.callWhen().returns(
82
+ NetworkShape.Vow$(NetworkShape.Port),
83
+ ),
84
+ allocateICQControllerPort: M.callWhen().returns(
85
+ NetworkShape.Vow$(NetworkShape.Port),
86
+ ),
63
87
  }),
64
88
  public: OrchestrationI,
65
89
  },
@@ -72,11 +96,16 @@ const prepareOrchestration = (zone, makeChainAccountKit) =>
72
96
  powers.init(/** @type {keyof OrchestrationPowers} */ (name), power);
73
97
  }
74
98
  }
75
- return { powers };
99
+ const icqConnections = zone.detached().mapStore('ICQConnections');
100
+ return /** @type {OrchestrationState} */ ({ powers, icqConnections });
76
101
  },
77
102
  {
78
103
  self: {
79
- async bindPort() {
104
+ async allocateICAControllerPort() {
105
+ const portAllocator = getPower(this.state.powers, 'portAllocator');
106
+ return E(portAllocator).allocateICAControllerPort();
107
+ },
108
+ async allocateICQControllerPort() {
80
109
  const portAllocator = getPower(this.state.powers, 'portAllocator');
81
110
  return E(portAllocator).allocateICAControllerPort();
82
111
  },
@@ -90,9 +119,9 @@ const prepareOrchestration = (zone, makeChainAccountKit) =>
90
119
  * @returns {Promise<ChainAccount>}
91
120
  */
92
121
  async makeAccount(hostConnectionId, controllerConnectionId) {
93
- const port = await this.facets.self.bindPort();
122
+ const port = await this.facets.self.allocateICAControllerPort();
94
123
 
95
- const remoteConnAddr = makeICAConnectionAddress(
124
+ const remoteConnAddr = makeICAChannelAddress(
96
125
  hostConnectionId,
97
126
  controllerConnectionId,
98
127
  );
@@ -104,9 +133,36 @@ const prepareOrchestration = (zone, makeChainAccountKit) =>
104
133
  chainAccountKit.connectionHandler,
105
134
  );
106
135
  // XXX if we fail, should we close the port (if it was created in this flow)?
107
-
108
136
  return chainAccountKit.account;
109
137
  },
138
+ /**
139
+ * @param {IBCConnectionID} controllerConnectionId
140
+ * @returns {Promise<ICQConnection>}
141
+ */
142
+ async provideICQConnection(controllerConnectionId) {
143
+ if (this.state.icqConnections.has(controllerConnectionId)) {
144
+ return this.state.icqConnections.get(controllerConnectionId)
145
+ .connection;
146
+ }
147
+ // allocate a new Port for every Connection
148
+ // TODO #9317 optimize ICQ port allocation
149
+ const port = await this.facets.self.allocateICQControllerPort();
150
+ const remoteConnAddr = makeICQChannelAddress(controllerConnectionId);
151
+ const icqConnectionKit = makeICQConnectionKit(port);
152
+
153
+ // await so we do not return/save a ICQConnection before it successfully instantiates
154
+ await E(port).connect(
155
+ remoteConnAddr,
156
+ icqConnectionKit.connectionHandler,
157
+ );
158
+
159
+ this.state.icqConnections.init(
160
+ controllerConnectionId,
161
+ icqConnectionKit,
162
+ );
163
+
164
+ return icqConnectionKit.connection;
165
+ },
110
166
  },
111
167
  },
112
168
  );
@@ -114,12 +170,17 @@ const prepareOrchestration = (zone, makeChainAccountKit) =>
114
170
  /** @param {Zone} zone */
115
171
  export const prepareOrchestrationTools = zone => {
116
172
  const makeChainAccountKit = prepareChainAccountKit(zone);
117
- const makeOrchestration = prepareOrchestration(zone, makeChainAccountKit);
173
+ const makeICQConnectionKit = prepareICQConnectionKit(zone);
174
+ const makeOrchestrationKit = prepareOrchestrationKit(
175
+ zone,
176
+ makeChainAccountKit,
177
+ makeICQConnectionKit,
178
+ );
118
179
 
119
- return harden({ makeOrchestration });
180
+ return harden({ makeOrchestrationKit });
120
181
  };
121
182
  harden(prepareOrchestrationTools);
122
183
 
123
184
  /** @typedef {ReturnType<typeof prepareOrchestrationTools>} OrchestrationTools */
124
- /** @typedef {ReturnType<OrchestrationTools['makeOrchestration']>} OrchestrationKit */
185
+ /** @typedef {ReturnType<OrchestrationTools['makeOrchestrationKit']>} OrchestrationKit */
125
186
  /** @typedef {OrchestrationKit['public']} OrchestrationService */
@@ -0,0 +1,20 @@
1
+ import { M } from '@endo/patterns';
2
+
3
+ export const ConnectionHandlerI = M.interface('ConnectionHandler', {
4
+ onOpen: M.callWhen(M.any(), M.string(), M.string(), M.any()).returns(M.any()),
5
+ onClose: M.callWhen(M.any(), M.any(), M.any()).returns(M.any()),
6
+ onReceive: M.callWhen(M.any(), M.string()).returns(M.any()),
7
+ });
8
+
9
+ export const ChainAddressShape = {
10
+ address: M.string(),
11
+ chainId: M.string(),
12
+ addressEncoding: M.string(),
13
+ };
14
+
15
+ export const Proto3Shape = {
16
+ typeUrl: M.string(),
17
+ value: M.string(),
18
+ };
19
+
20
+ export const CoinShape = { value: M.bigint(), denom: M.string() };
package/src/types.d.ts CHANGED
@@ -19,6 +19,13 @@ import type {
19
19
  } from '@agoric/vats/tools/ibc-utils.js';
20
20
  import type { Port } from '@agoric/network';
21
21
  import { MsgTransferResponse } from '@agoric/cosmic-proto/ibc/applications/transfer/v1/tx.js';
22
+ import type { IBCConnectionID } from '@agoric/vats';
23
+ import type { ICQConnection } from './exos/icqConnectionKit.js';
24
+
25
+ export type * from './service.js';
26
+ export type * from './vat-orchestration.js';
27
+ export type * from './exos/chainAccountKit.js';
28
+ export type * from './exos/icqConnectionKit.js';
22
29
 
23
30
  /**
24
31
  * static declaration of known chain types will allow type support for
@@ -114,6 +121,10 @@ export interface Orchestrator {
114
121
  getChain: <C extends keyof KnownChains>(chainName: C) => Promise<Chain<C>>;
115
122
 
116
123
  makeLocalAccount: () => Promise<LocalChainAccount>;
124
+ /** Send queries to ibc chains unknown to KnownChains */
125
+ provideICQConnection: (
126
+ controllerConnectionId: IBCConnectionID,
127
+ ) => ICQConnection;
117
128
 
118
129
  /**
119
130
  * For a denom, return information about a denom including the equivalent
@@ -3,7 +3,8 @@ import { Fail } from '@agoric/assert';
3
3
 
4
4
  /**
5
5
  * @import { IBCConnectionID } from '@agoric/vats';
6
- * @import { ChainAddress, CosmosValidatorAddress } from '../types.js';
6
+ * @import { ChainAddress } from '../types.js';
7
+ * @import { RemoteIbcAddress } from '@agoric/vats/tools/ibc-utils.js';
7
8
  */
8
9
 
9
10
  /**
@@ -14,8 +15,9 @@ import { Fail } from '@agoric/assert';
14
15
  * @param {'ordered' | 'unordered'} [opts.ordering] - channel ordering. currently only `ordered` is supported for ics27-1
15
16
  * @param {string} [opts.txType] - default is `sdk_multi_msg`
16
17
  * @param {string} [opts.version] - default is `ics27-1`
18
+ * @returns {RemoteIbcAddress}
17
19
  */
18
- export const makeICAConnectionAddress = (
20
+ export const makeICAChannelAddress = (
19
21
  hostConnectionId,
20
22
  controllerConnectionId,
21
23
  {
@@ -37,15 +39,30 @@ export const makeICAConnectionAddress = (
37
39
  });
38
40
  return `/ibc-hop/${controllerConnectionId}/ibc-port/icahost/${ordering}/${connString}`;
39
41
  };
42
+ harden(makeICAChannelAddress);
43
+
44
+ /**
45
+ * @param {IBCConnectionID} controllerConnectionId
46
+ * @param {{ version?: string }} [opts]
47
+ * @returns {RemoteIbcAddress}
48
+ */
49
+ export const makeICQChannelAddress = (
50
+ controllerConnectionId,
51
+ { version = 'icq-1' } = {},
52
+ ) => {
53
+ controllerConnectionId || Fail`controllerConnectionId is required`;
54
+ return `/ibc-hop/${controllerConnectionId}/ibc-port/icqhost/unordered/${version}`;
55
+ };
56
+ harden(makeICQChannelAddress);
40
57
 
41
58
  /**
42
59
  * Parse a chain address from a remote address string.
43
60
  * Assumes the address string is in a JSON format and contains an "address" field.
44
61
  * This function is designed to be safe against malformed inputs and unexpected data types, and will return `undefined` in those cases.
45
- * @param {string} remoteAddressString - remote address string, including version
46
- * @returns {string | undefined} returns undefined on error
62
+ * @param {RemoteIbcAddress} remoteAddressString - remote address string, including version
63
+ * @returns {ChainAddress['address'] | undefined} returns undefined on error
47
64
  */
48
- export const parseAddress = remoteAddressString => {
65
+ export const findAddressField = remoteAddressString => {
49
66
  try {
50
67
  // Extract JSON version string assuming it's always surrounded by {}
51
68
  const jsonStr = remoteAddressString?.match(/{.*?}/)?.[0];
@@ -55,4 +72,4 @@ export const parseAddress = remoteAddressString => {
55
72
  return undefined;
56
73
  }
57
74
  };
58
- harden(parseAddress);
75
+ harden(findAddressField);
@@ -0,0 +1,110 @@
1
+ // @ts-check
2
+ import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
3
+ import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
4
+ import { RequestQuery } from '@agoric/cosmic-proto/tendermint/abci/types.js';
5
+ import { atob, decodeBase64, encodeBase64 } from '@endo/base64';
6
+ import {
7
+ CosmosQuery,
8
+ CosmosResponse,
9
+ } from '@agoric/cosmic-proto/icq/v1/packet.js';
10
+ import { Type as PacketType } from '@agoric/cosmic-proto/ibc/applications/interchain_accounts/v1/packet.js';
11
+
12
+ /**
13
+ * @import {AnyJson, RequestQueryJson, Base64Any} from '@agoric/cosmic-proto';
14
+ * @import {ResponseQuery} from '@agoric/cosmic-proto/tendermint/abci/types.js';
15
+ * @import {InterchainAccountPacketData} from '@agoric/cosmic-proto/ibc/applications/interchain_accounts/v1/packet.js';
16
+ * @import {InterchainQueryPacketData} from '@agoric/cosmic-proto/icq/v1/packet.js';
17
+ */
18
+
19
+ /**
20
+ * Makes an IBC transaction packet from an array of messages. Expects the `value` of each message
21
+ * to be base64 encoded bytes.
22
+ * Skips checks for malformed messages in favor of interface guards.
23
+ * @param {AnyJson[]} msgs
24
+ * @param {Partial<Omit<TxBody, 'messages'>>} [opts]
25
+ * @returns {string} stringified InterchainAccountPacketData
26
+ * @throws {Error} if malformed messages are provided
27
+ */
28
+ export function makeTxPacket(msgs, opts) {
29
+ const messages = msgs.map(Any.fromJSON);
30
+ const bytes = TxBody.encode(
31
+ TxBody.fromPartial({
32
+ messages,
33
+ ...opts,
34
+ }),
35
+ ).finish();
36
+
37
+ return JSON.stringify(
38
+ /** @type {Base64Any<InterchainAccountPacketData>} */ ({
39
+ type: PacketType.TYPE_EXECUTE_TX,
40
+ data: encodeBase64(bytes),
41
+ memo: '',
42
+ }),
43
+ );
44
+ }
45
+ harden(makeTxPacket);
46
+
47
+ /**
48
+ * Makes an IBC query packet from an array of query messages. Expects the `data` of each message
49
+ * to be base64 encoded bytes.
50
+ * Skips checks for malformed messages in favor of interface guards.
51
+ * @param {RequestQueryJson[]} msgs
52
+ * @returns {string} stringified InterchainQueryPacketData
53
+ * @throws {Error} if malformed messages are provided
54
+ */
55
+ export function makeQueryPacket(msgs) {
56
+ const bytes = CosmosQuery.encode(
57
+ CosmosQuery.fromPartial({
58
+ requests: msgs.map(RequestQuery.fromJSON),
59
+ }),
60
+ ).finish();
61
+
62
+ return JSON.stringify(
63
+ /** @type {Base64Any<InterchainQueryPacketData>} */ ({
64
+ data: encodeBase64(bytes),
65
+ memo: '',
66
+ }),
67
+ );
68
+ }
69
+ harden(makeQueryPacket);
70
+
71
+ /**
72
+ * Looks for a result or error key in the response string, and returns
73
+ * a Base64Bytes string. This string can be decoded using the corresponding
74
+ * Msg*Response object.
75
+ * Error strings seem to be plain text and do not need decoding.
76
+ * @param {string} response
77
+ * @returns {string} - base64 encoded bytes string
78
+ * @throws {Error} if error key is detected in response string, or result key is not found
79
+ */
80
+ export function parseTxPacket(response) {
81
+ const { result, error } = JSON.parse(response);
82
+ if (result) return result;
83
+ else if (error) throw Error(error);
84
+ else throw Error(response);
85
+ }
86
+ harden(parseTxPacket);
87
+
88
+ /**
89
+ * Looks for a result or error key in the response string. If a result is found,
90
+ * `responses` is decoded via `CosmosResponse`. The `key` and `value` fields on the
91
+ * resulting entries are base64 encoded for inter-vat communication. These can be
92
+ * decoded using the corresponding Query*Response objects.
93
+ * Error strings seem to be plain text and do not need decoding.
94
+ * @param {string} response
95
+ * @returns {Base64Any<ResponseQuery>[]}
96
+ * @throws {Error} if error key is detected in response string, or result key is not found
97
+ */
98
+ export function parseQueryPacket(response) {
99
+ const result = parseTxPacket(response);
100
+ const { data } = JSON.parse(atob(result));
101
+ const { responses = [] } = CosmosResponse.decode(decodeBase64(data));
102
+ return harden(
103
+ responses.map(resp => ({
104
+ ...resp,
105
+ key: encodeBase64(resp.key),
106
+ value: encodeBase64(resp.value),
107
+ })),
108
+ );
109
+ }
110
+ harden(parseQueryPacket);
@@ -7,14 +7,14 @@ import { prepareOrchestrationTools } from './service.js';
7
7
 
8
8
  export const buildRootObject = (_vatPowers, _args, baggage) => {
9
9
  const zone = makeDurableZone(baggage);
10
- const { makeOrchestration } = prepareOrchestrationTools(
10
+ const { makeOrchestrationKit } = prepareOrchestrationTools(
11
11
  zone.subZone('orchestration'),
12
12
  );
13
13
 
14
14
  return Far('OrchestrationVat', {
15
15
  /** @param {Partial<OrchestrationPowers>} [initialPowers] */
16
- makeOrchestration(initialPowers = {}) {
17
- return makeOrchestration(initialPowers);
16
+ makeOrchestrationKit(initialPowers = {}) {
17
+ return makeOrchestrationKit(initialPowers);
18
18
  },
19
19
  });
20
20
  };
package/src/utils/tx.js DELETED
@@ -1,48 +0,0 @@
1
- // @ts-check
2
- import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
3
- import { encodeBase64 } from '@endo/base64';
4
- import { Any } from '@agoric/cosmic-proto/google/protobuf/any.js';
5
-
6
- /**
7
- * Makes an IBC packet from an array of messages. Expects the `value` of each message
8
- * to be base64 encoded bytes.
9
- * Skips checks for malformed messages in favor of interface guards.
10
- * @param {import('@agoric/cosmic-proto').AnyJson[]} msgs
11
- * // XXX intellisense does not seem to infer well here
12
- * @param {Omit<TxBody, 'messages'>} [opts]
13
- * @returns {string} - IBC TX packet
14
- * @throws {Error} if malformed messages are provided
15
- */
16
- export function makeTxPacket(msgs, opts) {
17
- const messages = msgs.map(Any.fromJSON);
18
- const bytes = TxBody.encode(
19
- TxBody.fromPartial({
20
- messages,
21
- ...opts,
22
- }),
23
- ).finish();
24
-
25
- return JSON.stringify({
26
- type: 1,
27
- data: encodeBase64(bytes),
28
- memo: '',
29
- });
30
- }
31
- harden(makeTxPacket);
32
-
33
- /**
34
- * Looks for a result or error key in the response string, and returns
35
- * a Base64Bytes string. This string can be decoded using the corresponding
36
- * Msg*Response object.
37
- * Error strings seem to be plain text and do not need decoding.
38
- * @param {string} response
39
- * @returns {string} - base64 encoded bytes string
40
- * @throws {Error} if error key is detected in response string, or result key is not found
41
- */
42
- export function parsePacketAck(response) {
43
- const { result, error } = JSON.parse(response);
44
- if (result) return result;
45
- else if (error) throw Error(error);
46
- else throw Error(response);
47
- }
48
- harden(parsePacketAck);