@agoric/orchestration 0.1.1-dev-81a66f2.0 → 0.1.1-dev-e468caa.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,4 +1,5 @@
1
1
  // eslint-disable-next-line import/export
2
2
  export * from './src/types.js';
3
3
  export * from './src/utils/address.js';
4
+ export * from './src/utils/tx.js';
4
5
  export * from './src/orchestration.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/orchestration",
3
- "version": "0.1.1-dev-81a66f2.0+81a66f2",
3
+ "version": "0.1.1-dev-e468caa.0+e468caa",
4
4
  "description": "Chain abstraction for Agoric's orchestration clients",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -29,16 +29,18 @@
29
29
  },
30
30
  "homepage": "https://github.com/Agoric/agoric-sdk#readme",
31
31
  "dependencies": {
32
- "@agoric/assert": "0.6.1-dev-81a66f2.0+81a66f2",
33
- "@agoric/ertp": "0.16.3-dev-81a66f2.0+81a66f2",
34
- "@agoric/internal": "0.3.3-dev-81a66f2.0+81a66f2",
35
- "@agoric/network": "0.1.1-dev-81a66f2.0+81a66f2",
36
- "@agoric/notifier": "0.6.3-dev-81a66f2.0+81a66f2",
37
- "@agoric/store": "0.9.3-dev-81a66f2.0+81a66f2",
38
- "@agoric/vat-data": "0.5.3-dev-81a66f2.0+81a66f2",
39
- "@agoric/vats": "0.15.2-dev-81a66f2.0+81a66f2",
40
- "@agoric/zoe": "0.26.3-dev-81a66f2.0+81a66f2",
41
- "@agoric/zone": "0.2.3-dev-81a66f2.0+81a66f2",
32
+ "@agoric/assert": "0.6.1-dev-e468caa.0+e468caa",
33
+ "@agoric/cosmic-proto": "0.4.1-dev-e468caa.0+e468caa",
34
+ "@agoric/ertp": "0.16.3-dev-e468caa.0+e468caa",
35
+ "@agoric/internal": "0.3.3-dev-e468caa.0+e468caa",
36
+ "@agoric/network": "0.1.1-dev-e468caa.0+e468caa",
37
+ "@agoric/notifier": "0.6.3-dev-e468caa.0+e468caa",
38
+ "@agoric/store": "0.9.3-dev-e468caa.0+e468caa",
39
+ "@agoric/vat-data": "0.5.3-dev-e468caa.0+e468caa",
40
+ "@agoric/vats": "0.15.2-dev-e468caa.0+e468caa",
41
+ "@agoric/zoe": "0.26.3-dev-e468caa.0+e468caa",
42
+ "@agoric/zone": "0.2.3-dev-e468caa.0+e468caa",
43
+ "@endo/base64": "^1.0.4",
42
44
  "@endo/far": "^1.1.1",
43
45
  "@endo/marshal": "^1.4.1",
44
46
  "@endo/patterns": "^1.3.1"
@@ -80,5 +82,5 @@
80
82
  "typeCoverage": {
81
83
  "atLeast": 96.96
82
84
  },
83
- "gitHead": "81a66f2f88ba213ace95a598e9ef1f95b0ff9e39"
85
+ "gitHead": "e468caafe7b1bb0a4b105d2acadb30874c5c30d5"
84
86
  }
@@ -1,5 +1,6 @@
1
1
  // @ts-check
2
2
  /** @file Use-object for the owner of a localchain account */
3
+ import { typedJson } from '@agoric/cosmic-proto/vatsafe';
3
4
  import { AmountShape } from '@agoric/ertp';
4
5
  import { makeTracer } from '@agoric/internal';
5
6
  import { UnguardedHelperI } from '@agoric/internal/src/typeGuards.js';
