@agoric/orchestration 0.1.1-calypso-dev-84eb287.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.
Files changed (120) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +5 -0
  3. package/index.d.ts +4 -0
  4. package/index.js +7 -0
  5. package/package.json +95 -0
  6. package/src/chain-info.d.ts +2318 -0
  7. package/src/chain-info.d.ts.map +1 -0
  8. package/src/chain-info.js +114 -0
  9. package/src/cosmos-api.d.ts +174 -0
  10. package/src/cosmos-api.d.ts.map +1 -0
  11. package/src/cosmos-api.ts +231 -0
  12. package/src/ethereum-api.d.ts +8 -0
  13. package/src/ethereum-api.d.ts.map +1 -0
  14. package/src/ethereum-api.ts +7 -0
  15. package/src/examples/sendAnywhere.contract.d.ts +31 -0
  16. package/src/examples/sendAnywhere.contract.d.ts.map +1 -0
  17. package/src/examples/sendAnywhere.contract.js +171 -0
  18. package/src/examples/stakeBld.contract.d.ts +121 -0
  19. package/src/examples/stakeBld.contract.d.ts.map +1 -0
  20. package/src/examples/stakeBld.contract.js +133 -0
  21. package/src/examples/stakeIca.contract.d.ts +93 -0
  22. package/src/examples/stakeIca.contract.d.ts.map +1 -0
  23. package/src/examples/stakeIca.contract.js +143 -0
  24. package/src/examples/swapExample.contract.d.ts +34 -0
  25. package/src/examples/swapExample.contract.d.ts.map +1 -0
  26. package/src/examples/swapExample.contract.js +155 -0
  27. package/src/examples/unbondExample.contract.d.ts +21 -0
  28. package/src/examples/unbondExample.contract.d.ts.map +1 -0
  29. package/src/examples/unbondExample.contract.js +105 -0
  30. package/src/exos/README.md +56 -0
  31. package/src/exos/agoric-names-tools.d.ts +27 -0
  32. package/src/exos/agoric-names-tools.d.ts.map +1 -0
  33. package/src/exos/agoric-names-tools.js +109 -0
  34. package/src/exos/chain-account-kit.d.ts +88 -0
  35. package/src/exos/chain-account-kit.d.ts.map +1 -0
  36. package/src/exos/chain-account-kit.js +210 -0
  37. package/src/exos/chain-hub.d.ts +77 -0
  38. package/src/exos/chain-hub.d.ts.map +1 -0
  39. package/src/exos/chain-hub.js +214 -0
  40. package/src/exos/cosmos-orchestration-account.d.ts +207 -0
  41. package/src/exos/cosmos-orchestration-account.d.ts.map +1 -0
  42. package/src/exos/cosmos-orchestration-account.js +536 -0
  43. package/src/exos/icq-connection-kit.d.ts +50 -0
  44. package/src/exos/icq-connection-kit.d.ts.map +1 -0
  45. package/src/exos/icq-connection-kit.js +135 -0
  46. package/src/exos/local-chain-facade.d.ts +81 -0
  47. package/src/exos/local-chain-facade.d.ts.map +1 -0
  48. package/src/exos/local-chain-facade.js +113 -0
  49. package/src/exos/local-orchestration-account.d.ts +176 -0
  50. package/src/exos/local-orchestration-account.d.ts.map +1 -0
  51. package/src/exos/local-orchestration-account.js +446 -0
  52. package/src/exos/orchestrator.d.ts +91 -0
  53. package/src/exos/orchestrator.d.ts.map +1 -0
  54. package/src/exos/orchestrator.js +141 -0
  55. package/src/exos/remote-chain-facade.d.ts +144 -0
  56. package/src/exos/remote-chain-facade.d.ts.map +1 -0
  57. package/src/exos/remote-chain-facade.js +145 -0
  58. package/src/facade.d.ts +57 -0
  59. package/src/facade.d.ts.map +1 -0
  60. package/src/facade.js +132 -0
  61. package/src/fetched-chain-info.d.ts +2311 -0
  62. package/src/fetched-chain-info.d.ts.map +1 -0
  63. package/src/fetched-chain-info.js +2288 -0
  64. package/src/internal.d.ts +13 -0
  65. package/src/internal.d.ts.map +1 -0
  66. package/src/internal.ts +20 -0
  67. package/src/orchestration-api.d.ts +158 -0
  68. package/src/orchestration-api.d.ts.map +1 -0
  69. package/src/orchestration-api.ts +201 -0
  70. package/src/proposals/orchestration-proposal.d.ts +53 -0
  71. package/src/proposals/orchestration-proposal.d.ts.map +1 -0
  72. package/src/proposals/orchestration-proposal.js +179 -0
  73. package/src/proposals/revise-chain-info.d.ts +21 -0
  74. package/src/proposals/revise-chain-info.d.ts.map +1 -0
  75. package/src/proposals/revise-chain-info.js +44 -0
  76. package/src/proposals/start-stakeAtom.d.ts +109 -0
  77. package/src/proposals/start-stakeAtom.d.ts.map +1 -0
  78. package/src/proposals/start-stakeAtom.js +105 -0
  79. package/src/proposals/start-stakeBld.d.ts +154 -0
  80. package/src/proposals/start-stakeBld.d.ts.map +1 -0
  81. package/src/proposals/start-stakeBld.js +103 -0
  82. package/src/proposals/start-stakeOsmo.d.ts +108 -0
  83. package/src/proposals/start-stakeOsmo.d.ts.map +1 -0
  84. package/src/proposals/start-stakeOsmo.js +106 -0
  85. package/src/service.d.ts +97 -0
  86. package/src/service.d.ts.map +1 -0
  87. package/src/service.js +259 -0
  88. package/src/typeGuards.d.ts +55 -0
  89. package/src/typeGuards.d.ts.map +1 -0
  90. package/src/typeGuards.js +104 -0
  91. package/src/types.d.ts +11 -0
  92. package/src/types.d.ts.map +1 -0
  93. package/src/types.ts +11 -0
  94. package/src/utils/address.d.ts +14 -0
  95. package/src/utils/address.d.ts.map +1 -0
  96. package/src/utils/address.js +79 -0
  97. package/src/utils/cosmos.d.ts +9 -0
  98. package/src/utils/cosmos.d.ts.map +1 -0
  99. package/src/utils/cosmos.js +36 -0
  100. package/src/utils/orc.d.ts +17 -0
  101. package/src/utils/orc.d.ts.map +1 -0
  102. package/src/utils/orc.js +38 -0
  103. package/src/utils/orchestrationAccount.d.ts +10 -0
  104. package/src/utils/orchestrationAccount.d.ts.map +1 -0
  105. package/src/utils/orchestrationAccount.js +22 -0
  106. package/src/utils/packet.d.ts +57 -0
  107. package/src/utils/packet.d.ts.map +1 -0
  108. package/src/utils/packet.js +112 -0
  109. package/src/utils/registry.d.ts +13 -0
  110. package/src/utils/registry.d.ts.map +1 -0
  111. package/src/utils/registry.js +131 -0
  112. package/src/utils/start-helper.d.ts +51 -0
  113. package/src/utils/start-helper.d.ts.map +1 -0
  114. package/src/utils/start-helper.js +109 -0
  115. package/src/utils/time.d.ts +31 -0
  116. package/src/utils/time.d.ts.map +1 -0
  117. package/src/utils/time.js +63 -0
  118. package/src/vat-orchestration.d.ts +58 -0
  119. package/src/vat-orchestration.d.ts.map +1 -0
  120. package/src/vat-orchestration.js +24 -0
