@agoric/orchestration 0.1.1-dev-4c65fc2.0 → 0.1.1-dev-76b8d53.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
@@ -2,4 +2,4 @@
2
2
  export * from './src/types.js';
3
3
  export * from './src/utils/address.js';
4
4
  export * from './src/utils/tx.js';
5
- export * from './src/orchestration.js';
5
+ export * from './src/service.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/orchestration",
3
- "version": "0.1.1-dev-4c65fc2.0+4c65fc2",
3
+ "version": "0.1.1-dev-76b8d53.0+76b8d53",
4
4
  "description": "Chain abstraction for Agoric's orchestration clients",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -29,17 +29,18 @@
29
29
  },
30
30
  "homepage": "https://github.com/Agoric/agoric-sdk#readme",
31
31
  "dependencies": {
32
- "@agoric/assert": "0.6.1-dev-4c65fc2.0+4c65fc2",
33
- "@agoric/cosmic-proto": "0.4.1-dev-4c65fc2.0+4c65fc2",
34
- "@agoric/ertp": "0.16.3-dev-4c65fc2.0+4c65fc2",
35
- "@agoric/internal": "0.3.3-dev-4c65fc2.0+4c65fc2",
36
- "@agoric/network": "0.1.1-dev-4c65fc2.0+4c65fc2",
37
- "@agoric/notifier": "0.6.3-dev-4c65fc2.0+4c65fc2",
38
- "@agoric/store": "0.9.3-dev-4c65fc2.0+4c65fc2",
39
- "@agoric/vat-data": "0.5.3-dev-4c65fc2.0+4c65fc2",
40
- "@agoric/vats": "0.15.2-dev-4c65fc2.0+4c65fc2",
41
- "@agoric/zoe": "0.26.3-dev-4c65fc2.0+4c65fc2",
42
- "@agoric/zone": "0.2.3-dev-4c65fc2.0+4c65fc2",
32
+ "@agoric/assert": "0.6.1-dev-76b8d53.0+76b8d53",
33
+ "@agoric/cosmic-proto": "0.4.1-dev-76b8d53.0+76b8d53",
34
+ "@agoric/ertp": "0.16.3-dev-76b8d53.0+76b8d53",
35
+ "@agoric/internal": "0.3.3-dev-76b8d53.0+76b8d53",
36
+ "@agoric/network": "0.1.1-dev-76b8d53.0+76b8d53",
37
+ "@agoric/notifier": "0.6.3-dev-76b8d53.0+76b8d53",
38
+ "@agoric/store": "0.9.3-dev-76b8d53.0+76b8d53",
39
+ "@agoric/time": "0.3.3-dev-76b8d53.0+76b8d53",
40
+ "@agoric/vat-data": "0.5.3-dev-76b8d53.0+76b8d53",
41
+ "@agoric/vats": "0.15.2-dev-76b8d53.0+76b8d53",
42
+ "@agoric/zoe": "0.26.3-dev-76b8d53.0+76b8d53",
43
+ "@agoric/zone": "0.2.3-dev-76b8d53.0+76b8d53",
43
44
  "@endo/base64": "^1.0.4",
44
45
  "@endo/far": "^1.1.1",
45
46
  "@endo/marshal": "^1.4.1",
@@ -81,5 +82,5 @@
81
82
  "typeCoverage": {
82
83
  "atLeast": 96.39
83
84
  },
84
- "gitHead": "4c65fc27a3e151be82ad228d58a7610d51b3c4a2"
85
+ "gitHead": "76b8d531fd9779664f767297a1a079ac500af731"
85
86
  }
@@ -11,7 +11,7 @@ import { prepareStakingAccountHolder } from './stakingAccountHolder.js';
11
11
 
12
12
  const trace = makeTracer('StakeAtom');
13
13
  /**
14
- * @import { Orchestration } from '../types.js';
14
+ * @import { OrchestrationService } from '../service.js'
15
15
  * @import { Baggage } from '@agoric/vat-data';
16
16
  * @import { IBCConnectionID } from '@agoric/vats';
17
17
  */
@@ -27,7 +27,7 @@ const trace = makeTracer('StakeAtom');
27
27
  *
28
28
  * @param {ZCF<StakeAtomTerms>} zcf
29
29
  * @param {{
30
- * orchestration: Orchestration;
30
+ * orchestration: OrchestrationService;
31
31
  * storageNode: StorageNode;
32
32
  * marshaller: Marshaller;
33
33
  * }} privateArgs
@@ -32,7 +32,7 @@ const { Fail } = assert;
32
32
  * @typedef {{
33
33
  * topicKit: RecorderKit<StakingAccountNotification>;
34
34
  * account: ChainAccount;
35
- * chainAddress: ChainAddress;
35
+ * chainAddress: string;
36
36
  * }} State
37
37
  */
38
38
 
