@ledgerhq/revault-sdk 0.25.0 → 0.25.1
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/lib/bake.cjs +1214 -1359
- package/lib/bake.cjs.map +1 -1
- package/lib/bake.d.cts +564 -562
- package/lib/bake.d.ts +564 -562
- package/lib/bake.js +1204 -1359
- package/lib/bake.js.map +1 -1
- package/lib/chunk-CKQMccvm.cjs +28 -0
- package/lib/crypto-BDBF_rfw.cjs +268 -0
- package/lib/crypto-BDBF_rfw.cjs.map +1 -0
- package/lib/crypto-CPCBrCSZ.js +245 -0
- package/lib/crypto-CPCBrCSZ.js.map +1 -0
- package/lib/crypto.cjs +5 -11
- package/lib/crypto.d.cts +10 -7
- package/lib/crypto.d.ts +10 -7
- package/lib/crypto.js +2 -11
- package/lib/device-0lcfYGHr.cjs +793 -0
- package/lib/device-0lcfYGHr.cjs.map +1 -0
- package/lib/device-B8Fww8_6.js +719 -0
- package/lib/device-B8Fww8_6.js.map +1 -0
- package/lib/device.cjs +6 -13
- package/lib/device.d.cts +2 -50
- package/lib/device.d.ts +2 -50
- package/lib/device.js +2 -13
- package/lib/engine.cjs +835 -886
- package/lib/engine.cjs.map +1 -1
- package/lib/engine.d.cts +3 -25
- package/lib/engine.d.ts +3 -25
- package/lib/engine.js +828 -885
- package/lib/engine.js.map +1 -1
- package/lib/index--fscLDTT.d.ts +237 -0
- package/lib/index-BE7oUpCb.d.cts +237 -0
- package/lib/index-C4Xjiv0k.d.cts +23 -0
- package/lib/index-CVjhPoZh.d.ts +23 -0
- package/lib/index.cjs +0 -1
- package/lib/index.d.cts +10 -5
- package/lib/index.d.ts +10 -5
- package/lib/index.js +1 -1
- package/lib/logger-Cme0iL1f.d.cts +10 -0
- package/lib/logger-DWLViq7U.d.ts +10 -0
- package/lib/onboarding.cjs +179 -181
- package/lib/onboarding.cjs.map +1 -1
- package/lib/onboarding.d.cts +1751 -1722
- package/lib/onboarding.d.ts +1751 -1722
- package/lib/onboarding.js +175 -181
- package/lib/onboarding.js.map +1 -1
- package/lib/trpc-CWGPr8Ox.d.ts +20 -0
- package/lib/trpc-C_dUOB1u.d.cts +20 -0
- package/lib/types-4CfR5--z.d.ts +203 -0
- package/lib/types-Byf9V35t.d.cts +203 -0
- package/lib/types-CQROu0nu.d.cts +6 -0
- package/lib/types-_QeBv1U4.d.ts +6 -0
- package/lib/types.cjs +0 -1
- package/lib/types.d.cts +3 -4
- package/lib/types.d.ts +3 -4
- package/lib/types.js +1 -1
- package/lib/utils-BGbdjOP9.js +61 -0
- package/lib/utils-BGbdjOP9.js.map +1 -0
- package/lib/utils-DbSFXp4c.cjs +86 -0
- package/lib/utils-DbSFXp4c.cjs.map +1 -0
- package/package.json +6 -6
- package/lib/chunk-2WGM7EWA.cjs +0 -282
- package/lib/chunk-2WGM7EWA.cjs.map +0 -1
- package/lib/chunk-AXI72NFY.js +0 -50
- package/lib/chunk-AXI72NFY.js.map +0 -1
- package/lib/chunk-DRMYBKL3.cjs +0 -820
- package/lib/chunk-DRMYBKL3.cjs.map +0 -1
- package/lib/chunk-KV5FO7N5.js +0 -282
- package/lib/chunk-KV5FO7N5.js.map +0 -1
- package/lib/chunk-LH4QQXUH.cjs +0 -50
- package/lib/chunk-LH4QQXUH.cjs.map +0 -1
- package/lib/chunk-PFZIW6UP.js +0 -820
- package/lib/chunk-PFZIW6UP.js.map +0 -1
- package/lib/crypto.cjs.map +0 -1
- package/lib/crypto.js.map +0 -1
- package/lib/device.cjs.map +0 -1
- package/lib/device.js.map +0 -1
- package/lib/index.cjs.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/logger-C-TPbnjv.d.cts +0 -9
- package/lib/logger-C-TPbnjv.d.ts +0 -9
- package/lib/types-C-ngtI5m.d.ts +0 -202
- package/lib/types-CpGEbAxh.d.cts +0 -19
- package/lib/types-CpGEbAxh.d.ts +0 -19
- package/lib/types-CryvY9pW.d.cts +0 -202
- package/lib/types-DJfbanPz.d.cts +0 -167
- package/lib/types-DJfbanPz.d.ts +0 -167
- package/lib/types.cjs.map +0 -1
- package/lib/types.js.map +0 -1
package/lib/bake.js
CHANGED
|
@@ -1,1418 +1,1263 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
fetchAll
|
|
4
|
-
} from "./chunk-AXI72NFY.js";
|
|
5
|
-
import {
|
|
6
|
-
genKeys
|
|
7
|
-
} from "./chunk-KV5FO7N5.js";
|
|
8
|
-
|
|
9
|
-
// src/bake/index.ts
|
|
10
|
-
import invariant5 from "invariant";
|
|
11
|
-
|
|
12
|
-
// src/bake/bake-pledge.ts
|
|
13
|
-
import invariant2 from "invariant";
|
|
14
|
-
|
|
15
|
-
// src/bake/utils.ts
|
|
1
|
+
import { n as createDeviceIndexResolver, r as fetchAll } from "./utils-BGbdjOP9.js";
|
|
2
|
+
import { t as genKeys } from "./crypto-CPCBrCSZ.js";
|
|
16
3
|
import invariant from "invariant";
|
|
4
|
+
import _ from "lodash";
|
|
5
|
+
import { match } from "ts-pattern";
|
|
6
|
+
import z from "zod";
|
|
7
|
+
//#region src/bake/utils.ts
|
|
17
8
|
function createBakeLogHelpers(logger) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
9
|
+
return {
|
|
10
|
+
logTitle(title) {
|
|
11
|
+
logger.info(`:: ${title} ::`);
|
|
12
|
+
},
|
|
13
|
+
logStep(msg) {
|
|
14
|
+
logger.info(`=> ${msg}`);
|
|
15
|
+
},
|
|
16
|
+
logSubStep(msg) {
|
|
17
|
+
logger.info(` -> ${msg}`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
29
20
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
INITIAL: false,
|
|
41
|
-
PENDING_APPROVAL: false
|
|
21
|
+
const IS_REQUEST_STATE_END_STATE = {
|
|
22
|
+
ABORTED: true,
|
|
23
|
+
BLOCKED: true,
|
|
24
|
+
EXECUTED: true,
|
|
25
|
+
EXPIRED: true,
|
|
26
|
+
FAILED: true,
|
|
27
|
+
STUCK: true,
|
|
28
|
+
EXECUTING: false,
|
|
29
|
+
INITIAL: false,
|
|
30
|
+
PENDING_APPROVAL: false
|
|
42
31
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
const isEndState = (state) => IS_REQUEST_STATE_END_STATE[state];
|
|
33
|
+
const DEFAULT_POLL_INTERVAL_MS = 250;
|
|
34
|
+
const DEFAULT_TIMEOUT_MS = 6e4;
|
|
35
|
+
const wait = (ms) => {
|
|
36
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
48
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Poll a request until its inflight field is false, indicating it has been processed by the system.
|
|
40
|
+
* Returns the updated request once inflight is false.
|
|
41
|
+
* Throws if the timeout is exceeded before inflight becomes false.
|
|
42
|
+
*/
|
|
49
43
|
async function waitForRequestInflightDone(client, request, logger, options) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return updated;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
throw new Error(
|
|
67
|
-
`Timed out waiting for request ${request.id} inflight to be processed (last inflight: ${JSON.stringify(
|
|
68
|
-
updated.inflight
|
|
69
|
-
)})`
|
|
70
|
-
);
|
|
44
|
+
const { logSubStep } = createBakeLogHelpers(logger);
|
|
45
|
+
if (request.inflight === void 0) return request;
|
|
46
|
+
logSubStep(`Waiting for inflight request to be processed...`);
|
|
47
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
48
|
+
const deadline = Date.now() + timeoutMs;
|
|
49
|
+
const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
50
|
+
let updated = request;
|
|
51
|
+
while (Date.now() < deadline) {
|
|
52
|
+
await wait(pollIntervalMs);
|
|
53
|
+
updated = await client.requests.findById.query({ id: request.id });
|
|
54
|
+
if (updated.inflight === void 0) return updated;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`Timed out waiting for request ${request.id} inflight to be processed (last inflight: ${JSON.stringify(updated.inflight)})`);
|
|
71
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Poll a request until it reaches a terminal state (EXECUTED, FAILED, etc.).
|
|
60
|
+
*
|
|
61
|
+
* Returns the request once it is no longer in EXECUTING/STUCK state.
|
|
62
|
+
* Throws if the request reaches FAILED or STUCK state, or if the timeout is exceeded.
|
|
63
|
+
*/
|
|
72
64
|
async function waitForRequestExecuted(client, request, logger, options) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw new Error(`Request is in error state ${updated.state}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
throw new Error(
|
|
96
|
-
`Timed out waiting for request ${request.id} to reach a terminal state (last state: ${updated.state})`
|
|
97
|
-
);
|
|
65
|
+
const { logSubStep } = createBakeLogHelpers(logger);
|
|
66
|
+
if (isEndState(request.state)) {
|
|
67
|
+
if (request.state === "EXECUTED") return request;
|
|
68
|
+
throw new Error(`Request is in error state ${request.state}`);
|
|
69
|
+
}
|
|
70
|
+
const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
71
|
+
const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
72
|
+
const deadline = Date.now() + timeoutMs;
|
|
73
|
+
logSubStep(`Waiting for request to be executed...`);
|
|
74
|
+
let updated = request;
|
|
75
|
+
while (Date.now() < deadline) {
|
|
76
|
+
await wait(pollIntervalMs);
|
|
77
|
+
updated = await client.requests.findById.query({ id: request.id });
|
|
78
|
+
if (isEndState(updated.state)) {
|
|
79
|
+
if (updated.state === "EXECUTED") return updated;
|
|
80
|
+
throw new Error(`Request is in error state ${updated.state}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`Timed out waiting for request ${request.id} to reach a terminal state (last state: ${updated.state})`);
|
|
98
84
|
}
|
|
99
85
|
async function approveRequest(request, approvers, logger) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
86
|
+
const { logSubStep } = createBakeLogHelpers(logger);
|
|
87
|
+
for (let i = 0; i < approvers.length; i++) {
|
|
88
|
+
logSubStep(`Collecting approval (${i + 1}/${approvers.length})...`);
|
|
89
|
+
const approver = approvers[i];
|
|
90
|
+
request = await approver.approve(request);
|
|
91
|
+
request = await waitForRequestInflightDone(approver.client, request, logger);
|
|
92
|
+
}
|
|
93
|
+
return waitForRequestExecuted(approvers[0].client, request, logger);
|
|
108
94
|
}
|
|
109
95
|
async function createAndApproveRequest(input, approvers, logger) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return approveRequest(request, approvers, logger);
|
|
96
|
+
const { logSubStep } = createBakeLogHelpers(logger);
|
|
97
|
+
const creator = approvers[0];
|
|
98
|
+
invariant(creator, "Need at least 1 user to create a request");
|
|
99
|
+
logSubStep(`Creating request of type ${input.type}...`);
|
|
100
|
+
return approveRequest(await creator.client.requests.create.mutate({ request: input }), approvers, logger);
|
|
116
101
|
}
|
|
117
102
|
function stripUndefinedDeep(obj) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
if (obj && typeof obj === "object") {
|
|
122
|
-
return Object.fromEntries(
|
|
123
|
-
Object.entries(obj).filter(([_2, v]) => v !== void 0).map(([k, v]) => [k, stripUndefinedDeep(v)])
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
return obj;
|
|
103
|
+
if (Array.isArray(obj)) return obj.map(stripUndefinedDeep);
|
|
104
|
+
if (obj && typeof obj === "object") return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== void 0).map(([k, v]) => [k, stripUndefinedDeep(v)]));
|
|
105
|
+
return obj;
|
|
127
106
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/bake/bake-pledge.ts
|
|
109
|
+
const flattenApprovers = (groups) => {
|
|
110
|
+
return groups.reduce((acc, cur) => {
|
|
111
|
+
acc.push(...cur.userIds.slice(0, cur.quorum));
|
|
112
|
+
return acc;
|
|
113
|
+
}, []);
|
|
135
114
|
};
|
|
136
115
|
async function bakePledgeAdjustment(options, logger) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
];
|
|
174
|
-
const approvers = await Promise.all(
|
|
175
|
-
approverIds.map(
|
|
176
|
-
(id) => admin.client.users.findById.query({ id }).then((user) => getDeviceIndex(user.deviceUserId)).then((index) => engine.withDeviceIndex(index).login())
|
|
177
|
-
)
|
|
178
|
-
);
|
|
179
|
-
await createAndApproveRequest(
|
|
180
|
-
{
|
|
181
|
-
type: "TRADELINK_ADJUST_PLEDGE",
|
|
182
|
-
data: {
|
|
183
|
-
accountId: account.id,
|
|
184
|
-
exchangeId: exchange.id,
|
|
185
|
-
amount: pledgeAdjustment.amount
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
approvers,
|
|
189
|
-
logger
|
|
190
|
-
);
|
|
191
|
-
logger.info(
|
|
192
|
-
`Successfully adjusted pledge for account ${pledgeAdjustment.account} on exchange ${pledgeAdjustment.exchange}`
|
|
193
|
-
);
|
|
116
|
+
const { pledgeAdjustment, engine } = options;
|
|
117
|
+
const getDeviceIndex = createDeviceIndexResolver(engine);
|
|
118
|
+
logger.info("Logging in with admin...");
|
|
119
|
+
const admin = await engine.withDeviceIndex(4).login();
|
|
120
|
+
logger.info("Fetching account, tradelink blueprint and network...");
|
|
121
|
+
const [matchingAccounts, blueprint, network] = await Promise.all([
|
|
122
|
+
fetchAll(admin.client.accounts.list.query, { name: pledgeAdjustment.account }),
|
|
123
|
+
admin.client.tradelink.getNetworkBlueprint.query(),
|
|
124
|
+
admin.client.tradelink.getNetwork.query()
|
|
125
|
+
]);
|
|
126
|
+
const account = matchingAccounts.find((account) => account.name === pledgeAdjustment.account);
|
|
127
|
+
invariant(account, `Account ${pledgeAdjustment.account} not found`);
|
|
128
|
+
invariant(blueprint, "Tradelink blueprint is not created yet");
|
|
129
|
+
invariant(network, "Tradelink network is not created yet");
|
|
130
|
+
const exchange = network.exchanges.find((exchange) => {
|
|
131
|
+
const blueprintExchange = blueprint.exchanges.find((blueprintExchange) => blueprintExchange.tradelinkId === exchange.tradelinkId);
|
|
132
|
+
invariant(blueprintExchange, `Exchange ${exchange.tradelinkId} not found in blueprint`);
|
|
133
|
+
return blueprintExchange.name === pledgeAdjustment.exchange;
|
|
134
|
+
});
|
|
135
|
+
invariant(exchange, `Exchange ${pledgeAdjustment.exchange} not found in network`);
|
|
136
|
+
const governance = account.governance;
|
|
137
|
+
invariant(governance.type === "TRADELINK", "Account governance is not TRADELINK");
|
|
138
|
+
const assetManager = network.assetManagers.find((am) => am.id === governance.assetManagerId);
|
|
139
|
+
invariant(assetManager, `Asset manager ${governance.assetManagerId} not found in network`);
|
|
140
|
+
invariant(!!governance.exchangeConfigs.find((e) => e.exchangeId === exchange.id), `Exchange ${exchange.id} is not configured for account ${account.id}`);
|
|
141
|
+
const approverIds = [...flattenApprovers(assetManager.groups), ...flattenApprovers(exchange.groups)];
|
|
142
|
+
const approvers = await Promise.all(approverIds.map((id) => admin.client.users.findById.query({ id }).then((user) => getDeviceIndex(user.deviceUserId)).then((index) => engine.withDeviceIndex(index).login())));
|
|
143
|
+
await createAndApproveRequest({
|
|
144
|
+
type: "TRADELINK_ADJUST_PLEDGE",
|
|
145
|
+
data: {
|
|
146
|
+
accountId: account.id,
|
|
147
|
+
exchangeId: exchange.id,
|
|
148
|
+
amount: pledgeAdjustment.amount
|
|
149
|
+
}
|
|
150
|
+
}, approvers, logger);
|
|
151
|
+
logger.info(`Successfully adjusted pledge for account ${pledgeAdjustment.account} on exchange ${pledgeAdjustment.exchange}`);
|
|
194
152
|
}
|
|
195
|
-
|
|
196
|
-
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/bake/bake-tradelink-organization.ts
|
|
197
155
|
async function bakeTradelinkOrganization(options, logger) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
if (existingBlueprint) {
|
|
268
|
-
logger.info(`[SKIPPED] Network blueprint for ${workspaceName} workspace already exists`);
|
|
269
|
-
} else {
|
|
270
|
-
await rootClient.tradelink.root.createNetworkBlueprint.mutate({
|
|
271
|
-
workspaceName,
|
|
272
|
-
custodian: { name: blueprint.custodian },
|
|
273
|
-
exchanges: blueprint.exchanges.map((_2) => ({ name: _2 })),
|
|
274
|
-
assetManagers: blueprint.assetManagers.map((_2) => ({ name: _2 }))
|
|
275
|
-
});
|
|
276
|
-
logger.info(`Created network blueprint for ${networkBlueprint.workspaceName} workspace`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
156
|
+
const { tradelink, engine, rootAuthToken, networkBlueprint } = options;
|
|
157
|
+
const { logStep } = createBakeLogHelpers(logger);
|
|
158
|
+
const rootClient = engine.createClient({ rootAuthToken });
|
|
159
|
+
/* v8 ignore else - TODO bake empty tradelink org -- @preserve */
|
|
160
|
+
if (tradelink.exchanges) {
|
|
161
|
+
logStep("Creating Tradelink organization exchanges...");
|
|
162
|
+
const existingOrgExchanges = await rootClient.tradelink.root.listOrgExchanges.query();
|
|
163
|
+
for (const orgExchange of tradelink.exchanges) {
|
|
164
|
+
const existingOrgExchange = existingOrgExchanges.find((e) => e.name === orgExchange.name);
|
|
165
|
+
if (existingOrgExchange) logger.info(`[SKIPPED] Organization exchange ${existingOrgExchange.name} (${existingOrgExchange.code}) already exists`);
|
|
166
|
+
else {
|
|
167
|
+
await rootClient.tradelink.root.createOrgExchange.mutate({
|
|
168
|
+
code: orgExchange.code,
|
|
169
|
+
name: orgExchange.name,
|
|
170
|
+
logoUrl: orgExchange.logo
|
|
171
|
+
});
|
|
172
|
+
logger.info(`Created organization exchange ${orgExchange.name} (${orgExchange.code})`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/* v8 ignore else -- @preserve */
|
|
177
|
+
if (tradelink.assetManagers) {
|
|
178
|
+
logStep("Creating Tradelink organization asset managers...");
|
|
179
|
+
const existingAssetManagers = await rootClient.tradelink.root.listOrgAssetManagers.query();
|
|
180
|
+
for (const orgAssetManager of tradelink.assetManagers) {
|
|
181
|
+
const existingOrgAssetManager = existingAssetManagers.find((e) => e.name === orgAssetManager.name);
|
|
182
|
+
if (existingOrgAssetManager) logger.info(`[SKIPPED] Organization asset manager ${existingOrgAssetManager.name} (${existingOrgAssetManager.code}) already exists`);
|
|
183
|
+
else {
|
|
184
|
+
await rootClient.tradelink.root.createOrgAssetManager.mutate({
|
|
185
|
+
code: orgAssetManager.code,
|
|
186
|
+
name: orgAssetManager.name,
|
|
187
|
+
logoUrl: orgAssetManager.logo
|
|
188
|
+
});
|
|
189
|
+
logger.info(`Created organization asset manager ${orgAssetManager.name} (${orgAssetManager.code})`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/* v8 ignore else -- @preserve */
|
|
194
|
+
if (tradelink.custodians) {
|
|
195
|
+
logStep("Creating Tradelink organization custodians...");
|
|
196
|
+
const existingCustodians = await rootClient.tradelink.root.listOrgCustodians.query();
|
|
197
|
+
for (const orgCustodian of tradelink.custodians) {
|
|
198
|
+
const existingOrgCustodian = existingCustodians.find((e) => e.name === orgCustodian.name);
|
|
199
|
+
if (existingOrgCustodian) logger.info(`[SKIPPED] Organization custodian ${existingOrgCustodian.name} (${existingOrgCustodian.code}) already exists`);
|
|
200
|
+
else {
|
|
201
|
+
await rootClient.tradelink.root.createOrgCustodian.mutate({
|
|
202
|
+
code: orgCustodian.code,
|
|
203
|
+
name: orgCustodian.name,
|
|
204
|
+
logoUrl: orgCustodian.logo
|
|
205
|
+
});
|
|
206
|
+
logger.info(`Created organization custodian ${orgCustodian.name} (${orgCustodian.code})`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/* v8 ignore else -- @preserve */
|
|
211
|
+
if (networkBlueprint) {
|
|
212
|
+
logStep("Creating Tradelink network blueprint...");
|
|
213
|
+
const { workspaceName, blueprint } = networkBlueprint;
|
|
214
|
+
if ((await rootClient.tradelink.root.listNetworkBlueprints.query()).find((e) => e.workspaceName === workspaceName)) logger.info(`[SKIPPED] Network blueprint for ${workspaceName} workspace already exists`);
|
|
215
|
+
else {
|
|
216
|
+
await rootClient.tradelink.root.createNetworkBlueprint.mutate({
|
|
217
|
+
workspaceName,
|
|
218
|
+
custodian: { name: blueprint.custodian },
|
|
219
|
+
exchanges: blueprint.exchanges.map((_) => ({ name: _ })),
|
|
220
|
+
assetManagers: blueprint.assetManagers.map((_) => ({ name: _ }))
|
|
221
|
+
});
|
|
222
|
+
logger.info(`Created network blueprint for ${networkBlueprint.workspaceName} workspace`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
279
225
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
import invariant3 from "invariant";
|
|
283
|
-
import _ from "lodash";
|
|
284
|
-
import { match } from "ts-pattern";
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/bake/bake-workspace.ts
|
|
285
228
|
async function bakeWorkspace(options, logger) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
(acc, cur) => acc.then(async (acc2) => {
|
|
876
|
-
acc2.push(await withDeviceIndex(cur));
|
|
877
|
-
return acc2;
|
|
878
|
-
}),
|
|
879
|
-
Promise.resolve([])
|
|
880
|
-
);
|
|
881
|
-
return usersWithDeviceIndex2;
|
|
882
|
-
}
|
|
883
|
-
async function withDeviceIndex(user) {
|
|
884
|
-
invariant3(user.deviceUserId, `User ${user.id} does not have any device user id`);
|
|
885
|
-
const deviceIndex = await findDeviceIndex(user.deviceUserId);
|
|
886
|
-
return { user, deviceIndex };
|
|
887
|
-
}
|
|
888
|
-
function userIdFromDeviceIndex(deviceIndex) {
|
|
889
|
-
const corresponding = usersWithDeviceIndex.find((o) => o.deviceIndex === deviceIndex);
|
|
890
|
-
invariant3(corresponding, `No user with device index ${deviceIndex}`);
|
|
891
|
-
return corresponding.user.id;
|
|
892
|
-
}
|
|
893
|
-
function userIdFromUserName(name) {
|
|
894
|
-
const user = existingApiUsers.find((u) => u.name === name);
|
|
895
|
-
invariant3(user, `User ${name} not found`);
|
|
896
|
-
return user.id;
|
|
897
|
-
}
|
|
898
|
-
function userIdFromDeviceIndexOrName(identifier) {
|
|
899
|
-
if (typeof identifier === "number") {
|
|
900
|
-
return userIdFromDeviceIndex(identifier);
|
|
901
|
-
}
|
|
902
|
-
return userIdFromUserName(identifier);
|
|
903
|
-
}
|
|
904
|
-
function groupIdFromName(groupName) {
|
|
905
|
-
const group = existingGroups.find((g) => g.name === groupName);
|
|
906
|
-
invariant3(group, `Group ${groupName} not found`);
|
|
907
|
-
return group.id;
|
|
908
|
-
}
|
|
909
|
-
function policyIdFromName(policyName) {
|
|
910
|
-
const policy = existingPolicies.find((g) => g.name === policyName);
|
|
911
|
-
invariant3(policy, `Policy ${policyName} not found`);
|
|
912
|
-
return policy.id;
|
|
913
|
-
}
|
|
914
|
-
function whitelistIdFromName(whitelistName) {
|
|
915
|
-
const whitelist = existingWhitelists.find((w) => w.name === whitelistName);
|
|
916
|
-
invariant3(whitelist, `Whitelist ${whitelistName} not found`);
|
|
917
|
-
return whitelist.id;
|
|
918
|
-
}
|
|
919
|
-
function accountIdFromName(accountName) {
|
|
920
|
-
const account = existingAccounts.find((a) => a.name === accountName);
|
|
921
|
-
invariant3(account, `Account "${accountName}" not found`);
|
|
922
|
-
return account.id;
|
|
923
|
-
}
|
|
924
|
-
function assetManagerIdFromName(assetManagerName) {
|
|
925
|
-
const orgAssetManager = tradelinkOrgAssetManagersByName[assetManagerName];
|
|
926
|
-
invariant3(
|
|
927
|
-
orgAssetManager,
|
|
928
|
-
`Can't find Tradelink organization asset manager ${assetManagerName}`
|
|
929
|
-
);
|
|
930
|
-
const assetManager = existingTradelinkNetwork?.assetManagers.find(
|
|
931
|
-
(assetManager2) => assetManager2.tradelinkId === orgAssetManager.tradelinkId
|
|
932
|
-
);
|
|
933
|
-
invariant3(assetManager, `Can't find Tradelink assetManager ${assetManagerName}`);
|
|
934
|
-
return assetManager.id;
|
|
935
|
-
}
|
|
936
|
-
function exchangeIdFromName(exchangeName) {
|
|
937
|
-
const exchange = existingTradelinkNetwork?.exchanges.find((exchange2) => {
|
|
938
|
-
const orgExchange = tradelinkOrgExchangesByName[exchangeName];
|
|
939
|
-
invariant3(orgExchange, `Can't find Tradelink organization exchange ${exchange2.tradelinkId}`);
|
|
940
|
-
return orgExchange.name === exchangeName;
|
|
941
|
-
});
|
|
942
|
-
invariant3(exchange, `Can't find Tradelink exchange ${exchangeName}`);
|
|
943
|
-
return exchange.id;
|
|
944
|
-
}
|
|
945
|
-
function tradelinkReviewStepFromManifest({
|
|
946
|
-
members,
|
|
947
|
-
quorum
|
|
948
|
-
}) {
|
|
949
|
-
return {
|
|
950
|
-
userIds: members.map(userIdFromDeviceIndex),
|
|
951
|
-
quorum
|
|
952
|
-
};
|
|
953
|
-
}
|
|
229
|
+
const { workspace, engine, workspaceName } = options;
|
|
230
|
+
const workspaceEngine = engine.withWorkspace(workspaceName);
|
|
231
|
+
const { logTitle, logStep, logSubStep } = createBakeLogHelpers(logger);
|
|
232
|
+
const findDeviceIndex = createDeviceIndexResolver(workspaceEngine);
|
|
233
|
+
logger.info("Authenticating with two admins...");
|
|
234
|
+
const [admin1, admin2] = await Promise.all([workspaceEngine.withDeviceIndex(4).login(), workspaceEngine.withDeviceIndex(5).login()]);
|
|
235
|
+
logger.info("Fetching existing data...");
|
|
236
|
+
const [existingUsers, existingGroups, existingWhitelists, existingPolicies, existingAccounts, existingEntities, tradelinkNetworkBlueprint] = await Promise.all([
|
|
237
|
+
fetchAll(admin1.client.users.list.query, { status: "ACTIVE" }),
|
|
238
|
+
fetchAll(admin1.client.groups.list.query, { status: "ACTIVE" }),
|
|
239
|
+
fetchAll(admin1.client.whitelists.list.query, { status: "ACTIVE" }),
|
|
240
|
+
fetchAll(admin1.client.policies.list.query, { isPrivate: false }),
|
|
241
|
+
fetchAll(admin1.client.accounts.list.query, { status: "ACTIVE" }),
|
|
242
|
+
fetchAll(admin1.client.entities.list.query, { status: "ACTIVE" }),
|
|
243
|
+
admin1.client.tradelink.getNetworkBlueprint.query()
|
|
244
|
+
]);
|
|
245
|
+
const tradelinkOrgExchangesByName = _.keyBy(tradelinkNetworkBlueprint?.exchanges, "name");
|
|
246
|
+
const tradelinkOrgAssetManagersByName = _.keyBy(tradelinkNetworkBlueprint?.assetManagers, "name");
|
|
247
|
+
let existingTradelinkNetwork = await admin1.client.tradelink.getNetwork.query();
|
|
248
|
+
const existingPsdUsers = existingUsers.filter((user) => user.deviceType === "PSD");
|
|
249
|
+
const existingApiUsers = existingUsers.filter((user) => user.deviceType === "API");
|
|
250
|
+
const usersWithDeviceIndex = await resolveDevicesIndexes(existingPsdUsers);
|
|
251
|
+
if (workspace.users) {
|
|
252
|
+
logTitle("Users");
|
|
253
|
+
if (workspace.users.admins) for (const deviceIndex of workspace.users.admins) await createUser("ADMIN", deviceIndex);
|
|
254
|
+
/* v8 ignore else -- @preserve */
|
|
255
|
+
if (workspace.users.operators) for (const deviceIndex of workspace.users.operators) await createUser("OPERATOR", deviceIndex);
|
|
256
|
+
if (workspace.users.api) for (const apiUser of workspace.users.api) await createApiUser(apiUser);
|
|
257
|
+
}
|
|
258
|
+
if (workspace.workspaceRules) {
|
|
259
|
+
logTitle("Workspace Rules");
|
|
260
|
+
for (const workspaceRule of workspace.workspaceRules) await createWorkspaceRule(workspaceRule);
|
|
261
|
+
}
|
|
262
|
+
if (workspace.groups) {
|
|
263
|
+
logTitle("Groups");
|
|
264
|
+
for (const group of workspace.groups) await createGroup(group);
|
|
265
|
+
}
|
|
266
|
+
if (workspace.whitelists) {
|
|
267
|
+
logTitle("Whitelists");
|
|
268
|
+
await createWhitelists(workspace.whitelists);
|
|
269
|
+
}
|
|
270
|
+
if (workspace.policies) {
|
|
271
|
+
logTitle("Policies");
|
|
272
|
+
for (const policy of workspace.policies) await createPolicy(policy);
|
|
273
|
+
}
|
|
274
|
+
if (workspace.tradelink) {
|
|
275
|
+
logTitle("Tradelink");
|
|
276
|
+
await createTradelink(workspace.tradelink);
|
|
277
|
+
}
|
|
278
|
+
if (workspace.accounts) {
|
|
279
|
+
logTitle("Accounts");
|
|
280
|
+
for (const account of workspace.accounts) await createAccount(account, workspace);
|
|
281
|
+
}
|
|
282
|
+
if (workspace.entities) {
|
|
283
|
+
logTitle("Entities");
|
|
284
|
+
for (const entity of workspace.entities) await createEntity(entity);
|
|
285
|
+
}
|
|
286
|
+
async function editDomainObjectIfNeeded(params) {
|
|
287
|
+
const previousRequests = await admin1.client.requests.list.query({
|
|
288
|
+
type: params.requestTypes,
|
|
289
|
+
targetId: params.existingId,
|
|
290
|
+
state: "EXECUTED",
|
|
291
|
+
sortBy: "createdAt",
|
|
292
|
+
sortOrder: "desc",
|
|
293
|
+
pageSize: 1
|
|
294
|
+
});
|
|
295
|
+
if (previousRequests?.results && previousRequests.results.length > 0) {
|
|
296
|
+
const stateRequest = previousRequests.results[0];
|
|
297
|
+
invariant(stateRequest && params.requestTypes.includes(stateRequest.type), `State request should be a ${params.requestTypes[0]} or ${params.requestTypes[1]} request`);
|
|
298
|
+
const lastData = params.extractLastData(stateRequest.data);
|
|
299
|
+
if (_.isEqual(stripUndefinedDeep(params.editData), stripUndefinedDeep(lastData))) {
|
|
300
|
+
logger.info(`[SKIPPED] ${params.entityType} "${params.entityName}" has not changed`);
|
|
301
|
+
return "skipped";
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
logStep(`Editing ${params.entityType.toLowerCase()} "${params.entityName}"...`);
|
|
305
|
+
await createAndApproveRequest(params.editInput, [admin1, admin2], logger);
|
|
306
|
+
logger.info(`[EDITED] ${params.entityType} "${params.entityName}" has been edited`);
|
|
307
|
+
return "edited";
|
|
308
|
+
}
|
|
309
|
+
async function createWhitelists(whitelists) {
|
|
310
|
+
for (const whitelist of whitelists) await createWhitelist(whitelist);
|
|
311
|
+
async function createWhitelist(whitelist) {
|
|
312
|
+
const existing = existingWhitelists.find((w) => w.name === whitelist.name);
|
|
313
|
+
const normalizeAddresses = (addresses) => addresses.map(({ destinationTags, ...rest }) => ({
|
|
314
|
+
...rest,
|
|
315
|
+
...destinationTags?.length ? { destinationTags } : {}
|
|
316
|
+
}));
|
|
317
|
+
const whitelistEditData = {
|
|
318
|
+
name: whitelist.name,
|
|
319
|
+
addresses: normalizeAddresses(whitelist.addresses)
|
|
320
|
+
};
|
|
321
|
+
if (existing) {
|
|
322
|
+
await editDomainObjectIfNeeded({
|
|
323
|
+
entityType: "Whitelist",
|
|
324
|
+
entityName: whitelist.name,
|
|
325
|
+
existingId: existing.id,
|
|
326
|
+
editInput: {
|
|
327
|
+
type: "EDIT_WHITELIST",
|
|
328
|
+
targetId: existing.id,
|
|
329
|
+
data: {
|
|
330
|
+
name: whitelist.name,
|
|
331
|
+
addresses: whitelist.addresses
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
editData: whitelistEditData,
|
|
335
|
+
requestTypes: ["CREATE_WHITELIST", "EDIT_WHITELIST"],
|
|
336
|
+
extractLastData: (data) => ({
|
|
337
|
+
name: data.name,
|
|
338
|
+
addresses: normalizeAddresses(data.addresses)
|
|
339
|
+
})
|
|
340
|
+
});
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const req = await createAndApproveRequest({
|
|
344
|
+
type: "CREATE_WHITELIST",
|
|
345
|
+
data: {
|
|
346
|
+
...whitelist,
|
|
347
|
+
description: whitelist.description ?? null
|
|
348
|
+
}
|
|
349
|
+
}, [admin1, admin2], logger);
|
|
350
|
+
logSubStep(`Whitelist "${whitelist.name}" has been created`);
|
|
351
|
+
const createdWhitelist = await admin1.client.whitelists.findById.query({ id: req.targetId });
|
|
352
|
+
existingWhitelists.push(createdWhitelist);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function govRuleFromManifest(rule) {
|
|
356
|
+
return {
|
|
357
|
+
operationType: rule.type,
|
|
358
|
+
reviewSteps: rule.steps.map((step) => {
|
|
359
|
+
if ("group" in step) return {
|
|
360
|
+
quorum: step.quorum,
|
|
361
|
+
groupId: groupIdFromName(step.group)
|
|
362
|
+
};
|
|
363
|
+
return {
|
|
364
|
+
quorum: step.quorum,
|
|
365
|
+
userIds: step.members.map(userIdFromDeviceIndexOrName)
|
|
366
|
+
};
|
|
367
|
+
}),
|
|
368
|
+
threshold: rule.threshold,
|
|
369
|
+
whitelistIds: rule.whitelistNames?.map(whitelistIdFromName)
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function rawSigningRuleFromManifest(rule) {
|
|
373
|
+
return {
|
|
374
|
+
operationType: rule.type,
|
|
375
|
+
reviewSteps: rule.steps.map((step) => ({
|
|
376
|
+
quorum: step.quorum,
|
|
377
|
+
userIds: step.members.map(userIdFromDeviceIndexOrName)
|
|
378
|
+
}))
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
async function createAccount(account, workspace) {
|
|
382
|
+
const existing = existingAccounts.find((a) => a.currency === account.currency && a.index === account.index && (a.contractAddress ?? null) === (account.contractAddress ?? null));
|
|
383
|
+
const governance = match(account.governance).with({ type: "TRADELINK" }, (gov) => ({
|
|
384
|
+
type: "TRADELINK",
|
|
385
|
+
assetManagerId: assetManagerIdFromName(gov.assetManager),
|
|
386
|
+
exchangeConfigs: gov.exchanges.map((exchangeConfig) => ({
|
|
387
|
+
exchangeId: exchangeIdFromName(exchangeConfig.name),
|
|
388
|
+
repledgeAuthorized: exchangeConfig.repledgeAuthorized === true,
|
|
389
|
+
needsAssetManagerApprovalForSettlements: exchangeConfig.needsAssetManagerApprovalForSettlements === true
|
|
390
|
+
}))
|
|
391
|
+
})).with({ type: "RAW_SIGNING" }, (gov) => ({
|
|
392
|
+
type: "RAW_SIGNING",
|
|
393
|
+
policy: { rules: gov.rules.map(rawSigningRuleFromManifest) }
|
|
394
|
+
})).with({ type: "CLASSIC" }, (gov) => ({
|
|
395
|
+
type: "CLASSIC",
|
|
396
|
+
policy: { rules: gov.rules.map(govRuleFromManifest) }
|
|
397
|
+
})).with({ type: "POLICY" }, (gov) => ({
|
|
398
|
+
type: "CLASSIC",
|
|
399
|
+
policy: { policyId: policyIdFromName(gov.policy) }
|
|
400
|
+
})).exhaustive();
|
|
401
|
+
const accountData = {
|
|
402
|
+
name: account.name,
|
|
403
|
+
currency: account.currency,
|
|
404
|
+
contractAddress: account.contractAddress ?? null,
|
|
405
|
+
tokenIdentifier: account.tokenIdentifier ?? null,
|
|
406
|
+
index: account.index,
|
|
407
|
+
governance
|
|
408
|
+
};
|
|
409
|
+
if (existing) {
|
|
410
|
+
const editData = {
|
|
411
|
+
name: accountData.name,
|
|
412
|
+
governance
|
|
413
|
+
};
|
|
414
|
+
if (await editDomainObjectIfNeeded({
|
|
415
|
+
entityType: "Account",
|
|
416
|
+
entityName: account.name,
|
|
417
|
+
existingId: existing.id,
|
|
418
|
+
editInput: {
|
|
419
|
+
type: "EDIT_ACCOUNT",
|
|
420
|
+
targetId: existing.id,
|
|
421
|
+
data: editData
|
|
422
|
+
},
|
|
423
|
+
editData,
|
|
424
|
+
requestTypes: ["CREATE_ACCOUNT", "EDIT_ACCOUNT"],
|
|
425
|
+
extractLastData: (data) => ({
|
|
426
|
+
name: data.name,
|
|
427
|
+
governance: data.governance
|
|
428
|
+
})
|
|
429
|
+
}) === "skipped") await activateHederaAccount(account, workspace, existing.id);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
logStep(`Creating account "${account.name}"`);
|
|
433
|
+
const req = await createAndApproveRequest({
|
|
434
|
+
type: "CREATE_ACCOUNT",
|
|
435
|
+
data: accountData
|
|
436
|
+
}, [admin1, admin2], logger);
|
|
437
|
+
const createdAccount = await admin1.client.accounts.findById.query({ id: req.targetId });
|
|
438
|
+
existingAccounts.push(createdAccount);
|
|
439
|
+
if (account.governance.type === "TRADELINK") {
|
|
440
|
+
logStep(`Activating tradelink account "${account.name}"`);
|
|
441
|
+
const assetManagerId = assetManagerIdFromName(account.governance.assetManager);
|
|
442
|
+
const assetManager = existingTradelinkNetwork?.assetManagers.find((assetManager) => assetManager.id === assetManagerId);
|
|
443
|
+
invariant(assetManager, `Asset manager for account exists`);
|
|
444
|
+
const loggedAssetManagerOperators = await Promise.all(assetManager.groups.flatMap(({ quorum, userIds }) => userIds.slice(0, quorum).map((userId) => {
|
|
445
|
+
const deviceIndex = usersWithDeviceIndex.find((o) => o.user.id === userId)?.deviceIndex;
|
|
446
|
+
return workspaceEngine.withDeviceIndex(deviceIndex).login();
|
|
447
|
+
})));
|
|
448
|
+
await createAndApproveRequest({
|
|
449
|
+
type: "TRADELINK_ACTIVATE_ACCOUNT",
|
|
450
|
+
targetId: req.targetId,
|
|
451
|
+
data: null
|
|
452
|
+
}, loggedAssetManagerOperators, logger);
|
|
453
|
+
}
|
|
454
|
+
if (req.targetId) await activateHederaAccount(account, workspace, req.targetId);
|
|
455
|
+
logSubStep(`Account ${account.name} has been created`);
|
|
456
|
+
}
|
|
457
|
+
async function activateHederaAccount(account, workspace, accountId) {
|
|
458
|
+
if (account.currency === "hedera" && account.contractAddress === void 0 && workspace.hederaData && accountId) {
|
|
459
|
+
const salt = engine.__options.salt;
|
|
460
|
+
const hederaAccountData = workspace.hederaData[salt]?.find((h) => h.index === account.index && h.activate);
|
|
461
|
+
if (hederaAccountData) try {
|
|
462
|
+
await admin1.client.accounts.hedera.activateAccount.mutate({
|
|
463
|
+
id: accountId,
|
|
464
|
+
onchainId: hederaAccountData.onChainId
|
|
465
|
+
});
|
|
466
|
+
logSubStep(`Hedera account "${account.name}" has been activated`);
|
|
467
|
+
} catch (error) {
|
|
468
|
+
logger.warning(`Activation for "${account.name}" failed: ${error}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
async function createUser(role, deviceIndex) {
|
|
473
|
+
const userEngine = workspaceEngine.withDeviceIndex(deviceIndex);
|
|
474
|
+
const deviceId = await userEngine.device.getDeviceUserId();
|
|
475
|
+
if (existingUsers.find((user) => user.deviceUserId === deviceId)) {
|
|
476
|
+
logger.info(`[SKIPPED] User with device index ${deviceIndex} already exists`);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
const name = `${role === "ADMIN" ? "Admin" : "Operator"} ${deviceIndex}`;
|
|
480
|
+
logStep(`Creating ${name}`);
|
|
481
|
+
logSubStep("Creating invitation...");
|
|
482
|
+
let registration = await admin1.client.userRegistrations.invite.mutate({
|
|
483
|
+
name,
|
|
484
|
+
role,
|
|
485
|
+
deviceUserId: deviceId,
|
|
486
|
+
psdModel: "STAX"
|
|
487
|
+
});
|
|
488
|
+
logSubStep("Completing registration...");
|
|
489
|
+
registration = await userEngine.register(registration);
|
|
490
|
+
logSubStep("Creating request...");
|
|
491
|
+
let req = await admin1.client.requests.create.mutate({ request: {
|
|
492
|
+
type: "CREATE_PSD_USER",
|
|
493
|
+
data: { registrationId: registration.id }
|
|
494
|
+
} });
|
|
495
|
+
req = await approveRequest(req, [admin1, admin2], logger);
|
|
496
|
+
logSubStep(`${name} has been created`);
|
|
497
|
+
const createdUser = await admin1.client.users.findById.query({ id: req.targetId });
|
|
498
|
+
existingUsers.push(createdUser);
|
|
499
|
+
usersWithDeviceIndex.push({
|
|
500
|
+
user: createdUser,
|
|
501
|
+
deviceIndex
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
async function createApiUser(apiUser) {
|
|
505
|
+
const pubKey = apiUser.pubKey ?? genKeys(apiUser.name).hexPubKey;
|
|
506
|
+
if (existingApiUsers.find((user) => user.pubKey === pubKey || user.name === apiUser.name)) {
|
|
507
|
+
logger.info(`[SKIPPED] API user already exists with name "${apiUser.name}" or public key "${pubKey}"`);
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
logStep(`Creating API user "${apiUser.name}"...`);
|
|
511
|
+
logSubStep("Creating request...");
|
|
512
|
+
let req = await admin1.client.requests.create.mutate({ request: {
|
|
513
|
+
type: "CREATE_API_USER",
|
|
514
|
+
data: {
|
|
515
|
+
name: apiUser.name,
|
|
516
|
+
role: apiUser.role,
|
|
517
|
+
pubKey,
|
|
518
|
+
viewAll: apiUser.viewAll
|
|
519
|
+
}
|
|
520
|
+
} });
|
|
521
|
+
req = await approveRequest(req, [admin1, admin2], logger);
|
|
522
|
+
logSubStep(`API user "${apiUser.name}" has been created`);
|
|
523
|
+
logSubStep("Generating credentials...");
|
|
524
|
+
const credentials = await admin1.client.users.resetApiUserCredentials.mutate({ id: req.targetId });
|
|
525
|
+
logSubStep(`API user "${apiUser.name}" credentials have been generated: apiKeyId=${credentials.apiKeyId}, apiKeySecret=${credentials.apiKeySecret}`);
|
|
526
|
+
const createdUser = await admin1.client.users.findById.query({ id: req.targetId });
|
|
527
|
+
existingApiUsers.push(createdUser);
|
|
528
|
+
}
|
|
529
|
+
async function createGroup(group) {
|
|
530
|
+
const existing = existingGroups.find((g) => g.name === group.name);
|
|
531
|
+
const members = group.members.map(userIdFromDeviceIndexOrName);
|
|
532
|
+
const groupData = {
|
|
533
|
+
name: group.name,
|
|
534
|
+
description: "",
|
|
535
|
+
members
|
|
536
|
+
};
|
|
537
|
+
if (existing) {
|
|
538
|
+
await editDomainObjectIfNeeded({
|
|
539
|
+
entityType: "Group",
|
|
540
|
+
entityName: group.name,
|
|
541
|
+
existingId: existing.id,
|
|
542
|
+
editInput: {
|
|
543
|
+
type: "EDIT_GROUP",
|
|
544
|
+
targetId: existing.id,
|
|
545
|
+
data: groupData
|
|
546
|
+
},
|
|
547
|
+
editData: groupData,
|
|
548
|
+
requestTypes: ["CREATE_GROUP", "EDIT_GROUP"],
|
|
549
|
+
extractLastData: (data) => ({
|
|
550
|
+
...data,
|
|
551
|
+
description: data.description ?? ""
|
|
552
|
+
})
|
|
553
|
+
});
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
logStep(`Creating group ${group.name}...`);
|
|
557
|
+
logSubStep("Creating request...");
|
|
558
|
+
let request = await admin1.client.requests.create.mutate({ request: {
|
|
559
|
+
type: "CREATE_GROUP",
|
|
560
|
+
data: groupData
|
|
561
|
+
} });
|
|
562
|
+
request = await approveRequest(request, [admin1, admin2], logger);
|
|
563
|
+
logSubStep(`${group.name} has been created`);
|
|
564
|
+
const createdGroup = await admin1.client.groups.findById.query({ id: request.targetId });
|
|
565
|
+
existingGroups.push(createdGroup);
|
|
566
|
+
}
|
|
567
|
+
async function createPolicy(policy) {
|
|
568
|
+
const existing = existingPolicies.find((p) => p.name === policy.name);
|
|
569
|
+
const policyEditData = {
|
|
570
|
+
name: policy.name,
|
|
571
|
+
description: "",
|
|
572
|
+
govRules: policy.rules.map(govRuleFromManifest)
|
|
573
|
+
};
|
|
574
|
+
if (existing) {
|
|
575
|
+
await editDomainObjectIfNeeded({
|
|
576
|
+
entityType: "Policy",
|
|
577
|
+
entityName: policy.name,
|
|
578
|
+
existingId: existing.id,
|
|
579
|
+
editInput: {
|
|
580
|
+
type: "EDIT_POLICY",
|
|
581
|
+
targetId: existing.id,
|
|
582
|
+
data: policyEditData
|
|
583
|
+
},
|
|
584
|
+
editData: policyEditData,
|
|
585
|
+
requestTypes: ["CREATE_POLICY", "EDIT_POLICY"],
|
|
586
|
+
extractLastData: (data) => ({
|
|
587
|
+
name: data.name,
|
|
588
|
+
description: data.description ?? "",
|
|
589
|
+
govRules: data.govRules
|
|
590
|
+
})
|
|
591
|
+
});
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
logStep(`Creating policy ${policy.name}...`);
|
|
595
|
+
logSubStep("Creating request...");
|
|
596
|
+
let request = await admin1.client.requests.create.mutate({ request: {
|
|
597
|
+
type: "CREATE_POLICY",
|
|
598
|
+
data: {
|
|
599
|
+
...policyEditData,
|
|
600
|
+
currency: policy.currency
|
|
601
|
+
}
|
|
602
|
+
} });
|
|
603
|
+
request = await approveRequest(request, [admin1, admin2], logger);
|
|
604
|
+
logSubStep(`${policy.name} has been created`);
|
|
605
|
+
const createdPolicy = await admin1.client.policies.findById.query({ id: request.targetId });
|
|
606
|
+
existingPolicies.push(createdPolicy);
|
|
607
|
+
}
|
|
608
|
+
async function createWorkspaceRule(workspaceRule) {
|
|
609
|
+
const { permission, quorum, members } = workspaceRule;
|
|
610
|
+
logStep(`Editing workspace rule for ${permission}...`);
|
|
611
|
+
const userIds = members.map(userIdFromDeviceIndexOrName);
|
|
612
|
+
logSubStep("Creating request...");
|
|
613
|
+
await createAndApproveRequest({
|
|
614
|
+
type: "EDIT_WORKSPACE_RULE",
|
|
615
|
+
data: {
|
|
616
|
+
permission,
|
|
617
|
+
reviewConfig: [{
|
|
618
|
+
quorum,
|
|
619
|
+
userIds
|
|
620
|
+
}]
|
|
621
|
+
}
|
|
622
|
+
}, [admin1, admin2], logger);
|
|
623
|
+
logSubStep(`Workspace rule for "${permission}" has been edited`);
|
|
624
|
+
}
|
|
625
|
+
async function createEntity(entity) {
|
|
626
|
+
const existing = existingEntities.find((e) => e.name === entity.name);
|
|
627
|
+
const accountIds = (entity.accounts ?? []).map(accountIdFromName);
|
|
628
|
+
const memberIds = (entity.members ?? []).map(userIdFromUserName);
|
|
629
|
+
const entityEditData = {
|
|
630
|
+
name: entity.name,
|
|
631
|
+
accountIds,
|
|
632
|
+
memberIds
|
|
633
|
+
};
|
|
634
|
+
if (existing) {
|
|
635
|
+
await editDomainObjectIfNeeded({
|
|
636
|
+
entityType: "Entity",
|
|
637
|
+
entityName: entity.name,
|
|
638
|
+
existingId: existing.id,
|
|
639
|
+
editInput: {
|
|
640
|
+
type: "EDIT_ENTITY",
|
|
641
|
+
targetId: existing.id,
|
|
642
|
+
data: entityEditData
|
|
643
|
+
},
|
|
644
|
+
editData: entityEditData,
|
|
645
|
+
requestTypes: ["CREATE_ENTITY", "EDIT_ENTITY"],
|
|
646
|
+
extractLastData: (data) => ({
|
|
647
|
+
name: data.name,
|
|
648
|
+
accountIds: data.accountIds,
|
|
649
|
+
memberIds: data.memberIds
|
|
650
|
+
})
|
|
651
|
+
});
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
logStep(`Creating entity "${entity.name}"...`);
|
|
655
|
+
const request = await createAndApproveRequest({
|
|
656
|
+
type: "CREATE_ENTITY",
|
|
657
|
+
data: entityEditData
|
|
658
|
+
}, [admin1, admin2], logger);
|
|
659
|
+
logSubStep(`Entity "${entity.name}" has been created`);
|
|
660
|
+
const createdEntity = await admin1.client.entities.findById.query({ id: request.targetId });
|
|
661
|
+
existingEntities.push(createdEntity);
|
|
662
|
+
}
|
|
663
|
+
async function createTradelink(tradelink) {
|
|
664
|
+
/* v8 ignore else -- @preserve */
|
|
665
|
+
if (tradelink.custodian) {
|
|
666
|
+
if (existingTradelinkNetwork === null) {
|
|
667
|
+
logStep("Creating custodian config...");
|
|
668
|
+
const { settlement, withdraw, arbitration } = tradelink.custodian;
|
|
669
|
+
const data = {
|
|
670
|
+
settlement: settlement.map(tradelinkReviewStepFromManifest),
|
|
671
|
+
withdraw: withdraw.map(tradelinkReviewStepFromManifest),
|
|
672
|
+
arbitration: arbitration.map(tradelinkReviewStepFromManifest)
|
|
673
|
+
};
|
|
674
|
+
logSubStep("Creating request...");
|
|
675
|
+
await approveRequest(await admin1.client.requests.create.mutate({ request: {
|
|
676
|
+
type: "TRADELINK_CREATE_CUSTODIAN",
|
|
677
|
+
data
|
|
678
|
+
} }), [admin1, admin2], logger);
|
|
679
|
+
logSubStep("Tradelink custodian config has been created");
|
|
680
|
+
existingTradelinkNetwork = await admin1.client.tradelink.getNetwork.query();
|
|
681
|
+
} else logger.info("[SKIPPED] Custodian config already exists");
|
|
682
|
+
const networkBlueprint = await admin1.client.tradelink.getNetworkBlueprint.query();
|
|
683
|
+
if (networkBlueprint !== null) {
|
|
684
|
+
/* v8 ignore else -- @preserve */
|
|
685
|
+
if (tradelink.exchanges !== void 0) await createTradelinkExchanges(networkBlueprint, tradelink.exchanges);
|
|
686
|
+
/* v8 ignore else -- @preserve */
|
|
687
|
+
if (tradelink.assetManagers !== void 0) await createTradelinkAssetManagers(networkBlueprint, tradelink.assetManagers);
|
|
688
|
+
} else logger.error("Will not create exchanges and asset managers because there is no network blueprint for current workspace");
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
async function createTradelinkExchanges(networkBlueprint, exchanges) {
|
|
692
|
+
invariant(existingTradelinkNetwork !== null, "Network should exist");
|
|
693
|
+
const blueprintExchanges = networkBlueprint.exchanges;
|
|
694
|
+
for (const exchange of exchanges) {
|
|
695
|
+
const blueprintExchange = blueprintExchanges.find(({ name }) => name === exchange.name);
|
|
696
|
+
invariant(blueprintExchange !== void 0, `Exchange ${exchange.name} does not exist in network blueprint`);
|
|
697
|
+
const exchangeId = blueprintExchange.tradelinkId;
|
|
698
|
+
logStep(`Creating exchange ${exchange.name}...`);
|
|
699
|
+
if (existingTradelinkNetwork.exchanges.find(({ tradelinkId }) => tradelinkId === exchangeId) !== void 0) {
|
|
700
|
+
logger.info(`[SKIPPED] Exchange "${exchange.name}" already exists`);
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
logSubStep("Creating request...");
|
|
704
|
+
await approveRequest(await admin1.client.requests.create.mutate({ request: {
|
|
705
|
+
type: "TRADELINK_CREATE_EXCHANGE",
|
|
706
|
+
data: {
|
|
707
|
+
tradelinkId: exchangeId,
|
|
708
|
+
groups: exchange.groups.map(tradelinkReviewStepFromManifest),
|
|
709
|
+
whitelist: exchange.whitelist
|
|
710
|
+
}
|
|
711
|
+
} }), [
|
|
712
|
+
admin1,
|
|
713
|
+
admin2,
|
|
714
|
+
...await Promise.all(exchange.groups.flatMap(({ quorum, members }) => members.slice(0, quorum).map((deviceIndex) => workspaceEngine.withDeviceIndex(deviceIndex).login())))
|
|
715
|
+
], logger);
|
|
716
|
+
existingTradelinkNetwork = await admin1.client.tradelink.getNetwork.query();
|
|
717
|
+
invariant(existingTradelinkNetwork !== null, "Tradelink network exists");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
async function createTradelinkAssetManagers(networkBlueprint, assetManagers) {
|
|
721
|
+
invariant(existingTradelinkNetwork !== null, "Network should exist");
|
|
722
|
+
const blueprintAssetManagers = networkBlueprint.assetManagers;
|
|
723
|
+
for (const assetManager of assetManagers) {
|
|
724
|
+
const blueprintAssetManager = blueprintAssetManagers.find(({ name }) => name === assetManager.name);
|
|
725
|
+
invariant(blueprintAssetManager !== void 0, `AssetManager ${assetManager.name} does not exist in network blueprint`);
|
|
726
|
+
const assetManagerId = blueprintAssetManager.tradelinkId;
|
|
727
|
+
logStep(`Creating assetManager ${assetManager.name}...`);
|
|
728
|
+
if (existingTradelinkNetwork.assetManagers.find(({ tradelinkId }) => tradelinkId === assetManagerId) !== void 0) {
|
|
729
|
+
logger.info(`[SKIPPED] Asset manager "${assetManager.name}" already exists`);
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
logSubStep("Creating request...");
|
|
733
|
+
await approveRequest(await admin1.client.requests.create.mutate({ request: {
|
|
734
|
+
type: "TRADELINK_CREATE_ASSET_MANAGER",
|
|
735
|
+
data: {
|
|
736
|
+
tradelinkId: assetManagerId,
|
|
737
|
+
groups: assetManager.groups.map(tradelinkReviewStepFromManifest),
|
|
738
|
+
whitelist: assetManager.whitelist
|
|
739
|
+
}
|
|
740
|
+
} }), [
|
|
741
|
+
admin1,
|
|
742
|
+
admin2,
|
|
743
|
+
...await Promise.all(assetManager.groups.flatMap(({ quorum, members }) => members.slice(0, quorum).map((deviceIndex) => workspaceEngine.withDeviceIndex(deviceIndex).login())))
|
|
744
|
+
], logger);
|
|
745
|
+
existingTradelinkNetwork = await admin1.client.tradelink.getNetwork.query();
|
|
746
|
+
invariant(existingTradelinkNetwork !== null, "Tradelink network exists");
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
async function resolveDevicesIndexes(users) {
|
|
750
|
+
return await users.reduce((acc, cur) => acc.then(async (acc) => {
|
|
751
|
+
acc.push(await withDeviceIndex(cur));
|
|
752
|
+
return acc;
|
|
753
|
+
}), Promise.resolve([]));
|
|
754
|
+
}
|
|
755
|
+
async function withDeviceIndex(user) {
|
|
756
|
+
invariant(user.deviceUserId, `User ${user.id} does not have any device user id`);
|
|
757
|
+
return {
|
|
758
|
+
user,
|
|
759
|
+
deviceIndex: await findDeviceIndex(user.deviceUserId)
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
function userIdFromDeviceIndex(deviceIndex) {
|
|
763
|
+
const corresponding = usersWithDeviceIndex.find((o) => o.deviceIndex === deviceIndex);
|
|
764
|
+
invariant(corresponding, `No user with device index ${deviceIndex}`);
|
|
765
|
+
return corresponding.user.id;
|
|
766
|
+
}
|
|
767
|
+
function userIdFromUserName(name) {
|
|
768
|
+
const user = existingApiUsers.find((u) => u.name === name);
|
|
769
|
+
invariant(user, `User ${name} not found`);
|
|
770
|
+
return user.id;
|
|
771
|
+
}
|
|
772
|
+
function userIdFromDeviceIndexOrName(identifier) {
|
|
773
|
+
if (typeof identifier === "number") return userIdFromDeviceIndex(identifier);
|
|
774
|
+
return userIdFromUserName(identifier);
|
|
775
|
+
}
|
|
776
|
+
function groupIdFromName(groupName) {
|
|
777
|
+
const group = existingGroups.find((g) => g.name === groupName);
|
|
778
|
+
invariant(group, `Group ${groupName} not found`);
|
|
779
|
+
return group.id;
|
|
780
|
+
}
|
|
781
|
+
function policyIdFromName(policyName) {
|
|
782
|
+
const policy = existingPolicies.find((g) => g.name === policyName);
|
|
783
|
+
invariant(policy, `Policy ${policyName} not found`);
|
|
784
|
+
return policy.id;
|
|
785
|
+
}
|
|
786
|
+
function whitelistIdFromName(whitelistName) {
|
|
787
|
+
const whitelist = existingWhitelists.find((w) => w.name === whitelistName);
|
|
788
|
+
invariant(whitelist, `Whitelist ${whitelistName} not found`);
|
|
789
|
+
return whitelist.id;
|
|
790
|
+
}
|
|
791
|
+
function accountIdFromName(accountName) {
|
|
792
|
+
const account = existingAccounts.find((a) => a.name === accountName);
|
|
793
|
+
invariant(account, `Account "${accountName}" not found`);
|
|
794
|
+
return account.id;
|
|
795
|
+
}
|
|
796
|
+
function assetManagerIdFromName(assetManagerName) {
|
|
797
|
+
const orgAssetManager = tradelinkOrgAssetManagersByName[assetManagerName];
|
|
798
|
+
invariant(orgAssetManager, `Can't find Tradelink organization asset manager ${assetManagerName}`);
|
|
799
|
+
const assetManager = existingTradelinkNetwork?.assetManagers.find((assetManager) => assetManager.tradelinkId === orgAssetManager.tradelinkId);
|
|
800
|
+
invariant(assetManager, `Can't find Tradelink assetManager ${assetManagerName}`);
|
|
801
|
+
return assetManager.id;
|
|
802
|
+
}
|
|
803
|
+
function exchangeIdFromName(exchangeName) {
|
|
804
|
+
const exchange = existingTradelinkNetwork?.exchanges.find((exchange) => {
|
|
805
|
+
const orgExchange = tradelinkOrgExchangesByName[exchangeName];
|
|
806
|
+
invariant(orgExchange, `Can't find Tradelink organization exchange ${exchange.tradelinkId}`);
|
|
807
|
+
return orgExchange.name === exchangeName;
|
|
808
|
+
});
|
|
809
|
+
invariant(exchange, `Can't find Tradelink exchange ${exchangeName}`);
|
|
810
|
+
return exchange.id;
|
|
811
|
+
}
|
|
812
|
+
function tradelinkReviewStepFromManifest({ members, quorum }) {
|
|
813
|
+
return {
|
|
814
|
+
userIds: members.map(userIdFromDeviceIndex),
|
|
815
|
+
quorum
|
|
816
|
+
};
|
|
817
|
+
}
|
|
954
818
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
import invariant4 from "invariant";
|
|
958
|
-
import { match as match2 } from "ts-pattern";
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region src/bake/recipe-workspace.ts
|
|
959
821
|
async function recipeWorkspace(options, logger) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
822
|
+
const { engine, workspaceName } = options;
|
|
823
|
+
const workspaceEngine = engine.withWorkspace(workspaceName);
|
|
824
|
+
const findDeviceIndex = createDeviceIndexResolver(workspaceEngine);
|
|
825
|
+
logger.info("Authenticating with admin...");
|
|
826
|
+
const admin = await workspaceEngine.withDeviceIndex(4).login();
|
|
827
|
+
logger.info("Fetching existing data...");
|
|
828
|
+
const [existingUsers, existingGroups, existingWhitelists, publicPolicies, privatePolicies, existingAccounts, existingEntities, existingWorkspaceRules] = await Promise.all([
|
|
829
|
+
fetchAll(admin.client.users.list.query, { status: "ACTIVE" }),
|
|
830
|
+
fetchAll(admin.client.groups.list.query, { status: "ACTIVE" }),
|
|
831
|
+
fetchAll(admin.client.whitelists.list.query, { status: "ACTIVE" }),
|
|
832
|
+
fetchAll(admin.client.policies.list.query, { isPrivate: false }),
|
|
833
|
+
fetchAll(admin.client.policies.list.query, { isPrivate: true }),
|
|
834
|
+
fetchAll(admin.client.accounts.list.query, { status: "ACTIVE" }),
|
|
835
|
+
fetchAll(admin.client.entities.list.query, { status: "ACTIVE" }),
|
|
836
|
+
admin.client.governance.listWorkspaceRules.query()
|
|
837
|
+
]);
|
|
838
|
+
const existingPolicies = [...publicPolicies, ...privatePolicies];
|
|
839
|
+
const existingPsdUsers = existingUsers.filter((user) => user.deviceType === "PSD");
|
|
840
|
+
const existingApiUsers = existingUsers.filter((user) => user.deviceType === "API");
|
|
841
|
+
logger.info("Resolving device indexes...");
|
|
842
|
+
const psdUsersWithIndex = await resolveDevicesIndexes(existingPsdUsers);
|
|
843
|
+
logger.info("Reading users...");
|
|
844
|
+
const adminIndexes = psdUsersWithIndex.filter((u) => u.user.role === "ADMIN").map((u) => u.deviceIndex);
|
|
845
|
+
const operatorIndexes = psdUsersWithIndex.filter((u) => u.user.role === "OPERATOR").map((u) => u.deviceIndex);
|
|
846
|
+
const apiUsers = existingApiUsers.map((user) => {
|
|
847
|
+
const role = match(user.role).with("ADMIN", "OPERATOR", (role) => role).with("READ_ONLY_API_KEY", (role) => {
|
|
848
|
+
throw new Error(`Unexpected API user role "${role}" for user ${user.name}`);
|
|
849
|
+
}).exhaustive();
|
|
850
|
+
return {
|
|
851
|
+
name: user.name,
|
|
852
|
+
role,
|
|
853
|
+
pubKey: user.pubKey,
|
|
854
|
+
viewAll: user.legacyViewAll
|
|
855
|
+
};
|
|
856
|
+
});
|
|
857
|
+
logger.info("Reading groups...");
|
|
858
|
+
const groups = existingGroups.map((group) => ({
|
|
859
|
+
name: group.name,
|
|
860
|
+
members: group.members.map((member) => resolveUserIdentifier(member.id))
|
|
861
|
+
}));
|
|
862
|
+
logger.info("Reading whitelists...");
|
|
863
|
+
const whitelists = existingWhitelists.map((whitelist) => ({
|
|
864
|
+
name: whitelist.name,
|
|
865
|
+
description: whitelist.description ?? void 0,
|
|
866
|
+
type: "TRANSACTION",
|
|
867
|
+
addresses: whitelist.addresses.map((addr) => ({
|
|
868
|
+
name: addr.name,
|
|
869
|
+
currency: addr.currency,
|
|
870
|
+
address: addr.address,
|
|
871
|
+
...addr.destinationTags?.length ? { destinationTags: addr.destinationTags } : {}
|
|
872
|
+
}))
|
|
873
|
+
}));
|
|
874
|
+
logger.info("Reading policies...");
|
|
875
|
+
const policies = publicPolicies.map((policy) => ({
|
|
876
|
+
name: policy.name,
|
|
877
|
+
currency: policy.currency,
|
|
878
|
+
rules: policy.rules.map(govRuleToManifest)
|
|
879
|
+
}));
|
|
880
|
+
logger.info("Reading accounts...");
|
|
881
|
+
const accounts = existingAccounts.map((account) => {
|
|
882
|
+
const governance = match(account.governance).with({ type: "CLASSIC" }, (gov) => {
|
|
883
|
+
const policy = existingPolicies.find((p) => p.id === gov.policyId);
|
|
884
|
+
invariant(policy, `Policy ${gov.policyId} not found for account ${account.name}`);
|
|
885
|
+
return policy.isPrivate ? {
|
|
886
|
+
type: "CLASSIC",
|
|
887
|
+
rules: policy.rules.map(govRuleToManifest)
|
|
888
|
+
} : {
|
|
889
|
+
type: "POLICY",
|
|
890
|
+
policy: policy.name
|
|
891
|
+
};
|
|
892
|
+
}).with({ type: "RAW_SIGNING" }, (gov) => {
|
|
893
|
+
const policy = existingPolicies.find((p) => p.id === gov.policyId);
|
|
894
|
+
invariant(policy, `Policy ${gov.policyId} not found for account ${account.name}`);
|
|
895
|
+
return {
|
|
896
|
+
type: "RAW_SIGNING",
|
|
897
|
+
rules: policy.rules.map((rule) => ({
|
|
898
|
+
type: "SIGN_DIGESTS",
|
|
899
|
+
steps: rule.reviewSteps.map((step) => {
|
|
900
|
+
invariant("userIds" in step, "RAW_SIGNING rules only support user steps");
|
|
901
|
+
return {
|
|
902
|
+
quorum: step.quorum,
|
|
903
|
+
members: step.userIds.map(resolveUserToDeviceIndexOrName)
|
|
904
|
+
};
|
|
905
|
+
})
|
|
906
|
+
}))
|
|
907
|
+
};
|
|
908
|
+
}).with({ type: "TRADELINK" }, (gov) => ({
|
|
909
|
+
type: "TRADELINK",
|
|
910
|
+
assetManager: resolveAssetManagerName(gov.assetManagerId),
|
|
911
|
+
exchanges: gov.exchangeConfigs.map((exchangeConfig) => ({
|
|
912
|
+
name: resolveExchangeName(exchangeConfig.exchangeId),
|
|
913
|
+
...exchangeConfig.repledgeAuthorized ? { repledgeAuthorized: true } : {},
|
|
914
|
+
...exchangeConfig.needsAssetManagerApprovalForSettlements ? { needsAssetManagerApprovalForSettlements: true } : {}
|
|
915
|
+
}))
|
|
916
|
+
})).exhaustive();
|
|
917
|
+
return {
|
|
918
|
+
name: account.name,
|
|
919
|
+
currency: account.currency,
|
|
920
|
+
index: account.index,
|
|
921
|
+
...account.contractAddress ? { contractAddress: account.contractAddress } : {},
|
|
922
|
+
...account.tokenIdentifier ? { tokenIdentifier: account.tokenIdentifier } : {},
|
|
923
|
+
governance
|
|
924
|
+
};
|
|
925
|
+
});
|
|
926
|
+
logger.info("Reading entities...");
|
|
927
|
+
const entities = existingEntities.map((entity) => ({
|
|
928
|
+
name: entity.name,
|
|
929
|
+
...entity.accountIds.length > 0 ? { accounts: entity.accountIds.map(accountNameFromId) } : {},
|
|
930
|
+
...entity.memberIds.length > 0 ? { members: entity.memberIds.map(userNameFromId) } : {}
|
|
931
|
+
}));
|
|
932
|
+
logger.info("Reading workspace rules...");
|
|
933
|
+
const workspaceRules = existingWorkspaceRules.results.filter((rule) => rule.permission !== "ROOT").map((rule) => {
|
|
934
|
+
invariant(rule.reviewSteps.length === 1, "Only one review step supported for workspace rules in manifest");
|
|
935
|
+
const step = rule.reviewSteps[0];
|
|
936
|
+
const quorum = step.quorum;
|
|
937
|
+
invariant(rule.permission !== "ROOT", "filtered out");
|
|
938
|
+
return {
|
|
939
|
+
permission: rule.permission,
|
|
940
|
+
quorum,
|
|
941
|
+
members: step.userIds.map(resolveUserToDeviceIndexOrName)
|
|
942
|
+
};
|
|
943
|
+
});
|
|
944
|
+
const workspace = {
|
|
945
|
+
users: {
|
|
946
|
+
...adminIndexes.length > 0 ? { admins: adminIndexes } : {},
|
|
947
|
+
...operatorIndexes.length > 0 ? { operators: operatorIndexes } : {},
|
|
948
|
+
...apiUsers.length > 0 ? { api: apiUsers } : {}
|
|
949
|
+
},
|
|
950
|
+
...groups.length > 0 ? { groups } : {},
|
|
951
|
+
...whitelists.length > 0 ? { whitelists } : {},
|
|
952
|
+
...policies.length > 0 ? { policies } : {},
|
|
953
|
+
...accounts.length > 0 ? { accounts } : {},
|
|
954
|
+
...entities.length > 0 ? { entities } : {},
|
|
955
|
+
...workspaceRules.length > 0 ? { workspaceRules } : {}
|
|
956
|
+
};
|
|
957
|
+
logger.info("Recipe completed");
|
|
958
|
+
return workspace;
|
|
959
|
+
async function resolveDevicesIndexes(users) {
|
|
960
|
+
const result = [];
|
|
961
|
+
for (const user of users) {
|
|
962
|
+
invariant(user.deviceUserId, `User ${user.id} does not have any device user id`);
|
|
963
|
+
const deviceIndex = await findDeviceIndex(user.deviceUserId);
|
|
964
|
+
result.push({
|
|
965
|
+
user,
|
|
966
|
+
deviceIndex
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
return result;
|
|
970
|
+
}
|
|
971
|
+
function resolveUserIdentifier(userId) {
|
|
972
|
+
const psdUser = psdUsersWithIndex.find((u) => u.user.id === userId);
|
|
973
|
+
if (psdUser) return psdUser.deviceIndex;
|
|
974
|
+
const apiUser = existingApiUsers.find((u) => u.id === userId);
|
|
975
|
+
if (apiUser) return apiUser.name;
|
|
976
|
+
throw new Error(`User ${userId} not found`);
|
|
977
|
+
}
|
|
978
|
+
function resolveUserToDeviceIndexOrName(userId) {
|
|
979
|
+
return resolveUserIdentifier(userId);
|
|
980
|
+
}
|
|
981
|
+
function validateGovernanceRuleType(value) {
|
|
982
|
+
invariant([
|
|
983
|
+
"SEND",
|
|
984
|
+
"STAKE",
|
|
985
|
+
"DEPLOY_CONTRACT",
|
|
986
|
+
"EXECUTE_CONTRACT",
|
|
987
|
+
"SIGN_MESSAGE",
|
|
988
|
+
"RECEIVE"
|
|
989
|
+
].includes(value), "Unexpected governance rule type: " + value);
|
|
990
|
+
return value;
|
|
991
|
+
}
|
|
992
|
+
function govRuleToManifest(rule) {
|
|
993
|
+
const operationType = validateGovernanceRuleType(rule.operationType);
|
|
994
|
+
const threshold = rule.threshold !== void 0 && (rule.threshold.min !== null || rule.threshold.max !== null) ? { threshold: {
|
|
995
|
+
min: rule.threshold.min,
|
|
996
|
+
max: rule.threshold.max
|
|
997
|
+
} } : {};
|
|
998
|
+
return {
|
|
999
|
+
type: operationType,
|
|
1000
|
+
steps: rule.reviewSteps.map((step) => {
|
|
1001
|
+
if ("groupId" in step) {
|
|
1002
|
+
const group = existingGroups.find((g) => g.id === step.groupId);
|
|
1003
|
+
invariant(group, `Group ${step.groupId} not found`);
|
|
1004
|
+
return {
|
|
1005
|
+
quorum: step.quorum,
|
|
1006
|
+
group: group.name
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
quorum: step.quorum,
|
|
1011
|
+
members: step.userIds.map(resolveUserToDeviceIndexOrName)
|
|
1012
|
+
};
|
|
1013
|
+
}),
|
|
1014
|
+
...threshold,
|
|
1015
|
+
...rule.whitelistIds && rule.whitelistIds.length > 0 ? { whitelistNames: rule.whitelistIds.map(whitelistNameFromId) } : {}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
function whitelistNameFromId(whitelistId) {
|
|
1019
|
+
const whitelist = existingWhitelists.find((w) => w.id === whitelistId);
|
|
1020
|
+
invariant(whitelist, `Whitelist ${whitelistId} not found`);
|
|
1021
|
+
return whitelist.name;
|
|
1022
|
+
}
|
|
1023
|
+
function accountNameFromId(accountId) {
|
|
1024
|
+
const account = existingAccounts.find((a) => a.id === accountId);
|
|
1025
|
+
invariant(account, `Account ${accountId} not found`);
|
|
1026
|
+
return account.name;
|
|
1027
|
+
}
|
|
1028
|
+
function userNameFromId(userId) {
|
|
1029
|
+
const user = existingApiUsers.find((u) => u.id === userId);
|
|
1030
|
+
invariant(user, `API user ${userId} not found`);
|
|
1031
|
+
return user.name;
|
|
1032
|
+
}
|
|
1033
|
+
function resolveAssetManagerName(assetManagerId) {
|
|
1034
|
+
return assetManagerId;
|
|
1035
|
+
}
|
|
1036
|
+
function resolveExchangeName(exchangeId) {
|
|
1037
|
+
return exchangeId;
|
|
1038
|
+
}
|
|
1178
1039
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1040
|
+
//#endregion
|
|
1041
|
+
//#region src/bake/recipe.ts
|
|
1181
1042
|
async function recipeManifest(options, logger) {
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
};
|
|
1043
|
+
return {
|
|
1044
|
+
manifestVersion: 2,
|
|
1045
|
+
workspace: await recipeWorkspace(options, logger)
|
|
1046
|
+
};
|
|
1187
1047
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
description: "API user public key. If not provided, one will be generated using the name as entropy"
|
|
1198
|
-
}).optional(),
|
|
1199
|
-
viewAll: z.boolean().meta({ description: "Whether the API user can view all accounts" })
|
|
1048
|
+
//#endregion
|
|
1049
|
+
//#region src/bake/types.ts
|
|
1050
|
+
const zManifestUser = z.number().meta({ description: "User device index" });
|
|
1051
|
+
const zManifestUserName = z.string().meta({ description: "User name" });
|
|
1052
|
+
const zManifestApiUser = z.object({
|
|
1053
|
+
name: z.string().meta({ description: "API user name" }),
|
|
1054
|
+
role: z.enum(["ADMIN", "OPERATOR"]).meta({ description: "API user role" }).default("OPERATOR"),
|
|
1055
|
+
pubKey: z.string().meta({ description: "API user public key. If not provided, one will be generated using the name as entropy" }).optional(),
|
|
1056
|
+
viewAll: z.boolean().meta({ description: "Whether the API user can view all accounts" })
|
|
1200
1057
|
});
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1058
|
+
const zManifestGroup = z.object({
|
|
1059
|
+
name: z.string().meta({ description: "Group name" }),
|
|
1060
|
+
members: z.array(z.union([zManifestUser, zManifestUserName])).meta({ description: "Group members" })
|
|
1204
1061
|
}).strict().meta({ description: "Operators group" });
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1062
|
+
const zManifestWhitelistAddress = z.object({
|
|
1063
|
+
name: z.string().meta({ description: "Whitelist address name" }),
|
|
1064
|
+
currency: z.string().meta({ description: "Whitelist address currency" }),
|
|
1065
|
+
address: z.string().meta({ description: "Whitelist address" }),
|
|
1066
|
+
destinationTags: z.array(z.string()).optional().meta({ description: "List of destination tags (applicable for Ripple currency)" })
|
|
1210
1067
|
}).strict();
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1068
|
+
const zManifestWhitelist = z.object({
|
|
1069
|
+
name: z.string().meta({ description: "Whitelist name" }),
|
|
1070
|
+
description: z.string().optional().meta({ description: "Whitelist description" }),
|
|
1071
|
+
type: z.enum(["TRANSACTION", "SMART_CONTRACT"]).optional().default("TRANSACTION").meta({ description: "Whitelist type" }),
|
|
1072
|
+
addresses: z.array(zManifestWhitelistAddress).meta({ description: "Whitelist addresses" })
|
|
1216
1073
|
}).strict().meta({ description: "Whitelist" });
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1074
|
+
const zManifestGovernanceStepWithUsers = z.object({
|
|
1075
|
+
quorum: z.number(),
|
|
1076
|
+
members: z.array(z.union([zManifestUser, zManifestUserName]))
|
|
1220
1077
|
}).strict();
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1078
|
+
const zManifestGovernanceStepWithGroup = z.object({
|
|
1079
|
+
quorum: z.number(),
|
|
1080
|
+
group: z.string()
|
|
1224
1081
|
}).strict();
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
threshold: z.object({
|
|
1242
|
-
min: z.string().nullable(),
|
|
1243
|
-
max: z.string().nullable()
|
|
1244
|
-
}).optional(),
|
|
1245
|
-
whitelistNames: z.array(z.string()).optional()
|
|
1082
|
+
const zManifestGovernanceStep = z.union([zManifestGovernanceStepWithUsers, zManifestGovernanceStepWithGroup]);
|
|
1083
|
+
const zManifestGovernanceRule = z.object({
|
|
1084
|
+
type: z.enum([
|
|
1085
|
+
"SEND",
|
|
1086
|
+
"STAKE",
|
|
1087
|
+
"DEPLOY_CONTRACT",
|
|
1088
|
+
"EXECUTE_CONTRACT",
|
|
1089
|
+
"SIGN_MESSAGE",
|
|
1090
|
+
"RECEIVE"
|
|
1091
|
+
]),
|
|
1092
|
+
steps: z.array(zManifestGovernanceStep).min(1),
|
|
1093
|
+
threshold: z.object({
|
|
1094
|
+
min: z.string().nullable(),
|
|
1095
|
+
max: z.string().nullable()
|
|
1096
|
+
}).optional(),
|
|
1097
|
+
whitelistNames: z.array(z.string()).optional()
|
|
1246
1098
|
}).strict();
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1099
|
+
const zManifestPolicy = z.object({
|
|
1100
|
+
name: z.string(),
|
|
1101
|
+
currency: z.string().nullable(),
|
|
1102
|
+
rules: z.array(zManifestGovernanceRule)
|
|
1251
1103
|
}).strict();
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1104
|
+
const zManifestClassicAccountGovernance = z.object({
|
|
1105
|
+
type: z.literal("CLASSIC"),
|
|
1106
|
+
rules: z.array(zManifestGovernanceRule)
|
|
1255
1107
|
}).strict();
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1108
|
+
const zManifestClassicAccountGovernanceWithPolicy = z.object({
|
|
1109
|
+
type: z.literal("POLICY"),
|
|
1110
|
+
policy: z.string()
|
|
1259
1111
|
}).strict();
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1112
|
+
const zManifestTradelinkExchangeConfig = z.object({
|
|
1113
|
+
name: z.string().meta({ description: "Exchange name" }),
|
|
1114
|
+
repledgeAuthorized: z.boolean().optional().meta({ description: "Whether repledge is authorized" }),
|
|
1115
|
+
needsAssetManagerApprovalForSettlements: z.boolean().optional().meta({ description: "Whether asset manager pre approval is needed" })
|
|
1264
1116
|
}).strict();
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1117
|
+
const zManifestTradelinkAccountGovernance = z.object({
|
|
1118
|
+
type: z.literal("TRADELINK"),
|
|
1119
|
+
assetManager: z.string().meta({ description: "Asset manager name" }),
|
|
1120
|
+
exchanges: z.array(zManifestTradelinkExchangeConfig)
|
|
1269
1121
|
}).strict();
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1122
|
+
const zManifestRawSigningGovernanceRule = z.object({
|
|
1123
|
+
type: z.literal("SIGN_DIGESTS"),
|
|
1124
|
+
steps: z.array(zManifestGovernanceStepWithUsers).min(1)
|
|
1273
1125
|
}).strict();
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1126
|
+
const zManifestRawSigningAccountGovernance = z.object({
|
|
1127
|
+
type: z.literal("RAW_SIGNING"),
|
|
1128
|
+
rules: z.array(zManifestRawSigningGovernanceRule)
|
|
1277
1129
|
}).strict();
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1130
|
+
const zManifestAccountGovernance = z.discriminatedUnion("type", [
|
|
1131
|
+
zManifestClassicAccountGovernance,
|
|
1132
|
+
zManifestTradelinkAccountGovernance,
|
|
1133
|
+
zManifestRawSigningAccountGovernance,
|
|
1134
|
+
zManifestClassicAccountGovernanceWithPolicy
|
|
1283
1135
|
]);
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1136
|
+
const zManifestAccount = z.object({
|
|
1137
|
+
name: z.string(),
|
|
1138
|
+
currency: z.string(),
|
|
1139
|
+
index: z.number().nonnegative(),
|
|
1140
|
+
contractAddress: z.string().optional(),
|
|
1141
|
+
tokenIdentifier: z.string().optional(),
|
|
1142
|
+
governance: zManifestAccountGovernance
|
|
1291
1143
|
}).strict();
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1144
|
+
const zManifestEntity = z.object({
|
|
1145
|
+
name: z.string().min(1).max(19).meta({ description: "Entity name" }),
|
|
1146
|
+
accounts: z.array(z.string()).optional().meta({ description: "Account names to include in this entity" }),
|
|
1147
|
+
members: z.array(z.string()).optional().meta({ description: "API operator names to include in this entity" })
|
|
1296
1148
|
}).strict().meta({ description: "Entity" });
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1149
|
+
const zManifestHederaData = z.object({
|
|
1150
|
+
index: z.number(),
|
|
1151
|
+
onChainId: z.string(),
|
|
1152
|
+
activate: z.boolean().default(true)
|
|
1301
1153
|
}).strict();
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1154
|
+
const zManifestPledgeAdjustment = z.object({
|
|
1155
|
+
account: z.string(),
|
|
1156
|
+
exchange: z.string(),
|
|
1157
|
+
amount: z.string()
|
|
1306
1158
|
}).strict();
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1159
|
+
const zManifestTradelinkGroup = z.object({
|
|
1160
|
+
members: z.array(zManifestUser),
|
|
1161
|
+
quorum: z.number()
|
|
1310
1162
|
}).strict().meta({ description: "Tradelink group" });
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1163
|
+
const zManifestTradelinkGroups = z.array(zManifestTradelinkGroup);
|
|
1164
|
+
const zManifestTradelinkWhitelistAddress = z.object({
|
|
1165
|
+
name: z.string().min(1),
|
|
1166
|
+
currency: z.string().min(1),
|
|
1167
|
+
address: z.string().min(1)
|
|
1316
1168
|
}).strict().meta({ description: "Tradelink whitelist address" });
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1169
|
+
const zManifestTradelinkWhitelist = z.array(zManifestTradelinkWhitelistAddress).meta({ description: "Tradelink whitelist" });
|
|
1170
|
+
const zManifestTradelinkEntity = z.object({
|
|
1171
|
+
name: z.string(),
|
|
1172
|
+
groups: zManifestTradelinkGroups,
|
|
1173
|
+
whitelist: zManifestTradelinkWhitelist
|
|
1322
1174
|
}).strict();
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1175
|
+
const zManifestTradelinkCustodian = z.object({
|
|
1176
|
+
settlement: zManifestTradelinkGroups,
|
|
1177
|
+
withdraw: zManifestTradelinkGroups,
|
|
1178
|
+
arbitration: zManifestTradelinkGroups
|
|
1327
1179
|
}).strict();
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1180
|
+
const zManifestTradelinkNetworkBlueprint = z.object({
|
|
1181
|
+
custodian: z.string(),
|
|
1182
|
+
exchanges: z.array(z.string()),
|
|
1183
|
+
assetManagers: z.array(z.string())
|
|
1332
1184
|
}).strict();
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1185
|
+
const zManifestTradelinkNetwork = z.object({
|
|
1186
|
+
networkBlueprint: zManifestTradelinkNetworkBlueprint.optional(),
|
|
1187
|
+
custodian: zManifestTradelinkCustodian.optional(),
|
|
1188
|
+
exchanges: z.array(zManifestTradelinkEntity).optional(),
|
|
1189
|
+
assetManagers: z.array(zManifestTradelinkEntity).optional()
|
|
1338
1190
|
}).strict().meta({ description: "Tradelink network configuration" });
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1191
|
+
const zManifestWorkspaceRule = z.object({
|
|
1192
|
+
permission: z.enum(["CREATE_ACCOUNT_WITH_POLICY"]),
|
|
1193
|
+
quorum: z.number().min(1),
|
|
1194
|
+
members: z.array(z.union([zManifestUser, zManifestUserName]))
|
|
1343
1195
|
}).strict().meta({ description: "Workspace rule for granting permissions" });
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1196
|
+
const zManifestWorkspace = z.object({
|
|
1197
|
+
tradelink: zManifestTradelinkNetwork.optional(),
|
|
1198
|
+
users: z.object({
|
|
1199
|
+
admins: z.array(zManifestUser).optional(),
|
|
1200
|
+
operators: z.array(zManifestUser).optional(),
|
|
1201
|
+
api: z.array(zManifestApiUser).optional()
|
|
1202
|
+
}).optional(),
|
|
1203
|
+
groups: z.array(zManifestGroup).optional(),
|
|
1204
|
+
whitelists: z.array(zManifestWhitelist).optional(),
|
|
1205
|
+
policies: z.array(zManifestPolicy).optional(),
|
|
1206
|
+
accounts: z.array(zManifestAccount).optional(),
|
|
1207
|
+
entities: z.array(zManifestEntity).optional(),
|
|
1208
|
+
hederaData: z.record(z.string(), z.array(zManifestHederaData)).optional(),
|
|
1209
|
+
pledges: z.array(zManifestPledgeAdjustment).optional(),
|
|
1210
|
+
workspaceRules: z.array(zManifestWorkspaceRule).optional()
|
|
1359
1211
|
}).strict().meta({ description: "Workspace configuration" });
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1212
|
+
const zManifestTradelinkOrgEntity = z.object({
|
|
1213
|
+
name: z.string(),
|
|
1214
|
+
code: z.string(),
|
|
1215
|
+
logo: z.string()
|
|
1364
1216
|
}).strict().meta({ description: "Tradelink organization entity" });
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1217
|
+
const zManifestTradelinkOrg = z.object({
|
|
1218
|
+
exchanges: z.array(zManifestTradelinkOrgEntity).optional(),
|
|
1219
|
+
assetManagers: z.array(zManifestTradelinkOrgEntity).optional(),
|
|
1220
|
+
custodians: z.array(zManifestTradelinkOrgEntity).optional()
|
|
1369
1221
|
}).strict().meta({ description: "Tradelink organization configuration" });
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1222
|
+
const zManifest = z.object({
|
|
1223
|
+
manifestVersion: z.literal(2),
|
|
1224
|
+
tradelink: zManifestTradelinkOrg.optional(),
|
|
1225
|
+
workspace: zManifestWorkspace.optional()
|
|
1374
1226
|
}).strict();
|
|
1375
|
-
|
|
1376
|
-
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/bake/index.ts
|
|
1377
1229
|
async function bakeManifest(options, logger) {
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1230
|
+
const { engine, workspaceName, rootAuthToken, manifest: { tradelink, workspace } } = options;
|
|
1231
|
+
if (tradelink) {
|
|
1232
|
+
invariant(rootAuthToken, `rootAuthToken is required to create Tradelink org entities`);
|
|
1233
|
+
await bakeTradelinkOrganization({
|
|
1234
|
+
engine,
|
|
1235
|
+
tradelink,
|
|
1236
|
+
rootAuthToken,
|
|
1237
|
+
...workspace?.tradelink?.networkBlueprint && workspaceName ? { networkBlueprint: {
|
|
1238
|
+
blueprint: workspace.tradelink.networkBlueprint,
|
|
1239
|
+
workspaceName
|
|
1240
|
+
} } : {}
|
|
1241
|
+
}, logger);
|
|
1242
|
+
}
|
|
1243
|
+
if (workspace) {
|
|
1244
|
+
invariant(workspaceName, `Can't bake workspace without specifying a workspace name`);
|
|
1245
|
+
await bakeWorkspace({
|
|
1246
|
+
engine,
|
|
1247
|
+
workspace,
|
|
1248
|
+
workspaceName
|
|
1249
|
+
}, logger);
|
|
1250
|
+
if (workspace.pledges) {
|
|
1251
|
+
const engineWithWorkspace = engine.withWorkspace(workspaceName);
|
|
1252
|
+
for (const pledgeAdjustment of workspace.pledges) await bakePledgeAdjustment({
|
|
1253
|
+
engine: engineWithWorkspace,
|
|
1254
|
+
pledgeAdjustment
|
|
1255
|
+
}, logger);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
logger.info("Successfully baked");
|
|
1407
1259
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
createAndApproveRequest,
|
|
1412
|
-
recipeManifest,
|
|
1413
|
-
zManifest,
|
|
1414
|
-
zManifestPolicy
|
|
1415
|
-
};
|
|
1416
|
-
/* v8 ignore else - TODO bake empty tradelink org -- @preserve */
|
|
1417
|
-
/* v8 ignore else -- @preserve */
|
|
1260
|
+
//#endregion
|
|
1261
|
+
export { bakeManifest, bakePledgeAdjustment, createAndApproveRequest, recipeManifest, zManifest, zManifestPolicy };
|
|
1262
|
+
|
|
1418
1263
|
//# sourceMappingURL=bake.js.map
|