@@ -114,7 +115,7 @@ export const prepareAccountHolder = (baggage, makeRecorderKit, zcf) => {
114
115
  // FIXME get values from proposal or args
115
116
  // FIXME brand handling and amount scaling
116
117
  const amount = {
117
- amount: ertpAmount.value,
118
+ amount: String(ertpAmount.value),
118
119
  denom: 'ubld',
119
120
  };
120
121
 
@@ -126,12 +127,11 @@ export const prepareAccountHolder = (baggage, makeRecorderKit, zcf) => {
126
127
  const delegatorAddress = await E(lca).getAddress();
127
128
  trace('delegatorAddress', delegatorAddress);
128
129
  const result = await E(lca).executeTx([
129
- {
130
- '@type': '/cosmos.staking.v1beta1.MsgDelegate',
130
+ typedJson('/cosmos.staking.v1beta1.MsgDelegate', {
131
131
  amount,
132
132
  validatorAddress,
133
133
  delegatorAddress,
134
- },
134
+ }),
135
135
  ]);
136
136
  trace('got result', result);
137
137
  return result;
@@ -2,20 +2,24 @@
2
2
  /**
3
3
  * @file Example contract that uses orchestration
4
4
  */
5
-
5
+ import { makeTracer } from '@agoric/internal';
6
6
  import { makeDurableZone } from '@agoric/zone/durable.js';
7
7
  import { V as E } from '@agoric/vat-data/vow.js';
8
8
  import { M } from '@endo/patterns';
9
+ import { prepareRecorderKitMakers } from '@agoric/zoe/src/contractSupport';
10
+ import { prepareStakingAccountHolder } from './stakingAccountHolder.js';
9
11
 
12
+ const trace = makeTracer('StakeAtom');
10
13
  /**
11
- * @import * as orchestration from '../types'
12
- * @import * as vatData from '@agoric/vat-data'
14
+ * @import { Orchestration } from '../types.js';
15
+ * @import { Baggage } from '@agoric/vat-data';
16
+ * @import { IBCConnectionID } from '@agoric/vats';
13
17
  */
14
18
 
15
19
  /**
16
20
  * @typedef {{
17
- * hostConnectionId: orchestration.ConnectionId;
18
- * controllerConnectionId: orchestration.ConnectionId;
21
+ * hostConnectionId: IBCConnectionID;
22
+ * controllerConnectionId: IBCConnectionID;
19
23
  * }} StakeAtomTerms
20
24
  */
21
25
 
@@ -23,26 +27,66 @@ import { M } from '@endo/patterns';
23
27
  *
24
28
  * @param {ZCF<StakeAtomTerms>} zcf
25
29
  * @param {{
26
- * orchestration: orchestration.Orchestration;
30
+ * orchestration: Orchestration;
31
+ * storageNode: StorageNode;
32
+ * marshaller: Marshaller;
27
33
  * }} privateArgs
28
- * @param {vatData.Baggage} baggage
34
+ * @param {Baggage} baggage
29
35
  */
30
36
  export const start = async (zcf, privateArgs, baggage) => {
31
37
  const { hostConnectionId, controllerConnectionId } = zcf.getTerms();
32
- const { orchestration } = privateArgs;
38
+ const { orchestration, marshaller, storageNode } = privateArgs;
33
39
 
34
40
  const zone = makeDurableZone(baggage);
35
41
 
42
+ const { makeRecorderKit } = prepareRecorderKitMakers(baggage, marshaller);
43
+
44
+ const makeStakingAccountHolder = prepareStakingAccountHolder(
45
+ baggage,
46
+ makeRecorderKit,
47
+ zcf,
48
+ );
49
+
50
+ async function createAccount() {
51
+ const account = await E(orchestration).createAccount(
52
+ hostConnectionId,
53
+ controllerConnectionId,
54
+ );
55
+ const accountAddress = await E(account).getAccountAddress();
56
+ trace('account address', accountAddress);
57
+ const { holder, invitationMakers } = makeStakingAccountHolder(
58
+ account,
59
+ storageNode,
60
+ accountAddress,
61
+ );
62
+ return {
63
+ publicSubscribers: holder.getPublicTopics(),
64
+ invitationMakers,
65
+ account: holder,
66
+ };
67
+ }
68
+
36
69
  const publicFacet = zone.exo(
37
70
  'StakeAtom',
38
71
  M.interface('StakeAtomI', {
39
72
  createAccount: M.callWhen().returns(M.remotable('ChainAccount')),
73
+ makeCreateAccountInvitation: M.call().returns(M.promise()),
40
74
  }),
41
75
  {
42
76
  async createAccount() {
43
- return E(orchestration).createAccount(
44
- hostConnectionId,
45
- controllerConnectionId,
77
+ trace('createAccount');
78
+ return createAccount().then(({ account }) => account);
79
+ },
80
+ makeCreateAccountInvitation() {
81
+ trace('makeCreateAccountInvitation');
82
+ return zcf.makeInvitation(
83
+ async seat => {
84
+ seat.exit();
85
+ return createAccount();
86
+ },
87
+ 'wantStakingAccount',
88
+ undefined,
89
+ undefined,
46
90
  );
47
91
  },
48
92
  },
@@ -50,3 +94,5 @@ export const start = async (zcf, privateArgs, baggage) => {
50
94
 
51
95
  return { publicFacet };
52
96
  };
97
+
98
+ /** @typedef {typeof start} StakeAtomSF */
@@ -0,0 +1,192 @@
1
+ // @ts-check
2
+ /** @file Use-object for the owner of a staking account */
3
+ import {
4
+ MsgDelegate,
5
+ MsgDelegateResponse,
6
+ } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
7
+ import { AmountShape } from '@agoric/ertp';
8
+ import { makeTracer } from '@agoric/internal';
9
+ import { UnguardedHelperI } from '@agoric/internal/src/typeGuards.js';
10
+ import { M, prepareExoClassKit } from '@agoric/vat-data';
11
+ import { TopicsRecordShape } from '@agoric/zoe/src/contractSupport/index.js';
12
+ import { decodeBase64 } from '@endo/base64';
13
+ import { E } from '@endo/far';
14
+ import { txToBase64 } from '../utils/tx.js';
15
+
16
+ /**
17
+ * @import { ChainAccount, ChainAddress } from '../types.js';
18
+ * @import { RecorderKit, MakeRecorderKit } from '@agoric/zoe/src/contractSupport/recorder.js';
19
+ * @import { Baggage } from '@agoric/swingset-liveslots';
20
+ */
21
+
22
+ const trace = makeTracer('StakingAccountHolder');
23
+
24
+ const { Fail } = assert;
25
+ /**
26
+ * @typedef {object} StakingAccountNotification
27
+ * @property {string} address
28
+ */
29
+
30
+ /**
31
+ * @typedef {{
32
+ * topicKit: RecorderKit<StakingAccountNotification>;
33
+ * account: ChainAccount;
34
+ * chainAddress: ChainAddress;
35
+ * }} State
36
+ */
37
+
38
+ const HolderI = M.interface('holder', {
39
+ getPublicTopics: M.call().returns(TopicsRecordShape),
40
+ makeDelegateInvitation: M.call(M.string(), AmountShape).returns(M.promise()),
41
+ makeCloseAccountInvitation: M.call().returns(M.promise()),
42
+ makeTransferAccountInvitation: M.call().returns(M.promise()),
43
+ delegate: M.callWhen(M.string(), AmountShape).returns(M.string()),
44
+ });
45
+
46
+ /** @type {{ [name: string]: [description: string, valueShape: Pattern] }} */
47
+ const PUBLIC_TOPICS = {
48
+ account: ['Staking Account holder status', M.any()],
49
+ };
50
+
51
+ /**
52
+ * @param {Baggage} baggage
53
+ * @param {MakeRecorderKit} makeRecorderKit
54
+ * @param {ZCF} zcf
55
+ */
56
+ export const prepareStakingAccountHolder = (baggage, makeRecorderKit, zcf) => {
57
+ const makeAccountHolderKit = prepareExoClassKit(
58
+ baggage,
59
+ 'Staking Account Holder',
60
+ {
61
+ helper: UnguardedHelperI,
62
+ holder: HolderI,
63
+ invitationMakers: M.interface('invitationMakers', {
64
+ Delegate: HolderI.payload.methodGuards.makeDelegateInvitation,
65
+ CloseAccount: HolderI.payload.methodGuards.makeCloseAccountInvitation,
66
+ TransferAccount:
67
+ HolderI.payload.methodGuards.makeTransferAccountInvitation,
68
+ }),
69
+ },
70
+ /**
71
+ * @param {ChainAccount} account
72
+ * @param {StorageNode} storageNode
73
+ * @param {ChainAddress} chainAddress
74
+ * @returns {State}
75
+ */
76
+ (account, storageNode, chainAddress) => {
77
+ // must be the fully synchronous maker because the kit is held in durable state
78
+ const topicKit = makeRecorderKit(storageNode, PUBLIC_TOPICS.account[1]);
79
+
80
+ return { account, chainAddress, topicKit };
81
+ },
82
+ {
83
+ helper: {
84
+ /** @throws if this holder no longer owns the account */
85
+ owned() {
86
+ const { account } = this.state;
87
+ if (!account) {
88
+ throw Fail`Using account holder after transfer`;
89
+ }
90
+ return account;
91
+ },
92
+ getUpdater() {
93
+ return this.state.topicKit.recorder;
94
+ },
95
+ /**
96
+ * _Assumes users has already sent funds to their ICA, until #9193
97
+ * @param {string} validatorAddress
98
+ * @param {Amount<'nat'>} ertpAmount
99
+ */
100
+ async delegate(validatorAddress, ertpAmount) {
101
+ // FIXME get values from proposal or args
102
+ // FIXME brand handling and amount scaling
103
+ const amount = {
104
+ amount: String(ertpAmount.value),
105
+ denom: 'uatom',
106
+ };
107
+
108
+ const account = this.facets.helper.owned();
109
+ const delegatorAddress = this.state.chainAddress;
110
+
111
+ const result = await E(account).executeEncodedTx([
112
+ txToBase64(
113
+ MsgDelegate.toProtoMsg({
114
+ delegatorAddress,
115
+ validatorAddress,
116
+ amount,
117
+ }),
118
+ ),
119
+ ]);
120
+
121
+ if (!result) throw Fail`Failed to delegate.`;
122
+ try {
123
+ const decoded = MsgDelegateResponse.decode(decodeBase64(result));
124
+ if (JSON.stringify(decoded) === '{}') return 'Success';
125
+ throw Fail`Unexpected response: ${result}`;
126
+ } catch (e) {
127
+ throw Fail`Unable to decode result: ${result}`;
128
+ }
129
+ },
130
+ },
131
+ invitationMakers: {
132
+ Delegate(validatorAddress, amount) {
133
+ return this.facets.holder.makeDelegateInvitation(
134
+ validatorAddress,
135
+ amount,
136
+ );
137
+ },
138
+ CloseAccount() {
139
+ return this.facets.holder.makeCloseAccountInvitation();
140
+ },
141
+ TransferAccount() {
142
+ return this.facets.holder.makeTransferAccountInvitation();
143
+ },
144
+ },
145
+ holder: {
146
+ getPublicTopics() {
147
+ const { topicKit } = this.state;
148
+ return harden({
149
+ account: {
150
+ description: PUBLIC_TOPICS.account[0],
151
+ subscriber: topicKit.subscriber,
152
+ storagePath: topicKit.recorder.getStoragePath(),
153
+ },
154
+ });
155
+ },
156
+ /**
157
+ *
158
+ * @param {string} validatorAddress
159
+ * @param {Amount<'nat'>} ertpAmount
160
+ */
161
+ async delegate(validatorAddress, ertpAmount) {
162
+ trace('delegate', validatorAddress, ertpAmount);
163
+ return this.facets.helper.delegate(validatorAddress, ertpAmount);
164
+ },
165
+ /**
166
+ *
167
+ * @param {string} validatorAddress
168
+ * @param {Amount<'nat'>} ertpAmount
169
+ */
170
+ makeDelegateInvitation(validatorAddress, ertpAmount) {
171
+ trace('makeDelegateInvitation', validatorAddress, ertpAmount);
172
+
173
+ return zcf.makeInvitation(async seat => {
174
+ seat.exit();
175
+ return this.facets.helper.delegate(validatorAddress, ertpAmount);
176
+ }, 'Delegate');
177
+ },
178
+ makeCloseAccountInvitation() {
179
+ throw Error('not yet implemented');
180
+ },
181
+ /**
182
+ * Starting a transfer revokes the account holder. The associated updater
183
+ * will get a special notification that the account is being transferred.
184
+ */
185
+ makeTransferAccountInvitation() {
186
+ throw Error('not yet implemented');
187
+ },
188
+ },
189
+ },
190
+ );
191
+ return makeAccountHolderKit;
192
+ };
@@ -5,24 +5,27 @@ import { makeTracer } from '@agoric/internal';
5
5
  import { V as E } from '@agoric/vat-data/vow.js';
6
6
  import { M } from '@endo/patterns';
7
7
  import { makeICAConnectionAddress, parseAddress } from './utils/address.js';
8
+ import { makeTxPacket, parsePacketAck } from './utils/tx.js';
8
9
  import '@agoric/network/exported.js';
9
10
 
10
11
  /**
11
- * @import { ConnectionId } from './types';
12
+ * @import { AttenuatedNetwork } from './types.js';
13
+ * @import { IBCConnectionID } from '@agoric/vats';
12
14
  * @import { Zone } from '@agoric/base-zone';
15
+ * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
13
16
  */
14
17
 
15
18
  const { Fail, bare } = assert;
16
19
  const trace = makeTracer('Orchestration');
17
20
 
21
+ /** @import {Proto3Msg} from './utils/tx.js'; */
22
+
18
23
  // TODO improve me
19
24
  /** @typedef {string} ChainAddress */
20
25
 
21
26
  /**
22
27
  * @typedef {object} OrchestrationPowers
23
- * @property {ERef<
24
- * import('@agoric/orchestration/src/types').AttenuatedNetwork
25
- * >} network
28
+ * @property {ERef<AttenuatedNetwork>} network
26
29
  */
27
30
 
28
31
  /**
@@ -46,11 +49,19 @@ const getPower = (powers, name) => {
46
49
  return /** @type {OrchestrationPowers[K]} */ (powers.get(name));
47
50
  };
48
51
 
52
+ export const Proto3Shape = {
53
+ typeUrl: M.string(),
54
+ value: M.string(),
55
+ };
56
+
49
57
  export const ChainAccountI = M.interface('ChainAccount', {
50
58
  getAccountAddress: M.call().returns(M.string()),
51
59
  getLocalAddress: M.call().returns(M.string()),
52
60
  getRemoteAddress: M.call().returns(M.string()),
53
61
  getPort: M.call().returns(M.remotable('Port')),
62
+ executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
63
+ .optional(M.record())
64
+ .returns(M.promise()),
54
65
  close: M.callWhen().returns(M.string()),
55
66
  });
56
67
 
@@ -73,7 +84,7 @@ const prepareChainAccount = zone =>
73
84
  /**
74
85
  * @type {{
75
86
  * port: Port;
76
- * connection: Connection | undefined;
87
+ * connection: Remote<Connection> | undefined;
77
88
  * localAddress: string | undefined;
78
89
  * requestedRemoteAddress: string;
79
90
  * remoteAddress: string | undefined;
@@ -112,24 +123,34 @@ const prepareChainAccount = zone =>
112
123
  getPort() {
113
124
  return this.state.port;
114
125
  },
126
+ /**
127
+ * @param {Proto3Msg[]} msgs
128
+ * @param {Omit<TxBody, 'messages'>} [opts]
129
+ * @returns {Promise<string>} - base64 encoded bytes string. Can be decoded using the corresponding `Msg*Response` object.
130
+ * @throws {Error} if packet fails to send or an error is returned
131
+ */
132
+ executeEncodedTx(msgs, opts) {
133
+ const { connection } = this.state;
134
+ if (!connection) throw Fail`connection not available`;
135
+ return E.when(
136
+ E(connection).send(makeTxPacket(msgs, opts)),
137
+ // if parsePacketAck cannot find a `result` key, it throws
138
+ ack => parsePacketAck(ack),
139
+ );
140
+ },
115
141
  async close() {
116
142
  /// XXX what should the behavior be here? and `onClose`?
117
143
  // - retrieve assets?
118
144
  // - revoke the port?
119
145
  const { connection } = this.state;
120
146
  if (!connection) throw Fail`connection not available`;
121
- await null;
122
- try {
123
- await E(connection).close();
124
- } catch (e) {
125
- throw Fail`Failed to close connection: ${e}`;
126
- }
147
+ await E(connection).close();
127
148
  return 'Connection closed';
128
149
  },
129
150
  },
130
151
  connectionHandler: {
131
152
  /**
132
- * @param {Connection} connection
153
+ * @param {Remote<Connection>} connection
133
154
  * @param {string} localAddr
134
155
  * @param {string} remoteAddr
135
156
  */
@@ -188,18 +209,18 @@ const prepareOrchestration = (zone, createChainAccount) =>
188
209
  self: {
189
210
  async bindPort() {
190
211
  const network = getPower(this.state.powers, 'network');
191
- const port = await E(network)
192
- .bind(`/ibc-port/icacontroller-${this.state.icaControllerNonce}`)
193
- .catch(e => Fail`Failed to bind port: ${e}`);
212
+ const port = await E(network).bind(
213
+ `/ibc-port/icacontroller-${this.state.icaControllerNonce}`,
214
+ );
194
215
  this.state.icaControllerNonce += 1;
195
216
  return port;
196
217
  },
197
218
  },
198
219
  public: {
199
220
  /**
200
- * @param {ConnectionId} hostConnectionId
221
+ * @param {IBCConnectionID} hostConnectionId
201
222
  * the counterparty connection_id
202
- * @param {ConnectionId} controllerConnectionId
223
+ * @param {IBCConnectionID} controllerConnectionId
203
224
  * self connection_id
204
225
  * @returns {Promise<ChainAccount>}
205
226
  */
@@ -213,10 +234,8 @@ const prepareOrchestration = (zone, createChainAccount) =>
213
234
  const chainAccount = createChainAccount(port, remoteConnAddr);
214
235
 
215
236
  // await so we do not return a ChainAccount before it successfully instantiates
216
- await E(port)
217
- .connect(remoteConnAddr, chainAccount.connectionHandler)
218
- // XXX if we fail, should we close the port (if it was created in this flow)?
219
- .catch(e => Fail`Failed to create ICA connection: ${bare(e)}`);
237
+ await E(port).connect(remoteConnAddr, chainAccount.connectionHandler);
238
+ // XXX if we fail, should we close the port (if it was created in this flow)?
220
239
 
221
240
  return chainAccount.account;
222
241
  },
@@ -1,16 +1,25 @@
1
1
  // @ts-check
2
2
  import { makeTracer } from '@agoric/internal';
3
+ import { makeStorageNodeChild } from '@agoric/internal/src/lib-chainStorage.js';
3
4
  import { E } from '@endo/far';
4
5
 
6
+ /** @import { StakeAtomSF, StakeAtomTerms} from '../contracts/stakeAtom.contract' */
7
+
5
8
  const trace = makeTracer('StartStakeAtom', true);
6
9
 
7
10
  /**
8
11
  * @param {BootstrapPowers & { installation: {consume: {stakeAtom: Installation<import('../contracts/stakeAtom.contract.js').start>}}}} powers
9
- * @param {{options: import('../contracts/stakeAtom.contract.js').StakeAtomTerms}} options
12
+ * @param {{options: StakeAtomTerms }} options
10
13
  */
11
14
  export const startStakeAtom = async (
12
15
  {
13
- consume: { orchestration, startUpgradable },
16
+ consume: {
17
+ agoricNames,
18
+ board,
19
+ chainStorage,
20
+ orchestration,
21
+ startUpgradable,
22
+ },
14
23
  installation: {
15
24
  consume: { stakeAtom },
16
25
  },
@@ -20,19 +29,28 @@ export const startStakeAtom = async (
20
29
  },
21
30
  { options: { hostConnectionId, controllerConnectionId } },
22
31
  ) => {
32
+ const VSTORAGE_PATH = 'stakeAtom';
23
33
  trace('startStakeAtom', { hostConnectionId, controllerConnectionId });
24
34
  await null;
25
35
 
26
- /** @type {StartUpgradableOpts<import('../contracts/stakeAtom.contract.js').start>} */
36
+ const storageNode = await makeStorageNodeChild(chainStorage, VSTORAGE_PATH);
37
+ const marshaller = await E(board).getPublishingMarshaller();
38
+ const atomIssuer = await E(agoricNames).lookup('issuer', 'ATOM');
39
+ trace('ATOM Issuer', atomIssuer);
40
+
41
+ /** @type {StartUpgradableOpts<StakeAtomSF>} */
27
42
  const startOpts = {
28
43
  label: 'stakeAtom',
29
44
  installation: stakeAtom,
45
+ issuerKeywordRecord: harden({ ATOM: atomIssuer }),
30
46
  terms: {
31
47
  hostConnectionId,
32
48
  controllerConnectionId,
33
49
  },
34
50
  privateArgs: {
35
51
  orchestration: await orchestration,
52
+ storageNode,
53
+ marshaller,
36
54
  },
37
55
  };
38
56
 
@@ -49,6 +67,9 @@ export const getManifestForStakeAtom = (
49
67
  manifest: {
50
68
  [startStakeAtom.name]: {
51
69
  consume: {
70
+ agoricNames: true,
71
+ board: true,
72
+ chainStorage: true,
52
73
  orchestration: true,
53
74
  startUpgradable: true,
54
75
  },
package/src/types.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { RouterProtocol } from '@agoric/network/src/router';
2
2
 
3
- export type ConnectionId = `connection-${number}`;
4
-
5
3
  export type AttenuatedNetwork = Pick<RouterProtocol, 'bind'>;
6
4
 
7
5
  export type * from './orchestration.js';
8
6
  export type * from './vat-orchestration.js';
7
+ export type * from './utils/tx.js';
@@ -1,11 +1,11 @@
1
1
  // @ts-check
2
2
  import { Fail } from '@agoric/assert';
3
3
 
4
- /** @import { ConnectionId } from '../types'; */
4
+ /** @import { IBCConnectionID } from '@agoric/vats'; */
5
5
 
6
6
  /**
7
- * @param {ConnectionId} hostConnectionId Counterpart Connection ID
8
- * @param {ConnectionId} controllerConnectionId Self Connection ID
7
+ * @param {IBCConnectionID} hostConnectionId Counterpart Connection ID
8
+ * @param {IBCConnectionID} controllerConnectionId Self Connection ID
9
9
  * @param {object} [opts]
10
10
  * @param {string} [opts.encoding] - message encoding format for the channel. default is `proto3`
11
11
  * @param {'ordered' | 'unordered'} [opts.ordering] - channel ordering. currently only `ordered` is supported for ics27-1
@@ -0,0 +1,65 @@
1
+ // @ts-check
2
+ import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
3
+ import { decodeBase64, encodeBase64 } from '@endo/base64';
4
+
5
+ /** @typedef {{ typeUrl: string; value: string; }} Proto3Msg */
6
+
7
+ /**
8
+ * Makes an IBC packet from an array of messages. Expects the `value` of each message
9
+ * to be base64 encoded bytes.
10
+ * Skips checks for malformed messages in favor of interface guards.
11
+ * @param {Proto3Msg[]} msgs
12
+ * // XXX intellisense does not seem to infer well here
13
+ * @param {Omit<TxBody, 'messages'>} [opts]
14
+ * @returns {string} - IBC TX packet
15
+ * @throws {Error} if malformed messages are provided
16
+ */
17
+ export function makeTxPacket(msgs, opts) {
18
+ const messages = msgs.map(msg => ({
19
+ typeUrl: msg.typeUrl,
20
+ value: decodeBase64(msg.value),
21
+ }));
22
+ const bytes = TxBody.encode(
23
+ TxBody.fromPartial({
24
+ messages,
25
+ ...opts,
26
+ }),
27
+ ).finish();
28
+
29
+ return JSON.stringify({
30
+ type: 1,
31
+ data: encodeBase64(bytes),
32
+ memo: '',
33
+ });
34
+ }
35
+ harden(makeTxPacket);
36
+
37
+ /**
38
+ * base64 encode an EncodeObject for cross-vat communication
39
+ * @param {{ typeUrl: string, value: Uint8Array }} tx
40
+ * @returns {Proto3Msg}
41
+ */
42
+ export function txToBase64(tx) {
43
+ return {
44
+ typeUrl: tx.typeUrl,
45
+ value: encodeBase64(tx.value),
46
+ };
47
+ }
48
+ harden(txToBase64);
49
+
50
+ /**
51
+ * Looks for a result or error key in the response string, and returns
52
+ * a Base64Bytes string. This string can be decoded using the corresponding
53
+ * Msg*Response object.
54
+ * Error strings seem to be plain text and do not need decoding.
55
+ * @param {string} response
56
+ * @returns {string} - base64 encoded bytes string
57
+ * @throws {Error} if error key is detected in response string, or result key is not found
58
+ */
59
+ export function parsePacketAck(response) {
60
+ const { result, error } = JSON.parse(response);
61
+ if (result) return result;
62
+ else if (error) throw Error(error);
63
+ else throw Error(response);
64
+ }
65
+ harden(parsePacketAck);