@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.
- package/LICENSE +201 -0
- package/README.md +5 -0
- package/index.d.ts +4 -0
- package/index.js +7 -0
- package/package.json +95 -0
- package/src/chain-info.d.ts +2318 -0
- package/src/chain-info.d.ts.map +1 -0
- package/src/chain-info.js +114 -0
- package/src/cosmos-api.d.ts +174 -0
- package/src/cosmos-api.d.ts.map +1 -0
- package/src/cosmos-api.ts +231 -0
- package/src/ethereum-api.d.ts +8 -0
- package/src/ethereum-api.d.ts.map +1 -0
- package/src/ethereum-api.ts +7 -0
- package/src/examples/sendAnywhere.contract.d.ts +31 -0
- package/src/examples/sendAnywhere.contract.d.ts.map +1 -0
- package/src/examples/sendAnywhere.contract.js +171 -0
- package/src/examples/stakeBld.contract.d.ts +121 -0
- package/src/examples/stakeBld.contract.d.ts.map +1 -0
- package/src/examples/stakeBld.contract.js +133 -0
- package/src/examples/stakeIca.contract.d.ts +93 -0
- package/src/examples/stakeIca.contract.d.ts.map +1 -0
- package/src/examples/stakeIca.contract.js +143 -0
- package/src/examples/swapExample.contract.d.ts +34 -0
- package/src/examples/swapExample.contract.d.ts.map +1 -0
- package/src/examples/swapExample.contract.js +155 -0
- package/src/examples/unbondExample.contract.d.ts +21 -0
- package/src/examples/unbondExample.contract.d.ts.map +1 -0
- package/src/examples/unbondExample.contract.js +105 -0
- package/src/exos/README.md +56 -0
- package/src/exos/agoric-names-tools.d.ts +27 -0
- package/src/exos/agoric-names-tools.d.ts.map +1 -0
- package/src/exos/agoric-names-tools.js +109 -0
- package/src/exos/chain-account-kit.d.ts +88 -0
- package/src/exos/chain-account-kit.d.ts.map +1 -0
- package/src/exos/chain-account-kit.js +210 -0
- package/src/exos/chain-hub.d.ts +77 -0
- package/src/exos/chain-hub.d.ts.map +1 -0
- package/src/exos/chain-hub.js +214 -0
- package/src/exos/cosmos-orchestration-account.d.ts +207 -0
- package/src/exos/cosmos-orchestration-account.d.ts.map +1 -0
- package/src/exos/cosmos-orchestration-account.js +536 -0
- package/src/exos/icq-connection-kit.d.ts +50 -0
- package/src/exos/icq-connection-kit.d.ts.map +1 -0
- package/src/exos/icq-connection-kit.js +135 -0
- package/src/exos/local-chain-facade.d.ts +81 -0
- package/src/exos/local-chain-facade.d.ts.map +1 -0
- package/src/exos/local-chain-facade.js +113 -0
- package/src/exos/local-orchestration-account.d.ts +176 -0
- package/src/exos/local-orchestration-account.d.ts.map +1 -0
- package/src/exos/local-orchestration-account.js +446 -0
- package/src/exos/orchestrator.d.ts +91 -0
- package/src/exos/orchestrator.d.ts.map +1 -0
- package/src/exos/orchestrator.js +141 -0
- package/src/exos/remote-chain-facade.d.ts +144 -0
- package/src/exos/remote-chain-facade.d.ts.map +1 -0
- package/src/exos/remote-chain-facade.js +145 -0
- package/src/facade.d.ts +57 -0
- package/src/facade.d.ts.map +1 -0
- package/src/facade.js +132 -0
- package/src/fetched-chain-info.d.ts +2311 -0
- package/src/fetched-chain-info.d.ts.map +1 -0
- package/src/fetched-chain-info.js +2288 -0
- package/src/internal.d.ts +13 -0
- package/src/internal.d.ts.map +1 -0
- package/src/internal.ts +20 -0
- package/src/orchestration-api.d.ts +158 -0
- package/src/orchestration-api.d.ts.map +1 -0
- package/src/orchestration-api.ts +201 -0
- package/src/proposals/orchestration-proposal.d.ts +53 -0
- package/src/proposals/orchestration-proposal.d.ts.map +1 -0
- package/src/proposals/orchestration-proposal.js +179 -0
- package/src/proposals/revise-chain-info.d.ts +21 -0
- package/src/proposals/revise-chain-info.d.ts.map +1 -0
- package/src/proposals/revise-chain-info.js +44 -0
- package/src/proposals/start-stakeAtom.d.ts +109 -0
- package/src/proposals/start-stakeAtom.d.ts.map +1 -0
- package/src/proposals/start-stakeAtom.js +105 -0
- package/src/proposals/start-stakeBld.d.ts +154 -0
- package/src/proposals/start-stakeBld.d.ts.map +1 -0
- package/src/proposals/start-stakeBld.js +103 -0
- package/src/proposals/start-stakeOsmo.d.ts +108 -0
- package/src/proposals/start-stakeOsmo.d.ts.map +1 -0
- package/src/proposals/start-stakeOsmo.js +106 -0
- package/src/service.d.ts +97 -0
- package/src/service.d.ts.map +1 -0
- package/src/service.js +259 -0
- package/src/typeGuards.d.ts +55 -0
- package/src/typeGuards.d.ts.map +1 -0
- package/src/typeGuards.js +104 -0
- package/src/types.d.ts +11 -0
- package/src/types.d.ts.map +1 -0
- package/src/types.ts +11 -0
- package/src/utils/address.d.ts +14 -0
- package/src/utils/address.d.ts.map +1 -0
- package/src/utils/address.js +79 -0
- package/src/utils/cosmos.d.ts +9 -0
- package/src/utils/cosmos.d.ts.map +1 -0
- package/src/utils/cosmos.js +36 -0
- package/src/utils/orc.d.ts +17 -0
- package/src/utils/orc.d.ts.map +1 -0
- package/src/utils/orc.js +38 -0
- package/src/utils/orchestrationAccount.d.ts +10 -0
- package/src/utils/orchestrationAccount.d.ts.map +1 -0
- package/src/utils/orchestrationAccount.js +22 -0
- package/src/utils/packet.d.ts +57 -0
- package/src/utils/packet.d.ts.map +1 -0
- package/src/utils/packet.js +112 -0
- package/src/utils/registry.d.ts +13 -0
- package/src/utils/registry.d.ts.map +1 -0
- package/src/utils/registry.js +131 -0
- package/src/utils/start-helper.d.ts +51 -0
- package/src/utils/start-helper.d.ts.map +1 -0
- package/src/utils/start-helper.js +109 -0
- package/src/utils/time.d.ts +31 -0
- package/src/utils/time.d.ts.map +1 -0
- package/src/utils/time.js +63 -0
- package/src/vat-orchestration.d.ts +58 -0
- package/src/vat-orchestration.d.ts.map +1 -0
- 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 */
|