@@ -71,7 +71,7 @@ export const prepareStakingAccountHolder = (baggage, makeRecorderKit, zcf) => {
71
71
  /**
72
72
  * @param {ChainAccount} account
73
73
  * @param {StorageNode} storageNode
74
- * @param {ChainAddress} chainAddress
74
+ * @param {string} chainAddress
75
75
  * @returns {State}
76
76
  */
77
77
  (account, storageNode, chainAddress) => {
@@ -93,6 +93,8 @@ export const prepareStakingAccountHolder = (baggage, makeRecorderKit, zcf) => {
93
93
  getUpdater() {
94
94
  return this.state.topicKit.recorder;
95
95
  },
96
+ // TODO move this beneath the Orchestration abstraction,
97
+ // to the OrchestrationAccount provided by createAccount()
96
98
  /**
97
99
  * _Assumes users has already sent funds to their ICA, until #9193
98
100
  * @param {string} validatorAddress
@@ -0,0 +1,102 @@
1
+ // @ts-check
2
+ import { Fail } from '@agoric/assert';
3
+ import { AmountMath, AmountShape } from '@agoric/ertp';
4
+ import { E, Far } from '@endo/far';
5
+ import { M } from '@endo/patterns';
6
+ import { makeOrchestrationFacade } from '../facade.js';
7
+ import { orcUtils } from '../utils/orc.js';
8
+
9
+ /**
10
+ * @import {Orchestrator, ChainAccount, CosmosValidatorAddress} from '../types.js'
11
+ * @import {TimerService} from '@agoric/time';
12
+ * @import {ERef} from '@endo/far'
13
+ * @import {OrchestrationService} from '../service.js';
14
+ * @import {Zone} from '@agoric/zone';
15
+ */
16
+
17
+ /**
18
+ * @param {ZCF} zcf
19
+ * @param {{
20
+ * orchestrationService: ERef<OrchestrationService>;
21
+ * storageNode: ERef<StorageNode>;
22
+ * timerService: ERef<TimerService>;
23
+ * zone: Zone;
24
+ * }} privateArgs
25
+ */
26
+ export const start = async (zcf, privateArgs) => {
27
+ const { orchestrationService, storageNode, timerService, zone } = privateArgs;
28
+
29
+ const { orchestrate } = makeOrchestrationFacade({
30
+ zone,
31
+ timerService,
32
+ zcf,
33
+ storageNode,
34
+ orchestrationService,
35
+ });
36
+
37
+ /** deprecated historical example */
38
+ /** @type {OfferHandler} */
39
+ const swapAndStakeHandler = orchestrate(
40
+ 'LSTTia',
41
+ { zcf },
42
+ // eslint-disable-next-line no-shadow -- this `zcf` is enclosed in a membrane
43
+ async (/** @type {Orchestrator} */ orch, { zcf }, seat, offerArgs) => {
44
+ const { give } = seat.getProposal();
45
+ !AmountMath.isEmpty(give.USDC.value) || Fail`Must provide USDC.`;
46
+
47
+ const celestia = await orch.getChain('celestia');
48
+ const agoric = await orch.getChain('agoric');
49
+
50
+ const [celestiaAccount, localAccount] = await Promise.all([
51
+ celestia.createAccount(),
52
+ agoric.createAccount(),
53
+ ]);
54
+
55
+ const tiaAddress = await celestiaAccount.getChainAddress();
56
+
57
+ // deposit funds from user seat to LocalChainAccount
58
+ const seatKit = zcf.makeEmptySeatKit();
59
+ zcf.atomicRearrange(harden([[seat, seatKit.zcfSeat, give]]));
60
+ // seat.exit() // exit user seat now, or later?
61
+ const payment = await E(seatKit.userSeat).getPayout('USDC');
62
+ await localAccount.deposit(payment);
63
+
64
+ // build swap instructions with orcUtils library
65
+ const transferMsg = orcUtils.makeOsmosisSwap({
66
+ destChain: 'celestia',
67
+ destAddress: tiaAddress,
68
+ amountIn: give.USDC,
69
+ brandOut: offerArgs.staked.brand,
70
+ slippage: 0.03,
71
+ });
72
+
73
+ await localAccount
74
+ .transferSteps(give.USDC, transferMsg)
75
+ .then(_txResult =>
76
+ celestiaAccount.delegate(offerArgs.validator, offerArgs.staked),
77
+ )
78
+ .catch(e => console.error(e));
79
+
80
+ // XXX close localAccount?
81
+ return celestiaAccount; // should be continuing inv since this is an offer?
82
+ },
83
+ );
84
+
85
+ const makeSwapAndStakeInvitation = () =>
86
+ zcf.makeInvitation(
87
+ swapAndStakeHandler,
88
+ 'Swap for TIA and stake',
89
+ undefined,
90
+ harden({
91
+ give: { USDC: AmountShape },
92
+ want: {}, // XXX ChainAccount Ownable?
93
+ exit: M.any(),
94
+ }),
95
+ );
96
+
97
+ const publicFacet = Far('SwapAndStake Public Facet', {
98
+ makeSwapAndStakeInvitation,
99
+ });
100
+
101
+ return harden({ publicFacet });
102
+ };
@@ -0,0 +1,87 @@
1
+ // @ts-check
2
+ import { Fail } from '@agoric/assert';
3
+ import { AmountMath, AmountShape } from '@agoric/ertp';
4
+ import { Far } from '@endo/far';
5
+ import { M } from '@endo/patterns';
6
+ import { makeOrchestrationFacade } from '../facade.js';
7
+
8
+ /**
9
+ * @import {Orchestrator, ChainAccount, CosmosValidatorAddress} from '../types.js'
10
+ * @import {TimerService} from '@agoric/time';
11
+ * @import {ERef} from '@endo/far'
12
+ * @import {OrchestrationService} from '../service.js';
13
+ * @import {Zone} from '@agoric/zone';
14
+ */
15
+
16
+ /**
17
+ * @param {ZCF} zcf
18
+ * @param {{
19
+ * orchestrationService: ERef<OrchestrationService>;
20
+ * storageNode: ERef<StorageNode>;
21
+ * timerService: ERef<TimerService>;
22
+ * zone: Zone;
23
+ * }} privateArgs
24
+ */
25
+ export const start = async (zcf, privateArgs) => {
26
+ const { orchestrationService, storageNode, timerService, zone } = privateArgs;
27
+
28
+ const { orchestrate } = makeOrchestrationFacade({
29
+ zone,
30
+ timerService,
31
+ zcf,
32
+ storageNode,
33
+ orchestrationService,
34
+ });
35
+
36
+ /** @type {OfferHandler} */
37
+ const unbondAndLiquidStake = orchestrate(
38
+ 'LSTTia',
39
+ { zcf },
40
+ // eslint-disable-next-line no-shadow -- this `zcf` is enclosed in a membrane
41
+ async (/** @type {Orchestrator} */ orch, { zcf }, seat, _offerArgs) => {
42
+ console.log('zcf within the membrane', zcf);
43
+ const { give } = seat.getProposal();
44
+ !AmountMath.isEmpty(give.USDC.value) || Fail`Must provide USDC.`;
45
+
46
+ // We would actually alreaady have the account from the orchestrator
47
+ // ??? could these be passed in? It would reduce the size of this handler,
48
+ // keeping it focused on long-running operations.
49
+ const celestia = await orch.getChain('celestia');
50
+ const celestiaAccount = await celestia.createAccount();
51
+
52
+ const delegations = await celestiaAccount.getDelegations();
53
+ const [undelegation] = await celestiaAccount.undelegate(delegations);
54
+
55
+ // wait for the undelegations to be complete (may take weeks)
56
+ await undelegation.completion;
57
+
58
+ // ??? should this be synchronous? depends on how names are resolved.
59
+ const stride = await orch.getChain('stride');
60
+ const strideAccount = await stride.createAccount();
61
+
62
+ // TODO the `TIA` string actually needs to be the Brand from AgoricNames
63
+ const tiaAmt = await celestiaAccount.getBalance('TIA');
64
+ await celestiaAccount.transfer(tiaAmt, strideAccount.getChainAddress());
65
+
66
+ await strideAccount.liquidStake(tiaAmt);
67
+ },
68
+ );
69
+
70
+ const makeUnbondAndLiquidStakeInvitation = () =>
71
+ zcf.makeInvitation(
72
+ unbondAndLiquidStake,
73
+ 'Unbond and liquid stake',
74
+ undefined,
75
+ harden({
76
+ give: { USDC: AmountShape },
77
+ want: {}, // XXX ChainAccount Ownable?
78
+ exit: M.any(),
79
+ }),
80
+ );
81
+
82
+ const publicFacet = Far('SwapAndStake Public Facet', {
83
+ makeUnbondAndLiquidStakeInvitation,
84
+ });
85
+
86
+ return harden({ publicFacet });
87
+ };
package/src/facade.js ADDED
@@ -0,0 +1,47 @@
1
+ // @ts-check
2
+ /** @file Orchestration service */
3
+
4
+ /**
5
+ * @import {Zone} from '@agoric/zone';
6
+ * @import {TimerService} from '@agoric/time';
7
+ * @import {OrchestrationService} from './service.js';
8
+ * @import {OrchestrationHandlerMaker} from './types.js';
9
+ */
10
+
11
+ /**
12
+ *
13
+ * @param {{
14
+ * zone: Zone;
15
+ * timerService: ERef<TimerService>;
16
+ * zcf: ERef<ZCF>;
17
+ * storageNode: ERef<StorageNode>;
18
+ * orchestrationService: ERef<OrchestrationService>;
19
+ * }} powers
20
+ */
21
+ export const makeOrchestrationFacade = ({
22
+ zone,
23
+ timerService,
24
+ zcf,
25
+ storageNode,
26
+ orchestrationService,
27
+ }) => {
28
+ console.log('makeOrchestrationFacade got', {
29
+ zone,
30
+ timerService,
31
+ zcf,
32
+ storageNode,
33
+ orchestrationService,
34
+ });
35
+
36
+ return {
37
+ /**
38
+ * @type {OrchestrationHandlerMaker}
39
+ */
40
+ orchestrate(durableName, ctx, fn) {
41
+ console.log('orchestrate got', durableName, ctx, fn);
42
+ throw new Error('Not yet implemented');
43
+ },
44
+ };
45
+ };
46
+ harden(makeOrchestrationFacade);
47
+ /** @typedef {ReturnType<typeof makeOrchestrationFacade>} OrchestrationFacade */
@@ -2,7 +2,11 @@
2
2
  import { V as E } from '@agoric/vat-data/vow.js';
3
3
  import { Far } from '@endo/far';
4
4
 
5
- /** @import { AttenuatedNetwork, Orchestration, OrchestrationVat } from '../types' */
5
+ /**
6
+ * @import { AttenuatedNetwork } from '../types'
7
+ * @import { OrchestrationService } from '../service.js'
8
+ * @import { OrchestrationVat } from '../vat-orchestration.js'
9
+ */
6
10
 
7
11
  /**
8
12
  * @param {BootstrapPowers & {
@@ -67,7 +71,7 @@ export const setupOrchestrationVat = async (
67
71
  /**
68
72
  * @param {BootstrapPowers & {
69
73
  * consume: {
70
- * orchestration: Orchestration;
74
+ * orchestration: OrchestrationService;
71
75
  * };
72
76
  * }} powers
73
77
  * @param {object} _options
@@ -2,14 +2,16 @@
2
2
  /** @file Orchestration service */
3
3
  import { NonNullish } from '@agoric/assert';
4
4
  import { makeTracer } from '@agoric/internal';
5
+ import '@agoric/network/exported.js';
5
6
  import { V as E } from '@agoric/vat-data/vow.js';
6
7
  import { M } from '@endo/patterns';
8
+ import { PaymentShape, PurseShape } from '@agoric/ertp';
9
+ import { InvitationShape } from '@agoric/zoe/src/typeGuards.js';
7
10
  import { makeICAConnectionAddress, parseAddress } from './utils/address.js';
8
11
  import { makeTxPacket, parsePacketAck } from './utils/tx.js';
9
- import '@agoric/network/exported.js';
10
12
 
11
13
  /**
12
- * @import { AttenuatedNetwork } from './types.js';
14
+ * @import { AttenuatedNetwork, ChainAccount, ChainAddress } from './types.js';
13
15
  * @import { IBCConnectionID } from '@agoric/vats';
14
16
  * @import { Zone } from '@agoric/base-zone';
15
17
  * @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js';
@@ -20,9 +22,6 @@ const trace = makeTracer('Orchestration');
20
22
 
21
23
  /** @import {AnyJson} from '@agoric/cosmic-proto'; */
22
24
 
23
- // TODO improve me
24
- /** @typedef {string} ChainAddress */
25
-
26
25
  /**
27
26
  * @typedef {object} OrchestrationPowers
28
27
  * @property {ERef<AttenuatedNetwork>} network
@@ -59,10 +58,14 @@ export const ChainAccountI = M.interface('ChainAccount', {
59
58
  getLocalAddress: M.call().returns(M.string()),
60
59
  getRemoteAddress: M.call().returns(M.string()),
61
60
  getPort: M.call().returns(M.remotable('Port')),
61
+ executeTx: M.call(M.arrayOf(M.record())).returns(M.promise()),
62
62
  executeEncodedTx: M.call(M.arrayOf(Proto3Shape))
63
63
  .optional(M.record())
64
64
  .returns(M.promise()),
65
- close: M.callWhen().returns(M.string()),
65
+ close: M.callWhen().returns(M.undefined()),
66
+ deposit: M.callWhen(PaymentShape).returns(M.undefined()),
67
+ getPurse: M.callWhen().returns(PurseShape),
68
+ prepareTransfer: M.callWhen().returns(InvitationShape),
66
69
  });
67
70
 
68
71
  export const ConnectionHandlerI = M.interface('ConnectionHandler', {
@@ -88,7 +91,7 @@ const prepareChainAccount = zone =>
88
91
  * localAddress: string | undefined;
89
92
  * requestedRemoteAddress: string;
90
93
  * remoteAddress: string | undefined;
91
- * accountAddress: ChainAddress | undefined;
94
+ * accountAddress: string | undefined;
92
95
  * }}
93
96
  */ (
94
97
  harden({
@@ -102,6 +105,9 @@ const prepareChainAccount = zone =>
102
105
  ),
103
106
  {
104
107
  account: {
108
+ /**
109
+ * @returns {string} the address of the account on the chain
110
+ */
105
111
  getAccountAddress() {
106
112
  return NonNullish(
107
113
  this.state.accountAddress,
@@ -123,7 +129,11 @@ const prepareChainAccount = zone =>
123
129
  getPort() {
124
130
  return this.state.port;
125
131
  },
132
+ executeTx() {
133
+ throw new Error('not yet implemented');
134
+ },
126
135
  /**
136
+ * Submit a transaction on behalf of the remote account for execution on the remote chain.
127
137
  * @param {AnyJson[]} msgs
128
138
  * @param {Omit<TxBody, 'messages'>} [opts]
129
139
  * @returns {Promise<string>} - base64 encoded bytes string. Can be decoded using the corresponding `Msg*Response` object.
@@ -138,6 +148,9 @@ const prepareChainAccount = zone =>
138
148
  ack => parsePacketAck(ack),
139
149
  );
140
150
  },
151
+ /**
152
+ * Close the remote account
153
+ */
141
154
  async close() {
142
155
  /// XXX what should the behavior be here? and `onClose`?
143
156
  // - retrieve assets?
@@ -145,7 +158,23 @@ const prepareChainAccount = zone =>
145
158
  const { connection } = this.state;
146
159
  if (!connection) throw Fail`connection not available`;
147
160
  await E(connection).close();
148
- return 'Connection closed';
161
+ },
162
+ async deposit(payment) {
163
+ console.log('deposit got', payment);
164
+ throw new Error('not yet implemented');
165
+ },
166
+ /**
167
+ * get Purse for a brand to .withdraw() a Payment from the account
168
+ * @param {Brand} brand
169
+ */
170
+ async getPurse(brand) {
171
+ console.log('getPurse got', brand);
172
+ throw new Error('not yet implemented');
173
+ },
174
+
175
+ /* transfer account to new holder */
176
+ async prepareTransfer() {
177
+ throw new Error('not yet implemented');
149
178
  },
150
179
  },
151
180
  connectionHandler: {
@@ -253,7 +282,6 @@ export const prepareOrchestrationTools = zone => {
253
282
  harden(prepareOrchestrationTools);
254
283
 
255
284
  /** @typedef {ReturnType<ReturnType<typeof prepareChainAccount>>} ChainAccountKit */
256
- /** @typedef {ChainAccountKit['account']} ChainAccount */
257
285
  /** @typedef {ReturnType<typeof prepareOrchestrationTools>} OrchestrationTools */
258
286
  /** @typedef {ReturnType<OrchestrationTools['makeOrchestration']>} OrchestrationKit */
259
- /** @typedef {OrchestrationKit['public']} Orchestration */
287
+ /** @typedef {OrchestrationKit['public']} OrchestrationService */
package/src/types.d.ts CHANGED
@@ -1,7 +1,457 @@
1
- import type { RouterProtocol } from '@agoric/network/src/router';
1
+ import type { Amount, Brand, Payment, Purse } from '@agoric/ertp/exported.js';
2
+ import type { Timestamp } from '@agoric/time';
3
+ import type { Invitation } from '@agoric/zoe/exported.js';
4
+ import type { Any } from '@agoric/cosmic-proto/google/protobuf/any';
5
+ import type { AnyJson } from '@agoric/cosmic-proto';
6
+ import type { MsgUndelegateResponse } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/tx.js';
7
+ import type {
8
+ Delegation,
9
+ Redelegation,
10
+ UnbondingDelegation,
11
+ } from '@agoric/cosmic-proto/cosmos/staking/v1beta1/staking.js';
2
12
 
3
13
  export type AttenuatedNetwork = Pick<RouterProtocol, 'bindPort'>;
4
14
 
5
- export type * from './orchestration.js';
6
- export type * from './vat-orchestration.js';
7
- export type * from './utils/tx.js';
15
+ /**
16
+ * static declaration of known chain types will allow type support for
17
+ * additional chain-specific operations like `liquidStake`
18
+ */
19
+ export type KnownChains = {
20
+ stride: {
21
+ info: CosmosChainInfo;
22
+ methods: {
23
+ liquidStake: (amount: AmountArg) => Promise<void>;
24
+ };
25
+ };
26
+ cosmos: { info: CosmosChainInfo; methods: {} };
27
+ agoric: {
28
+ info: Omit<CosmosChainInfo, 'ibcConnectionInfo'>;
29
+ methods: {
30
+ // TODO reference type from #8624 `packages/vats/src/localchain.js`
31
+ /**
32
+ * Register a hook to intercept an incoming IBC Transfer and handle it.
33
+ * Calling without arguments will unregister the hook.
34
+ */
35
+ interceptTransfer: (tap?: {
36
+ upcall: (args: any) => Promise<any>;
37
+ }) => Promise<void>;
38
+ };
39
+ };
40
+ celestia: { info: CosmosChainInfo; methods: {} };
41
+ osmosis: { info: CosmosChainInfo; methods: {} };
42
+ };
43
+
44
+ /** A helper type for type extensions. */
45
+ export type TypeUrl = string;
46
+
47
+ /**
48
+ * A denom that designates a token type on some blockchain.
49
+ *
50
+ * Multiple denoms may designate the same underlying base denom (e.g., `uist`,
51
+ * `uatom`) on different Chains or on the same Chain via different paths. On
52
+ * Cosmos chains, all but the base denom are IBC style denoms, but that may vary
53
+ * across other chains. All the denoms that designate the same underlying base
54
+ * denom form an equivalence class, along with the unique Brand on the local
55
+ * Chain. Some operations accept any member of the equivalence class to
56
+ * effectively designate the corresponding token type on the target chain.
57
+ */
58
+ export type Denom = string; // ibc/... or uist
59
+
60
+ /**
61
+ * In many cases, either a denom string or a local Brand can be used to
62
+ * designate a remote token type.
63
+ */
64
+ export type DenomArg = Brand | Denom;
65
+
66
+ export type Proto3JSONMsg = {
67
+ '@type': TypeUrl;
68
+ value: Record<string, unknown>;
69
+ };
70
+ /** An address on some blockchain, e.g., cosmos, eth, etc. */
71
+ export type ChainAddress = {
72
+ /** e.g. 1 for Ethereum, agoric-3 for Agoric, cosmoshub-4 for Cosmos */
73
+ chainId: string;
74
+ address: string;
75
+ // TODO what's the right way to scope the address? it's not chainId
76
+ addressEncoding: 'bech32' | 'ethereum';
77
+ };
78
+
79
+ /** An address for a validator on some blockchain, e.g., cosmos, eth, etc. */
80
+ export type CosmosValidatorAddress = ChainAddress & {
81
+ // TODO document why this is the format
82
+ address: `${string}valoper${string}`;
83
+ addressEncoding: 'bech32';
84
+ };
85
+
86
+ /** Details for setup will be determined in the implementation. */
87
+ export interface OrchestrationGovernor {
88
+ registerChain: (
89
+ chainName: string,
90
+ info: ChainInfo,
91
+ methods?: Record<string, any>,
92
+ ) => Promise<void>;
93
+ }
94
+
95
+ /** Description for an amount of some fungible currency */
96
+ export type ChainAmount = {
97
+ denom: Denom;
98
+ value: bigint; // Nat
99
+ };
100
+
101
+ /** Amounts can be provided as pure data using denoms or as native Amounts */
102
+ export type AmountArg = ChainAmount | Amount;
103
+
104
+ // chainName: managed like agoricNames. API consumers can make/provide their own
105
+ export interface Orchestrator {
106
+ getChain: <C extends keyof KnownChains>(chainName: C) => Promise<Chain<C>>;
107
+
108
+ makeLocalAccount: () => Promise<LocalChainAccount>;
109
+
110
+ /**
111
+ * For a denom, return information about a denom including the equivalent
112
+ * local Brand, the Chain on which the denom is held, and the Chain that
113
+ * issues the corresponding asset.
114
+ * @param denom
115
+ */
116
+ getBrandInfo: <
117
+ HoldingChain extends keyof KnownChains,
118
+ IssuingChain extends keyof KnownChains,
119
+ >(
120
+ denom: Denom,
121
+ ) => {
122
+ /** The well-known Brand on Agoric for the direct asset */
123
+ brand?: Brand;
124
+ /** The Chain at which the argument `denom` exists (where the asset is currently held) */
125
+ chain: Chain<HoldingChain>;
126
+ /** The Chain that is the issuer of the underlying asset */
127
+ base: Chain<IssuingChain>;
128
+ /** the Denom for the underlying asset on its issuer chain */
129
+ baseDenom: Denom;
130
+ };
131
+ /**
132
+ * Convert an amount described in native data to a local, structured Amount.
133
+ * @param amount - the described amount
134
+ * @returns the Amount in local structuerd format
135
+ */
136
+ asAmount: (amount: ChainAmount) => Amount;
137
+ }
138
+
139
+ // orchestrate('LSTTia', { zcf }, async (orch, { zcf }, seat, offerArgs) => {...})
140
+ // export type OrchestrationHandlerMaker<Context> =
141
+ // TODO @turadg add typed so that the ctx object and args are consistently typed
142
+ export type OrchestrationHandlerMaker = <C extends object>(
143
+ durableName: string,
144
+ ctx: C,
145
+ fn: (orc: Orchestrator, ctx2: C, ...args) => object,
146
+ ) => (...args) => object;
147
+
148
+ /**
149
+ * Info for an Ethereum-based chain.
150
+ */
151
+ export type EthChainInfo = {
152
+ chainId: string;
153
+ allegedName: string;
154
+ };
155
+
156
+ /**
157
+ * Info for a Cosmos-based chain.
158
+ */
159
+ export type CosmosChainInfo = {
160
+ chainId: string;
161
+ ibcConnectionInfo: {
162
+ id: string; // e.g. connection-0
163
+ client_id: string; // '07-tendermint-0'
164
+ state: 'OPEN' | 'TRYOPEN' | 'INIT' | 'CLOSED';
165
+ counterparty: {
166
+ client_id: string;
167
+ connection_id: string;
168
+ prefix: {
169
+ key_prefix: string;
170
+ };
171
+ };
172
+ versions: { identifier: string; features: string[] }[];
173
+ delay_period: bigint;
174
+ };
175
+ icaEnabled: boolean;
176
+ icqEnabled: boolean;
177
+ pfmEnabled: boolean;
178
+ ibcHooksEnabled: boolean;
179
+ /**
180
+ *
181
+ */
182
+ allowedMessages: TypeUrl[];
183
+ allowedQueries: TypeUrl[];
184
+ };
185
+
186
+ export type ChainInfo = CosmosChainInfo | EthChainInfo;
187
+
188
+ // marker interface
189
+ interface QueryResult {}
190
+
191
+ /**
192
+ * An object for access the core functions of a remote chain.
193
+ *
194
+ * Note that "remote" can mean the local chain; it's just that
195
+ * accounts are treated as remote/arms length for consistency.
196
+ */
197
+ export interface Chain<C extends keyof KnownChains> {
198
+ getChainInfo: () => Promise<KnownChains[C]['info']>;
199
+
200
+ // "makeAccount" suggests an operation within a vat
201
+ /**
202
+ * Creates a new account on the remote chain.
203
+ * @returns an object that controls a new remote account on Chain
204
+ */
205
+ createAccount: () => Promise<OrchestrationAccount<C>>;
206
+ // FUTURE supply optional port object; also fetch port object
207
+
208
+ /**
209
+ * Low level operation to query external chain state (e.g., governance params)
210
+ * @param queries
211
+ * @returns
212
+ *
213
+ */
214
+ query: (queries: Proto3JSONMsg[]) => Promise<Iterable<QueryResult>>;
215
+
216
+ /**
217
+ * Get the Denom on this Chain corresponding to the denom or Brand on
218
+ * this or another Chain.
219
+ * @param denom
220
+ * @returns
221
+ */
222
+ getLocalDenom: (denom: DenomArg) => Promise<Denom>;
223
+ }
224
+
225
+ /**
226
+ * Low level object that supports queries and operations for an account on a remote chain.
227
+ */
228
+ export interface ChainAccount {
229
+ /**
230
+ * @returns the address of the account on the remote chain
231
+ */
232
+ getAccountAddress: () => string;
233
+ /**
234
+ * Submit a transaction on behalf of the remote account for execution on the remote chain.
235
+ * @param msgs - records for the transaction
236
+ * @returns acknowledgement string
237
+ */
238
+ executeTx: (msgs: Proto3JSONMsg[]) => Promise<string>;
239
+ /**
240
+ * Submit a transaction on behalf of the remote account for execution on the remote chain.
241
+ * @param msgs - records for the transaction
242
+ * @returns acknowledge string
243
+ */
244
+ executeEncodedTx: (msgs: AnyJson[]) => Promise<string>;
245
+ /** deposit payment from zoe to the account*/
246
+ deposit: (payment: Payment) => Promise<void>;
247
+ /** get Purse for a brand to .withdraw() a Payment from the account */
248
+ getPurse: (brand: Brand) => Promise<Purse>;
249
+ /**
250
+ * Close the remote account
251
+ */
252
+ close: () => Promise<void>;
253
+ /* transfer account to new holder */
254
+ prepareTransfer: () => Promise<Invitation>;
255
+ }
256
+
257
+ export interface Undelegation {
258
+ // TODO return MsgCancelUnbondingDelegationResponse when that type is available
259
+ cancel: () => Promise<void>;
260
+ response: MsgUndelegateResponse;
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>;
266
+ }
267
+
268
+ /**
269
+ * An object that supports high-level operations for an account on a remote chain.
270
+ */
271
+ export interface BaseOrchestrationAccount {
272
+ /** @returns the underlying low-level operation object. */
273
+ getChainAcccount: () => Promise<ChainAccount>;
274
+
275
+ /**
276
+ * @returns the address of the account on the remote chain
277
+ */
278
+ getChainAddress: () => ChainAddress;
279
+
280
+ /** @returns an array of amounts for every balance in the account. */
281
+ getBalances: () => Promise<ChainAmount[]>;
282
+
283
+ /** @returns the balance of a specific denom for the account. */
284
+ getBalance: (denom: DenomArg) => Promise<ChainAmount>;
285
+
286
+ getDenomTrace: (
287
+ denom: string,
288
+ ) => Promise<{ path: string; base_denom: string }>;
289
+
290
+ /**
291
+ * @returns all active delegations from the account to any validator (or [] if none)
292
+ */
293
+ getDelegations: () => Promise<Delegation[]>;
294
+
295
+ /**
296
+ * @returns the active delegation from the account to a specific validator. Return an
297
+ * empty Delegation if there is no delegation.
298
+ */
299
+ getDelegation: (validator: CosmosValidatorAddress) => Promise<Delegation>;
300
+
301
+ /**
302
+ * @returns the unbonding delegations from the account to any validator (or [] if none)
303
+ */
304
+ getUnbondingDelegations: () => Promise<UnbondingDelegation[]>;
305
+
306
+ /**
307
+ * @returns the unbonding delegations from the account to a specific validator (or [] if none)
308
+ */
309
+ getUnbondingDelegation: (
310
+ validator: CosmosValidatorAddress,
311
+ ) => Promise<UnbondingDelegation>;
312
+
313
+ getRedelegations: () => Promise<Redelegation[]>;
314
+
315
+ getRedelegation: (
316
+ srcValidator: CosmosValidatorAddress,
317
+ dstValidator?: CosmosValidatorAddress,
318
+ ) => Promise<Redelegation>;
319
+
320
+ /**
321
+ * Get the pending rewards for the account.
322
+ * @returns the amounts of the account's rewards pending from all validators
323
+ */
324
+ getRewards: () => Promise<ChainAmount[]>;
325
+
326
+ /**
327
+ * Get the rewards pending with a specific validator.
328
+ * @param validator - the validator address to query for
329
+ * @returns the amount of the account's rewards pending from a specific validator
330
+ */
331
+ getReward: (validator: CosmosValidatorAddress) => Promise<ChainAmount[]>;
332
+
333
+ /**
334
+ * Transfer amount to another account on the same chain. The promise settles when the transfer is complete.
335
+ * @param toAccount - the account to send the amount to. MUST be on the same chain
336
+ * @param amount - the amount to send
337
+ * @returns void
338
+ */
339
+ send: (toAccount: ChainAddress, amount: AmountArg) => Promise<void>;
340
+
341
+ /**
342
+ * Delegate an amount to a validator. The promise settles when the delegation is complete.
343
+ * @param validator - the validator to delegate to
344
+ * @param amount - the amount to delegate
345
+ * @returns void
346
+ */
347
+ delegate: (
348
+ validator: CosmosValidatorAddress,
349
+ amount: AmountArg,
350
+ ) => Promise<void>;
351
+
352
+ /**
353
+ * Redelegate from one delegator to another.
354
+ * Settles when teh redelegation is established, not 21 days later.
355
+ * @param srcValidator - the current validator for the delegation.
356
+ * @param dstValidator - the validator that will receive the delegation.
357
+ * @param amount - how much to redelegate.
358
+ * @returns
359
+ */
360
+ redelegate: (
361
+ srcValidator: CosmosValidatorAddress,
362
+ dstValidator: CosmosValidatorAddress,
363
+ amount: AmountArg,
364
+ ) => Promise<void>;
365
+
366
+ /**
367
+ * Undelegate multiple delegations (concurrently). To delegate independently, pass an array with one item.
368
+ * @param delegations - the delegation to undelegate
369
+ */
370
+ undelegate: (delegations: Delegation[]) => Promise<Undelegation[]>;
371
+
372
+ /**
373
+ * Withdraw rewards from all validators. The promise settles when the rewards are withdrawn.
374
+ * @returns The total amounts of rewards withdrawn
375
+ */
376
+ withdrawRewards: () => Promise<ChainAmount[]>;
377
+
378
+ /**
379
+ * Withdraw rewards from a specific validator. The promise settles when the rewards are withdrawn.
380
+ * @param validator - the validator to withdraw rewards from
381
+ * @returns
382
+ */
383
+ withdrawReward: (validator: CosmosValidatorAddress) => Promise<ChainAmount[]>;
384
+
385
+ /**
386
+ * Transfer an amount to another account, typically on another chain.
387
+ * The promise settles when the transfer is complete.
388
+ * @param amount - the amount to transfer.
389
+ * @param destination - the account to transfer the amount to.
390
+ * @param memo - an optional memo to include with the transfer, which could drive custom PFM behavior
391
+ * @returns void
392
+ *
393
+ * TODO document the mapping from the address to the destination chain.
394
+ */
395
+ transfer: (
396
+ amount: AmountArg,
397
+ destination: ChainAddress,
398
+ memo?: string,
399
+ ) => Promise<void>;
400
+
401
+ /**
402
+ * Transfer an amount to another account in multiple steps. The promise settles when
403
+ * the entire path of the transfer is complete.
404
+ * @param amount - the amount to transfer
405
+ * @param msg - the transfer message, including follow-up steps
406
+ * @returns void
407
+ */
408
+ transferSteps: (amount: AmountArg, msg: TransferMsg) => Promise<void>;
409
+ /**
410
+ * deposit payment from zoe to the account. For remote accounts,
411
+ * an IBC Transfer will be executed to transfer funds there.
412
+ */
413
+ deposit: (payment: Payment) => Promise<void>;
414
+ }
415
+
416
+ export type OrchestrationAccount<C extends keyof KnownChains> =
417
+ BaseOrchestrationAccount & KnownChains[C]['methods'];
418
+
419
+ /**
420
+ * Internal structure for TransferMsgs.
421
+ *
422
+ * NOTE Expected to change, so consider an opaque structure.
423
+ */
424
+ export type TransferMsg = {
425
+ toAccount: ChainAddress;
426
+ timeout?: Timestamp;
427
+ next?: TransferMsg;
428
+ data?: object;
429
+ };
430
+
431
+ // Example
432
+ // await icaNoble.transferSteps(usdcAmt,
433
+ // osmosisSwap(tiaBrand, { pool: 1224, slippage: 0.05 }, icaCel.getAddress()));
434
+
435
+ /**
436
+ * @param pool - Required. Pool number
437
+ */
438
+ export type OsmoSwapOptions = {
439
+ pool: string;
440
+ slippage?: Number;
441
+ };
442
+
443
+ /**
444
+ * Make a TransferMsg for a swap operation.
445
+ * @param denom - the currency to swap to
446
+ * @param options
447
+ * @param slippage - the maximum acceptable slippage
448
+ */
449
+ export type OsmoSwapFn = (
450
+ denom: DenomArg,
451
+ options: Partial<OsmoSwapOptions>,
452
+ next: TransferMsg | ChainAddress,
453
+ ) => TransferMsg;
454
+
455
+ type AfterAction = { destChain: string; destAddress: ChainAddress };
456
+ type SwapExact = { amountIn: Amount; amountOut: Amount };
457
+ type SwapMaxSlippage = { amountIn: Amount; brandOut: Brand; slippage: number };
@@ -1,7 +1,10 @@
1
1
  // @ts-check
2
2
  import { Fail } from '@agoric/assert';
3
3
 
4
- /** @import { IBCConnectionID } from '@agoric/vats'; */
4
+ /**
5
+ * @import { IBCConnectionID } from '@agoric/vats';
6
+ * @import { ChainAddress, CosmosValidatorAddress } from '../types.js';
7
+ */
5
8
 
6
9
  /**
7
10
  * @param {IBCConnectionID} hostConnectionId Counterpart Connection ID
@@ -52,3 +55,4 @@ export const parseAddress = remoteAddressString => {
52
55
  return undefined;
53
56
  }
54
57
  };
58
+ harden(parseAddress);
@@ -0,0 +1,37 @@
1
+ // @ts-check
2
+ /** @import {AfterAction, SwapMaxSlippage, TransferMsg} from '../types.js' */
3
+
4
+ export const orcUtils = {
5
+ /**
6
+ * unwinds denom with PFM, if necessary
7
+ *
8
+ * @param {Omit<TransferMsg, 'memo'>} _args
9
+ * @returns {TransferMsg}
10
+ */
11
+ makeTransferMsg: _args => {
12
+ // XXX mocked, so typescript is happy
13
+ return {
14
+ toAccount: {
15
+ chainId: 'osmosis-test',
16
+ address: 'osmo1234',
17
+ addressEncoding: 'bech32',
18
+ },
19
+ };
20
+ },
21
+ /**
22
+ * SwapExact or SwapMaxSlippage, with optional AfterAction
23
+ * @param { (SwapExact | SwapMaxSlippage) &
24
+ (AfterAction | Record<string, never>)} _args
25
+ * @returns {TransferMsg}
26
+ */
27
+ makeOsmosisSwap(_args) {
28
+ // XXX mocked, so typescript is happy
29
+ return {
30
+ toAccount: {
31
+ chainId: 'osmosis-test',
32
+ address: 'osmo1234',
33
+ addressEncoding: 'bech32',
34
+ },
35
+ };
36
+ },
37
+ };
@@ -1,9 +1,9 @@
1
1
  // @ts-check
2
2
  import { Far } from '@endo/far';
3
3
  import { makeDurableZone } from '@agoric/zone/durable.js';
4
- import { prepareOrchestrationTools } from './orchestration.js';
4
+ import { prepareOrchestrationTools } from './service.js';
5
5
 
6
- /** @import { OrchestrationPowers } from './types.js' */
6
+ /** @import { OrchestrationPowers } from './service.js' */
7
7
 
8
8
  export const buildRootObject = (_vatPowers, _args, baggage) => {
9
9
  const zone = makeDurableZone(baggage);