@@ -0,0 +1,210 @@
1
+ /** @file ChainAccount exo */
2
+ import { NonNullish } from '@agoric/assert';
3
+ import { makeTracer } from '@agoric/internal';
4
+ import { VowShape } from '@agoric/vow';
5
+ import { E } from '@endo/far';
6
+ import { M } from '@endo/patterns';
7
+ import {
8
+ ChainAddressShape,
9
+ OutboundConnectionHandlerI,
10
+ Proto3Shape,
11
+ } from '../typeGuards.js';
12
+ import { findAddressField } from '../utils/address.js';
13
+ import { makeTxPacket, parseTxPacket } from '../utils/packet.js';
14
+
15
+ /**
16
+ * @import {Zone} from '@agoric/base-zone';
17
+ * @import {Connection, Port} from '@agoric/network';
18
+ * @import {Remote, Vow, VowTools} from '@agoric/vow';
19
+ * @import {AnyJson} from '@agoric/cosmic-proto';
20
+ * @import {TxBody} from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
21
+ * @import {LocalIbcAddress, RemoteIbcAddress} from '@agoric/vats/tools/ibc-utils.js';
22
+ * @import {ChainAddress} from '../types.js';
23
+ */
24
+
25
+ const { Fail } = assert;
26
+ const trace = makeTracer('ChainAccountKit');
27
+
28
+ /** @typedef {'UNPARSABLE_CHAIN_ADDRESS'} UnparsableChainAddress */
29
+ const UNPARSABLE_CHAIN_ADDRESS = 'UNPARSABLE_CHAIN_ADDRESS';
30
+
31
+ export const ChainAccountI = M.interface('ChainAccount', {
32
+ getAddress: M.call().returns(ChainAddressShape),
33
+ getBalance: M.call(M.string()).returns(VowShape),
34
+ getBalances: M.call().returns(VowShape),
35
+ getLocalAddress: M.call().returns(M.string()),
36
+ getRemoteAddress: M.call().returns(M.string()),
37
+ getPort: M.call().returns(M.remotable('Port')),
38
+ executeTx: M.call(M.arrayOf(M.record())).returns(VowShape),
39
+ executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
40
+ .optional(M.record())
41
+ .returns(VowShape),
42
+ close: M.call().returns(VowShape),
43
+ getPurse: M.call().returns(VowShape),
44
+ });
45
+
46
+ /**
47
+ * @typedef {{
48
+ * chainId: string;
49
+ * port: Port;
50
+ * connection: Remote<Connection> | undefined;
51
+ * localAddress: LocalIbcAddress | undefined;
52
+ * requestedRemoteAddress: string;
53
+ * remoteAddress: RemoteIbcAddress | undefined;
54
+ * chainAddress: ChainAddress | undefined;
55
+ * }} State
56
+ */
57
+
58
+ /**
59
+ * @param {Zone} zone
60
+ * @param {VowTools} vowTools
61
+ */
62
+ export const prepareChainAccountKit = (zone, { watch, asVow }) =>
63
+ zone.exoClassKit(
64
+ 'ChainAccountKit',
65
+ {
66
+ account: ChainAccountI,
67
+ connectionHandler: OutboundConnectionHandlerI,
68
+ parseTxPacketWatcher: M.interface('ParseTxPacketWatcher', {
69
+ onFulfilled: M.call(M.string())
70
+ .optional(M.arrayOf(M.undefined())) // does not need watcherContext
71
+ .returns(M.string()),
72
+ }),
73
+ },
74
+ /**
75
+ * @param {string} chainId
76
+ * @param {Port} port
77
+ * @param {string} requestedRemoteAddress
78
+ */
79
+ (chainId, port, requestedRemoteAddress) =>
80
+ /** @type {State} */ ({
81
+ chainId,
82
+ port,
83
+ connection: undefined,
84
+ requestedRemoteAddress,
85
+ remoteAddress: undefined,
86
+ chainAddress: undefined,
87
+ localAddress: undefined,
88
+ }),
89
+ {
90
+ parseTxPacketWatcher: {
91
+ /** @param {string} ack */
92
+ onFulfilled(ack) {
93
+ return parseTxPacket(ack);
94
+ },
95
+ },
96
+ account: {
97
+ /** @returns {ChainAddress} */
98
+ getAddress() {
99
+ return NonNullish(
100
+ this.state.chainAddress,
101
+ 'ICA channel creation acknowledgement not yet received.',
102
+ );
103
+ },
104
+ getBalance(_denom) {
105
+ // TODO https://github.com/Agoric/agoric-sdk/issues/9610
106
+ // UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
107
+ return asVow(() => Fail`not yet implemented`);
108
+ },
109
+ getBalances() {
110
+ // TODO https://github.com/Agoric/agoric-sdk/issues/9610
111
+ // UNTIL https://github.com/Agoric/agoric-sdk/issues/9326
112
+ return asVow(() => Fail`not yet implemented`);
113
+ },
114
+ getLocalAddress() {
115
+ return NonNullish(
116
+ this.state.localAddress,
117
+ 'local address not available',
118
+ );
119
+ },
120
+ getRemoteAddress() {
121
+ return NonNullish(
122
+ this.state.remoteAddress,
123
+ 'remote address not available',
124
+ );
125
+ },
126
+ getPort() {
127
+ return this.state.port;
128
+ },
129
+ executeTx() {
130
+ return asVow(() => Fail`not yet implemented`);
131
+ },
132
+ /**
133
+ * Submit a transaction on behalf of the remote account for execution on
134
+ * the remote chain.
135
+ *
136
+ * @param {AnyJson[]} msgs
137
+ * @param {Omit<TxBody, 'messages'>} [opts]
138
+ * @returns {Vow<string>} - base64 encoded bytes string. Can be decoded
139
+ * using the corresponding `Msg*Response` object.
140
+ * @throws {Error} if packet fails to send or an error is returned
141
+ */
142
+ executeEncodedTx(msgs, opts) {
143
+ return asVow(() => {
144
+ const { connection } = this.state;
145
+ if (!connection) throw Fail`connection not available`;
146
+ return watch(
147
+ E(connection).send(makeTxPacket(msgs, opts)),
148
+ this.facets.parseTxPacketWatcher,
149
+ );
150
+ });
151
+ },
152
+ /**
153
+ * Close the remote account
154
+ *
155
+ * @returns {Vow<void>}
156
+ * @throws {Error} if connection is not available or already closed
157
+ */
158
+ close() {
159
+ return asVow(() => {
160
+ /// TODO #9192 what should the behavior be here? and `onClose`?
161
+ // - retrieve assets?
162
+ // - revoke the port?
163
+ const { connection } = this.state;
164
+ if (!connection) throw Fail`connection not available`;
165
+ return E(connection).close();
166
+ });
167
+ },
168
+ /**
169
+ * get Purse for a brand to .withdraw() a Payment from the account
170
+ *
171
+ * @param {Brand} brand
172
+ */
173
+ getPurse(brand) {
174
+ console.log('getPurse got', brand);
175
+ return asVow(() => Fail`not yet implemented`);
176
+ },
177
+ },
178
+ connectionHandler: {
179
+ /**
180
+ * @param {Remote<Connection>} connection
181
+ * @param {LocalIbcAddress} localAddr
182
+ * @param {RemoteIbcAddress} remoteAddr
183
+ */
184
+ async onOpen(connection, localAddr, remoteAddr) {
185
+ trace(`ICA Channel Opened for ${localAddr} at ${remoteAddr}`);
186
+ this.state.connection = connection;
187
+ this.state.remoteAddress = remoteAddr;
188
+ this.state.localAddress = localAddr;
189
+ this.state.chainAddress = harden({
190
+ // FIXME need a fallback value like icacontroller-1-connection-1 if this fails
191
+ // https://github.com/Agoric/agoric-sdk/issues/9066
192
+ address: findAddressField(remoteAddr) || UNPARSABLE_CHAIN_ADDRESS,
193
+ chainId: this.state.chainId,
194
+ addressEncoding: 'bech32',
195
+ });
196
+ },
197
+ /**
198
+ * @param {Remote<Connection>} _connection
199
+ * @param {unknown} reason
200
+ */
201
+ async onClose(_connection, reason) {
202
+ trace(`ICA Channel closed. Reason: ${reason}`);
203
+ // FIXME handle connection closing https://github.com/Agoric/agoric-sdk/issues/9192
204
+ // XXX is there a scenario where a connection will unexpectedly close? _I think yes_
205
+ },
206
+ },
207
+ },
208
+ );
209
+
210
+ /** @typedef {ReturnType<ReturnType<typeof prepareChainAccountKit>>} ChainAccountKit */
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @import {NameHub} from '@agoric/vats';
3
+ * @import {Vow} from '@agoric/vow';
4
+ * @import {CosmosChainInfo, IBCConnectionInfo} from '../cosmos-api.js';
5
+ * @import {ChainInfo, KnownChains} from '../chain-info.js';
6
+ * @import {Remote} from '@agoric/internal';
7
+ * @import {Zone} from '@agoric/zone';
8
+ */
9
+ /**
10
+ * @template {string} K
11
+ * @typedef {K extends keyof KnownChains
12
+ * ? Omit<KnownChains[K], 'connections'>
13
+ * : ChainInfo} ActualChainInfo
14
+ */
15
+ /** agoricNames key for ChainInfo hub */
16
+ export const CHAIN_KEY: "chain";
17
+ /** namehub for connection info */
18
+ export const CONNECTIONS_KEY: "chainConnection";
19
+ export function connectionKey(chainId1: string, chainId2: string): string;
20
+ export function makeChainHub(agoricNames: Remote<NameHub>, zone?: Zone | undefined): import("@endo/exo").Guarded<{
21
+ /**
22
+ * Register a new chain. The name will override a name in well known chain
23
+ * names.
24
+ *
25
+ * If a durable zone was not provided, registration will not survive a
26
+ * reincarnation of the vat. Then if the chain is not yet in the well known
27
+ * names at that point, it will have to be registered again. In an unchanged
28
+ * contract `start` the call will happen again naturally.
29
+ *
30
+ * @param {string} name
31
+ * @param {CosmosChainInfo} chainInfo
32
+ */
33
+ registerChain(name: string, chainInfo: CosmosChainInfo): void;
34
+ /**
35
+ * @template {string} K
36
+ * @param {K} chainName
37
+ * @returns {Vow<ActualChainInfo<K>>}
38
+ */
39
+ getChainInfo<K extends string>(chainName: K): Vow<ActualChainInfo<K>>;
40
+ /**
41
+ * @param {string} chainId1
42
+ * @param {string} chainId2
43
+ * @param {IBCConnectionInfo} connectionInfo
44
+ */
45
+ registerConnection(chainId1: string, chainId2: string, connectionInfo: IBCConnectionInfo): void;
46
+ /**
47
+ * @param {string | { chainId: string }} chain1
48
+ * @param {string | { chainId: string }} chain2
49
+ * @returns {Vow<IBCConnectionInfo>}
50
+ */
51
+ getConnectionInfo(chain1: string | {
52
+ chainId: string;
53
+ }, chain2: string | {
54
+ chainId: string;
55
+ }): Vow<IBCConnectionInfo>;
56
+ /**
57
+ * @template {string} C1
58
+ * @template {string} C2
59
+ * @param {C1} chainName1
60
+ * @param {C2} chainName2
61
+ * @returns {Vow<
62
+ * [ActualChainInfo<C1>, ActualChainInfo<C2>, IBCConnectionInfo]
63
+ * >}
64
+ */
65
+ getChainsAndConnection<C1 extends string, C2 extends string>(chainName1: C1, chainName2: C2): Vow<[ActualChainInfo<C1>, ActualChainInfo<C2>, IBCConnectionInfo]>;
66
+ }>;
67
+ export type ActualChainInfo<K extends string> = K extends keyof KnownChains ? Omit<KnownChains[K], "connections"> : ChainInfo;
68
+ export type ChainHub = ReturnType<typeof makeChainHub>;
69
+ import type { NameHub } from '@agoric/vats';
70
+ import type { Remote } from '@agoric/internal';
71
+ import type { Zone } from '@agoric/zone';
72
+ import type { CosmosChainInfo } from '../cosmos-api.js';
73
+ import type { Vow } from '@agoric/vow';
74
+ import type { IBCConnectionInfo } from '../cosmos-api.js';
75
+ import type { KnownChains } from '../chain-info.js';
76
+ import type { ChainInfo } from '../chain-info.js';
77
+ //# sourceMappingURL=chain-hub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chain-hub.d.ts","sourceRoot":"","sources":["chain-hub.js"],"names":[],"mappings":"AAcA;;;;;;;GAOG;AAEH;;;;;GAKG;AAEH,wCAAwC;AACxC,gCAAiC;AACjC,kCAAkC;AAClC,gDAAiD;AAqB1C,wCAHI,MAAM,YACN,MAAM,UAUhB;AAoCM,0CAHI,OAAO,OAAO,CAAC;IAgBtB;;;;;;;;;;;OAWG;wBAFQ,MAAM,aACN,eAAe;IAK1B;;;;OAIG;iBAHmB,CAAC,SAAV,MAAQ,aACV,CAAC,GACC,IAAI,eAAe,CAAC,CAAC,CAAC,CAAC;IAoBpC;;;;OAIG;iCAHQ,MAAM,YACN,MAAM,kBACN,iBAAiB;IAO5B;;;;OAIG;8BAHQ,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,UAC5B,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAC1B,IAAI,iBAAiB,CAAC;IAqBnC;;;;;;;;OAQG;2BAPmB,EAAE,SAAX,MAAQ,EACC,EAAE,SAAX,MAAQ,cACV,EAAE,cACF,EAAE,GACA,IACZ,CAAK,eAAe,CAAC,EAAE,CAAC,EAAE,eAAe,CAAC,EAAE,CAAC,EAAE,iBAAiB,CAAC,CAC9D;GAsBP;4BA5LqB,CAAC,SAAV,MAAQ,IACR,CAAC,SAAS,MAAM,WAAW,GACjC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,GACnC,SAAS;uBA0LF,UAAU,CAAC,OAAO,YAAY,CAAC;6BAtMnB,cAAc;4BAIf,kBAAkB;0BACpB,cAAc;qCAHgB,kBAAkB;yBADjD,aAAa;uCACkB,kBAAkB;iCAC9B,kBAAkB;+BAAlB,kBAAkB"}
@@ -0,0 +1,214 @@
1
+ import { VowShape } from '@agoric/vow';
2
+ // eslint-disable-next-line no-restricted-syntax
3
+ import { heapVowTools } from '@agoric/vow/vat.js';
4
+ import { makeHeapZone } from '@agoric/zone';
5
+ import { E } from '@endo/far';
6
+ import { M } from '@endo/patterns';
7
+ import { CosmosChainInfoShape, IBCConnectionInfoShape } from '../typeGuards.js';
8
+
9
+ // FIXME test thoroughly whether heap suffices for ChainHub
10
+ // eslint-disable-next-line no-restricted-syntax
11
+ const { allVows, watch } = heapVowTools;
12
+
13
+ const { Fail } = assert;
14
+
15
+ /**
16
+ * @import {NameHub} from '@agoric/vats';
17
+ * @import {Vow} from '@agoric/vow';
18
+ * @import {CosmosChainInfo, IBCConnectionInfo} from '../cosmos-api.js';
19
+ * @import {ChainInfo, KnownChains} from '../chain-info.js';
20
+ * @import {Remote} from '@agoric/internal';
21
+ * @import {Zone} from '@agoric/zone';
22
+ */
23
+
24
+ /**
25
+ * @template {string} K
26
+ * @typedef {K extends keyof KnownChains
27
+ * ? Omit<KnownChains[K], 'connections'>
28
+ * : ChainInfo} ActualChainInfo
29
+ */
30
+
31
+ /** agoricNames key for ChainInfo hub */
32
+ export const CHAIN_KEY = 'chain';
33
+ /** namehub for connection info */
34
+ export const CONNECTIONS_KEY = 'chainConnection';
35
+
36
+ /**
37
+ * Character used in a connection tuple key to separate the two chain ids. Valid
38
+ * because a chainId can contain only alphanumerics and dash.
39
+ *
40
+ * Vstorage keys can be only alphanumerics, dash or underscore. That leaves
41
+ * underscore as the only valid separator.
42
+ *
43
+ * @see {@link https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-2.md}
44
+ */
45
+ const CHAIN_ID_SEPARATOR = '_';
46
+
47
+ /**
48
+ * The entries of the top-level namehubs in agoricNames are reflected to
49
+ * vstorage. But only the top level. So we combine the 2 chain ids into 1 key.
50
+ * Connections are directionless, so we sort the ids.
51
+ *
52
+ * @param {string} chainId1
53
+ * @param {string} chainId2
54
+ */
55
+ export const connectionKey = (chainId1, chainId2) => {
56
+ if (
57
+ chainId1.includes(CHAIN_ID_SEPARATOR) ||
58
+ chainId2.includes(CHAIN_ID_SEPARATOR)
59
+ ) {
60
+ Fail`invalid chain id ${chainId1} or ${chainId2}`;
61
+ }
62
+ return [chainId1, chainId2].sort().join(CHAIN_ID_SEPARATOR);
63
+ };
64
+
65
+ const ChainIdArgShape = M.or(
66
+ M.string(),
67
+ M.splitRecord(
68
+ {
69
+ chainId: M.string(),
70
+ },
71
+ undefined,
72
+ M.any(),
73
+ ),
74
+ );
75
+
76
+ const ChainHubI = M.interface('ChainHub', {
77
+ registerChain: M.call(M.string(), CosmosChainInfoShape).returns(),
78
+ getChainInfo: M.call(M.string()).returns(VowShape),
79
+ registerConnection: M.call(
80
+ M.string(),
81
+ M.string(),
82
+ IBCConnectionInfoShape,
83
+ ).returns(),
84
+ getConnectionInfo: M.call(ChainIdArgShape, ChainIdArgShape).returns(VowShape),
85
+ getChainsAndConnection: M.call(M.string(), M.string()).returns(VowShape),
86
+ });
87
+
88
+ /**
89
+ * Make a new ChainHub in the zone (or in the heap if no zone is provided).
90
+ *
91
+ * The resulting object is an Exo singleton. It has no precious state. It's only
92
+ * state is a cache of queries to agoricNames and whatever info was provided in
93
+ * registration calls. When you need a newer version you can simply make a hub
94
+ * hub and repeat the registrations.
95
+ *
96
+ * @param {Remote<NameHub>} agoricNames
97
+ * @param {Zone} [zone]
98
+ */
99
+ export const makeChainHub = (agoricNames, zone = makeHeapZone()) => {
100
+ /** @type {MapStore<string, CosmosChainInfo>} */
101
+ const chainInfos = zone.mapStore('chainInfos', {
102
+ keyShape: M.string(),
103
+ valueShape: CosmosChainInfoShape,
104
+ });
105
+ /** @type {MapStore<string, IBCConnectionInfo>} */
106
+ const connectionInfos = zone.mapStore('connectionInfos', {
107
+ keyShape: M.string(),
108
+ valueShape: IBCConnectionInfoShape,
109
+ });
110
+
111
+ const chainHub = zone.exo('ChainHub', ChainHubI, {
112
+ /**
113
+ * Register a new chain. The name will override a name in well known chain
114
+ * names.
115
+ *
116
+ * If a durable zone was not provided, registration will not survive a
117
+ * reincarnation of the vat. Then if the chain is not yet in the well known
118
+ * names at that point, it will have to be registered again. In an unchanged
119
+ * contract `start` the call will happen again naturally.
120
+ *
121
+ * @param {string} name
122
+ * @param {CosmosChainInfo} chainInfo
123
+ */
124
+ registerChain(name, chainInfo) {
125
+ chainInfos.init(name, chainInfo);
126
+ },
127
+ /**
128
+ * @template {string} K
129
+ * @param {K} chainName
130
+ * @returns {Vow<ActualChainInfo<K>>}
131
+ */
132
+ getChainInfo(chainName) {
133
+ // Either from registerChain or memoized remote lookup()
134
+ if (chainInfos.has(chainName)) {
135
+ return /** @type {Vow<ActualChainInfo<K>>} */ (
136
+ watch(chainInfos.get(chainName))
137
+ );
138
+ }
139
+
140
+ return watch(E(agoricNames).lookup(CHAIN_KEY, chainName), {
141
+ onFulfilled: chainInfo => {
142
+ chainInfos.init(chainName, chainInfo);
143
+ return chainInfo;
144
+ },
145
+ onRejected: _cause => {
146
+ throw assert.error(`chain not found:${chainName}`);
147
+ },
148
+ });
149
+ },
150
+ /**
151
+ * @param {string} chainId1
152
+ * @param {string} chainId2
153
+ * @param {IBCConnectionInfo} connectionInfo
154
+ */
155
+ registerConnection(chainId1, chainId2, connectionInfo) {
156
+ const key = connectionKey(chainId1, chainId2);
157
+ connectionInfos.init(key, connectionInfo);
158
+ },
159
+
160
+ /**
161
+ * @param {string | { chainId: string }} chain1
162
+ * @param {string | { chainId: string }} chain2
163
+ * @returns {Vow<IBCConnectionInfo>}
164
+ */
165
+ getConnectionInfo(chain1, chain2) {
166
+ const chainId1 = typeof chain1 === 'string' ? chain1 : chain1.chainId;
167
+ const chainId2 = typeof chain2 === 'string' ? chain2 : chain2.chainId;
168
+ const key = connectionKey(chainId1, chainId2);
169
+ if (connectionInfos.has(key)) {
170
+ return watch(connectionInfos.get(key));
171
+ }
172
+
173
+ return watch(E(agoricNames).lookup(CONNECTIONS_KEY, key), {
174
+ onFulfilled: connectionInfo => {
175
+ connectionInfos.init(key, connectionInfo);
176
+ return connectionInfo;
177
+ },
178
+ onRejected: _cause => {
179
+ throw assert.error(`connection not found: ${chainId1}<->${chainId2}`);
180
+ },
181
+ });
182
+ },
183
+
184
+ /**
185
+ * @template {string} C1
186
+ * @template {string} C2
187
+ * @param {C1} chainName1
188
+ * @param {C2} chainName2
189
+ * @returns {Vow<
190
+ * [ActualChainInfo<C1>, ActualChainInfo<C2>, IBCConnectionInfo]
191
+ * >}
192
+ */
193
+ getChainsAndConnection(chainName1, chainName2) {
194
+ return watch(
195
+ allVows([
196
+ chainHub.getChainInfo(chainName1),
197
+ chainHub.getChainInfo(chainName2),
198
+ ]),
199
+ {
200
+ onFulfilled: ([chain1, chain2]) => {
201
+ return watch(chainHub.getConnectionInfo(chain2, chain1), {
202
+ onFulfilled: connectionInfo => {
203
+ return [chain1, chain2, connectionInfo];
204
+ },
205
+ });
206
+ },
207
+ },
208
+ );
209
+ },
210
+ });
211
+
212
+ return chainHub;
213
+ };
214
+ /** @typedef {ReturnType<typeof makeChainHub>} ChainHub */