@agoric/orchestration 0.1.1-dev-8e82183.0 → 0.1.1-dev-5a4779a.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/package.json +14 -14
- package/src/examples/stakeAtom.contract.js +12 -12
- package/src/examples/stakeBld.contract.js +1 -1
- package/src/examples/swapExample.contract.js +3 -3
- package/src/examples/unbondExample.contract.js +4 -6
- package/src/exos/chainAccountKit.js +200 -0
- package/src/exos/stakingAccountKit.js +5 -5
- package/src/service.js +16 -179
- package/src/types.d.ts +43 -29
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/orchestration",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-5a4779a.0+5a4779a",
|
|
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-
|
|
33
|
-
"@agoric/cosmic-proto": "0.4.1-dev-
|
|
34
|
-
"@agoric/ertp": "0.16.3-dev-
|
|
35
|
-
"@agoric/internal": "0.3.3-dev-
|
|
36
|
-
"@agoric/network": "0.1.1-dev-
|
|
37
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
38
|
-
"@agoric/store": "0.9.3-dev-
|
|
39
|
-
"@agoric/time": "0.3.3-dev-
|
|
40
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
41
|
-
"@agoric/vats": "0.15.2-dev-
|
|
42
|
-
"@agoric/zoe": "0.26.3-dev-
|
|
43
|
-
"@agoric/zone": "0.2.3-dev-
|
|
32
|
+
"@agoric/assert": "0.6.1-dev-5a4779a.0+5a4779a",
|
|
33
|
+
"@agoric/cosmic-proto": "0.4.1-dev-5a4779a.0+5a4779a",
|
|
34
|
+
"@agoric/ertp": "0.16.3-dev-5a4779a.0+5a4779a",
|
|
35
|
+
"@agoric/internal": "0.3.3-dev-5a4779a.0+5a4779a",
|
|
36
|
+
"@agoric/network": "0.1.1-dev-5a4779a.0+5a4779a",
|
|
37
|
+
"@agoric/notifier": "0.6.3-dev-5a4779a.0+5a4779a",
|
|
38
|
+
"@agoric/store": "0.9.3-dev-5a4779a.0+5a4779a",
|
|
39
|
+
"@agoric/time": "0.3.3-dev-5a4779a.0+5a4779a",
|
|
40
|
+
"@agoric/vat-data": "0.5.3-dev-5a4779a.0+5a4779a",
|
|
41
|
+
"@agoric/vats": "0.15.2-dev-5a4779a.0+5a4779a",
|
|
42
|
+
"@agoric/zoe": "0.26.3-dev-5a4779a.0+5a4779a",
|
|
43
|
+
"@agoric/zone": "0.2.3-dev-5a4779a.0+5a4779a",
|
|
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": "
|
|
85
|
+
"gitHead": "5a4779a5b0240bccd150607474d80beac23da3cd"
|
|
86
86
|
}
|
|
@@ -47,17 +47,17 @@ export const start = async (zcf, privateArgs, baggage) => {
|
|
|
47
47
|
zcf,
|
|
48
48
|
);
|
|
49
49
|
|
|
50
|
-
async function
|
|
51
|
-
const account = await E(orchestration).
|
|
50
|
+
async function makeAccount() {
|
|
51
|
+
const account = await E(orchestration).makeAccount(
|
|
52
52
|
hostConnectionId,
|
|
53
53
|
controllerConnectionId,
|
|
54
54
|
);
|
|
55
|
-
const
|
|
56
|
-
trace('
|
|
55
|
+
const address = await E(account).getAddress();
|
|
56
|
+
trace('chain address', address);
|
|
57
57
|
const { holder, invitationMakers } = makeStakingAccountKit(
|
|
58
58
|
account,
|
|
59
59
|
storageNode,
|
|
60
|
-
|
|
60
|
+
address,
|
|
61
61
|
);
|
|
62
62
|
return {
|
|
63
63
|
publicSubscribers: holder.getPublicTopics(),
|
|
@@ -69,20 +69,20 @@ export const start = async (zcf, privateArgs, baggage) => {
|
|
|
69
69
|
const publicFacet = zone.exo(
|
|
70
70
|
'StakeAtom',
|
|
71
71
|
M.interface('StakeAtomI', {
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
makeAccount: M.callWhen().returns(M.remotable('ChainAccount')),
|
|
73
|
+
makeAcountInvitationMaker: M.call().returns(M.promise()),
|
|
74
74
|
}),
|
|
75
75
|
{
|
|
76
|
-
async
|
|
77
|
-
trace('
|
|
78
|
-
return
|
|
76
|
+
async makeAccount() {
|
|
77
|
+
trace('makeAccount');
|
|
78
|
+
return makeAccount().then(({ account }) => account);
|
|
79
79
|
},
|
|
80
|
-
|
|
80
|
+
makeAcountInvitationMaker() {
|
|
81
81
|
trace('makeCreateAccountInvitation');
|
|
82
82
|
return zcf.makeInvitation(
|
|
83
83
|
async seat => {
|
|
84
84
|
seat.exit();
|
|
85
|
-
return
|
|
85
|
+
return makeAccount();
|
|
86
86
|
},
|
|
87
87
|
'wantStakingAccount',
|
|
88
88
|
undefined,
|
|
@@ -48,7 +48,7 @@ export const start = async (zcf, privateArgs, baggage) => {
|
|
|
48
48
|
const { give } = seat.getProposal();
|
|
49
49
|
trace('makeStakeBldInvitation', give);
|
|
50
50
|
// XXX type appears local but it's remote
|
|
51
|
-
const account = await E(privateArgs.localchain).
|
|
51
|
+
const account = await E(privateArgs.localchain).makeAccount();
|
|
52
52
|
const lcaSeatKit = zcf.makeEmptySeatKit();
|
|
53
53
|
atomicTransfer(zcf, seat, lcaSeatKit.zcfSeat, give);
|
|
54
54
|
seat.exit();
|
|
@@ -48,11 +48,11 @@ export const start = async (zcf, privateArgs) => {
|
|
|
48
48
|
const agoric = await orch.getChain('agoric');
|
|
49
49
|
|
|
50
50
|
const [celestiaAccount, localAccount] = await Promise.all([
|
|
51
|
-
celestia.
|
|
52
|
-
agoric.
|
|
51
|
+
celestia.makeAccount(),
|
|
52
|
+
agoric.makeAccount(),
|
|
53
53
|
]);
|
|
54
54
|
|
|
55
|
-
const tiaAddress = await celestiaAccount.
|
|
55
|
+
const tiaAddress = await celestiaAccount.getAddress();
|
|
56
56
|
|
|
57
57
|
// deposit funds from user seat to LocalChainAccount
|
|
58
58
|
const seatKit = zcf.makeEmptySeatKit();
|
|
@@ -47,21 +47,19 @@ export const start = async (zcf, privateArgs) => {
|
|
|
47
47
|
// ??? could these be passed in? It would reduce the size of this handler,
|
|
48
48
|
// keeping it focused on long-running operations.
|
|
49
49
|
const celestia = await orch.getChain('celestia');
|
|
50
|
-
const celestiaAccount = await celestia.
|
|
50
|
+
const celestiaAccount = await celestia.makeAccount();
|
|
51
51
|
|
|
52
52
|
const delegations = await celestiaAccount.getDelegations();
|
|
53
|
-
const [undelegation] = await celestiaAccount.undelegate(delegations);
|
|
54
|
-
|
|
55
53
|
// wait for the undelegations to be complete (may take weeks)
|
|
56
|
-
await
|
|
54
|
+
await celestiaAccount.undelegate(delegations);
|
|
57
55
|
|
|
58
56
|
// ??? should this be synchronous? depends on how names are resolved.
|
|
59
57
|
const stride = await orch.getChain('stride');
|
|
60
|
-
const strideAccount = await stride.
|
|
58
|
+
const strideAccount = await stride.makeAccount();
|
|
61
59
|
|
|
62
60
|
// TODO the `TIA` string actually needs to be the Brand from AgoricNames
|
|
63
61
|
const tiaAmt = await celestiaAccount.getBalance('TIA');
|
|
64
|
-
await celestiaAccount.transfer(tiaAmt, strideAccount.
|
|
62
|
+
await celestiaAccount.transfer(tiaAmt, strideAccount.getAddress());
|
|
65
63
|
|
|
66
64
|
await strideAccount.liquidStake(tiaAmt);
|
|
67
65
|
},
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @file Orchestration service */
|
|
3
|
+
import { NonNullish } from '@agoric/assert';
|
|
4
|
+
import { makeTracer } from '@agoric/internal';
|
|
5
|
+
|
|
6
|
+
// XXX ambient types runtime imports until https://github.com/Agoric/agoric-sdk/issues/6512
|
|
7
|
+
import '@agoric/network/exported.js';
|
|
8
|
+
|
|
9
|
+
import { V as E } from '@agoric/vat-data/vow.js';
|
|
10
|
+
import { M } from '@endo/patterns';
|
|
11
|
+
import { PaymentShape, PurseShape } from '@agoric/ertp';
|
|
12
|
+
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
|
|
13
|
+
import { parseAddress } from '../utils/address.js';
|
|
14
|
+
import { makeTxPacket, parsePacketAck } from '../utils/tx.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @import { Connection, Port } from '@agoric/network';
|
|
18
|
+
* @import { Remote } from '@agoric/vow';
|
|
19
|
+
* @import { Zone } from '@agoric/base-zone';
|
|
20
|
+
* @import { AnyJson } from '@agoric/cosmic-proto';
|
|
21
|
+
* @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
|
|
22
|
+
* @import { LocalIbcAddress, RemoteIbcAddress } from '@agoric/vats/tools/ibc-utils.js';
|
|
23
|
+
* @import { ChainAddress } from '../types.js';
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
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
|
+
};
|
|
39
|
+
|
|
40
|
+
/** @typedef {'UNPARSABLE_CHAIN_ADDRESS'} UnparsableChainAddress */
|
|
41
|
+
const UNPARSABLE_CHAIN_ADDRESS = 'UNPARSABLE_CHAIN_ADDRESS';
|
|
42
|
+
|
|
43
|
+
export const ChainAccountI = M.interface('ChainAccount', {
|
|
44
|
+
getAddress: M.call().returns(ChainAddressShape),
|
|
45
|
+
getLocalAddress: M.call().returns(M.string()),
|
|
46
|
+
getRemoteAddress: M.call().returns(M.string()),
|
|
47
|
+
getPort: M.call().returns(M.remotable('Port')),
|
|
48
|
+
executeTx: M.call(M.arrayOf(M.record())).returns(M.promise()),
|
|
49
|
+
executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
|
|
50
|
+
.optional(M.record())
|
|
51
|
+
.returns(M.promise()),
|
|
52
|
+
close: M.callWhen().returns(M.undefined()),
|
|
53
|
+
deposit: M.callWhen(PaymentShape).returns(M.undefined()),
|
|
54
|
+
getPurse: M.callWhen().returns(PurseShape),
|
|
55
|
+
prepareTransfer: M.callWhen().returns(InvitationShape),
|
|
56
|
+
});
|
|
57
|
+
|
|
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
|
+
});
|
|
63
|
+
|
|
64
|
+
/** @param {Zone} zone */
|
|
65
|
+
export const prepareChainAccountKit = zone =>
|
|
66
|
+
zone.exoClassKit(
|
|
67
|
+
'ChainAccount',
|
|
68
|
+
{ account: ChainAccountI, connectionHandler: ConnectionHandlerI },
|
|
69
|
+
/**
|
|
70
|
+
* @param {Port} port
|
|
71
|
+
* @param {string} requestedRemoteAddress
|
|
72
|
+
*/
|
|
73
|
+
(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
|
+
*/ (
|
|
84
|
+
harden({
|
|
85
|
+
port,
|
|
86
|
+
connection: undefined,
|
|
87
|
+
requestedRemoteAddress,
|
|
88
|
+
remoteAddress: undefined,
|
|
89
|
+
chainAddress: undefined,
|
|
90
|
+
localAddress: undefined,
|
|
91
|
+
})
|
|
92
|
+
),
|
|
93
|
+
{
|
|
94
|
+
account: {
|
|
95
|
+
/**
|
|
96
|
+
* @returns {ChainAddress}
|
|
97
|
+
*/
|
|
98
|
+
getAddress() {
|
|
99
|
+
return NonNullish(
|
|
100
|
+
this.state.chainAddress,
|
|
101
|
+
'ICA channel creation acknowledgement not yet received.',
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
getLocalAddress() {
|
|
105
|
+
return NonNullish(
|
|
106
|
+
this.state.localAddress,
|
|
107
|
+
'local address not available',
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
getRemoteAddress() {
|
|
111
|
+
return NonNullish(
|
|
112
|
+
this.state.remoteAddress,
|
|
113
|
+
'remote address not available',
|
|
114
|
+
);
|
|
115
|
+
},
|
|
116
|
+
getPort() {
|
|
117
|
+
return this.state.port;
|
|
118
|
+
},
|
|
119
|
+
executeTx() {
|
|
120
|
+
throw new Error('not yet implemented');
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Submit a transaction on behalf of the remote account for execution on the remote chain.
|
|
124
|
+
* @param {AnyJson[]} msgs
|
|
125
|
+
* @param {Omit<TxBody, 'messages'>} [opts]
|
|
126
|
+
* @returns {Promise<string>} - base64 encoded bytes string. Can be decoded using the corresponding `Msg*Response` object.
|
|
127
|
+
* @throws {Error} if packet fails to send or an error is returned
|
|
128
|
+
*/
|
|
129
|
+
executeEncodedTx(msgs, opts) {
|
|
130
|
+
const { connection } = this.state;
|
|
131
|
+
if (!connection) throw Fail`connection not available`;
|
|
132
|
+
return E.when(
|
|
133
|
+
E(connection).send(makeTxPacket(msgs, opts)),
|
|
134
|
+
// if parsePacketAck cannot find a `result` key, it throws
|
|
135
|
+
ack => parsePacketAck(ack),
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Close the remote account
|
|
140
|
+
*/
|
|
141
|
+
async close() {
|
|
142
|
+
/// XXX what should the behavior be here? and `onClose`?
|
|
143
|
+
// - retrieve assets?
|
|
144
|
+
// - revoke the port?
|
|
145
|
+
const { connection } = this.state;
|
|
146
|
+
if (!connection) throw Fail`connection not available`;
|
|
147
|
+
await E(connection).close();
|
|
148
|
+
},
|
|
149
|
+
async deposit(payment) {
|
|
150
|
+
console.log('deposit got', payment);
|
|
151
|
+
throw new Error('not yet implemented');
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* get Purse for a brand to .withdraw() a Payment from the account
|
|
155
|
+
* @param {Brand} brand
|
|
156
|
+
*/
|
|
157
|
+
async getPurse(brand) {
|
|
158
|
+
console.log('getPurse got', brand);
|
|
159
|
+
throw new Error('not yet implemented');
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/* transfer account to new holder */
|
|
163
|
+
async prepareTransfer() {
|
|
164
|
+
throw new Error('not yet implemented');
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
connectionHandler: {
|
|
168
|
+
/**
|
|
169
|
+
* @param {Remote<Connection>} connection
|
|
170
|
+
* @param {LocalIbcAddress} localAddr
|
|
171
|
+
* @param {RemoteIbcAddress} remoteAddr
|
|
172
|
+
*/
|
|
173
|
+
async onOpen(connection, localAddr, remoteAddr) {
|
|
174
|
+
trace(`ICA Channel Opened for ${localAddr} at ${remoteAddr}`);
|
|
175
|
+
this.state.connection = connection;
|
|
176
|
+
this.state.remoteAddress = remoteAddr;
|
|
177
|
+
this.state.localAddress = localAddr;
|
|
178
|
+
// XXX parseAddress currently throws, should it return '' instead?
|
|
179
|
+
this.state.chainAddress = harden({
|
|
180
|
+
address: parseAddress(remoteAddr) || UNPARSABLE_CHAIN_ADDRESS,
|
|
181
|
+
// TODO get this from `Chain` object #9063
|
|
182
|
+
// XXX how do we get a chainId for an unknown chain? seems it may need to be a user supplied arg
|
|
183
|
+
chainId: 'FIXME',
|
|
184
|
+
addressEncoding: 'bech32',
|
|
185
|
+
});
|
|
186
|
+
},
|
|
187
|
+
async onClose(_connection, reason) {
|
|
188
|
+
trace(`ICA Channel closed. Reason: ${reason}`);
|
|
189
|
+
// XXX handle connection closing
|
|
190
|
+
// XXX is there a scenario where a connection will unexpectedly close? _I think yes_
|
|
191
|
+
},
|
|
192
|
+
async onReceive(connection, bytes) {
|
|
193
|
+
trace(`ICA Channel onReceive`, connection, bytes);
|
|
194
|
+
return '';
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
/** @typedef {ReturnType<ReturnType<typeof prepareChainAccountKit>>} ChainAccountKit */
|
|
@@ -25,14 +25,14 @@ const trace = makeTracer('StakingAccountHolder');
|
|
|
25
25
|
const { Fail } = assert;
|
|
26
26
|
/**
|
|
27
27
|
* @typedef {object} StakingAccountNotification
|
|
28
|
-
* @property {
|
|
28
|
+
* @property {ChainAddress} chainAddress
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* @typedef {{
|
|
33
33
|
* topicKit: RecorderKit<StakingAccountNotification>;
|
|
34
34
|
* account: ChainAccount;
|
|
35
|
-
* chainAddress:
|
|
35
|
+
* chainAddress: ChainAddress;
|
|
36
36
|
* }} State
|
|
37
37
|
*/
|
|
38
38
|
|
|
@@ -71,7 +71,7 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
|
|
|
71
71
|
/**
|
|
72
72
|
* @param {ChainAccount} account
|
|
73
73
|
* @param {StorageNode} storageNode
|
|
74
|
-
* @param {
|
|
74
|
+
* @param {ChainAddress} chainAddress
|
|
75
75
|
* @returns {State}
|
|
76
76
|
*/
|
|
77
77
|
(account, storageNode, chainAddress) => {
|
|
@@ -94,7 +94,7 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
|
|
|
94
94
|
return this.state.topicKit.recorder;
|
|
95
95
|
},
|
|
96
96
|
// TODO move this beneath the Orchestration abstraction,
|
|
97
|
-
// to the OrchestrationAccount provided by
|
|
97
|
+
// to the OrchestrationAccount provided by makeAccount()
|
|
98
98
|
/**
|
|
99
99
|
* _Assumes users has already sent funds to their ICA, until #9193
|
|
100
100
|
* @param {string} validatorAddress
|
|
@@ -109,7 +109,7 @@ export const prepareStakingAccountKit = (baggage, makeRecorderKit, zcf) => {
|
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
const account = this.facets.helper.owned();
|
|
112
|
-
const delegatorAddress = this.state.chainAddress;
|
|
112
|
+
const delegatorAddress = this.state.chainAddress.address;
|
|
113
113
|
|
|
114
114
|
const result = await E(account).executeEncodedTx([
|
|
115
115
|
/** @type {AnyJson} */ (
|
package/src/service.js
CHANGED
|
@@ -1,31 +1,22 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
/** @file Orchestration service */
|
|
3
|
-
import { NonNullish } from '@agoric/assert';
|
|
4
|
-
import { makeTracer } from '@agoric/internal';
|
|
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
|
|
|
9
7
|
import { V as E } from '@agoric/vat-data/vow.js';
|
|
10
8
|
import { M } from '@endo/patterns';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { makeICAConnectionAddress, parseAddress } from './utils/address.js';
|
|
14
|
-
import { makeTxPacket, parsePacketAck } from './utils/tx.js';
|
|
9
|
+
import { prepareChainAccountKit } from './exos/chainAccountKit.js';
|
|
10
|
+
import { makeICAConnectionAddress } from './utils/address.js';
|
|
15
11
|
|
|
16
12
|
/**
|
|
17
|
-
* @import {
|
|
18
|
-
* @import {Remote} from '@agoric/vow';
|
|
13
|
+
* @import { PortAllocator} from '@agoric/network';
|
|
19
14
|
* @import { IBCConnectionID } from '@agoric/vats';
|
|
20
15
|
* @import { Zone } from '@agoric/base-zone';
|
|
21
|
-
* @import {
|
|
22
|
-
* @import { AttenuatedNetwork, ChainAccount, ChainAddress } from './types.js';
|
|
16
|
+
* @import { ChainAccount } from './types.js';
|
|
23
17
|
*/
|
|
24
18
|
|
|
25
19
|
const { Fail, bare } = assert;
|
|
26
|
-
const trace = makeTracer('Orchestration');
|
|
27
|
-
|
|
28
|
-
/** @import {AnyJson} from '@agoric/cosmic-proto'; */
|
|
29
20
|
|
|
30
21
|
/**
|
|
31
22
|
* @typedef {object} OrchestrationPowers
|
|
@@ -53,173 +44,17 @@ const getPower = (powers, name) => {
|
|
|
53
44
|
return /** @type {OrchestrationPowers[K]} */ (powers.get(name));
|
|
54
45
|
};
|
|
55
46
|
|
|
56
|
-
export const Proto3Shape = {
|
|
57
|
-
typeUrl: M.string(),
|
|
58
|
-
value: M.string(),
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export const ChainAccountI = M.interface('ChainAccount', {
|
|
62
|
-
getAccountAddress: M.call().returns(M.string()),
|
|
63
|
-
getLocalAddress: M.call().returns(M.string()),
|
|
64
|
-
getRemoteAddress: M.call().returns(M.string()),
|
|
65
|
-
getPort: M.call().returns(M.remotable('Port')),
|
|
66
|
-
executeTx: M.call(M.arrayOf(M.record())).returns(M.promise()),
|
|
67
|
-
executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
|
|
68
|
-
.optional(M.record())
|
|
69
|
-
.returns(M.promise()),
|
|
70
|
-
close: M.callWhen().returns(M.undefined()),
|
|
71
|
-
deposit: M.callWhen(PaymentShape).returns(M.undefined()),
|
|
72
|
-
getPurse: M.callWhen().returns(PurseShape),
|
|
73
|
-
prepareTransfer: M.callWhen().returns(InvitationShape),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
export const ConnectionHandlerI = M.interface('ConnectionHandler', {
|
|
77
|
-
onOpen: M.callWhen(M.any(), M.string(), M.string(), M.any()).returns(M.any()),
|
|
78
|
-
onClose: M.callWhen(M.any(), M.any(), M.any()).returns(M.any()),
|
|
79
|
-
onReceive: M.callWhen(M.any(), M.string()).returns(M.any()),
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
/** @param {Zone} zone */
|
|
83
|
-
const prepareChainAccount = zone =>
|
|
84
|
-
zone.exoClassKit(
|
|
85
|
-
'ChainAccount',
|
|
86
|
-
{ account: ChainAccountI, connectionHandler: ConnectionHandlerI },
|
|
87
|
-
/**
|
|
88
|
-
* @param {Port} port
|
|
89
|
-
* @param {string} requestedRemoteAddress
|
|
90
|
-
*/
|
|
91
|
-
(port, requestedRemoteAddress) =>
|
|
92
|
-
/**
|
|
93
|
-
* @type {{
|
|
94
|
-
* port: Port;
|
|
95
|
-
* connection: Remote<Connection> | undefined;
|
|
96
|
-
* localAddress: string | undefined;
|
|
97
|
-
* requestedRemoteAddress: string;
|
|
98
|
-
* remoteAddress: string | undefined;
|
|
99
|
-
* accountAddress: string | undefined;
|
|
100
|
-
* }}
|
|
101
|
-
*/ (
|
|
102
|
-
harden({
|
|
103
|
-
port,
|
|
104
|
-
connection: undefined,
|
|
105
|
-
requestedRemoteAddress,
|
|
106
|
-
remoteAddress: undefined,
|
|
107
|
-
accountAddress: undefined,
|
|
108
|
-
localAddress: undefined,
|
|
109
|
-
})
|
|
110
|
-
),
|
|
111
|
-
{
|
|
112
|
-
account: {
|
|
113
|
-
/**
|
|
114
|
-
* @returns {string} the address of the account on the chain
|
|
115
|
-
*/
|
|
116
|
-
getAccountAddress() {
|
|
117
|
-
return NonNullish(
|
|
118
|
-
this.state.accountAddress,
|
|
119
|
-
'Error parsing account address from remote address',
|
|
120
|
-
);
|
|
121
|
-
},
|
|
122
|
-
getLocalAddress() {
|
|
123
|
-
return NonNullish(
|
|
124
|
-
this.state.localAddress,
|
|
125
|
-
'local address not available',
|
|
126
|
-
);
|
|
127
|
-
},
|
|
128
|
-
getRemoteAddress() {
|
|
129
|
-
return NonNullish(
|
|
130
|
-
this.state.remoteAddress,
|
|
131
|
-
'remote address not available',
|
|
132
|
-
);
|
|
133
|
-
},
|
|
134
|
-
getPort() {
|
|
135
|
-
return this.state.port;
|
|
136
|
-
},
|
|
137
|
-
executeTx() {
|
|
138
|
-
throw new Error('not yet implemented');
|
|
139
|
-
},
|
|
140
|
-
/**
|
|
141
|
-
* Submit a transaction on behalf of the remote account for execution on the remote chain.
|
|
142
|
-
* @param {AnyJson[]} msgs
|
|
143
|
-
* @param {Omit<TxBody, 'messages'>} [opts]
|
|
144
|
-
* @returns {Promise<string>} - base64 encoded bytes string. Can be decoded using the corresponding `Msg*Response` object.
|
|
145
|
-
* @throws {Error} if packet fails to send or an error is returned
|
|
146
|
-
*/
|
|
147
|
-
executeEncodedTx(msgs, opts) {
|
|
148
|
-
const { connection } = this.state;
|
|
149
|
-
if (!connection) throw Fail`connection not available`;
|
|
150
|
-
return E.when(
|
|
151
|
-
E(connection).send(makeTxPacket(msgs, opts)),
|
|
152
|
-
// if parsePacketAck cannot find a `result` key, it throws
|
|
153
|
-
ack => parsePacketAck(ack),
|
|
154
|
-
);
|
|
155
|
-
},
|
|
156
|
-
/**
|
|
157
|
-
* Close the remote account
|
|
158
|
-
*/
|
|
159
|
-
async close() {
|
|
160
|
-
/// XXX 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
|
-
await E(connection).close();
|
|
166
|
-
},
|
|
167
|
-
async deposit(payment) {
|
|
168
|
-
console.log('deposit got', payment);
|
|
169
|
-
throw new Error('not yet implemented');
|
|
170
|
-
},
|
|
171
|
-
/**
|
|
172
|
-
* get Purse for a brand to .withdraw() a Payment from the account
|
|
173
|
-
* @param {Brand} brand
|
|
174
|
-
*/
|
|
175
|
-
async getPurse(brand) {
|
|
176
|
-
console.log('getPurse got', brand);
|
|
177
|
-
throw new Error('not yet implemented');
|
|
178
|
-
},
|
|
179
|
-
|
|
180
|
-
/* transfer account to new holder */
|
|
181
|
-
async prepareTransfer() {
|
|
182
|
-
throw new Error('not yet implemented');
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
connectionHandler: {
|
|
186
|
-
/**
|
|
187
|
-
* @param {Remote<Connection>} connection
|
|
188
|
-
* @param {string} localAddr
|
|
189
|
-
* @param {string} remoteAddr
|
|
190
|
-
*/
|
|
191
|
-
async onOpen(connection, localAddr, remoteAddr) {
|
|
192
|
-
trace(`ICA Channel Opened for ${localAddr} at ${remoteAddr}`);
|
|
193
|
-
this.state.connection = connection;
|
|
194
|
-
this.state.remoteAddress = remoteAddr;
|
|
195
|
-
this.state.localAddress = localAddr;
|
|
196
|
-
// XXX parseAddress currently throws, should it return '' instead?
|
|
197
|
-
this.state.accountAddress = parseAddress(remoteAddr);
|
|
198
|
-
},
|
|
199
|
-
async onClose(_connection, reason) {
|
|
200
|
-
trace(`ICA Channel closed. Reason: ${reason}`);
|
|
201
|
-
// XXX handle connection closing
|
|
202
|
-
// XXX is there a scenario where a connection will unexpectedly close? _I think yes_
|
|
203
|
-
},
|
|
204
|
-
async onReceive(connection, bytes) {
|
|
205
|
-
trace(`ICA Channel onReceive`, connection, bytes);
|
|
206
|
-
return '';
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
},
|
|
210
|
-
);
|
|
211
|
-
|
|
212
47
|
export const OrchestrationI = M.interface('Orchestration', {
|
|
213
|
-
|
|
48
|
+
makeAccount: M.callWhen(M.string(), M.string()).returns(
|
|
214
49
|
M.remotable('ChainAccount'),
|
|
215
50
|
),
|
|
216
51
|
});
|
|
217
52
|
|
|
218
53
|
/**
|
|
219
54
|
* @param {Zone} zone
|
|
220
|
-
* @param {ReturnType<typeof
|
|
55
|
+
* @param {ReturnType<typeof prepareChainAccountKit>} makeChainAccountKit
|
|
221
56
|
*/
|
|
222
|
-
const prepareOrchestration = (zone,
|
|
57
|
+
const prepareOrchestration = (zone, makeChainAccountKit) =>
|
|
223
58
|
zone.exoClassKit(
|
|
224
59
|
'Orchestration',
|
|
225
60
|
{
|
|
@@ -254,20 +89,23 @@ const prepareOrchestration = (zone, createChainAccount) =>
|
|
|
254
89
|
* self connection_id
|
|
255
90
|
* @returns {Promise<ChainAccount>}
|
|
256
91
|
*/
|
|
257
|
-
async
|
|
92
|
+
async makeAccount(hostConnectionId, controllerConnectionId) {
|
|
258
93
|
const port = await this.facets.self.bindPort();
|
|
259
94
|
|
|
260
95
|
const remoteConnAddr = makeICAConnectionAddress(
|
|
261
96
|
hostConnectionId,
|
|
262
97
|
controllerConnectionId,
|
|
263
98
|
);
|
|
264
|
-
const
|
|
99
|
+
const chainAccountKit = makeChainAccountKit(port, remoteConnAddr);
|
|
265
100
|
|
|
266
101
|
// await so we do not return a ChainAccount before it successfully instantiates
|
|
267
|
-
await E(port).connect(
|
|
102
|
+
await E(port).connect(
|
|
103
|
+
remoteConnAddr,
|
|
104
|
+
chainAccountKit.connectionHandler,
|
|
105
|
+
);
|
|
268
106
|
// XXX if we fail, should we close the port (if it was created in this flow)?
|
|
269
107
|
|
|
270
|
-
return
|
|
108
|
+
return chainAccountKit.account;
|
|
271
109
|
},
|
|
272
110
|
},
|
|
273
111
|
},
|
|
@@ -275,14 +113,13 @@ const prepareOrchestration = (zone, createChainAccount) =>
|
|
|
275
113
|
|
|
276
114
|
/** @param {Zone} zone */
|
|
277
115
|
export const prepareOrchestrationTools = zone => {
|
|
278
|
-
const
|
|
279
|
-
const makeOrchestration = prepareOrchestration(zone,
|
|
116
|
+
const makeChainAccountKit = prepareChainAccountKit(zone);
|
|
117
|
+
const makeOrchestration = prepareOrchestration(zone, makeChainAccountKit);
|
|
280
118
|
|
|
281
119
|
return harden({ makeOrchestration });
|
|
282
120
|
};
|
|
283
121
|
harden(prepareOrchestrationTools);
|
|
284
122
|
|
|
285
|
-
/** @typedef {ReturnType<ReturnType<typeof prepareChainAccount>>} ChainAccountKit */
|
|
286
123
|
/** @typedef {ReturnType<typeof prepareOrchestrationTools>} OrchestrationTools */
|
|
287
124
|
/** @typedef {ReturnType<OrchestrationTools['makeOrchestration']>} OrchestrationKit */
|
|
288
125
|
/** @typedef {OrchestrationKit['public']} OrchestrationService */
|
package/src/types.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Invitation } from '@agoric/zoe/exported.js';
|
|
|
4
4
|
import type { Any } from '@agoric/cosmic-proto/google/protobuf/any';
|
|
5
5
|
import type { AnyJson } from '@agoric/cosmic-proto';
|
|
6
6
|
import type {
|
|
7
|
-
|
|
7
|
+
MsgBeginRedelegateResponse,
|
|
8
8
|
MsgUndelegateResponse,
|
|
9
9
|
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
|
|
10
10
|
import type {
|
|
@@ -12,6 +12,13 @@ import type {
|
|
|
12
12
|
Redelegation,
|
|
13
13
|
UnbondingDelegation,
|
|
14
14
|
} from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js';
|
|
15
|
+
import type { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
|
|
16
|
+
import type {
|
|
17
|
+
LocalIbcAddress,
|
|
18
|
+
RemoteIbcAddress,
|
|
19
|
+
} from '@agoric/vats/tools/ibc-utils.js';
|
|
20
|
+
import type { Port } from '@agoric/network';
|
|
21
|
+
import { MsgTransferResponse } from '@agoric/cosmic-proto/ibc/applications/transfer/v1/tx.js';
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* static declaration of known chain types will allow type support for
|
|
@@ -203,7 +210,7 @@ export interface Chain<C extends keyof KnownChains> {
|
|
|
203
210
|
* Creates a new account on the remote chain.
|
|
204
211
|
* @returns an object that controls a new remote account on Chain
|
|
205
212
|
*/
|
|
206
|
-
|
|
213
|
+
makeAccount: () => Promise<OrchestrationAccount<C>>;
|
|
207
214
|
// FUTURE supply optional port object; also fetch port object
|
|
208
215
|
|
|
209
216
|
/**
|
|
@@ -230,7 +237,7 @@ export interface ChainAccount {
|
|
|
230
237
|
/**
|
|
231
238
|
* @returns the address of the account on the remote chain
|
|
232
239
|
*/
|
|
233
|
-
|
|
240
|
+
getAddress: () => ChainAddress;
|
|
234
241
|
/**
|
|
235
242
|
* Submit a transaction on behalf of the remote account for execution on the remote chain.
|
|
236
243
|
* @param msgs - records for the transaction
|
|
@@ -239,10 +246,14 @@ export interface ChainAccount {
|
|
|
239
246
|
executeTx: (msgs: Proto3JSONMsg[]) => Promise<string>;
|
|
240
247
|
/**
|
|
241
248
|
* Submit a transaction on behalf of the remote account for execution on the remote chain.
|
|
242
|
-
* @param msgs - records for the transaction
|
|
243
|
-
* @
|
|
249
|
+
* @param {AnyJson[]} msgs - records for the transaction
|
|
250
|
+
* @param {Partial<Omit<TxBody, 'messages'>>} [opts] - optional parameters for the Tx, like `timeoutHeight` and `memo`
|
|
251
|
+
* @returns acknowledgement string
|
|
244
252
|
*/
|
|
245
|
-
executeEncodedTx: (
|
|
253
|
+
executeEncodedTx: (
|
|
254
|
+
msgs: AnyJson[],
|
|
255
|
+
opts?: Partial<Omit<TxBody, 'messages'>>,
|
|
256
|
+
) => Promise<string>;
|
|
246
257
|
/** deposit payment from zoe to the account*/
|
|
247
258
|
deposit: (payment: Payment) => Promise<void>;
|
|
248
259
|
/** get Purse for a brand to .withdraw() a Payment from the account */
|
|
@@ -253,16 +264,12 @@ export interface ChainAccount {
|
|
|
253
264
|
close: () => Promise<void>;
|
|
254
265
|
/* transfer account to new holder */
|
|
255
266
|
prepareTransfer: () => Promise<Invitation>;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
* Resolves when the undelegation is complete and the tokens are no longer bonded.
|
|
263
|
-
* Note it may take weeks.
|
|
264
|
-
*/
|
|
265
|
-
completion: Promise<void>;
|
|
267
|
+
/** @returns the address of the remote channel */
|
|
268
|
+
getRemoteAddress: () => RemoteIbcAddress;
|
|
269
|
+
/** @returns the address of the local channel */
|
|
270
|
+
getLocalAddress: () => LocalIbcAddress;
|
|
271
|
+
/** @returns the port the ICA channel is bound to */
|
|
272
|
+
getPort: () => Port;
|
|
266
273
|
}
|
|
267
274
|
|
|
268
275
|
/**
|
|
@@ -275,7 +282,7 @@ export interface BaseOrchestrationAccount {
|
|
|
275
282
|
/**
|
|
276
283
|
* @returns the address of the account on the remote chain
|
|
277
284
|
*/
|
|
278
|
-
|
|
285
|
+
getAddress: () => ChainAddress;
|
|
279
286
|
|
|
280
287
|
/** @returns an array of amounts for every balance in the account. */
|
|
281
288
|
getBalances: () => Promise<ChainAmount[]>;
|
|
@@ -361,13 +368,14 @@ export interface BaseOrchestrationAccount {
|
|
|
361
368
|
srcValidator: CosmosValidatorAddress,
|
|
362
369
|
dstValidator: CosmosValidatorAddress,
|
|
363
370
|
amount: AmountArg,
|
|
364
|
-
) => Promise<
|
|
371
|
+
) => Promise<MsgBeginRedelegateResponse>;
|
|
365
372
|
|
|
366
373
|
/**
|
|
367
374
|
* Undelegate multiple delegations (concurrently). To delegate independently, pass an array with one item.
|
|
368
|
-
*
|
|
375
|
+
* Resolves when the undelegation is complete and the tokens are no longer bonded. Note it may take weeks.
|
|
376
|
+
* @param {Delegation[]} delegations - the delegation to undelegate
|
|
369
377
|
*/
|
|
370
|
-
undelegate: (delegations: Delegation[]) => Promise<
|
|
378
|
+
undelegate: (delegations: Delegation[]) => Promise<MsgUndelegateResponse>;
|
|
371
379
|
|
|
372
380
|
/**
|
|
373
381
|
* Withdraw rewards from all validators. The promise settles when the rewards are withdrawn.
|
|
@@ -396,7 +404,7 @@ export interface BaseOrchestrationAccount {
|
|
|
396
404
|
amount: AmountArg,
|
|
397
405
|
destination: ChainAddress,
|
|
398
406
|
memo?: string,
|
|
399
|
-
) => Promise<
|
|
407
|
+
) => Promise<MsgTransferResponse>;
|
|
400
408
|
|
|
401
409
|
/**
|
|
402
410
|
* Transfer an amount to another account in multiple steps. The promise settles when
|
|
@@ -405,7 +413,10 @@ export interface BaseOrchestrationAccount {
|
|
|
405
413
|
* @param msg - the transfer message, including follow-up steps
|
|
406
414
|
* @returns void
|
|
407
415
|
*/
|
|
408
|
-
transferSteps: (
|
|
416
|
+
transferSteps: (
|
|
417
|
+
amount: AmountArg,
|
|
418
|
+
msg: TransferMsg,
|
|
419
|
+
) => Promise<MsgTransferResponse>;
|
|
409
420
|
/**
|
|
410
421
|
* deposit payment from zoe to the account. For remote accounts,
|
|
411
422
|
* an IBC Transfer will be executed to transfer funds there.
|
|
@@ -428,12 +439,11 @@ export type TransferMsg = {
|
|
|
428
439
|
data?: object;
|
|
429
440
|
};
|
|
430
441
|
|
|
431
|
-
// Example
|
|
432
|
-
// await icaNoble.transferSteps(usdcAmt,
|
|
433
|
-
// osmosisSwap(tiaBrand, { pool: 1224, slippage: 0.05 }, icaCel.getAddress()));
|
|
434
|
-
|
|
435
442
|
/**
|
|
436
443
|
* @param pool - Required. Pool number
|
|
444
|
+
* @example
|
|
445
|
+
* await icaNoble.transferSteps(usdcAmt,
|
|
446
|
+
* osmosisSwap(tiaBrand, { pool: 1224, slippage: 0.05 }, icaCel.getAddress()));
|
|
437
447
|
*/
|
|
438
448
|
export type OsmoSwapOptions = {
|
|
439
449
|
pool: string;
|
|
@@ -452,6 +462,10 @@ export type OsmoSwapFn = (
|
|
|
452
462
|
next: TransferMsg | ChainAddress,
|
|
453
463
|
) => TransferMsg;
|
|
454
464
|
|
|
455
|
-
type AfterAction = { destChain: string; destAddress: ChainAddress };
|
|
456
|
-
type SwapExact = { amountIn: Amount; amountOut: Amount };
|
|
457
|
-
type SwapMaxSlippage = {
|
|
465
|
+
export type AfterAction = { destChain: string; destAddress: ChainAddress };
|
|
466
|
+
export type SwapExact = { amountIn: Amount; amountOut: Amount };
|
|
467
|
+
export type SwapMaxSlippage = {
|
|
468
|
+
amountIn: Amount;
|
|
469
|
+
brandOut: Brand;
|
|
470
|
+
slippage: number;
|
|
471
|
+
};
|