@ledgerhq/vault-common 2.1.1 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.turbo/turbo-build.log +19 -59
  2. package/CHANGELOG.md +12 -0
  3. package/lib/{chunk-GAKIXPAF.js → chunk-65DEEXP4.js} +1 -1
  4. package/lib/chunk-65DEEXP4.js.map +1 -0
  5. package/lib/createHSMBridge.d.ts +1 -1
  6. package/lib/{index-Cm_O9VIx.d.ts → index-BpLhb-bQ.d.ts} +11 -0
  7. package/lib/index.d.ts +2 -2
  8. package/lib/index.js +2 -2
  9. package/lib/recipeManifest.d.ts +1 -1
  10. package/lib/reviewAPIRequest.d.ts +1 -1
  11. package/lib/types/index.d.ts +1 -1
  12. package/lib/types/index.js +2 -2
  13. package/lib/utils.d.ts +1 -1
  14. package/package.json +8 -9
  15. package/tsup.config.ts +1 -1
  16. package/lib/chunk-54MXA3ZY.mjs +0 -14
  17. package/lib/chunk-54MXA3ZY.mjs.map +0 -1
  18. package/lib/chunk-6TT6A6YA.mjs +0 -1176
  19. package/lib/chunk-6TT6A6YA.mjs.map +0 -1
  20. package/lib/chunk-GAKIXPAF.js.map +0 -1
  21. package/lib/chunk-HU7O2ZFW.mjs +0 -249
  22. package/lib/chunk-HU7O2ZFW.mjs.map +0 -1
  23. package/lib/chunk-J5LGTIGS.mjs +0 -10
  24. package/lib/chunk-J5LGTIGS.mjs.map +0 -1
  25. package/lib/chunk-MNUHUKY3.mjs +0 -503
  26. package/lib/chunk-MNUHUKY3.mjs.map +0 -1
  27. package/lib/chunk-QNNV5GBH.mjs +0 -1261
  28. package/lib/chunk-QNNV5GBH.mjs.map +0 -1
  29. package/lib/chunk-VOB7PA3G.mjs +0 -97
  30. package/lib/chunk-VOB7PA3G.mjs.map +0 -1
  31. package/lib/chunk-ZJCMYPBL.mjs +0 -83
  32. package/lib/chunk-ZJCMYPBL.mjs.map +0 -1
  33. package/lib/createHSMBridge.d.mts +0 -27
  34. package/lib/createHSMBridge.mjs +0 -10
  35. package/lib/createHSMBridge.mjs.map +0 -1
  36. package/lib/crypto/utils.d.mts +0 -14
  37. package/lib/crypto/utils.mjs +0 -12
  38. package/lib/crypto/utils.mjs.map +0 -1
  39. package/lib/index-Cm_O9VIx.d.mts +0 -2010
  40. package/lib/index.d.mts +0 -161
  41. package/lib/index.mjs +0 -3252
  42. package/lib/index.mjs.map +0 -1
  43. package/lib/recipeManifest.d.mts +0 -6
  44. package/lib/recipeManifest.mjs +0 -11
  45. package/lib/recipeManifest.mjs.map +0 -1
  46. package/lib/reviewAPIRequest.d.mts +0 -20
  47. package/lib/reviewAPIRequest.mjs +0 -11
  48. package/lib/reviewAPIRequest.mjs.map +0 -1
  49. package/lib/types/index.d.mts +0 -6
  50. package/lib/types/index.mjs +0 -10
  51. package/lib/types/index.mjs.map +0 -1
  52. package/lib/utils.d.mts +0 -29
  53. package/lib/utils.mjs +0 -38
  54. package/lib/utils.mjs.map +0 -1
package/lib/index.mjs DELETED
@@ -1,3252 +0,0 @@
1
- import {
2
- createHSMBridge_default
3
- } from "./chunk-ZJCMYPBL.mjs";
4
- import {
5
- recipeManifest
6
- } from "./chunk-MNUHUKY3.mjs";
7
- import {
8
- authenticate,
9
- createDefaultRunner_default,
10
- decodeChallenge,
11
- getAuthTokens,
12
- getTradelinkPledge,
13
- getTradelinkRecipient,
14
- performRequest,
15
- prepareRequest_default,
16
- reviewAPIRequest_default,
17
- signAndApprove
18
- } from "./chunk-6TT6A6YA.mjs";
19
- import {
20
- createNetwork
21
- } from "./chunk-VOB7PA3G.mjs";
22
- import {
23
- LIGHT_EVM_CURRENCIES,
24
- deserializeUnitValue,
25
- extractSecureChannel,
26
- getAccountTypeByCurrency,
27
- getAccountUnit,
28
- getCryptoCurrencyById,
29
- getCurrencyOrToken,
30
- getCurrencyUnit,
31
- getDefaultUsername,
32
- getGateAccountUnit,
33
- getTokenUnit,
34
- getWorkspaceFromGate,
35
- listCryptoCurrencies,
36
- queue,
37
- serializeUnitValue,
38
- unwrapConnection,
39
- vaultCoins,
40
- wait,
41
- xpubToExtendedPubKey
42
- } from "./chunk-QNNV5GBH.mjs";
43
- import {
44
- genKeys
45
- } from "./chunk-HU7O2ZFW.mjs";
46
- import {
47
- GateGroupRequestTypeDefs,
48
- feesLevels
49
- } from "./chunk-54MXA3ZY.mjs";
50
- import {
51
- __export
52
- } from "./chunk-J5LGTIGS.mjs";
53
-
54
- // src/bakeManifest.ts
55
- import { SILENT_LOGGER as SILENT_LOGGER2 } from "@ledgerhq/vault-utils";
56
- import chalk from "chalk";
57
- import orderBy from "lodash/orderBy";
58
- import sortBy2 from "lodash/sortBy";
59
-
60
- // src/deserializeManifest.ts
61
- var fromRawUser = (u) => typeof u === "number" ? { device: u } : u;
62
- var fromRawAPIUser = (u) => typeof u === "string" ? { name: u } : u;
63
- function deserializeManifest(manifest) {
64
- const { users, ...rest } = manifest;
65
- return {
66
- ...rest,
67
- ...users ? {
68
- users: {
69
- ...users.operators ? { operators: users.operators.map(fromRawUser) } : {},
70
- ...users.admins ? { admins: users.admins.map(fromRawUser) } : {},
71
- ...users.api ? { api: users.api.map(fromRawAPIUser) } : {},
72
- ...users.apiV2 ? { apiV2: users.apiV2 } : {}
73
- }
74
- } : {}
75
- };
76
- }
77
- var deserializeManifest_default = deserializeManifest;
78
-
79
- // src/fetchTokens.ts
80
- import { SILENT_LOGGER } from "@ledgerhq/vault-utils";
81
- function erc20TokenToGateTokenCurrency(erc20) {
82
- const parent_currency = erc20.blockchain_name === "foundation" ? "ethereum" : erc20.blockchain_name === "ropsten" ? "ethereum_ropsten" : erc20.blockchain_name === "goerli" ? "ethereum_goerli" : null;
83
- if (!parent_currency) {
84
- throw new Error(
85
- `Can't determine parent_currency from ERC20 token with blockchain_name "${erc20.blockchain_name}"`
86
- );
87
- }
88
- return {
89
- contract_address: erc20.contract_address,
90
- family: "ethereum",
91
- name: erc20.name,
92
- parent_currency,
93
- ticker: erc20.ticker,
94
- token_type: "erc20",
95
- units: [
96
- {
97
- name: erc20.ticker,
98
- code: erc20.ticker,
99
- magnitude: erc20.decimals
100
- }
101
- ],
102
- __legacy_hsm_account_parameters: erc20.hsm_account_parameters,
103
- __legacy_hsm_signature: erc20.hsm_signature
104
- };
105
- }
106
- async function fetchERC20Tokens(ctx, { logger = SILENT_LOGGER } = {}) {
107
- if (process.env.LEGACY_TOKENS) {
108
- const res = await ctx.network("GET", "/currencies/erc20s");
109
- return res.erc20s.map(erc20TokenToGateTokenCurrency);
110
- }
111
- let tokens;
112
- try {
113
- tokens = await ctx.network("GET", "/currencies/tokens");
114
- } catch (err) {
115
- logger.error(
116
- "Fetching tokens has failed, if you are targeting a gate < Vault 3.6, set environment variable LEGACY_TOKENS to 1"
117
- );
118
- logger.error("e.g: LEGACY_TOKENS=1 ledger-vault <command>");
119
- throw err;
120
- }
121
- return tokens;
122
- }
123
- var fetchTokens_default = fetchERC20Tokens;
124
-
125
- // src/revault-compat.ts
126
- import { createEngine } from "@ledgerhq/revault-sdk/engine";
127
- import { onboard } from "@ledgerhq/revault-sdk/onboarding";
128
-
129
- // src/genSeed.ts
130
- import { entropyToMnemonic } from "bip39";
131
- import hex from "crypto-js/enc-hex";
132
- import sha256 from "crypto-js/sha256";
133
- var hashCode = (str) => {
134
- return Buffer.from(hex.stringify(sha256(str)), "hex");
135
- };
136
- var genSeed = (salt = "", index = 1, options = {}) => {
137
- const { overrideSeeds } = options;
138
- const overriddenSeed = !!overrideSeeds && overrideSeeds[index - 1];
139
- if (overriddenSeed) return overriddenSeed;
140
- const entropy = hashCode(`${salt}_${index}`);
141
- return entropyToMnemonic(entropy.toString("hex"));
142
- };
143
- var genSeed_default = genSeed;
144
-
145
- // src/revault-compat.ts
146
- function genSeed2({ salt, index }) {
147
- return genSeed_default(salt, index);
148
- }
149
- async function onboardWithRevault(options, logger) {
150
- const engineOptions = {
151
- salt: options.salt,
152
- baseUrl: options.revaultApiUrl,
153
- deviceApiUrl: options.deviceApiUrl,
154
- genSeed: genSeed2
155
- };
156
- const revaultLogger = {
157
- ...logger,
158
- warning: logger.info,
159
- debug: (msg) => {
160
- if (process.env.DEBUG === "1") {
161
- console.log("DEBUG", msg);
162
- }
163
- },
164
- output: logger.info
165
- };
166
- const baseEngine = createEngine(engineOptions, revaultLogger);
167
- const rootClient = baseEngine.createClient({ rootAuthToken: options.revaultRootAuthToken });
168
- try {
169
- const onboarding = await rootClient.onboarding.startOnboarding.mutate({
170
- hsmScriptsVersion: options.hsmScriptsVersion,
171
- workspaceName: options.workspace,
172
- compartmentId: options.compartmentId,
173
- mode: "GATE_COMPAT"
174
- });
175
- const onboardingEngine = baseEngine.withOnboarding(onboarding.id);
176
- await onboard({ engine: onboardingEngine }, revaultLogger);
177
- } catch (err) {
178
- if (err instanceof Error && err.message.includes("has already started onboarding")) {
179
- logger.info("Onboarding already started, assuming it's finished, skipping");
180
- return;
181
- }
182
- throw err;
183
- }
184
- }
185
-
186
- // src/utilsComparison.ts
187
- import isEqual from "lodash/isEqual";
188
- import sortBy from "lodash/sortBy";
189
- var areGroupsDifferent = (group1, group2) => {
190
- if (group1.users.length !== group2.users.length) return true;
191
- const sortedUsers1 = sortBy(group1.users, [(user) => user.toString()]);
192
- const sortedUsers2 = sortBy(group2.users, [(user) => user.toString()]);
193
- return !isEqual(sortedUsers1, sortedUsers2);
194
- };
195
- var areWorkspaceRulesDifferent = (rule1, rule2) => {
196
- if (rule1.permission !== rule2.permission) return true;
197
- if (rule1.steps.length !== rule2.steps.length) return true;
198
- return rule1.steps.some((step1, i) => {
199
- const step2 = rule2.steps[i];
200
- if (step1.quorum !== step2.quorum) return true;
201
- if (!isEqual(sortBy(step1.users), sortBy(step2.users))) return true;
202
- return false;
203
- });
204
- };
205
- var formatAddress = (a) => `${a.currency}-${a.address}`;
206
- var areWhitelistsDifferent = (whitelist1, whitelist2) => {
207
- if (whitelist1.addresses.length !== whitelist2.addresses.length) return true;
208
- const _addresses1 = whitelist1.addresses.map(formatAddress);
209
- const _addresses2 = whitelist2.addresses.map(formatAddress);
210
- return !isEqual(sortBy(_addresses1), sortBy(_addresses2));
211
- };
212
- function sanitizeThreshold(step) {
213
- return { ...step, min: step.min || 0 };
214
- }
215
- var areAccountsDifferent = (account1, account2) => {
216
- if (account1.name !== account2.name) return true;
217
- if (account1.tradelink_data || account2.tradelink_data) return false;
218
- const account1Rules = account1.rules;
219
- const account2Rules = account2.rules;
220
- if (!!account1Rules !== !!account2Rules) return true;
221
- if (!account1Rules || !account2Rules) return false;
222
- if (account1Rules.length !== account2Rules.length) return true;
223
- let isDifferent = false;
224
- account1Rules.forEach((rule, index) => {
225
- const account2Rule = account2Rules[index];
226
- if (!account2Rule) throw new Error(`No rule found at index ${index}`);
227
- if (rule.length !== account2Rule.length) {
228
- isDifferent = true;
229
- return;
230
- }
231
- rule.forEach((step) => {
232
- if (step.type === "WHITELIST") {
233
- const same = account2Rule.find(
234
- (s) => s.type === "WHITELIST" && isEqual(sortBy(s.whitelists), sortBy(step.whitelists))
235
- );
236
- if (!same) {
237
- isDifferent = true;
238
- return;
239
- }
240
- }
241
- if (step.type === "THRESHOLD") {
242
- const sanitized = sanitizeThreshold(step);
243
- const same = account2Rule.find((s) => {
244
- if (s.type === "THRESHOLD") {
245
- const sanitizeStep = sanitizeThreshold(s);
246
- return sanitizeStep.min === sanitized.min && sanitizeStep.max === sanitized.max;
247
- }
248
- return false;
249
- });
250
- if (!same) {
251
- isDifferent = true;
252
- return;
253
- }
254
- }
255
- if (step.type === "MULTI_AUTHORIZATIONS") {
256
- const same = account2Rule.find(
257
- (s) => s.type === "MULTI_AUTHORIZATIONS"
258
- );
259
- if (!same || areStepsOfMultiAuthDifferent(step.steps, same.steps)) {
260
- isDifferent = true;
261
- return;
262
- }
263
- }
264
- });
265
- });
266
- return isDifferent;
267
- };
268
- var areVaultEntitiesDifferent = (vaultEntity1, vaultEntity2) => {
269
- if (vaultEntity1.accounts && vaultEntity2.accounts && vaultEntity1.accounts.length !== vaultEntity2.accounts.length)
270
- return true;
271
- return !isEqual(sortBy(vaultEntity1.accounts), sortBy(vaultEntity2.accounts));
272
- };
273
- var areStepsOfMultiAuthDifferent = (steps1, steps2) => {
274
- if (steps1.length !== steps2.length) return true;
275
- let isDifferent = false;
276
- steps1.forEach((s, index) => {
277
- const s2 = steps2[index];
278
- if (!s2) throw new Error(`No step found at index ${index}`);
279
- if (s.quorum !== s2.quorum) {
280
- isDifferent = true;
281
- return;
282
- }
283
- if ("group" in s && !("group" in s2) || "group" in s2 && !("group" in s)) {
284
- isDifferent = true;
285
- return;
286
- }
287
- if ("group" in s && "group" in s2 && s.group !== s2.group) {
288
- isDifferent = true;
289
- return;
290
- }
291
- if ("users" in s && "users" in s2) {
292
- if (s.users.length !== s2.users.length || !isEqual(sortBy(s.users), sortBy(s2.users))) {
293
- isDifferent = true;
294
- return;
295
- }
296
- }
297
- });
298
- return isDifferent;
299
- };
300
-
301
- // src/bakeManifest.ts
302
- var DEFAULT_QUORUM = 2;
303
- async function bakeManifest(_manifest, pool, options = {}) {
304
- const { logger = SILENT_LOGGER2, waitForActive } = options;
305
- const manifest = deserializeManifest_default(_manifest);
306
- if (_manifest.salt) {
307
- pool.setSalt(_manifest.salt);
308
- }
309
- if (options.revaultOnboarding) {
310
- logger.step("[REVAULT] Onboarding");
311
- await onboardWithRevault(
312
- {
313
- ...pool.getRevaultCompatOptions(),
314
- ...options.revaultOnboarding
315
- },
316
- logger
317
- );
318
- } else {
319
- logger.info("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
320
- logger.info("\u2551 DEPRECATION NOTICE - LEGACY ONBOARDING HAS BEEN DECOMMISSIONED \u2551");
321
- logger.info("\u2551 \u2551");
322
- logger.info("\u2551 Onboarding skipped, as the new onboarding options were not provided. \u2551");
323
- logger.info("\u2551 Please refer to changelog & announcements to see how you can adapt \u2551");
324
- logger.info("\u2551 your code or command. \u2551");
325
- logger.info("\u2551 \u2551");
326
- logger.info("\u2551 See v2.0 release of vault-cli/vault-common on GitHub. \u2551");
327
- logger.info("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D");
328
- }
329
- const runner = options.runner || createDefaultRunner_default(pool, options);
330
- const manifestFromGate = await recipeManifest(pool, { saveAccountsIndexes: true });
331
- const { rawData } = manifestFromGate;
332
- const {
333
- groupsByName,
334
- whitelistsByName,
335
- accountsByName,
336
- usersWithDevice,
337
- usersWithoutDevice,
338
- vaultEntitiesByName,
339
- exchangesByName,
340
- policiesByName,
341
- tradelinkCustodiansByName,
342
- tradelinkHSMCustodiansByName,
343
- tradelinkExchangesByName,
344
- tradelinkHSMExchangesByName,
345
- tradelinkHSMAssetManagersByName,
346
- tradelinkAssetManagersByName,
347
- tradelinkOnboardingStatus
348
- } = rawData;
349
- const gateManifest = manifestFromGate.manifest;
350
- const __usersByDevice = {
351
- ...usersWithDevice.reduce((acc, curr) => {
352
- return { ...acc, ...{ [curr.deviceIndex]: curr.user } };
353
- }, {})
354
- };
355
- const __usersByName = {
356
- ...usersWithDevice.reduce((acc, curr) => {
357
- return { ...acc, ...{ [curr.user.username]: curr.user } };
358
- }, {}),
359
- ...usersWithoutDevice.reduce((acc, curr) => {
360
- return { ...acc, ...{ [curr.user.username]: curr.user } };
361
- }, {})
362
- };
363
- const __groupsIDsByName = {
364
- ...Object.keys(groupsByName).reduce((acc, curr) => {
365
- const group = groupsByName[curr];
366
- if (!group) return acc;
367
- return { ...acc, ...{ [curr]: group.id } };
368
- }, {})
369
- };
370
- const __whitelistsIDsByName = {
371
- ...Object.keys(whitelistsByName).reduce((acc, curr) => {
372
- const whitelist = whitelistsByName[curr];
373
- if (!whitelist) return acc;
374
- return { ...acc, ...{ [curr]: whitelist.id } };
375
- }, {})
376
- };
377
- const __hsmAMsIDsByName = {
378
- ...Object.keys(tradelinkHSMAssetManagersByName).reduce((acc, curr) => {
379
- const am = tradelinkHSMAssetManagersByName[curr];
380
- if (!am) return acc;
381
- return { ...acc, ...{ [curr]: am.id } };
382
- }, {})
383
- };
384
- const __hsmExchangesIDsByName = {
385
- ...Object.keys(tradelinkHSMExchangesByName).reduce((acc, curr) => {
386
- const exchange = tradelinkHSMExchangesByName[curr];
387
- if (!exchange) return acc;
388
- return { ...acc, ...{ [curr]: exchange.id } };
389
- }, {})
390
- };
391
- const __hsmCustodiansIDsByName = {
392
- ...Object.keys(tradelinkHSMCustodiansByName).reduce((acc, curr) => {
393
- const custodian = tradelinkHSMCustodiansByName[curr];
394
- if (!custodian) return acc;
395
- return { ...acc, ...{ [curr]: custodian.id } };
396
- }, {})
397
- };
398
- const __accountsByName = { ...accountsByName };
399
- let tokens = [];
400
- const shouldLoadTokens = manifest.accounts && manifest.accounts.find((account) => "contractAddress" in account);
401
- if (shouldLoadTokens) {
402
- const admin = await pool.login(4);
403
- tokens = await fetchTokens_default(admin, { logger });
404
- }
405
- if (manifest.users) {
406
- const adminsAndOperators = [];
407
- if (manifest.users.admins) {
408
- adminsAndOperators.push(
409
- ...manifest.users.admins.map(
410
- (a) => ({ role: "admin", device: a.device })
411
- )
412
- );
413
- }
414
- if (manifest.users.operators) {
415
- adminsAndOperators.push(
416
- ...manifest.users.operators.map(
417
- (a) => ({ role: "operator", device: a.device })
418
- )
419
- );
420
- }
421
- await queue(
422
- sortBy2(adminsAndOperators, (u) => u.device),
423
- genCreateAdminsAndOperators()
424
- );
425
- if (manifest.users.api) {
426
- logger.step("LAM users creation");
427
- await queue(manifest.users.api, genCreateAPIUser());
428
- }
429
- if (manifest.users.apiV2) {
430
- logger.step("API users creation");
431
- await queue(manifest.users.apiV2, genCreateAPIV2User());
432
- }
433
- }
434
- if (manifest.workspaceRules) {
435
- logger.step("Workspace rules creation");
436
- await queue(manifest.workspaceRules, createWorkspaceRule);
437
- }
438
- if (manifest.groups) {
439
- logger.step("Groups");
440
- await queue(manifest.groups, createGroup);
441
- }
442
- if (manifest.whitelists) {
443
- logger.step("Whitelists");
444
- await queue(manifest.whitelists, createWhitelist);
445
- }
446
- if (manifest.tradelink) {
447
- logger.step("TradeLink");
448
- const adminDevices = await pool.getOnboardingAdminDevices();
449
- await queue(
450
- adminDevices.map((a) => ({ user: a[1], contractNames: ["TRADELINK_ADDENDUM", "TRADELINK"] })),
451
- approveContracts
452
- );
453
- await queue(manifest.tradelink.custodians, createTradelinkCustodian);
454
- await queue(manifest.tradelink.assetManagers, createTradelinkAssetManager);
455
- await queue(manifest.tradelink.exchanges, createTradelinkExchange);
456
- await createTradelinkNetwork(manifest.tradelink);
457
- if (manifest.tradelink.onboarded) {
458
- await onboardTradelinkNetwork(manifest.tradelink);
459
- await queue(
460
- manifest.tradelink.exchanges,
461
- (exchange) => onboardTradelinkEntity(exchange, "exchanges")
462
- );
463
- await queue(
464
- manifest.tradelink.assetManagers,
465
- (am) => onboardTradelinkEntity(am, "asset_managers")
466
- );
467
- }
468
- }
469
- if (manifest.accounts) {
470
- logger.step("Accounts");
471
- manifest.accounts = orderBy(manifest.accounts, "contractAddress", "desc");
472
- await queue(manifest.accounts, createAccount);
473
- }
474
- if (manifest.entities) {
475
- logger.step("Entities");
476
- await queue(manifest.entities, createVaultEntity);
477
- }
478
- if (manifest.exchanges) {
479
- logger.step("Exchanges");
480
- await queue(manifest.exchanges, createVaultExchange);
481
- }
482
- if (manifest.policies) {
483
- logger.step("Policies");
484
- await queue(manifest.policies, createVaultPolicy);
485
- }
486
- if (manifest.quorum) {
487
- logger.step("Quorum");
488
- await editQuorum(manifest.quorum);
489
- }
490
- if (manifest.contractApprovals) {
491
- logger.step("Contract Approvals");
492
- await queue(manifest.contractApprovals, approveContracts);
493
- }
494
- logger.success("Done");
495
- async function editQuorum(quorum) {
496
- if (gateManifest.quorum === quorum || quorum === DEFAULT_QUORUM && !gateManifest.quorum) {
497
- logger.info(`Quorum is already set to ${quorum}, skipping`);
498
- return;
499
- }
500
- await runner.editQuorum({ quorum });
501
- logger.info(`Quorum updated to ${quorum}`);
502
- }
503
- function genCreateAdminsAndOperators() {
504
- return async function createGenericUser(user) {
505
- const fn = genCreateUser(user.role);
506
- return fn(user.device);
507
- };
508
- }
509
- async function setViewAll(username, gateUserId, viewAll) {
510
- logger.info(`Setting view-all permission to ${viewAll} for API user ${username}...`);
511
- const admin = await pool.login(4);
512
- await admin.network("PUT", `/people/${gateUserId}`, { view_all_override: viewAll });
513
- logger.info(`API user ${username} has now view-all permission set to ${viewAll}`);
514
- }
515
- function genCreateAPIV2User() {
516
- return async function createAPIV2User(apiV2User) {
517
- const username = apiV2User.name;
518
- const existingUser = usersWithoutDevice.find((uwd) => uwd.user.username === username);
519
- if (existingUser) {
520
- if (["APPROVED", "ACTIVE"].includes(existingUser.user.status)) {
521
- logger.info(`Skipping registration of API user ${username}`);
522
- __usersByName[existingUser.user.username] = existingUser.user;
523
- const targetViewAll = !!apiV2User.viewAll;
524
- const existingViewAll = !!existingUser.user.view_all_override;
525
- const shouldUpdateViewAll = targetViewAll !== existingViewAll;
526
- if (shouldUpdateViewAll) {
527
- await setViewAll(username, existingUser.user.id, targetViewAll);
528
- }
529
- return;
530
- }
531
- }
532
- const params = {
533
- user: apiV2User,
534
- name: username,
535
- publicKey: apiV2User.publicKey ?? genKeys(username).hexPubKey,
536
- role: apiV2User.role
537
- };
538
- const userRequest = await runner.createAPIV2User(params);
539
- const userAccessRequest = await runner.createAPIV2UserAccess(params);
540
- __usersByName[userRequest.user.username] = userRequest.user;
541
- logger.info(`(+) Created API user: ${username}`);
542
- if (apiV2User.viewAll) {
543
- await setViewAll(username, userRequest.user.id, true);
544
- }
545
- logger.info(
546
- chalk`{red.bold IMPORTANT:} {red The API user credentials will not be displayed again so note them somewhere}`
547
- );
548
- logger.info(
549
- JSON.stringify({
550
- api_key_id: userAccessRequest.api_key_id,
551
- api_key_secret: userAccessRequest.api_key_secret
552
- })
553
- );
554
- };
555
- }
556
- function genCreateAPIUser() {
557
- return async function createUser(apiUser) {
558
- const username = apiUser.name;
559
- const existingUser = usersWithDevice.find((ud) => ud.user.username === username);
560
- if (existingUser) {
561
- if (existingUser.user.status === "ACTIVE") {
562
- logger.info(`Skipping registration of LAM ${username}`);
563
- __usersByName[existingUser.user.username] = existingUser.user;
564
- return;
565
- }
566
- }
567
- const { device_id } = await pool.lamAPI.createInvitation(username);
568
- const params = { user: apiUser, userID: device_id, name: username };
569
- const request = await runner.createAPIUser(params, manifestFromGate);
570
- __usersByName[request.user.username] = request.user;
571
- logger.info(`(+) Created LAM api user: ${username}`);
572
- };
573
- }
574
- function genCreateUser(role) {
575
- return async function createUser(device) {
576
- const name = manifest.customUsernames && manifest.customUsernames[device] || getDefaultUsername(role, device);
577
- const userID = await pool.getUserID(device);
578
- const findByUserID = (ud) => ud.user.user_id === userID;
579
- const existingUser = usersWithDevice.find(findByUserID);
580
- if (existingUser) {
581
- logger.info(`Skipping registration of ${role} ${device}`);
582
- __usersByDevice[device] = existingUser.user;
583
- __usersByName[existingUser.user.username] = existingUser.user;
584
- return;
585
- }
586
- const createUserParams = { role, userID, name, device };
587
- const request = await runner.createUser(createUserParams);
588
- __usersByDevice[device] = request.user;
589
- __usersByName[request.user.username] = request.user;
590
- logger.info(`(+) Created ${name}`);
591
- };
592
- }
593
- async function createWorkspaceRule(rule) {
594
- const existingRule = gateManifest.workspaceRules?.find((r) => r.permission === rule.permission);
595
- if (!existingRule || areWorkspaceRulesDifferent(rule, existingRule)) {
596
- const step = rule.steps[0];
597
- logger.info(
598
- `Configuring workspace rule ${rule.permission} with ${step.quorum} approvals out of ${step.users.length} user(s)`
599
- );
600
- await runner.editWorkspaceRule({ rule, usersByName: __usersByName });
601
- } else {
602
- logger.info(`Skipping edition of workspace rule ${rule.permission}`);
603
- }
604
- }
605
- async function createGroup(group) {
606
- let isEdit = false;
607
- const existingGroup = groupsByName[group.name];
608
- const isApprovingPendingGroup = existingGroup && existingGroup.status === "PENDING" && group.status !== "PENDING";
609
- if (existingGroup) {
610
- if (existingGroup && existingGroup.status === "ACTIVE") {
611
- if (!gateManifest.groups) {
612
- throw new Error(`No groups in gate manifest`);
613
- }
614
- const groupFromGateManifest = gateManifest.groups.find((g) => g.name === group.name);
615
- if (!groupFromGateManifest) {
616
- throw new Error(`Can't find group ${group.name}`);
617
- }
618
- isEdit = areGroupsDifferent(groupFromGateManifest, group);
619
- }
620
- if (!isEdit && !isApprovingPendingGroup) {
621
- logger.info(`Skipping creation of group ${group.name}`);
622
- return;
623
- }
624
- }
625
- const data = {
626
- group,
627
- usersByDevice: __usersByDevice,
628
- usersByName: __usersByName,
629
- existingGroup
630
- };
631
- const existingRequest = !!isApprovingPendingGroup && !!existingGroup && existingGroup.last_request || null;
632
- const noApproval = shouldNotApprove(group) || shouldRejectRequest(group);
633
- const bakeGroupParams = {
634
- group,
635
- data,
636
- noApproval,
637
- existingRequest
638
- };
639
- const request = isEdit ? await runner.editGroup(bakeGroupParams) : await runner.createGroup(bakeGroupParams);
640
- __groupsIDsByName[group.name] = existingGroup && existingGroup.id || request.group.id;
641
- if (shouldRejectRequest(group)) {
642
- const admin = await pool.login(4);
643
- admin.rejectRequest(request.id);
644
- logger.info(`(+) Rejected group ${group.name}`);
645
- } else {
646
- logger.info(`(+) ${isEdit ? "Edited" : "Created"} group ${group.name}`);
647
- }
648
- }
649
- async function createTradelinkCustodian(tradelinkEntity) {
650
- const existingCustodian = tradelinkCustodiansByName[tradelinkEntity.name];
651
- if (existingCustodian) {
652
- logger.info(`Skipping creation of tradelink custodian ${tradelinkEntity.name}`);
653
- return;
654
- }
655
- const bakeTLCustodianParams = {
656
- tradelinkEntity,
657
- type: "custodians"
658
- };
659
- await runner.createTradelinkEntity(bakeTLCustodianParams);
660
- logger.info(`(+) Created Tradelink custodian ${tradelinkEntity.name}`);
661
- }
662
- async function approveContracts(approval) {
663
- const { contractNames, user: deviceIndex } = approval;
664
- const user = await pool.login(deviceIndex);
665
- try {
666
- const unapproved = await user.get(
667
- "/contracts/unapproved"
668
- );
669
- const contracts = unapproved.filter((c) => contractNames.includes(c.contract_name));
670
- const promises = contracts.map((c) => {
671
- logger.info(`Approving contract "${c.contract_name}" with user ${deviceIndex}`);
672
- return user.post("/contracts/approve", c);
673
- });
674
- await Promise.all(promises);
675
- } catch (e) {
676
- logger.error(`Error approving contracts by user ${deviceIndex}`, e);
677
- }
678
- }
679
- async function createTradelinkExchange(tradelinkEntity) {
680
- const existingExchange = tradelinkExchangesByName[tradelinkEntity.name];
681
- if (existingExchange) {
682
- logger.info(`Skipping creation of tradelink exchange ${tradelinkEntity.name}`);
683
- return;
684
- }
685
- const bakeTLCustodianParams = {
686
- tradelinkEntity,
687
- type: "exchanges"
688
- };
689
- await runner.createTradelinkEntity(bakeTLCustodianParams);
690
- logger.info(`(+) Created Tradelink exchange ${tradelinkEntity.name}`);
691
- }
692
- async function createTradelinkAssetManager(tradelinkEntity) {
693
- const existingCustodian = tradelinkAssetManagersByName[tradelinkEntity.name];
694
- if (existingCustodian) {
695
- logger.info(`Skipping creation of tradelink asset manager ${tradelinkEntity.name}`);
696
- return;
697
- }
698
- const bakeTLCustodianParams = {
699
- tradelinkEntity,
700
- type: "asset_managers"
701
- };
702
- await runner.createTradelinkEntity(bakeTLCustodianParams);
703
- logger.info(`(+) Created Tradelink asset manager ${tradelinkEntity.name}`);
704
- }
705
- function formatTradelinkNetworkParams(manifestTLEntity) {
706
- const operators = [];
707
- if (manifestTLEntity.users.operators) {
708
- manifestTLEntity.users.operators.forEach((operator) => {
709
- const user = __usersByDevice[operator];
710
- if (user) operators.push(user.pub_key);
711
- });
712
- }
713
- if (manifestTLEntity.users.apiV2) {
714
- manifestTLEntity.users.apiV2.forEach((apiV2) => {
715
- const user = __usersByName[apiV2];
716
- if (user) operators.push(user.pub_key);
717
- });
718
- }
719
- return {
720
- id: manifestTLEntity.id,
721
- name: manifestTLEntity.name,
722
- operators
723
- };
724
- }
725
- function formatTradelinkNetworkParamsWithAddresses(manifestTLEntity) {
726
- const data = formatTradelinkNetworkParams(manifestTLEntity);
727
- return {
728
- ...data,
729
- addresses: manifestTLEntity.addresses
730
- };
731
- }
732
- async function createTradelinkNetwork(tradelink) {
733
- const custodians = tradelink.custodians.map((custodian) => {
734
- return formatTradelinkNetworkParams(custodian);
735
- });
736
- const exchanges = tradelink.exchanges.map((exchange) => {
737
- return formatTradelinkNetworkParamsWithAddresses(exchange);
738
- });
739
- const assetManagers = tradelink.assetManagers.map((am) => {
740
- return formatTradelinkNetworkParamsWithAddresses(am);
741
- });
742
- if (custodians.length === 0) {
743
- throw new Error(`Custodian ${tradelink.custodians} not found`);
744
- }
745
- const bakeTLNetworkParams = { custodians, exchanges, assetManagers };
746
- await runner.createTradelinkNetwork(bakeTLNetworkParams);
747
- logger.info(`(+) Created Tradelink network`);
748
- }
749
- async function onboardTradelinkNetwork(tradelink) {
750
- if (tradelinkOnboardingStatus.status == "CUSTODIAN_ONBOARDED" || tradelinkOnboardingStatus.status == "EXCHANGE_ONBOARDED" || tradelinkOnboardingStatus.status == "HSM_READY") {
751
- logger.info(`Skipping onboarding of custodian`);
752
- return;
753
- }
754
- const custodian = tradelink.custodians.map((custodian2) => {
755
- return formatTradelinkNetworkParams(custodian2);
756
- })[0];
757
- if (!custodian) {
758
- throw new Error(`Custodian ${tradelink.custodians} not found`);
759
- }
760
- const request = await runner.createTradelink({
761
- type: "CREATE_TRADELINK",
762
- operators: custodian.operators
763
- });
764
- __hsmCustodiansIDsByName[custodian.name] = request.tradelink.id;
765
- logger.info(`(+) ${custodian.name} Tradelink custodian Onboarded`);
766
- }
767
- async function onboardTradelinkEntity(entity, entityType) {
768
- if (tradelinkHSMAssetManagersByName[entity.name]?.status === "ACTIVE") {
769
- logger.info(`Skipping onboarding of asset manager ${entity.name}`);
770
- return;
771
- }
772
- if (tradelinkHSMExchangesByName[entity.name]?.status === "ACTIVE") {
773
- logger.info(`Skipping onboarding of exchange ${entity.name}`);
774
- return;
775
- }
776
- let approver;
777
- if (entity.users.apiV2 && entity.users.apiV2.length > 0) {
778
- const userName = entity.users.apiV2[0];
779
- approver = __usersByName[userName];
780
- }
781
- if (!approver) {
782
- throw new Error(`No approver found for ${entity.name}`);
783
- }
784
- const { name, operators, addresses } = formatTradelinkNetworkParamsWithAddresses(entity);
785
- const bakeParams = {
786
- tradelinkEntity: { name, operators, addresses },
787
- type: entityType,
788
- tradelinkEntityApprover: { name: approver.username, role: approver.role }
789
- };
790
- const request = await runner.onboardTradelinkEntity(bakeParams);
791
- if (entityType === "asset_managers")
792
- __hsmAMsIDsByName[entity.name] = request.tradelink_asset_manager.id;
793
- else
794
- __hsmExchangesIDsByName[entity.name] = request.tradelink_exchange.id;
795
- logger.info(`(+) ${entity.name} Tradelink ${entityType} Onboarded`);
796
- }
797
- async function createWhitelist(whitelist) {
798
- let isEdit = false;
799
- const existingWhitelist = whitelistsByName[whitelist.name];
800
- const isApprovingPendingWhitelist = existingWhitelist && existingWhitelist.status === "PENDING" && whitelist.status !== "PENDING";
801
- if (existingWhitelist && existingWhitelist.status === "ACTIVE") {
802
- if (!gateManifest.whitelists) {
803
- throw new Error(`No whitelists in gate manifest`);
804
- }
805
- const whitelistFromGateManifest = gateManifest.whitelists.find(
806
- (w) => w.name === whitelist.name
807
- );
808
- if (!whitelistFromGateManifest) {
809
- throw new Error(`Can't find whitelist ${whitelist.name}`);
810
- }
811
- isEdit = areWhitelistsDifferent(whitelistFromGateManifest, whitelist);
812
- if (!isEdit && !isApprovingPendingWhitelist) {
813
- logger.info(`Skipping creation of whitelist ${whitelist.name}`);
814
- return;
815
- }
816
- }
817
- const noApproval = shouldNotApprove(whitelist) || shouldRejectRequest(whitelist);
818
- const existingRequest = !!isApprovingPendingWhitelist && !!existingWhitelist && existingWhitelist.last_request || null;
819
- const data = { whitelist, existingWhitelist };
820
- const bakeWLParams = { whitelist, data, existingRequest, noApproval };
821
- const request = isEdit ? await runner.editWhitelist(bakeWLParams) : await runner.createWhitelist(bakeWLParams);
822
- __whitelistsIDsByName[whitelist.name] = existingWhitelist && existingWhitelist.id || request.whitelist.id;
823
- if (shouldRejectRequest(whitelist)) {
824
- const admin = await pool.login(4);
825
- admin.rejectRequest(request.id);
826
- logger.info(`(+) Rejected whitelist ${whitelist.name}`);
827
- } else {
828
- logger.info(`(+) ${isEdit ? "Edited" : "Created"} whitelist ${whitelist.name}`);
829
- }
830
- }
831
- function getTradelinkAPIUserFromAssetManager(entity) {
832
- const entityAM = manifest.tradelink?.assetManagers.filter((am) => am.name === entity.name)[0];
833
- if (!entityAM) {
834
- throw new Error(`AssetManager ${entity.name} not found`);
835
- }
836
- const approverUserName = entityAM.users.apiV2[0];
837
- if (!approverUserName) {
838
- throw new Error(`No approver found for ${entity.name}`);
839
- }
840
- const user = __usersByName[approverUserName];
841
- if (!user) {
842
- throw new Error(`User ${approverUserName} not found`);
843
- }
844
- return { name: user.username, role: user.role };
845
- }
846
- async function createAccount(accountDesc) {
847
- let isEdit = false;
848
- const existingAccount = __accountsByName[accountDesc.name] || findAccountByCurrencyAndIndex({ accountDesc, __accountsByName }) || findExistingChildrenAccount({ accountDesc, __accountsByName });
849
- const isApprovingPendingAccount = existingAccount && existingAccount.status === "PENDING" && accountDesc.status !== "PENDING";
850
- if (existingAccount) {
851
- if (existingAccount.status === "VIEW_ONLY") {
852
- isEdit = true;
853
- } else if (existingAccount.status === "ACTIVE") {
854
- if (!gateManifest.accounts) {
855
- throw new Error("No accounts in gate manifest");
856
- }
857
- const accountFromGateManifest = gateManifest.accounts.find(
858
- (a) => a.name === accountDesc.name || "currency" in accountDesc && "index" in accountDesc && "currency" in a && "index" in a && accountDesc.currency === a.currency && accountDesc.index === a.index && (!("contractAddress" in accountDesc) && !("contractAddress" in a) || "contractAddress" in accountDesc && "contractAddress" in a && accountDesc.contractAddress === a.contractAddress)
859
- );
860
- if (!accountFromGateManifest) {
861
- throw new Error(`Can't find account ${accountDesc.name}`);
862
- }
863
- isEdit = areAccountsDifferent(accountFromGateManifest, accountDesc);
864
- }
865
- if (!isEdit && !isApprovingPendingAccount) {
866
- logger.info(`Skipping creation of account ${accountDesc.name}`);
867
- return;
868
- }
869
- }
870
- const data = {
871
- account: accountDesc,
872
- existingAccount,
873
- usersByDevice: __usersByDevice,
874
- usersByName: __usersByName,
875
- groupsIDsByName: __groupsIDsByName,
876
- whitelistsIDsByName: __whitelistsIDsByName,
877
- accountsByName: __accountsByName,
878
- hsmAssetManagersIDsByName: __hsmAMsIDsByName,
879
- hsmExchangesIDsByName: __hsmExchangesIDsByName,
880
- hsmCustodiansIDsByName: __hsmCustodiansIDsByName,
881
- tokens
882
- };
883
- const existingRequest = isApprovingPendingAccount && !!existingAccount && existingAccount.last_request || null;
884
- const noApproval = shouldNotApprove(accountDesc) || shouldRejectRequest(accountDesc);
885
- let tradelinkAM;
886
- if ("tradelink" in manifest && "tradelink_data" in accountDesc && accountDesc.tradelink_data) {
887
- const assetManager = manifest.tradelink?.assetManagers[0];
888
- if (!assetManager) {
889
- throw new Error(`AssetManager not found`);
890
- }
891
- tradelinkAM = getTradelinkAPIUserFromAssetManager(assetManager);
892
- }
893
- const bakeAccountParams = {
894
- account: accountDesc,
895
- data,
896
- existingRequest,
897
- noApproval,
898
- waitForActive,
899
- tradelinkAM
900
- };
901
- const request = isEdit ? await runner.editAccount(bakeAccountParams) : await runner.createAccount(bakeAccountParams);
902
- __accountsByName[accountDesc.name] = request.account;
903
- if (shouldRejectRequest(accountDesc)) {
904
- const admin = await pool.login(4);
905
- admin.rejectRequest(request.id);
906
- logger.info(`(+) Rejected account ${accountDesc.name}`);
907
- } else {
908
- logger.info(`(+) ${isEdit ? "Edited" : "Created"} account ${accountDesc.name}`);
909
- }
910
- if ("config" in accountDesc && !!accountDesc.config) {
911
- const { config } = accountDesc;
912
- const admin = await pool.login(4);
913
- const account = __accountsByName[accountDesc.name];
914
- if (config.nftGallery) {
915
- await admin.post(`/accounts/${account.id}/nfts/activate`, {});
916
- logger.info(`(+) Enabled NFT Gallery on ${accountDesc.name}`);
917
- }
918
- }
919
- }
920
- async function createVaultEntity(vaultEntity) {
921
- let isEdit = false;
922
- const existingVaultEntity = vaultEntitiesByName[vaultEntity.name];
923
- const isApprovingPendingVaultEntity = existingVaultEntity && existingVaultEntity.status === "PENDING" && vaultEntity.status !== "PENDING";
924
- if (existingVaultEntity) {
925
- if (existingVaultEntity && existingVaultEntity.status === "ACTIVE") {
926
- if (!gateManifest.entities) {
927
- throw new Error(`No entities in gate manifest`);
928
- }
929
- const vaultEntityFromGateManifest = gateManifest.entities.find(
930
- (e) => e.name === vaultEntity.name
931
- );
932
- if (!vaultEntityFromGateManifest) {
933
- throw new Error(`Can't find entity ${vaultEntity.name}`);
934
- }
935
- isEdit = areVaultEntitiesDifferent(vaultEntityFromGateManifest, vaultEntity);
936
- }
937
- if (!isEdit && !isApprovingPendingVaultEntity) {
938
- logger.info(`Skipping creation of entity ${vaultEntity.name}`);
939
- return;
940
- }
941
- }
942
- const data = {
943
- vaultEntity,
944
- accountsByName: __accountsByName,
945
- existingVaultEntity
946
- };
947
- const existingRequest = !!isApprovingPendingVaultEntity && !!existingVaultEntity && existingVaultEntity.last_request || null;
948
- const noApproval = shouldNotApprove(vaultEntity) || shouldRejectRequest(vaultEntity);
949
- const bakeVaultEntityParams = {
950
- vaultEntity,
951
- data,
952
- noApproval,
953
- existingRequest
954
- };
955
- const request = isEdit ? await runner.editVaultEntity(bakeVaultEntityParams) : await runner.createVaultEntity(bakeVaultEntityParams);
956
- if (shouldRejectRequest(vaultEntity)) {
957
- const admin = await pool.login(4);
958
- await admin.rejectRequest(request.id);
959
- logger.info(`(+) Rejected entity ${vaultEntity.name}`);
960
- } else {
961
- logger.info(`(+) ${isEdit ? "Edited" : "Created"} entity ${vaultEntity.name}`);
962
- }
963
- }
964
- async function createVaultExchange(exchange) {
965
- const existingExchange = exchangesByName[exchange.name];
966
- const isApprovingPendingExchange = existingExchange && existingExchange.status === "PENDING" && exchange.status !== "PENDING";
967
- if (existingExchange) {
968
- if (existingExchange && existingExchange.status === "ACTIVE") {
969
- if (!gateManifest.exchanges) {
970
- throw new Error(`No exchanges in gate manifest`);
971
- }
972
- const exchangeFromGateManifest = gateManifest.exchanges.find(
973
- (e) => e.name === exchange.name
974
- );
975
- if (!exchangeFromGateManifest) {
976
- throw new Error(`Can't find exchange ${exchange.name}`);
977
- }
978
- }
979
- if (!isApprovingPendingExchange) {
980
- logger.info(`Skipping creation of exchange ${exchange.name}`);
981
- return;
982
- }
983
- }
984
- const data = {
985
- exchange,
986
- usersByDevice: __usersByDevice,
987
- usersByName: __usersByName,
988
- groupsIDsByName: __groupsIDsByName,
989
- existingExchange
990
- };
991
- const existingRequest = !!isApprovingPendingExchange && !!existingExchange && existingExchange.last_request || null;
992
- const noApproval = shouldNotApprove(exchange) || shouldRejectRequest(exchange);
993
- const bakeExchangeParams = {
994
- exchange,
995
- data,
996
- noApproval,
997
- existingRequest
998
- };
999
- const request = await runner.createExchange(bakeExchangeParams);
1000
- if (shouldRejectRequest(exchange)) {
1001
- const admin = await pool.login(4);
1002
- await admin.rejectRequest(request.id);
1003
- logger.info(`(+) Rejected exchange ${exchange.name}`);
1004
- } else {
1005
- logger.info(`(+) Created exchange ${exchange.name}`);
1006
- }
1007
- }
1008
- async function createVaultPolicy(policy) {
1009
- const existingPolicy = policiesByName[policy.name];
1010
- const isApprovingPendingPolicy = existingPolicy && existingPolicy.status === "PENDING" && policy.status !== "PENDING";
1011
- if (existingPolicy) {
1012
- if (existingPolicy && existingPolicy.status === "ACTIVE") {
1013
- if (!gateManifest.policies) {
1014
- throw new Error(`No policies in gate manifest`);
1015
- }
1016
- const policyFromGateManifest = gateManifest.policies.find((e) => e.name === policy.name);
1017
- if (!policyFromGateManifest) {
1018
- throw new Error(`Can't find policy ${policy.name}`);
1019
- }
1020
- }
1021
- if (!isApprovingPendingPolicy) {
1022
- logger.info(`Skipping creation of policy ${policy.name}`);
1023
- return;
1024
- }
1025
- }
1026
- const data = {
1027
- policy,
1028
- usersByDevice: __usersByDevice,
1029
- usersByName: __usersByName,
1030
- whitelistsIDsByName: __whitelistsIDsByName,
1031
- groupsIDsByName: __groupsIDsByName,
1032
- existingPolicy
1033
- };
1034
- const existingRequest = !!isApprovingPendingPolicy && !!existingPolicy && existingPolicy.last_request || null;
1035
- const noApproval = shouldNotApprove(policy) || shouldRejectRequest(policy);
1036
- const bakePolicyParams = {
1037
- policy,
1038
- data,
1039
- noApproval,
1040
- existingRequest
1041
- };
1042
- const request = await runner.createPolicy(bakePolicyParams);
1043
- if (shouldRejectRequest(policy)) {
1044
- const admin = await pool.login(4);
1045
- await admin.rejectRequest(request.id);
1046
- logger.info(`(+) Rejected policy ${policy.name}`);
1047
- } else {
1048
- logger.info(`(+) Created policy ${policy.name}`);
1049
- }
1050
- }
1051
- }
1052
- var shouldNotApprove = (manifestEntity) => manifestEntity.status === "PENDING";
1053
- var shouldRejectRequest = (manifestEntity) => manifestEntity.status === "ABORTED";
1054
- var findExistingChildrenAccount = ({
1055
- accountDesc,
1056
- __accountsByName
1057
- }) => {
1058
- if (!("parentAccount" in accountDesc) || !("contractAddress" in accountDesc)) return;
1059
- const accounts = Object.keys(__accountsByName).map((name) => {
1060
- return __accountsByName[name];
1061
- }).filter(Boolean);
1062
- const parent = accounts.find((a) => !!a && a.name === accountDesc.parentAccount);
1063
- if (!parent) return;
1064
- const children = accounts.filter((a) => !!a && a.parent === parent.id);
1065
- if (children.length === 0) return;
1066
- return children.find((c) => !!c && c.contract_address === accountDesc.contractAddress);
1067
- };
1068
- var findAccountByCurrencyAndIndex = ({
1069
- accountDesc,
1070
- __accountsByName
1071
- }) => {
1072
- if (!("currency" in accountDesc) || !("index" in accountDesc)) return;
1073
- const accounts = Object.values(__accountsByName);
1074
- return accounts.find(
1075
- (account) => account.currency === accountDesc.currency && account.index === accountDesc.index && ("contractAddress" in accountDesc ? accountDesc.contractAddress === account.contract_address : !account.contract_address)
1076
- );
1077
- };
1078
-
1079
- // src/configcat.ts
1080
- import { SILENT_LOGGER as SILENT_LOGGER3 } from "@ledgerhq/vault-utils";
1081
- import axios from "axios";
1082
- var configCatEndpoint = "https://api.configcat.com/v1";
1083
- async function createConfigCatEnvironment(ccConfig, environmentName) {
1084
- const {
1085
- data: { environmentId }
1086
- } = await axios.post(
1087
- `${configCatEndpoint}/products/${ccConfig.productId}/environments`,
1088
- {
1089
- name: environmentName,
1090
- color: "#DADBEE",
1091
- description: "Created by ledger-vault CLI"
1092
- },
1093
- {
1094
- headers: {
1095
- "Content-Type": "application/json",
1096
- Authorization: `Basic ${ccConfig.apiKey}`
1097
- }
1098
- }
1099
- );
1100
- const { data: getCredentialsResponse } = await axios.get(
1101
- `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentId}`,
1102
- {
1103
- headers: {
1104
- "Content-Type": "application/json",
1105
- Authorization: `Basic ${ccConfig.apiKey}`
1106
- }
1107
- }
1108
- );
1109
- return {
1110
- sdkKey: getCredentialsResponse.primary,
1111
- productId: ccConfig.productId,
1112
- configId: ccConfig.configId,
1113
- environmentId
1114
- };
1115
- }
1116
- async function getEnvironmentIDFromEnvironmentName(ccConfig, environmentName) {
1117
- const { data: getEnvironmentsResponse } = await axios.get(`${configCatEndpoint}/products/${ccConfig.productId}/environments`, {
1118
- headers: {
1119
- "Content-Type": "application/json",
1120
- Authorization: `Basic ${ccConfig.apiKey}`
1121
- }
1122
- });
1123
- const environment = getEnvironmentsResponse?.find(
1124
- (environment2) => environment2.name === environmentName
1125
- );
1126
- return environment?.environmentId;
1127
- }
1128
- async function deleteConfigCatEnvironment(ccConfig, environmentName) {
1129
- const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);
1130
- if (environmentID === void 0) {
1131
- throw Error(`Unable to find ConfigCat environment '${environmentName}'`);
1132
- }
1133
- await axios.delete(`${configCatEndpoint}/environments/${environmentID}`, {
1134
- headers: {
1135
- Authorization: `Basic ${ccConfig.apiKey}`
1136
- }
1137
- });
1138
- }
1139
- async function setConfigCatFeatureFlagValues(ccConfig, environmentName, flagValues, { logger = SILENT_LOGGER3 } = {}) {
1140
- const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);
1141
- if (environmentID === void 0) {
1142
- throw Error(`Unable to find ConfigCat environment '${environmentName}'`);
1143
- }
1144
- const { data: getFlagsResponse } = await axios.get(
1145
- `${configCatEndpoint}/configs/${ccConfig.configId}/settings`,
1146
- {
1147
- headers: {
1148
- Authorization: `Basic ${ccConfig.apiKey}`
1149
- }
1150
- }
1151
- );
1152
- const settingValues = getFlagsResponse.filter((configCatFlag) => Object.keys(flagValues).includes(configCatFlag.key)).map((configCatFlag) => {
1153
- let flagValue = flagValues[configCatFlag.key];
1154
- if (configCatFlag.settingType == "string" && typeof flagValue != "string") {
1155
- flagValue = JSON.stringify(flagValue);
1156
- }
1157
- return { settingId: configCatFlag.settingId, value: flagValue };
1158
- });
1159
- try {
1160
- await axios.post(
1161
- `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentID}/values`,
1162
- { settingValues },
1163
- { headers: { Authorization: `Basic ${ccConfig.apiKey}` } }
1164
- );
1165
- } catch (err) {
1166
- logger.error(`Could not set feature flags`);
1167
- logger.error(err);
1168
- }
1169
- }
1170
-
1171
- // src/createDevicesPool.ts
1172
- import { SILENT_LOGGER as SILENT_LOGGER4 } from "@ledgerhq/vault-utils";
1173
- import invariant2 from "invariant";
1174
- import io from "socket.io-client";
1175
-
1176
- // src/device/index.ts
1177
- var device_exports = {};
1178
- __export(device_exports, {
1179
- createRunner: () => createRunner
1180
- });
1181
- import { openTransportReplayer } from "@ledgerhq/hw-transport-mocker";
1182
- import { registerTransportModule, withDevicePolling } from "@ledgerhq/live-common-stub";
1183
- import { listen } from "@ledgerhq/logs";
1184
- import { from } from "rxjs";
1185
- var mockTransport = Promise.resolve({
1186
- open: () => Promise.resolve(),
1187
- close: () => Promise.resolve()
1188
- });
1189
- listen(
1190
- /* istanbul ignore next */
1191
- (log) => {
1192
- if (log.type === "apdu" && process.env.DEBUG === "1") {
1193
- console.log(`${log.type}: ${log.message ? log.message : ""}`);
1194
- }
1195
- }
1196
- );
1197
- registerTransportModule({
1198
- id: "software",
1199
- open: (id) => {
1200
- if (id !== "software") return;
1201
- return mockTransport;
1202
- },
1203
- disconnect: () => null
1204
- });
1205
- var createRunner = (allInteractions, { readOnly, transport, recordStore, acceptPollingError } = {
1206
- transport: "software"
1207
- }) => async (_interactions, _data, options) => {
1208
- const interactions = [...readOnly ? [] : [allInteractions.getU2FPublicKey], ..._interactions];
1209
- const responses = { ..._data };
1210
- for (let i = 0; i < interactions.length; i++) {
1211
- const interaction = interactions[i];
1212
- if (!interaction) throw new Error("Invalid interaction");
1213
- if (recordStore) {
1214
- const replayer = await openTransportReplayer(recordStore);
1215
- const response = await interaction.action({
1216
- ...responses,
1217
- transport: replayer
1218
- });
1219
- responses[interaction.responseKey] = response;
1220
- } else {
1221
- const obs = withDevicePolling(transport)(
1222
- (transport2) => {
1223
- return from(interaction.action({ ...responses, transport: transport2 }));
1224
- },
1225
- // always throw on error:
1226
- // eslint-disable-next-line
1227
- acceptPollingError ?? (() => false)
1228
- );
1229
- if (options?.onStepStart) {
1230
- options.onStepStart(interaction.responseKey);
1231
- }
1232
- const res = await obs.toPromise();
1233
- responses[interaction.responseKey] = res;
1234
- if (options?.onStepDone) {
1235
- options.onStepDone({ responseKey: interaction.responseKey, value: res });
1236
- }
1237
- }
1238
- }
1239
- return responses;
1240
- };
1241
-
1242
- // src/device/createAPIDevice.ts
1243
- import { nanoid } from "nanoid";
1244
- var ROLE_TO_BYTES = {
1245
- shared_owner: Buffer.from([2]),
1246
- admin: Buffer.from([1]),
1247
- operator: Buffer.from([0])
1248
- };
1249
- var ENDPOINTS = {
1250
- GET_PUBLIC_KEY: "/get-public-key",
1251
- GET_ATTESTATION: "/get-attestation",
1252
- OPEN_SESSION: "/open-session",
1253
- AUTHENTICATE: "/authenticate",
1254
- REGISTER: "/register",
1255
- VALIDATE_VAULT_OPERATION: "/validate-vault-operation",
1256
- GENERATE_KEY_FRAGMENTS: "/generate-key-fragments",
1257
- REGISTER_DATA: "/u2f-register-data",
1258
- GET_CURRENT_DEVICE: "/current-device",
1259
- // NEW SECURE CHANNEL ENDPOINTS
1260
- INITIATE_PAIRING_HANDSHAKE: "/initiate-pairing-handshake",
1261
- GENERATE_SEED_COMPONENT: "/generate-key-component",
1262
- FINALIZE_PAIRING_HANDSHAKE: "/finalize-pairing-handshake",
1263
- START_K_PATTERN_AS_RESPONDER: "/start-k-pattern-as-responder",
1264
- START_KK_PATTERN_AS_RESPONDER: "/start-kk-pattern-as-responder",
1265
- VALIDATE_OP: "/validate-op"
1266
- };
1267
- var deviceTypeBufferFromPsdModel = {
1268
- BLUE: Buffer.from([0]),
1269
- STAX: Buffer.from([2])
1270
- };
1271
- var pathArrayToString = (path) => {
1272
- if (!path[0] || !path[1]) throw new Error("Invalid path array");
1273
- return `${path[0] & 268435455}'/${path[1] & 268435455}'`;
1274
- };
1275
- var __DEVICE_API_NETWORK__ = createNetwork({
1276
- baseURL: process.env.VAULT_DEVICE_API_URL || "https://localhost:8443/device-api"
1277
- });
1278
- function setDeviceAPIEndpoint(url) {
1279
- __DEVICE_API_NETWORK__ = createNetwork({ baseURL: url });
1280
- }
1281
- function createAPIDevice(options = {}) {
1282
- const { deviceAPISessionID, overrideSeeds, deviceAPIURL, psdModel = "STAX" } = options;
1283
- let { salt = "" } = options;
1284
- let __CURRENT_SEED__ = "";
1285
- let __PSD_MODEL__ = psdModel;
1286
- const __DEVICE_API_SESSION_ID__ = deviceAPISessionID || nanoid();
1287
- const setSeed = (seed) => {
1288
- __CURRENT_SEED__ = seed;
1289
- };
1290
- const setSalt = (newSalt) => {
1291
- salt = newSalt;
1292
- };
1293
- const deviceNetwork = (method, url, _data = {}, requestOptions = {}) => {
1294
- const data = {
1295
- seed: __CURRENT_SEED__,
1296
- sessionID: __DEVICE_API_SESSION_ID__,
1297
- psd_model: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString("hex"),
1298
- ..._data,
1299
- version: 3
1300
- // This is used to choose Noise Channel mode
1301
- };
1302
- const deviceAPINetwork = deviceAPIURL ? createNetwork({ baseURL: deviceAPIURL }) : __DEVICE_API_NETWORK__;
1303
- return deviceAPINetwork(method, url, data, requestOptions);
1304
- };
1305
- const getPublicKey = async (transport, path, secp256k1 = true) => {
1306
- const data = await deviceNetwork("POST", ENDPOINTS.GET_PUBLIC_KEY, {
1307
- path: pathArrayToString(path),
1308
- secp256k1
1309
- });
1310
- return {
1311
- pubKey: data.pubKey,
1312
- signature: Buffer.from(data.attestation, "hex")
1313
- };
1314
- };
1315
- const registerData = async (transport, challenge) => {
1316
- const data = await deviceNetwork("POST", ENDPOINTS.REGISTER_DATA, {
1317
- challenge: challenge.toString("hex")
1318
- });
1319
- return Buffer.from(data, "hex");
1320
- };
1321
- const switchDevice = async (id) => {
1322
- setSeed(genSeed_default(salt, id, { overrideSeeds }));
1323
- };
1324
- const register = async (transport, challenge, application, name, userRole, registerData2) => {
1325
- const data = await deviceNetwork("POST", ENDPOINTS.REGISTER, {
1326
- challenge: challenge.toString("hex"),
1327
- name,
1328
- role: ROLE_TO_BYTES[userRole].toString("hex"),
1329
- hsm_data: registerData2,
1330
- type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString("hex"),
1331
- application
1332
- });
1333
- const response = Buffer.from(data, "hex");
1334
- let i = 0;
1335
- const rfu = response.slice(i, i += 1)[0];
1336
- const pubKey = response.slice(i, i += 65).toString("hex");
1337
- const keyHandleLength = response.slice(i, ++i)[0];
1338
- if (typeof keyHandleLength === "undefined") throw new Error("Invalid key handle length");
1339
- const keyHandle = response.slice(i, i += keyHandleLength);
1340
- return {
1341
- u2f_register: response.slice(0, response.length - 2),
1342
- keyHandle,
1343
- rfu,
1344
- pubKey
1345
- };
1346
- };
1347
- const getAttestationCertificate = async () => {
1348
- const data = await deviceNetwork("POST", ENDPOINTS.GET_ATTESTATION);
1349
- return Buffer.from(data, "hex");
1350
- };
1351
- const validateVaultOperation = async (transport, path, channel) => {
1352
- const data = await deviceNetwork("POST", ENDPOINTS.VALIDATE_OP, {
1353
- path: pathArrayToString(path),
1354
- actions: channel.w_actions
1355
- });
1356
- return data;
1357
- };
1358
- const authenticate2 = async (transport, challenge, application, keyHandle, userName, role, workspace) => {
1359
- const data = await deviceNetwork("POST", ENDPOINTS.AUTHENTICATE, {
1360
- challenge: challenge.toString("hex"),
1361
- application,
1362
- key_handle: keyHandle.toString("hex"),
1363
- name: userName,
1364
- // @ts-ignore
1365
- role: ROLE_TO_BYTES[role.toLowerCase()].toString("hex"),
1366
- type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString("hex"),
1367
- workspaceName: workspace
1368
- });
1369
- const response = Buffer.concat([Buffer.from(data, "hex"), Buffer.from("9000", "hex")]);
1370
- const userPresence = response.slice(0, 1);
1371
- const counter = response.slice(1, 5);
1372
- const signature = response.slice(5, response.length - 2).toString("hex");
1373
- return {
1374
- userPresence,
1375
- counter,
1376
- signature,
1377
- rawResponse: response.slice(0, response.length - 2).toString("hex")
1378
- };
1379
- };
1380
- const getUserID = async () => {
1381
- const { blue_device_id } = await deviceNetwork("POST", "/blue-device-id");
1382
- return blue_device_id.toUpperCase();
1383
- };
1384
- const initiatePairingHandshake = async () => {
1385
- return deviceNetwork("POST", ENDPOINTS.INITIATE_PAIRING_HANDSHAKE, {});
1386
- };
1387
- const finalizePairingHandshake = async (transport, handshake, ciphertext) => {
1388
- const data = {
1389
- handshake,
1390
- ciphertext
1391
- };
1392
- return deviceNetwork("POST", ENDPOINTS.FINALIZE_PAIRING_HANDSHAKE, data);
1393
- };
1394
- const startKpatternAsResponder = async (transport, scriptID, handshake, handshakeAttestation, partitionID) => {
1395
- const data = {
1396
- script_id: scriptID,
1397
- handshake,
1398
- // FIXME apparently for device-api it's normal to have empty string as handshake_attestation
1399
- handshake_attestation: "",
1400
- partition_id: partitionID
1401
- };
1402
- return deviceNetwork("POST", ENDPOINTS.START_K_PATTERN_AS_RESPONDER, data);
1403
- };
1404
- const hasPartitionID = async () => {
1405
- const partitionIDs = await deviceNetwork("POST", "/get-partition-ids");
1406
- return partitionIDs.length > 0;
1407
- };
1408
- const getConfidentialityKey = () => deviceNetwork("POST", "/get-confidentiality-key");
1409
- const updatePSDPartitionPairing = (p) => deviceNetwork("POST", "/set-partition-key", p.pairingData);
1410
- return {
1411
- getPsdModel: () => __PSD_MODEL__,
1412
- setPsdModel: (psdModel2) => __PSD_MODEL__ = psdModel2,
1413
- setSeed,
1414
- setSalt,
1415
- authenticate: authenticate2,
1416
- getAttestationCertificate,
1417
- getPublicKey,
1418
- getUserID,
1419
- register,
1420
- registerData,
1421
- switchDevice,
1422
- // NEW SECURE CHANNEL ENDPOINTS
1423
- validateVaultOperation,
1424
- initiatePairingHandshake,
1425
- finalizePairingHandshake,
1426
- startKpatternAsResponder,
1427
- hasPartitionID,
1428
- getConfidentialityKey,
1429
- updatePSDPartitionPairing
1430
- };
1431
- }
1432
- var isAPIDevice = (device) => {
1433
- return "getPsdModel" in device;
1434
- };
1435
- var createAPIDevice_default = createAPIDevice;
1436
-
1437
- // src/device/createHWDevice.ts
1438
- import { TransportStatusError } from "@ledgerhq/hw-transport";
1439
- import chalk2 from "chalk";
1440
- import invariant from "invariant";
1441
- import readline from "readline";
1442
-
1443
- // src/device/constants.ts
1444
- var all61xxStatus = [];
1445
- for (let i = 24832; i <= 25087; i++) {
1446
- all61xxStatus.push(i);
1447
- }
1448
- var PAGINATED_STATUS = all61xxStatus;
1449
- var U2F_PATH = [2153139284, 2153067078];
1450
- var CONFIDENTIALITY_PATH = [2153139284, 2151894598];
1451
- var VALIDATION_PATH = [2153139284, 2153136460];
1452
- var ACCOUNT_MANAGER_SESSION = 2;
1453
- var STREAMING_RESPONSE = 2;
1454
- var STREAMING_NEXT_ACTION = 1;
1455
- var APPID_VAULT_ADMINISTRATOR = "ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9";
1456
-
1457
- // src/device/createHWDevice.ts
1458
- var STATUS_LENGTH = 2;
1459
- var MAX_CHUNK_LENGTH = 250;
1460
- var removeStatus = (result) => result.slice(0, result.length - STATUS_LENGTH);
1461
- var buildAttestation = (cliMsg, attestation, partitionID) => {
1462
- const sigBuffer = Buffer.from(attestation.signature, "base64");
1463
- const sigLengthBuffer = Buffer.alloc(1);
1464
- sigLengthBuffer.writeUIntBE(sigBuffer.length, 0, 1);
1465
- const certBuffer = Buffer.from(attestation.certificate, "base64");
1466
- const certLengthBuffer = Buffer.alloc(1);
1467
- certLengthBuffer.writeUIntBE(certBuffer.length, 0, 1);
1468
- const pubBuffer = Buffer.from(attestation.attestation_pub, "base64");
1469
- const certChainData = Buffer.concat([
1470
- sigLengthBuffer,
1471
- sigBuffer,
1472
- pubBuffer,
1473
- certLengthBuffer,
1474
- certBuffer
1475
- ]);
1476
- const cliMsgBuffer = Buffer.from(cliMsg, "hex");
1477
- const cliMsgLengthBuffer = Buffer.alloc(1);
1478
- cliMsgLengthBuffer.writeUIntBE(cliMsgBuffer.length, 0, 1);
1479
- const pidBuffer = Buffer.from(partitionID, "hex");
1480
- const pidLengthBuffer = Buffer.alloc(1);
1481
- pidLengthBuffer.writeUIntBE(pidBuffer.length, 0, 1);
1482
- const certChainDataLengthBuff = Buffer.alloc(2);
1483
- certChainDataLengthBuff.writeUInt16BE(certChainData.length, 0);
1484
- const data = Buffer.concat([
1485
- cliMsgLengthBuffer,
1486
- cliMsgBuffer,
1487
- certChainDataLengthBuff,
1488
- certChainData,
1489
- pidLengthBuffer,
1490
- pidBuffer
1491
- ]);
1492
- const totalLengthBuffer = Buffer.alloc(2);
1493
- totalLengthBuffer.writeUInt16BE(data.length, 0);
1494
- const toSend = Buffer.concat([totalLengthBuffer, data]);
1495
- return toSend;
1496
- };
1497
- function promptUserInput(query) {
1498
- const rl = readline.createInterface({
1499
- input: process.stdin,
1500
- output: process.stdout
1501
- });
1502
- return new Promise(
1503
- (resolve) => rl.question(query, (ans) => {
1504
- rl.close();
1505
- resolve(ans);
1506
- })
1507
- );
1508
- }
1509
- var splits = (chunk, buffer) => {
1510
- const chunks = [];
1511
- for (let i = 0, size = chunk; i < buffer.length; i += size, size = chunk) {
1512
- chunks.push(buffer.slice(i, i + size));
1513
- }
1514
- return chunks;
1515
- };
1516
- var sendByChunk = async (transport, command, data, chunkSize = MAX_CHUNK_LENGTH) => {
1517
- const chunks = splits(chunkSize, data);
1518
- let response = Buffer.from([]);
1519
- for (let i = 0; i < chunks.length; i++) {
1520
- const chunk = chunks[i];
1521
- const apdu = [...command];
1522
- apdu[2] = i && 128;
1523
- response = await transport.send(
1524
- apdu[0],
1525
- apdu[1],
1526
- apdu[2],
1527
- apdu[3],
1528
- chunk,
1529
- [36864, ...PAGINATED_STATUS]
1530
- );
1531
- }
1532
- return response;
1533
- };
1534
- var ROLE_TO_BYTES2 = {
1535
- shared_owner: Buffer.from([2]),
1536
- admin: Buffer.from([1]),
1537
- operator: Buffer.from([0])
1538
- };
1539
- function createHWDevice(options) {
1540
- const { promptToSwitch } = options;
1541
- const getPublicKey = async (transport, path, secp256k1 = true) => {
1542
- const data = Buffer.concat([
1543
- Buffer.from([path.length]),
1544
- ...path.map((derivation) => {
1545
- const buf = Buffer.alloc(4);
1546
- buf.writeUInt32BE(derivation, 0);
1547
- return buf;
1548
- })
1549
- ]);
1550
- let curve = 1;
1551
- if (!secp256k1) {
1552
- curve = 2;
1553
- }
1554
- const response = await transport.send(224, 64, curve, 0, data);
1555
- const pubKeyLength = response.readInt8(0);
1556
- const pubKey = response.slice(1, pubKeyLength + 1).toString("hex").toUpperCase();
1557
- const signature = response.slice(pubKeyLength + 1, response.length - STATUS_LENGTH);
1558
- return { pubKey, signature };
1559
- };
1560
- const registerData = async (transport, challenge) => {
1561
- invariant(challenge.length === 32, "challenge hex is expected to have 32 bytes");
1562
- const response = await transport.send(224, 3, 0, 0, challenge);
1563
- return removeStatus(response);
1564
- };
1565
- const register = async (transport, challenge, application, userName, userRole) => {
1566
- invariant(challenge.length === 32, "challenge hex is expected to have 32 bytes");
1567
- const applicationBuf = Buffer.from(application, "hex");
1568
- invariant(applicationBuf.length === 32, "application hex is expected to have 32 bytes");
1569
- const userNameBuff = Buffer.from(userName);
1570
- const keyHandleData = Buffer.concat([
1571
- ROLE_TO_BYTES2[userRole.toLowerCase()],
1572
- Buffer.from([userNameBuff.length]),
1573
- userNameBuff
1574
- ]);
1575
- const keyHandleDataLength = Buffer.alloc(2);
1576
- keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);
1577
- const data = Buffer.concat([challenge, applicationBuf, keyHandleDataLength, keyHandleData]);
1578
- const response = await sendByChunk(transport, [224, 1, 0, 0], data);
1579
- let i = 0;
1580
- const rfu = response.slice(i, i += 1)[0];
1581
- const pubKey = response.slice(i, i += 65).toString("hex");
1582
- const keyHandleLength = response.slice(i, ++i)[0];
1583
- const keyHandle = response.slice(i, i += keyHandleLength);
1584
- return {
1585
- u2f_register: removeStatus(response),
1586
- keyHandle,
1587
- rfu,
1588
- pubKey
1589
- };
1590
- };
1591
- const getAttestationCertificate = async (transport) => {
1592
- const response = await transport.send(224, 65, 0, 0);
1593
- return removeStatus(response);
1594
- };
1595
- const authenticate2 = async (transport, challenge, application, keyHandle, name, role, workspace) => {
1596
- invariant(challenge.length === 32, "challenge hex is expected to have 32 bytes");
1597
- const applicationBuf = Buffer.from(application, "hex");
1598
- invariant(applicationBuf.length === 32, "application hex is expected to have 32 bytes");
1599
- const nameBuf = Buffer.from(name);
1600
- const workspaceBuf = Buffer.from(workspace);
1601
- const roleBuf = ROLE_TO_BYTES2[role.toLowerCase()];
1602
- const keyHandleData = Buffer.concat([
1603
- roleBuf,
1604
- Buffer.from([nameBuf.length]),
1605
- nameBuf,
1606
- Buffer.from([workspaceBuf.length]),
1607
- workspaceBuf
1608
- ]);
1609
- const keyHandleDataLength = Buffer.alloc(2);
1610
- keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);
1611
- const data = Buffer.concat([
1612
- challenge,
1613
- applicationBuf,
1614
- Buffer.from([keyHandle.length]),
1615
- keyHandle,
1616
- keyHandleDataLength,
1617
- keyHandleData
1618
- ]);
1619
- const response = await sendByChunk(transport, [224, 2, 0, 0], data);
1620
- const userPresence = response.slice(0, 1);
1621
- const counter = response.slice(1, 5);
1622
- const signature = response.slice(5, response.length - STATUS_LENGTH).toString("hex");
1623
- return {
1624
- userPresence,
1625
- counter,
1626
- signature,
1627
- rawResponse: removeStatus(response).toString("hex")
1628
- };
1629
- };
1630
- const switchDevice = async () => {
1631
- if (promptToSwitch) {
1632
- await promptUserInput(
1633
- chalk2`{red.green [SWITCH DEVICE]} press {red ENTER} when the device is plugged on.`
1634
- );
1635
- }
1636
- };
1637
- const initiatePairingHandshake = async (transport) => {
1638
- const res = await transport.send(224, 70, 0, 0);
1639
- const pubKeyLength = res.readInt8(0);
1640
- const pubKey = res.slice(1, pubKeyLength + 1).toString("hex").toUpperCase();
1641
- const attestation = res.slice(pubKeyLength + 1, res.length - STATUS_LENGTH);
1642
- return { pubKey, attestation };
1643
- };
1644
- const validateVaultOperation = async (transport, path, channel) => {
1645
- const paths = Buffer.concat([
1646
- Buffer.from([path.length]),
1647
- ...path.map((derivation) => {
1648
- const buf = Buffer.alloc(4);
1649
- buf.writeUInt32BE(derivation, 0);
1650
- return buf;
1651
- })
1652
- ]);
1653
- let nextActionId = 1;
1654
- let finalResponse;
1655
- while (!finalResponse) {
1656
- const screen = Buffer.from(channel.w_actions[nextActionId - 1], "base64");
1657
- const length = Buffer.alloc(2);
1658
- length.writeUInt16BE(screen.length, 0);
1659
- const data = Buffer.concat([paths, length, screen]);
1660
- const response = await sendByChunk(transport, [224, 69, 0, 0], data);
1661
- const responseType = response.readInt8(0);
1662
- let responseStatus = response.readUInt16BE(response.length - 2);
1663
- if (responseType === STREAMING_NEXT_ACTION) {
1664
- nextActionId = response.readUIntBE(1, 2);
1665
- } else if (responseType === STREAMING_RESPONSE) {
1666
- finalResponse = response.slice(3, response.length - 2);
1667
- while (responseStatus !== 36864) {
1668
- const resp = await transport.send(0, 192, 0, 0);
1669
- responseStatus = resp.readUInt16BE(resp.length - 2);
1670
- finalResponse = Buffer.concat([finalResponse, removeStatus(resp)]);
1671
- }
1672
- } else {
1673
- throw Error(`${responseType}`);
1674
- }
1675
- }
1676
- return finalResponse.toString("base64");
1677
- };
1678
- const finalizePairingHandshake = async (transport, handshake, ciphertext, handshakeAttestation) => {
1679
- const response = await sendByChunk(
1680
- transport,
1681
- [224, 71, 0, 2],
1682
- buildAttestation(handshake, handshakeAttestation, ciphertext)
1683
- );
1684
- return removeStatus(response).toString("hex");
1685
- };
1686
- const startKpatternAsResponder = async (transport, scriptID, handshake, handshakeAttestation, partitionID) => {
1687
- const data = buildAttestation(handshake, handshakeAttestation, partitionID);
1688
- const response = await sendByChunk(transport, [224, 73, 0, scriptID], data);
1689
- return removeStatus(response).toString("hex");
1690
- };
1691
- const getUserID = () => Promise.resolve("");
1692
- const hasPartitionID = async ({ transport }) => {
1693
- const apdu = [224, 81, 0, 0, 181];
1694
- const res = await transport.exchange(Buffer.from(apdu));
1695
- const hex2 = Buffer.from(res).toString("hex");
1696
- return hex2 !== "6f45" && hex2 !== "6a87";
1697
- };
1698
- const getConfidentialityKey = async ({ transport }) => {
1699
- const apdu = [224, 77, 0, 0];
1700
- const bufWithStatus = await transport.send(...apdu);
1701
- const status = bufWithStatus.readUInt16BE(bufWithStatus.length - 2);
1702
- if (status !== 36864) {
1703
- throw new TransportStatusError(status);
1704
- }
1705
- const buf = removeStatus(bufWithStatus);
1706
- const keyLength = buf[0];
1707
- const key = buf.slice(1, 1 + keyLength);
1708
- const sig = buf.slice(1 + keyLength + 1);
1709
- const res = {
1710
- confidentiality_key_curve25519: key.toString("hex"),
1711
- confidentiality_key_signature: sig.toString("hex")
1712
- };
1713
- return res;
1714
- };
1715
- const updatePSDPartitionPairing = async ({
1716
- transport,
1717
- pairingData
1718
- }) => {
1719
- const psdPayload = Buffer.from(pairingData.blob, "base64");
1720
- const length = Buffer.alloc(2);
1721
- length.writeUInt16BE(psdPayload.length, 0);
1722
- const final = Buffer.concat([length, psdPayload]);
1723
- await sendByChunk(transport, [224, 78, 0, 0], final);
1724
- };
1725
- return {
1726
- authenticate: authenticate2,
1727
- switchDevice,
1728
- getAttestationCertificate,
1729
- getPublicKey,
1730
- register,
1731
- registerData,
1732
- initiatePairingHandshake,
1733
- validateVaultOperation,
1734
- finalizePairingHandshake,
1735
- startKpatternAsResponder,
1736
- getUserID,
1737
- hasPartitionID,
1738
- getConfidentialityKey,
1739
- updatePSDPartitionPairing
1740
- };
1741
- }
1742
- var createHWDevice_default = createHWDevice;
1743
-
1744
- // src/device/createInteractions.ts
1745
- function noValidChannel(channels) {
1746
- const keys = Object.keys(channels);
1747
- return keys.length === 0 || keys.length === 1 && keys[0] === "success";
1748
- }
1749
- function buildCertif(attestation, signature) {
1750
- const certLen = attestation.readInt8(32 + 65 + 1);
1751
- return {
1752
- code_hash: attestation.slice(0, 32).toString("base64"),
1753
- attestation_pub: attestation.slice(32, 32 + 65).toString("base64"),
1754
- certificate: attestation.slice(32 + 65, 32 + 65 + 2 + certLen).toString("base64"),
1755
- signature: Buffer.from(signature, "hex").toString("base64")
1756
- };
1757
- }
1758
- function createInteractions(device) {
1759
- const {
1760
- getPublicKey,
1761
- getAttestationCertificate,
1762
- register,
1763
- registerData,
1764
- authenticate: authenticate2,
1765
- validateVaultOperation,
1766
- initiatePairingHandshake,
1767
- finalizePairingHandshake,
1768
- startKpatternAsResponder,
1769
- hasPartitionID,
1770
- getConfidentialityKey,
1771
- updatePSDPartitionPairing
1772
- } = device;
1773
- const initPairing = {
1774
- responseKey: "psd_ephemeral_pubkey",
1775
- action: ({ transport }) => initiatePairingHandshake(transport)
1776
- };
1777
- const getU2FPublicKey = {
1778
- responseKey: "u2f_key",
1779
- action: ({ transport }) => getPublicKey(transport, U2F_PATH, false)
1780
- };
1781
- const getSecureChannel = {
1782
- responseKey: "secure_channels",
1783
- action: async ({ request_id, network }) => network("GET", `/requests/${request_id}/challenge`, {})
1784
- };
1785
- const doStartKpatternAsResponder = {
1786
- responseKey: "start-k-pattern",
1787
- action: async ({ transport, secure_channels, u2f_key }) => {
1788
- if (noValidChannel(secure_channels)) {
1789
- throw new Error("Request finished");
1790
- }
1791
- const channel = secure_channels[u2f_key.pubKey.toUpperCase()];
1792
- if (!channel) {
1793
- throw new Error("No channel for device");
1794
- }
1795
- await startKpatternAsResponder(
1796
- transport,
1797
- ACCOUNT_MANAGER_SESSION,
1798
- channel.handshake,
1799
- channel.handshake_attestation,
1800
- channel.partition_id
1801
- );
1802
- }
1803
- };
1804
- const validateDevice = {
1805
- responseKey: "validate_device",
1806
- action: ({ transport, secure_channels, u2f_key }) => {
1807
- const channel = secure_channels[u2f_key.pubKey.toUpperCase()];
1808
- return validateVaultOperation(transport, VALIDATION_PATH, channel);
1809
- }
1810
- };
1811
- const validatePayload = {
1812
- responseKey: "validate_payload",
1813
- action: ({ secure_channels, u2f_key, validate_device }) => {
1814
- const channel = secure_channels[u2f_key.pubKey.toUpperCase()];
1815
- const handshake_attestation = {
1816
- code_hash: channel.handshake_attestation.code_hash,
1817
- attestation_pub: channel.handshake_attestation.attestation_pub,
1818
- certificate: channel.handshake_attestation.certificate,
1819
- signature: channel.handshake_attestation.signature
1820
- };
1821
- return Promise.resolve({
1822
- public_key: u2f_key.pubKey,
1823
- challenge_response: validate_device,
1824
- handshake: Buffer.from(channel.handshake, "hex").toString("base64"),
1825
- handshake_attestation
1826
- });
1827
- }
1828
- };
1829
- const postApproval = {
1830
- responseKey: "post",
1831
- action: async ({ request_id, u2f_key: { pubKey }, validate_payload, network }) => network("POST", `/requests/${request_id}/approve`, {
1832
- requestId: request_id,
1833
- ...validate_payload,
1834
- public_key: pubKey.toString("hex")
1835
- })
1836
- };
1837
- const postSimpleApproval = {
1838
- responseKey: "post",
1839
- action: async ({ request_id, u2f_key: { pubKey }, network }) => network("POST", `/requests/${request_id}/approve`, {
1840
- requestId: request_id,
1841
- public_key: pubKey.toString("hex")
1842
- })
1843
- };
1844
- const getConfidentialityPublicKey = {
1845
- responseKey: "confidentiality_key",
1846
- action: ({ transport }) => getPublicKey(transport, CONFIDENTIALITY_PATH)
1847
- };
1848
- const getAttestation = {
1849
- responseKey: "attestation",
1850
- action: ({ transport }) => getAttestationCertificate(transport)
1851
- };
1852
- const getU2FChallenge = {
1853
- responseKey: "u2f_challenge",
1854
- action: async ({ u2f_key: { pubKey }, network }) => {
1855
- const challenge = await network("GET", `/u2f/authentications/${pubKey}/challenge`);
1856
- if (!challenge.key_handle) {
1857
- throw new Error("Unknown device");
1858
- }
1859
- return challenge;
1860
- }
1861
- };
1862
- const getValidationPublicKey = {
1863
- responseKey: "validation_key",
1864
- action: ({ transport }) => getPublicKey(transport, VALIDATION_PATH)
1865
- };
1866
- const u2fAuthenticate = {
1867
- device: true,
1868
- responseKey: "u2f_authenticate",
1869
- action: ({ transport, u2f_challenge: { challenge, key_handle, role, name }, workspace }) => authenticate2(
1870
- transport,
1871
- Buffer.from(challenge, "base64"),
1872
- APPID_VAULT_ADMINISTRATOR,
1873
- Buffer.from(key_handle, "hex"),
1874
- name,
1875
- role,
1876
- workspace
1877
- )
1878
- };
1879
- const readOnlyLogin = (username) => ({
1880
- responseKey: "u2f_sign",
1881
- action: async ({ network }) => {
1882
- const gateUsers = await network("GET", "/dev/people/pub_keys");
1883
- const currentUser = gateUsers.find((u) => u.username === username);
1884
- if (!currentUser) {
1885
- throw new Error(`Can't find user ${username}`);
1886
- }
1887
- return network("POST", `/dev/authentication/view_only/${currentUser.pub_key}`);
1888
- }
1889
- });
1890
- const postU2FSignature = {
1891
- responseKey: "u2f_sign",
1892
- action: ({ u2f_authenticate, network }) => network("POST", `/u2f/authentications/authenticate`, {
1893
- authentication: u2f_authenticate.rawResponse
1894
- })
1895
- };
1896
- const finalizePairing = {
1897
- responseKey: "pairing_payload",
1898
- action: ({ onboardingRegisterChallenge, transport, u2f_key }) => {
1899
- const secure_channel = extractSecureChannel(
1900
- onboardingRegisterChallenge,
1901
- u2f_key.pubKey.toUpperCase()
1902
- );
1903
- return finalizePairingHandshake(
1904
- transport,
1905
- secure_channel.handshake,
1906
- secure_channel.ciphertext,
1907
- secure_channel.handshake_attestation
1908
- );
1909
- }
1910
- };
1911
- const onboardingRegisterDevice = {
1912
- responseKey: "u2f_register",
1913
- action: async ({ u2f_key, onboardingRegisterChallenge, role, username, transport }) => {
1914
- const secure_channel = extractSecureChannel(
1915
- onboardingRegisterChallenge,
1916
- u2f_key.pubKey.toUpperCase()
1917
- );
1918
- return register(
1919
- transport,
1920
- Buffer.from(secure_channel.challenge, "hex"),
1921
- APPID_VAULT_ADMINISTRATOR,
1922
- username,
1923
- role,
1924
- secure_channel.u2f_register_data
1925
- );
1926
- }
1927
- };
1928
- const onboardingRegisterData = {
1929
- responseKey: "register_data",
1930
- action: ({ transport, u2f_key, onboardingRegisterChallenge }) => {
1931
- const register_challenge = onboardingRegisterChallenge[u2f_key.pubKey.toString("hex").toUpperCase()] || onboardingRegisterChallenge;
1932
- return registerData(transport, Buffer.from(register_challenge.challenge, "hex"));
1933
- }
1934
- };
1935
- const onboardingPostResult = {
1936
- responseKey: "register_input",
1937
- action: ({
1938
- register_data,
1939
- pairing_payload,
1940
- validation_key,
1941
- u2f_register,
1942
- attestation,
1943
- u2f_key
1944
- }) => {
1945
- const attestationOffset = 67 + u2f_register.u2f_register.readInt8(66);
1946
- const registration_payload = Buffer.concat([
1947
- u2f_register.u2f_register.slice(0, attestationOffset),
1948
- Buffer.from([attestation.length]),
1949
- attestation,
1950
- u2f_register.u2f_register.slice(attestationOffset)
1951
- ]);
1952
- const data = {
1953
- public_key: u2f_key.pubKey.toString("hex"),
1954
- key_handle: u2f_register.keyHandle.toString("hex"),
1955
- validation_key: validation_key.pubKey.toString("hex"),
1956
- register_data: register_data.toString("hex"),
1957
- u2f_register: registration_payload.toString("hex"),
1958
- certificate: buildCertif(attestation, validation_key.signature),
1959
- pairing_payload
1960
- };
1961
- return Promise.resolve(data);
1962
- }
1963
- };
1964
- const postUserRegistration = {
1965
- responseKey: "result",
1966
- action: ({ urlID, register_input, member, network }) => network("POST", `/requests/registration/${urlID}/authenticate`, {
1967
- ...register_input,
1968
- name: member.user.username
1969
- })
1970
- };
1971
- const operatorGetChallenge = {
1972
- responseKey: "onboardingRegisterChallenge",
1973
- action: ({
1974
- u2f_key,
1975
- confidentiality_key,
1976
- attestation,
1977
- urlID,
1978
- network,
1979
- psd_ephemeral_pubkey
1980
- }) => {
1981
- const data = {
1982
- public_key: u2f_key.pubKey,
1983
- confidentiality_key: confidentiality_key.pubKey,
1984
- ...isAPIDevice(device) && device.getPsdModel() === "STAX" ? { device_type: "02" } : (
1985
- /* istanbul ignore next */
1986
- {}
1987
- ),
1988
- ...{
1989
- psd_ephemeral_key: psd_ephemeral_pubkey.pubKey,
1990
- psd_ephemeral_key_attestation: buildCertif(attestation, psd_ephemeral_pubkey.attestation)
1991
- }
1992
- };
1993
- return network("POST", `/requests/registration/${urlID}/challenge`, data);
1994
- }
1995
- };
1996
- const ensurePartitionPairing = {
1997
- responseKey: "hasPairing",
1998
- action: async ({ transport, network }) => {
1999
- if (!await hasPartitionID({ transport })) {
2000
- const payload = await getConfidentialityKey({ transport });
2001
- const url = "/authentications/prepare-psd-key-update";
2002
- const pairingData = await network("POST", url, payload);
2003
- await updatePSDPartitionPairing({ transport, pairingData });
2004
- }
2005
- return true;
2006
- }
2007
- };
2008
- const validateOperation = [
2009
- getU2FPublicKey,
2010
- doStartKpatternAsResponder,
2011
- validateDevice,
2012
- validatePayload
2013
- ];
2014
- const approveFlow = [getSecureChannel, ...validateOperation, postApproval];
2015
- const approveFlowWithoutHSM = [postSimpleApproval];
2016
- const loginFlow = [
2017
- getU2FPublicKey,
2018
- getU2FChallenge,
2019
- u2fAuthenticate,
2020
- postU2FSignature,
2021
- ensurePartitionPairing
2022
- ];
2023
- const readOnlyLoginFlow = (username) => [readOnlyLogin(username)];
2024
- const registerUserFlow = [
2025
- getAttestation,
2026
- getU2FPublicKey,
2027
- getConfidentialityPublicKey,
2028
- initPairing,
2029
- operatorGetChallenge,
2030
- finalizePairing,
2031
- onboardingRegisterDevice,
2032
- getValidationPublicKey,
2033
- onboardingRegisterData,
2034
- onboardingPostResult,
2035
- postUserRegistration
2036
- ];
2037
- return {
2038
- approveFlow,
2039
- approveFlowWithoutHSM,
2040
- doStartKpatternAsResponder,
2041
- getSecureChannel,
2042
- getU2FChallenge,
2043
- getU2FPublicKey,
2044
- initPairing,
2045
- loginFlow,
2046
- readOnlyLoginFlow,
2047
- registerUserFlow,
2048
- u2fAuthenticate,
2049
- validateOperation,
2050
- ensurePartitionPairing,
2051
- finalizePairing,
2052
- getAttestation,
2053
- getConfidentialityPublicKey,
2054
- getValidationPublicKey,
2055
- operatorGetChallenge,
2056
- postU2FSignature,
2057
- postUserRegistration,
2058
- validateDevice,
2059
- validatePayload,
2060
- postApproval,
2061
- postSimpleApproval
2062
- };
2063
- }
2064
-
2065
- // src/createDevicesPool.ts
2066
- var createDevicesPool = (options, { logger = SILENT_LOGGER4 } = {}) => {
2067
- const {
2068
- salt = "",
2069
- gate,
2070
- apiGateway,
2071
- overrideSeeds,
2072
- lamURL,
2073
- lamAPIKey,
2074
- notifierURL,
2075
- deviceAPISessionID,
2076
- networkDelay,
2077
- readOnlyUser,
2078
- transport = "software",
2079
- recordStore,
2080
- deviceAPIURL,
2081
- psdModel = "STAX"
2082
- } = options;
2083
- let adminDevicesCache = [];
2084
- const device = transport === "software" ? createAPIDevice_default({
2085
- deviceAPISessionID,
2086
- salt,
2087
- overrideSeeds,
2088
- deviceAPIURL,
2089
- psdModel
2090
- }) : createHWDevice_default({ promptToSwitch: transport !== "nodehid-replayer" });
2091
- const setSalt = (salt2) => {
2092
- invariant2(
2093
- transport === "software",
2094
- `Tried to use \`setSalt\` with transport not being "software"`
2095
- );
2096
- const apiDevice = device;
2097
- apiDevice.setSalt(salt2);
2098
- };
2099
- const interactions = createInteractions(device);
2100
- let deviceRun = createRunner(interactions, {
2101
- readOnly: !!readOnlyUser,
2102
- transport,
2103
- recordStore
2104
- });
2105
- const { loginFlow, readOnlyLoginFlow, registerUserFlow, approveFlow, approveFlowWithoutHSM } = interactions;
2106
- const workspace = getWorkspaceFromGate(gate);
2107
- const network = createNetwork({
2108
- baseURL: gate,
2109
- networkDelay
2110
- });
2111
- const runWithDevice = async (deviceIndex, interactions2, data) => {
2112
- await device.switchDevice(deviceIndex);
2113
- invariant2(deviceRun, "deviceRun not initialized");
2114
- return deviceRun(interactions2, data);
2115
- };
2116
- const getUserID = async (deviceIndex) => {
2117
- await device.switchDevice(deviceIndex);
2118
- return device.getUserID();
2119
- };
2120
- const registerDevice = async (deviceIndex, request) => {
2121
- const data = {
2122
- network,
2123
- workspace,
2124
- username: request.user.username,
2125
- role: request.user.role.toLowerCase(),
2126
- // FIXME absolutely wrong name (legacy code)
2127
- member: request,
2128
- urlID: request.url_id
2129
- };
2130
- return runWithDevice(deviceIndex, registerUserFlow, data);
2131
- };
2132
- const tokensByDeviceIndex = {};
2133
- const login = async (deviceIndex) => {
2134
- const isReadOnlyUser = typeof deviceIndex === "string" || !!readOnlyUser;
2135
- deviceRun = createRunner(interactions, {
2136
- readOnly: isReadOnlyUser,
2137
- transport,
2138
- recordStore
2139
- });
2140
- if (typeof deviceIndex === "number") {
2141
- await device.switchDevice(deviceIndex);
2142
- }
2143
- let _token = tokensByDeviceIndex[deviceIndex] || null;
2144
- const interceptToken = (token) => {
2145
- tokensByDeviceIndex[deviceIndex] = token;
2146
- _token = token;
2147
- };
2148
- const injectToken = () => _token;
2149
- if (!_token || process.env.NODE_ENV === "test") {
2150
- const network2 = createNetwork({
2151
- baseURL: gate,
2152
- interceptToken,
2153
- injectToken,
2154
- networkDelay
2155
- });
2156
- await deviceRun(
2157
- typeof deviceIndex === "string" ? readOnlyLoginFlow(deviceIndex) : readOnlyUser ? readOnlyLoginFlow(readOnlyUser) : loginFlow,
2158
- { network: network2, workspace }
2159
- );
2160
- if (!_token) {
2161
- throw new Error("Could not extract token from login");
2162
- }
2163
- }
2164
- const authNetwork = createNetwork({
2165
- baseURL: gate,
2166
- token: _token,
2167
- networkDelay
2168
- });
2169
- const authRun = async (interactions2, data) => {
2170
- if (typeof deviceIndex === "number") {
2171
- await runWithDevice(deviceIndex, interactions2, data);
2172
- } else {
2173
- throw new Error(`Can't authRun on read-only mode (deviceIdentifier: ${deviceIndex})`);
2174
- }
2175
- };
2176
- const post = async (url, payload) => authNetwork("POST", url, payload);
2177
- const rejectRequest = async (requestID) => post(`/requests/${requestID}/abort`, {});
2178
- const approveRequest = (request) => authRun(approveFlow, { request_id: request.id, network: authNetwork });
2179
- const approveRequestWithoutHSM = (request) => authRun(approveFlowWithoutHSM, {
2180
- request_id: request.id,
2181
- network: authNetwork
2182
- });
2183
- let socket = null;
2184
- return {
2185
- network: authNetwork,
2186
- run: authRun,
2187
- get: async (url, requestOptions) => authNetwork("GET", url, void 0, requestOptions),
2188
- post,
2189
- rejectRequest,
2190
- approveRequest,
2191
- approveRequestWithoutHSM,
2192
- getToken: () => _token,
2193
- connectSocket: async () => {
2194
- if (!socket) {
2195
- if (!notifierURL) {
2196
- throw new Error("No notifierURL specified");
2197
- }
2198
- if (!_token) {
2199
- throw new Error("No token");
2200
- }
2201
- socket = createSocket(_token, notifierURL);
2202
- const { pub_key } = await authNetwork("GET", "/people/me");
2203
- socket.emit("join", { username: pub_key, workspace });
2204
- logger.success("Socket connected");
2205
- }
2206
- return socket;
2207
- },
2208
- onEvent: (cb) => {
2209
- if (!socket) {
2210
- throw new Error("Call connectSocket() first");
2211
- }
2212
- socket.on("notification", cb);
2213
- }
2214
- };
2215
- };
2216
- const bruteforceDeviceIndex = async (userID) => {
2217
- for (let i = 1; i < 100; i++) {
2218
- const found = await getUserID(i);
2219
- if (found.toUpperCase() === userID.toUpperCase()) return i;
2220
- }
2221
- throw new Error(`Could not find device index for user ${userID}`);
2222
- };
2223
- async function getOnboardingAdminDevices() {
2224
- if (adminDevicesCache.length > 0) return adminDevicesCache;
2225
- adminDevicesCache = [
2226
- ["Admin 1", 4],
2227
- ["Admin 2", 5],
2228
- ["Admin 3", 6]
2229
- ];
2230
- return adminDevicesCache;
2231
- }
2232
- const collectQuorumDevices = async () => {
2233
- const devices = adminDevicesCache.map((adminDevice) => adminDevice[1]).slice(0, 2);
2234
- const admin = await login(devices[0]);
2235
- const org = await admin.get("/organization", {});
2236
- const { quorum } = org;
2237
- if (quorum > 2) {
2238
- const adminsC = await admin.get(
2239
- "/people?status=ACTIVE&role=ADMIN&pageSize=-1",
2240
- {}
2241
- );
2242
- let admins = adminsC.edges.map((e) => e.node);
2243
- admins.sort((a, b) => a.id > b.id ? 1 : -1);
2244
- admins = admins.slice(2, quorum);
2245
- for (const adm of admins) {
2246
- const deviceIndex = await bruteforceDeviceIndex(adm.user_id);
2247
- devices.push(deviceIndex);
2248
- }
2249
- }
2250
- return devices;
2251
- };
2252
- const runWithQuorum = async (iteratee) => {
2253
- const devices = await collectQuorumDevices();
2254
- for (let i = 0; i < devices.length; i++) {
2255
- const device2 = devices[i];
2256
- if (!device2) throw new Error("Invalid device");
2257
- const admin = await login(device2);
2258
- try {
2259
- await iteratee(admin);
2260
- } catch (err) {
2261
- const reqAlreadyExistsErr = "REQUEST_ALREADY_APPROVED_OR_ABORTED_BY_THIS_MEMBER_EXCEPTION";
2262
- if (err.name === reqAlreadyExistsErr) continue;
2263
- throw err;
2264
- }
2265
- }
2266
- };
2267
- const apiNetwork = createNetwork({
2268
- baseURL: lamURL || "",
2269
- networkDelay
2270
- });
2271
- const headersAPILam = lamAPIKey ? { headers: { "X-Ledger-API-Key": lamAPIKey } } : {};
2272
- const createInvitation = async (name) => {
2273
- try {
2274
- const r = await apiNetwork("POST", `/api_users/${name}`, {}, headersAPILam);
2275
- return r;
2276
- } catch (e) {
2277
- return apiNetwork("GET", `/api_users/${name}`, {}, headersAPILam);
2278
- }
2279
- };
2280
- const registerUser = async (name, uuid) => {
2281
- return apiNetwork("POST", `/api_users/${name}/register/${uuid}`, {}, headersAPILam);
2282
- };
2283
- const lamAPI = {
2284
- createInvitation,
2285
- registerUser
2286
- };
2287
- const createSocket = (token, notifierURL2) => {
2288
- const client = io(notifierURL2, {
2289
- transportOptions: {
2290
- polling: {
2291
- extraHeaders: {
2292
- Cookie: `ledger_workspace=${workspace}; ledger_token=${token}`
2293
- }
2294
- }
2295
- }
2296
- });
2297
- return client;
2298
- };
2299
- const getRevaultCompatOptions = () => {
2300
- return {
2301
- workspace,
2302
- deviceApiUrl: deviceAPIURL ?? process.env.VAULT_DEVICE_API_URL ?? "https://localhost:8443/device-api",
2303
- salt
2304
- };
2305
- };
2306
- return {
2307
- getRevaultCompatOptions,
2308
- workspace,
2309
- network,
2310
- gate,
2311
- apiGateway,
2312
- login,
2313
- lamAPI,
2314
- getUserID,
2315
- bruteforceDeviceIndex,
2316
- setSalt,
2317
- registerDevice,
2318
- runWithDevice,
2319
- runWithQuorum,
2320
- device,
2321
- interactions,
2322
- getOnboardingAdminDevices,
2323
- psdModel
2324
- };
2325
- };
2326
- var createDevicesPool_default = createDevicesPool;
2327
-
2328
- // src/createFaucet.ts
2329
- var createPralineFaucet = (url) => async (opts) => {
2330
- const network = createNetwork({
2331
- baseURL: url
2332
- });
2333
- const { recipient, amount } = opts;
2334
- const endpoint = `/chain/faucet/${recipient}/${amount}`;
2335
- const res = await network("POST", endpoint);
2336
- return res.tx;
2337
- };
2338
- function createFaucet(opts) {
2339
- const implems = {};
2340
- if (opts.pralineBTCTestnetURL) {
2341
- implems["bitcoin_testnet"] = createPralineFaucet(opts.pralineBTCTestnetURL);
2342
- }
2343
- const faucet = (opts2) => {
2344
- const { currency, ...rest } = opts2;
2345
- const implem = implems[currency];
2346
- if (!implem) {
2347
- throw new Error(`Unsupported faucet currency ${opts2.currency}`);
2348
- }
2349
- return implem(rest);
2350
- };
2351
- return faucet;
2352
- }
2353
- var createFaucet_default = createFaucet;
2354
-
2355
- // src/createPledge.ts
2356
- import { SILENT_LOGGER as SILENT_LOGGER5 } from "@ledgerhq/vault-utils";
2357
- async function createPledge({
2358
- pool,
2359
- account,
2360
- accountsByName,
2361
- exchange,
2362
- amount,
2363
- apiUser,
2364
- gate,
2365
- apiGateway
2366
- }, { logger = SILENT_LOGGER5 } = {}) {
2367
- const gateAccount = accountsByName[account.name];
2368
- if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);
2369
- const apiNetwork = createNetwork({
2370
- baseURL: apiGateway
2371
- });
2372
- logger.info(`Authenticate for ${apiUser.name}`);
2373
- const workspace = getWorkspaceFromGate(gate);
2374
- const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);
2375
- const pledge = await getTradelinkPledge({
2376
- apiNetwork,
2377
- workspace,
2378
- gateAccount,
2379
- bearerToken,
2380
- exchange
2381
- });
2382
- logger.info("Create pledge");
2383
- let tokens = [];
2384
- const shouldLoadTokens = !!gateAccount.contract_address;
2385
- if (shouldLoadTokens) {
2386
- const admin = await pool.login(4);
2387
- tokens = await fetchTokens_default(admin, { logger });
2388
- }
2389
- const unit = getAccountUnit(account, tokens);
2390
- const serializedAmount = serializeUnitValue(unit, amount);
2391
- const data = {
2392
- type: "CREATE_PLEDGE_INCREMENT",
2393
- data: {
2394
- pledge_subaccount_id: pledge.pledge_subaccount_id,
2395
- pledge_data: {
2396
- amount: serializedAmount,
2397
- account_name: account.name,
2398
- currency: gateAccount.currency,
2399
- exchange_name: exchange
2400
- }
2401
- }
2402
- };
2403
- if (shouldLoadTokens) {
2404
- data.data.pledge_data.contract_address = gateAccount.contract_address;
2405
- }
2406
- const pledgeRequestResp = await apiNetwork("POST", "/requests", data, {
2407
- headers: {
2408
- "X-Ledger-Workspace": workspace,
2409
- "Content-Type": "application/json",
2410
- Authorization: `Bearer ${bearerToken}`
2411
- }
2412
- });
2413
- logger.info(`Approving pledge ${pledgeRequestResp.id}`);
2414
- logger.info("Decode challenge");
2415
- const apiChallenge = await decodeChallenge({
2416
- apiNetwork,
2417
- workspace,
2418
- bearerToken,
2419
- requestID: pledgeRequestResp.id,
2420
- reviewType: "APPROVE"
2421
- });
2422
- logger.info(apiChallenge.decodedChallenge);
2423
- logger.info("Sign and approve");
2424
- return await signAndApprove({
2425
- apiNetwork,
2426
- workspace,
2427
- bearerToken,
2428
- requestID: pledgeRequestResp.id,
2429
- apiUser,
2430
- challenge: apiChallenge.challenge,
2431
- reviewType: "APPROVE"
2432
- });
2433
- }
2434
- var createPledge_default = createPledge;
2435
-
2436
- // src/createSettlement.ts
2437
- import { SILENT_LOGGER as SILENT_LOGGER6 } from "@ledgerhq/vault-utils";
2438
- import { v4 as uuidv4 } from "uuid";
2439
- async function createSettlement({
2440
- pool,
2441
- account,
2442
- accountsByName,
2443
- exchange,
2444
- percentage,
2445
- apiUser,
2446
- gate,
2447
- apiGateway
2448
- }, { logger = SILENT_LOGGER6 } = {}) {
2449
- const gateAccount = accountsByName[account.name];
2450
- if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);
2451
- const apiNetwork = createNetwork({
2452
- baseURL: apiGateway
2453
- });
2454
- logger.info(`Authenticate for ${apiUser.name}`);
2455
- const workspace = getWorkspaceFromGate(gate);
2456
- const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);
2457
- const pledge = await getTradelinkPledge({
2458
- apiNetwork,
2459
- workspace,
2460
- gateAccount,
2461
- bearerToken,
2462
- exchange
2463
- });
2464
- const recipient = await getTradelinkRecipient({ pool, gateAccount, pledge });
2465
- const settlementAmount = Math.floor(pledge.amount * percentage / 100);
2466
- let txIntent = {
2467
- account_id: gateAccount.id,
2468
- fees_strategy: {
2469
- type: "SPEED",
2470
- data: {
2471
- speed: "FAST"
2472
- }
2473
- },
2474
- transaction_data: {
2475
- account_name: gateAccount.name,
2476
- amount: String(settlementAmount),
2477
- max_fees: "0",
2478
- recipient
2479
- }
2480
- };
2481
- if (gateAccount.account_type === "Ethereum" || gateAccount.account_type === "Erc20") {
2482
- txIntent = {
2483
- ...txIntent,
2484
- transaction_data: {
2485
- ...txIntent.transaction_data,
2486
- currency: gateAccount.currency
2487
- },
2488
- transaction_type: "ETHEREUM_LIKE_SEND"
2489
- };
2490
- } else {
2491
- throw new Error("Unsupported account type for outbound transactions");
2492
- }
2493
- const maxFeesResp = await apiNetwork(
2494
- "POST",
2495
- "/transactions/estimate-fees",
2496
- {
2497
- type: "CREATE_TRANSACTION",
2498
- data: txIntent
2499
- },
2500
- {
2501
- headers: {
2502
- "X-Ledger-Workspace": workspace,
2503
- "Content-Type": "application/json",
2504
- Authorization: `Bearer ${bearerToken}`
2505
- }
2506
- }
2507
- );
2508
- txIntent.transaction_data.max_fees = maxFeesResp.max_fees;
2509
- const settlementIntent = {
2510
- id: String(uuidv4()),
2511
- from_pledge_id: pledge.id,
2512
- inbound_transaction_intent: null,
2513
- outbound_transaction_intent: txIntent,
2514
- meta: {}
2515
- };
2516
- logger.info("Post settlement");
2517
- return await apiNetwork("POST", "/settlements", settlementIntent, {
2518
- headers: {
2519
- "X-Ledger-Workspace": workspace,
2520
- "Content-Type": "application/json",
2521
- Authorization: `Bearer ${bearerToken}`
2522
- }
2523
- });
2524
- }
2525
- var createSettlement_default = createSettlement;
2526
-
2527
- // src/deployInstance.ts
2528
- import { SILENT_LOGGER as SILENT_LOGGER7 } from "@ledgerhq/vault-utils";
2529
- import duration from "humanize-duration";
2530
- import isEqual2 from "lodash/isEqual";
2531
- import io2 from "socket.io-client";
2532
-
2533
- // src/constants.ts
2534
- var DEFAULT_VAULT_REMOTE_URL = "https://remote.minivault.ledger-sbx.com";
2535
- var DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES = 10;
2536
-
2537
- // src/deployInstance.ts
2538
- var toSimplifiedState = (deployment) => {
2539
- return {
2540
- error: deployment.error,
2541
- status: deployment.status,
2542
- currentStep: deployment.currentStep,
2543
- totalPods: deployment.instance.pods.length,
2544
- totalHealthyPods: deployment.instance.pods.filter(
2545
- // TODO centralize types with vault-remote
2546
- (p) => p.status === "HEALTHY"
2547
- ).length
2548
- };
2549
- };
2550
- async function deploy(opts, { logger = SILENT_LOGGER7 } = {}) {
2551
- const {
2552
- remoteURL = DEFAULT_VAULT_REMOTE_URL,
2553
- watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,
2554
- useHTTPPolling = false,
2555
- ...payload
2556
- } = opts;
2557
- const now = Date.now();
2558
- logger.step(`Deploying ${payload.name}`);
2559
- const url = remoteURL.replace(/https:\/\/(.*?)\./, `https://${payload.name}.`);
2560
- const network = createNetwork({ baseURL: `${remoteURL}/api` });
2561
- const deployment = await network("POST", "/deploy", payload);
2562
- const gateURL = `https://${deployment.instance.name}.${deployment.instance.host}/gate/minivault`;
2563
- const gateNetwork = createNetwork({ baseURL: gateURL });
2564
- let state = toSimplifiedState(deployment);
2565
- const logState = () => {
2566
- const step = deployment.steps.find((s) => s.key === state.currentStep);
2567
- logger.info(`[${step.labelCurrent}] (${state.totalHealthyPods}/${state.totalPods})`);
2568
- };
2569
- let resolve = null, reject = null;
2570
- const promise = new Promise((res, rej) => {
2571
- resolve = res;
2572
- reject = rej;
2573
- });
2574
- const deployTimeout = setTimeout(
2575
- () => reject(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),
2576
- watchTimeoutMinutes * 60 * 1e3
2577
- );
2578
- const onState = (p) => {
2579
- const newState = toSimplifiedState(p);
2580
- if (!isEqual2(state, newState)) {
2581
- state = newState;
2582
- logState();
2583
- }
2584
- if (newState.error) {
2585
- reject(newState.error);
2586
- } else if (newState.status === "SUCCESS") {
2587
- const elapsed = Date.now() - now;
2588
- const displayDuration = duration(elapsed, { round: true });
2589
- logger.success(`Successfully deployed instance in ${displayDuration}`);
2590
- logger.success(url);
2591
- clearTimeout(deployTimeout);
2592
- resolve();
2593
- }
2594
- };
2595
- async function pollGate() {
2596
- try {
2597
- logger.info(`Polling onboarding state on ${gateURL}/onboarding/state ...`);
2598
- await gateNetwork("GET", "/onboarding/state");
2599
- resolve();
2600
- } catch {
2601
- await wait(3e3);
2602
- pollGate();
2603
- }
2604
- }
2605
- if (useHTTPPolling) {
2606
- logger.info("Using HTTP polling strategy (will not output deployment steps)");
2607
- logger.info("Waiting 30 seconds for domain to be deployed...");
2608
- await wait(3e4);
2609
- logger.info("Waiting for Gate to be ready...");
2610
- pollGate();
2611
- } else {
2612
- logState();
2613
- const socket = io2(remoteURL, { transports: ["polling"] });
2614
- socket.on("deployment-state", onState);
2615
- socket.emit("join-deployment", deployment.id);
2616
- socket.on("disconnect", () => {
2617
- reject(new Error("Socket disconnected!"));
2618
- });
2619
- }
2620
- await promise;
2621
- return { url };
2622
- }
2623
-
2624
- // src/destroy.ts
2625
- import { SILENT_LOGGER as SILENT_LOGGER8 } from "@ledgerhq/vault-utils";
2626
- async function destroy(opts, { logger = SILENT_LOGGER8 } = {}) {
2627
- const { remoteURL = DEFAULT_VAULT_REMOTE_URL, name } = opts;
2628
- const network = createNetwork({ baseURL: `${remoteURL}/api` });
2629
- await network("DELETE", `/instances/${name}`);
2630
- logger.success(`Successfully destroyed instance ${name}`);
2631
- if (opts.wait) {
2632
- logger.info("Waiting for namespace to disappear...");
2633
- while (await isInstanceAlive(name)) {
2634
- await new Promise((r) => setTimeout(r, 2e3));
2635
- }
2636
- logger.info("Done");
2637
- }
2638
- async function isInstanceAlive(name2) {
2639
- try {
2640
- await network("GET", `/instances/${name2}`);
2641
- return true;
2642
- } catch {
2643
- return false;
2644
- }
2645
- }
2646
- }
2647
-
2648
- // src/getMVInstances.ts
2649
- import { SILENT_LOGGER as SILENT_LOGGER9 } from "@ledgerhq/vault-utils";
2650
- async function getMVInstances(opts, { logger = SILENT_LOGGER9 } = {}) {
2651
- const { remoteURL = DEFAULT_VAULT_REMOTE_URL } = opts;
2652
- logger.info("Fetching instances...");
2653
- const network = createNetwork({ baseURL: `${remoteURL}/api` });
2654
- const instances = await network("GET", "/instances");
2655
- return instances;
2656
- }
2657
-
2658
- // src/send.ts
2659
- import { SILENT_LOGGER as SILENT_LOGGER10 } from "@ledgerhq/vault-utils";
2660
- import BigNumber from "bignumber.js";
2661
- var SUPPORTED_ACCOUNT_TYPES = [
2662
- "Bitcoin",
2663
- "Ethereum",
2664
- "Tezos",
2665
- "Polkadot",
2666
- "Stellar",
2667
- "Erc20",
2668
- "Solana"
2669
- ];
2670
- async function send({
2671
- pool,
2672
- transaction,
2673
- account,
2674
- accountsByName,
2675
- whitelistsByName,
2676
- noApproval,
2677
- groups = []
2678
- }, { logger = SILENT_LOGGER10 } = {}) {
2679
- const gateAccount = accountsByName[account.name];
2680
- if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);
2681
- if (transaction.feesLevel === "CUSTOM" && gateAccount.account_type !== "Ethereum" && gateAccount.account_type !== "Erc20") {
2682
- throw new Error("Custom fees are only supported for Ethereum and ERC20 accounts");
2683
- }
2684
- if (transaction.feesLevel === "CUSTOM" && (!transaction.gasPrice || !transaction.gasLimit)) {
2685
- throw new Error("Gas price and gas limit are required when using custom fees");
2686
- }
2687
- if (SUPPORTED_ACCOUNT_TYPES.indexOf(gateAccount.account_type) === -1) {
2688
- throw new Error(`Account type ${gateAccount.account_type} is not supported YET`);
2689
- }
2690
- let tokens = [];
2691
- const shouldLoadTokens = !!gateAccount.contract_address;
2692
- if (shouldLoadTokens) {
2693
- const admin = await pool.login(4);
2694
- tokens = await fetchTokens_default(admin, { logger });
2695
- }
2696
- const unit = getAccountUnit(account, tokens);
2697
- const serializedAmount = serializeUnitValue(unit, transaction.amount);
2698
- const matchingRule = getMatchingMultiAuthRule({
2699
- account,
2700
- transaction,
2701
- whitelistsByName
2702
- });
2703
- const { steps } = matchingRule;
2704
- const usedUsers = [];
2705
- const firstStep = steps[0];
2706
- if (!firstStep) throw new Error(`No creator step found`);
2707
- const creator = await loginWithStepUser({ pool, groups, step: firstStep, usedUsers });
2708
- const feesPayload = {
2709
- amount: serializedAmount,
2710
- recipient: transaction.recipient,
2711
- fees_level: transaction.feesLevel || "NORMAL",
2712
- ...transaction.transactionType ? { type: transaction.transactionType } : {}
2713
- };
2714
- if (gateAccount.account_type === "Bitcoin" && transaction.utxosPickingStrategy) {
2715
- Object.assign(feesPayload, {
2716
- utxo_picking_strategy: transaction.utxosPickingStrategy
2717
- });
2718
- }
2719
- let fees = {
2720
- fees_per_byte: "0",
2721
- fees: transaction.feesLevel || "NORMAL",
2722
- gas_limit: transaction.gasLimit,
2723
- gas_price: transaction.gasPrice
2724
- };
2725
- let feesEIP1559 = {
2726
- base_fees: transaction.feesLevel || "NORMAL",
2727
- gas_limit: transaction.gasLimit,
2728
- max_fees: transaction.maxFees,
2729
- max_fees_buffer_factor: transaction.maxFeesBufferFactor,
2730
- priority_fees: transaction.priorityFees
2731
- };
2732
- if (transaction.feesLevel !== "CUSTOM") {
2733
- const feesResponse = await creator.network(
2734
- "POST",
2735
- `/accounts/${gateAccount.id}/transactions/fees`,
2736
- feesPayload
2737
- );
2738
- "priority_fees" in feesResponse ? feesEIP1559 = feesResponse : fees = feesResponse;
2739
- }
2740
- let txFees = {};
2741
- if ((gateAccount.account_type === "Ethereum" || gateAccount.account_type === "Erc20") && !feesEIP1559.priority_fees) {
2742
- txFees = {
2743
- fees_level: feesPayload.fees_level,
2744
- max_fees: new BigNumber(fees.gas_price).times(fees.gas_limit).toFixed(),
2745
- gas_price: new BigNumber(fees.gas_price).toFixed(),
2746
- gas_limit: new BigNumber(fees.gas_limit).toFixed()
2747
- };
2748
- }
2749
- if ((gateAccount.account_type === "Ethereum" || gateAccount.account_type === "Erc20") && feesEIP1559.priority_fees) {
2750
- txFees = {
2751
- fees_level: feesPayload.fees_level,
2752
- max_fees: new BigNumber(feesEIP1559.max_fees).toFixed(),
2753
- gas_limit: new BigNumber(feesEIP1559.gas_limit).toFixed(),
2754
- priority_fees: new BigNumber(feesEIP1559.priority_fees).toFixed(),
2755
- max_fees_buffer_factor: new BigNumber(feesEIP1559.max_fees_buffer_factor).toFixed()
2756
- };
2757
- }
2758
- if (gateAccount.account_type === "Bitcoin") {
2759
- txFees = {
2760
- fees_level: feesPayload.fees_level,
2761
- fees_per_byte: new BigNumber(fees.fees_per_byte).toFixed(),
2762
- max_fees: new BigNumber(fees.fees).toFixed()
2763
- };
2764
- if (transaction.utxosPickingStrategy) {
2765
- Object.assign(txFees, {
2766
- utxo_picking_strategy: transaction.utxosPickingStrategy
2767
- });
2768
- }
2769
- }
2770
- if (gateAccount.account_type === "Tezos") {
2771
- txFees = {
2772
- fees_level: feesPayload.fees_level,
2773
- gas_limit: new BigNumber(fees.gas_limit).toFixed(),
2774
- max_fees: new BigNumber(fees.fees).toFixed(),
2775
- storage_limit: fees.storage_limit
2776
- };
2777
- }
2778
- if (gateAccount.account_type === "Polkadot") {
2779
- txFees = {
2780
- fees_level: feesPayload.fees_level,
2781
- max_fees: new BigNumber(fees.fees).toFixed()
2782
- };
2783
- }
2784
- if (gateAccount.account_type === "Stellar") {
2785
- txFees = {
2786
- fees_level: feesPayload.fees_level,
2787
- max_fees: new BigNumber(fees.fees).toFixed()
2788
- };
2789
- }
2790
- if (gateAccount.account_type === "Solana") {
2791
- txFees = {
2792
- fees_level: feesPayload.fees_level,
2793
- max_fees: new BigNumber(fees.fees).toFixed()
2794
- };
2795
- }
2796
- const payload = {
2797
- type: "CREATE_TRANSACTION",
2798
- account_id: gateAccount.id,
2799
- transaction: {
2800
- ...transaction.transactionType ? { type: transaction.transactionType } : {},
2801
- recipient: transaction.recipient,
2802
- amount: serializedAmount,
2803
- ...txFees
2804
- }
2805
- };
2806
- if (transaction.contractPayload) {
2807
- Object.assign(payload.transaction, {
2808
- contract_payload: transaction.contractPayload
2809
- });
2810
- }
2811
- if (transaction.title || transaction.comment) {
2812
- Object.assign(payload.transaction, {
2813
- note: {
2814
- title: transaction.title || "",
2815
- content: transaction.comment || ""
2816
- }
2817
- });
2818
- }
2819
- logger.info("Creating transaction request");
2820
- const request = await creator.post("/requests", payload);
2821
- if (noApproval) {
2822
- logger.info("Transaction created (skipped approval)");
2823
- } else {
2824
- logger.info("Approving request");
2825
- await creator.approveRequest(request);
2826
- for (let i = 1; i < steps.length; i++) {
2827
- const step = steps[i];
2828
- if (!step) throw new Error(`No step at index ${i}`);
2829
- for (let j = 0; j < step.quorum; j++) {
2830
- const approver = await loginWithStepUser({ pool, step, usedUsers, groups });
2831
- logger.info("Approving request");
2832
- await approver.approveRequest(request);
2833
- }
2834
- }
2835
- logger.success("Transaction created & approved");
2836
- }
2837
- const tx = await creator.network("GET", `/transactions/${request.target_id}`);
2838
- return tx;
2839
- }
2840
- var TX_TYPE_TO_PRESET = {
2841
- DELEGATE: "TEZOS_DELEGATION",
2842
- UNDELEGATE: "TEZOS_DELEGATION"
2843
- };
2844
- function getMatchingMultiAuthRule({
2845
- account,
2846
- transaction,
2847
- whitelistsByName
2848
- }) {
2849
- let multiAuthRule;
2850
- if (!account.rules) {
2851
- throw new Error(`No rules defined in account '${account.name}'`);
2852
- }
2853
- const rulesSet = account.rules.filter((rules) => {
2854
- if (!transaction.transactionType || !Object.keys(TX_TYPE_TO_PRESET).includes(transaction.transactionType)) {
2855
- return !rules.some((r) => Object.values(TX_TYPE_TO_PRESET).includes(r.type));
2856
- }
2857
- return rules.some(
2858
- // @ts-ignore
2859
- (r) => r.type === TX_TYPE_TO_PRESET[transaction.transactionType]
2860
- );
2861
- });
2862
- rulesSet.find((rules) => {
2863
- const multiAuth = rules.find(
2864
- (r) => r.type === "MULTI_AUTHORIZATIONS"
2865
- );
2866
- const threshold = rules.find((r) => r.type === "THRESHOLD");
2867
- const whitelist = rules.find((r) => r.type === "WHITELIST");
2868
- if (threshold) {
2869
- const amount = new BigNumber(transaction.amount);
2870
- if (threshold.min && amount.isLessThan(threshold.min)) {
2871
- return false;
2872
- }
2873
- if (threshold.max && amount.isGreaterThan(threshold.max)) {
2874
- return false;
2875
- }
2876
- }
2877
- if (whitelist) {
2878
- const allowedAddresses = whitelist.whitelists.reduce(
2879
- (acc, curr) => {
2880
- const w = whitelistsByName[curr];
2881
- if (!w) return [];
2882
- return [...acc, ...w.addresses];
2883
- },
2884
- []
2885
- );
2886
- if (!allowedAddresses.find((a) => a.address === transaction.recipient)) {
2887
- throw new Error(`Can't find ${transaction.recipient} in account whitelist(s)`);
2888
- }
2889
- }
2890
- multiAuthRule = multiAuth;
2891
- return true;
2892
- });
2893
- if (!multiAuthRule) {
2894
- throw new Error(`Can't find matching rule for transaction`);
2895
- }
2896
- return multiAuthRule;
2897
- }
2898
- function loginWithStepUser({
2899
- pool,
2900
- step,
2901
- usedUsers,
2902
- groups
2903
- }) {
2904
- const user = "group" in step ? groups.find((g) => g.name === step.group).users.find((u) => usedUsers.indexOf(u) === -1) : step.users.find((u) => usedUsers.indexOf(u) === -1);
2905
- if (!user) {
2906
- throw new Error(`No available user to approve`);
2907
- }
2908
- if (typeof user === "string") {
2909
- throw new Error(`Sending with API user is not supported YET`);
2910
- }
2911
- usedUsers.push(user);
2912
- return pool.login(user);
2913
- }
2914
- var send_default = send;
2915
-
2916
- // src/upgradeInstance.ts
2917
- import { SILENT_LOGGER as SILENT_LOGGER11 } from "@ledgerhq/vault-utils";
2918
- import duration2 from "humanize-duration";
2919
- import io3 from "socket.io-client";
2920
- async function upgrade(opts, { logger = SILENT_LOGGER11 } = {}) {
2921
- const {
2922
- remoteURL = DEFAULT_VAULT_REMOTE_URL,
2923
- watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,
2924
- ...payload
2925
- } = opts;
2926
- const now = Date.now();
2927
- logger.step(`Upgrading ${payload.name}`);
2928
- const socket = io3(remoteURL);
2929
- const network = createNetwork({ baseURL: remoteURL });
2930
- const { jobID } = await network("PUT", "/api/upgrade", payload);
2931
- let resolve = null;
2932
- let reject = null;
2933
- const promise = new Promise((res, rej) => {
2934
- resolve = res;
2935
- reject = rej;
2936
- });
2937
- const upgradeTimeout = setTimeout(
2938
- () => reject(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),
2939
- watchTimeoutMinutes * 60 * 1e3
2940
- );
2941
- socket.on("job-log", (msg) => logger[msg.type](`(remote log)> ${msg.msg}`));
2942
- socket.on("job-success", () => {
2943
- const elapsed = Date.now() - now;
2944
- const displayDuration = duration2(elapsed, { round: true });
2945
- logger.success(`Successfully upgraded instance in ${displayDuration}`);
2946
- clearTimeout(upgradeTimeout);
2947
- resolve();
2948
- });
2949
- socket.on("job-error", (errMsg) => reject(new Error(errMsg)));
2950
- socket.emit("join-job", jobID);
2951
- await promise;
2952
- }
2953
-
2954
- // src/validateManifest.ts
2955
- import sortBy3 from "lodash/sortBy";
2956
- function validateManifest(_manifest) {
2957
- const manifest = deserializeManifest_default(_manifest);
2958
- if (manifest.users) {
2959
- const allRegularUsers = sortBy3(
2960
- [...manifest.users.operators || [], ...manifest.users.admins || []],
2961
- (u) => u.device
2962
- );
2963
- if (allRegularUsers.length) {
2964
- allRegularUsers.forEach((u, i) => {
2965
- if (i > 0) {
2966
- const user = allRegularUsers[i];
2967
- if (!user) throw new Error(`Can't find user with index ${i}`);
2968
- const prevUser = allRegularUsers[i - 1];
2969
- if (!prevUser) throw new Error(`Can't find user with index ${i - 1}`);
2970
- if (user.device !== prevUser.device + 1) {
2971
- throw new Error("Users indexes are not contiguous");
2972
- }
2973
- } else {
2974
- if (u.device !== 10) {
2975
- throw new Error(`Users devices indexes must start at 10`);
2976
- }
2977
- }
2978
- });
2979
- }
2980
- }
2981
- if (manifest.groups) {
2982
- manifest.groups.forEach((group) => {
2983
- group.users.forEach((id) => {
2984
- if (!manifest.users || typeof id === "number" && (!manifest.users.operators || !manifest.users.operators.find((i) => i.device === id)) || typeof id === "string" && (!manifest.users.api || !manifest.users.api.find((i) => i.name === id)) && (!manifest.users.apiV2 || !manifest.users.apiV2.find((i) => i.name === id))) {
2985
- throw new Error(`Group ${group.name} is referring to unknown operator ${id}`);
2986
- }
2987
- });
2988
- });
2989
- }
2990
- if (manifest.accounts) {
2991
- manifest.accounts.forEach((account) => {
2992
- if (!("currency" in account) && !("contractAddress" in account)) {
2993
- throw new Error(
2994
- // @ts-ignore
2995
- `No currency or contractAddress in account "${account.name}"`
2996
- );
2997
- }
2998
- if ("currency" in account) {
2999
- try {
3000
- getCryptoCurrencyById(account.currency);
3001
- } catch (e) {
3002
- throw Error(`Invalid account currency: "${account.currency}"`);
3003
- }
3004
- }
3005
- if (account.rules && account.rules.length) {
3006
- account.rules.forEach((rule) => {
3007
- rule.forEach((r, j) => {
3008
- if (r.type === "MULTI_AUTHORIZATIONS") {
3009
- r.steps.forEach((step, i) => {
3010
- if ("group" in step && "users" in step) {
3011
- throw new Error(
3012
- `Can't have both 'users' and 'group' in step ${i + 1} of MULTI_AUTHORIZATIONS rule of rules set ${j + 1} of account "${account.name}"`
3013
- );
3014
- } else if ("group" in step) {
3015
- if (!manifest.groups || !manifest.groups.find((group) => group.name === step.group)) {
3016
- throw new Error(
3017
- `Account "${account.name}": Group "${step.group}" does not exist`
3018
- );
3019
- }
3020
- } else if ("users" in step) {
3021
- step.users.forEach((userId) => {
3022
- if (!manifest.users || typeof userId === "number" && (!manifest.users.operators || !manifest.users.operators.find((o) => o.device === userId)) || typeof userId === "string" && (!manifest.users.api || !manifest.users.api.find((o) => o.name === userId)) && (!manifest.users.apiV2 || !manifest.users.apiV2.find((o) => o.name === userId))) {
3023
- throw new Error(
3024
- `Account "${account.name}": User with id ${userId} does not exist`
3025
- );
3026
- }
3027
- });
3028
- } else {
3029
- throw new Error(
3030
- `No group or users in step ${i + 1} of MULTI_AUTHORIZATIONS rule of rules set ${j + 1} of account "${account.name}"`
3031
- );
3032
- }
3033
- });
3034
- }
3035
- if (r.type === "WHITELIST") {
3036
- r.whitelists.forEach((whitelist) => {
3037
- if (!manifest.whitelists || !manifest.whitelists.find((w) => w.name === whitelist)) {
3038
- throw new Error(
3039
- `Account "${account.name}": whitelist "${whitelist}" does not exist`
3040
- );
3041
- }
3042
- });
3043
- }
3044
- });
3045
- });
3046
- } else {
3047
- if (!manifest.users || !manifest.users.operators || !manifest.users.operators.length) {
3048
- throw new Error("Need at least 1 operator");
3049
- }
3050
- }
3051
- if (account.tradelink_data) {
3052
- const accountTradelinkData = account.tradelink_data;
3053
- if (!manifest.tradelink) {
3054
- throw new Error("Account has tradelink_data but manifest does not");
3055
- }
3056
- const { exchanges, assetManagers, custodians } = manifest.tradelink;
3057
- accountTradelinkData.exchanges.forEach((exchange) => {
3058
- if (!exchanges.find((e) => e.name === exchange.name)) {
3059
- throw new Error(
3060
- `Account "${account.name}": exchange "${exchange.name}" does not exist`
3061
- );
3062
- }
3063
- });
3064
- if (!assetManagers.find((e) => e.name === accountTradelinkData.asset_manager.name)) {
3065
- throw new Error(
3066
- `Account "${account.name}": asset manager "${account.tradelink_data.asset_manager.name}" does not exist`
3067
- );
3068
- }
3069
- if (!custodians.find((e) => e.name === accountTradelinkData.custodian.name)) {
3070
- throw new Error(
3071
- `Account "${account.name}": custodian "${account.tradelink_data.custodian.name}" does not exist`
3072
- );
3073
- }
3074
- }
3075
- });
3076
- }
3077
- if (manifest.entities) {
3078
- manifest.entities.forEach((entity) => {
3079
- entity.accounts?.forEach((accountName) => {
3080
- if (!manifest.accounts || !manifest.accounts.find(({ name }) => accountName === name)) {
3081
- throw new Error(
3082
- `Entity ${entity.name} is referring to an unknown account ${accountName}`
3083
- );
3084
- }
3085
- });
3086
- });
3087
- }
3088
- if (manifest.whitelists) {
3089
- manifest.whitelists.forEach((whitelist) => {
3090
- whitelist.addresses.map((address) => {
3091
- try {
3092
- getCryptoCurrencyById(address.currency);
3093
- } catch (e) {
3094
- throw Error(`Invalid whitelist currency: "${address.currency}"`);
3095
- }
3096
- });
3097
- });
3098
- }
3099
- if (manifest.tradelink) {
3100
- manifest.tradelink.exchanges.forEach((exchange) => {
3101
- const operators = exchange.users?.operators || [];
3102
- const apiV2 = exchange.users?.apiV2 || [];
3103
- if (!operators.length && !apiV2.length) {
3104
- throw new Error(`Exchange ${exchange.name} has no users`);
3105
- }
3106
- operators.forEach((operator) => {
3107
- if (!manifest.users?.operators?.find((o) => o.device === operator)) {
3108
- throw new Error(`Exchange ${exchange.name} refers to unknown operator ${operator}`);
3109
- }
3110
- });
3111
- apiV2.forEach((api) => {
3112
- if (!manifest.users?.apiV2?.find((o) => o.name === api)) {
3113
- throw new Error(`Exchange ${exchange.name} refers to unknown API User ${api}`);
3114
- }
3115
- });
3116
- });
3117
- manifest.tradelink.assetManagers.forEach((assetManager) => {
3118
- const operators = assetManager.users?.operators || [];
3119
- const apiV2 = assetManager.users?.apiV2 || [];
3120
- if (!operators.length && !apiV2.length) {
3121
- throw new Error(`Asset Manager ${assetManager.name} has no users`);
3122
- }
3123
- operators.forEach((operator) => {
3124
- if (!manifest.users?.operators?.find((o) => o.device === operator)) {
3125
- throw new Error(
3126
- `Asset Manager ${assetManager.name} refers to unknown operator ${operator}`
3127
- );
3128
- }
3129
- });
3130
- apiV2.forEach((api) => {
3131
- if (!manifest.users?.apiV2?.find((o) => o.name === api)) {
3132
- throw new Error(`Asset Manager ${assetManager.name} refers to unknown API User ${api}`);
3133
- }
3134
- });
3135
- });
3136
- manifest.tradelink.custodians.forEach((custodian) => {
3137
- const operators = custodian.users?.operators || [];
3138
- const apiV2 = custodian.users?.apiV2 || [];
3139
- if (!operators.length && !apiV2.length) {
3140
- throw new Error(`Custodian ${custodian.name} has no users`);
3141
- }
3142
- operators.forEach((operator) => {
3143
- if (!manifest.users?.operators?.find((o) => o.device === operator)) {
3144
- throw new Error(`Custodian ${custodian.name} refers to unknown operator ${operator}`);
3145
- }
3146
- });
3147
- apiV2.forEach((api) => {
3148
- if (!manifest.users?.apiV2?.find((o) => o.name === api)) {
3149
- throw new Error(`Custodian ${custodian.name} refers to unknown API User ${api}`);
3150
- }
3151
- });
3152
- });
3153
- }
3154
- }
3155
-
3156
- // src/wipeBackend.ts
3157
- import { SILENT_LOGGER as SILENT_LOGGER12 } from "@ledgerhq/vault-utils";
3158
- async function wipeBackend(wipeOpts, runnableOpts = {}) {
3159
- const { logger = SILENT_LOGGER12 } = runnableOpts;
3160
- if (!wipeOpts.hsmEndpoint) {
3161
- throw new Error("HSM endpoint is empty, please provide one");
3162
- }
3163
- logger.step("Wiping backend data");
3164
- const gateNetworkOptions = { baseURL: wipeOpts.gate };
3165
- const gateNetwork = createNetwork(gateNetworkOptions);
3166
- try {
3167
- await gateNetwork("POST", "/maintenance/wipe");
3168
- } catch {
3169
- logger.info(
3170
- "[WARN] POST for /maintenance/wipe did not worked, attempting legacy GET (see VG-8670)"
3171
- );
3172
- await gateNetwork("GET", "/maintenance/wipe");
3173
- logger.info("[WARN] legacy GET /maintenance/wipe successful");
3174
- }
3175
- if (wipeOpts.lam) {
3176
- const lamNetworkOptions = { baseURL: wipeOpts.lam };
3177
- const headersAPILam = wipeOpts.lamAPIKey ? { headers: { "X-Ledger-API-Key": wipeOpts.lamAPIKey } } : {};
3178
- const lamNetwork = createNetwork(lamNetworkOptions);
3179
- logger.step("Wiping LAM data");
3180
- try {
3181
- const lamWipeResponse = await lamNetwork(
3182
- "DELETE",
3183
- "/api_users/maintenance/wipe",
3184
- {},
3185
- headersAPILam
3186
- );
3187
- if (!lamWipeResponse) {
3188
- logger.info("[WARN] LAM wipe did not work as expected!");
3189
- }
3190
- } catch (err) {
3191
- logger.info(`Error while wiping the LAM: ${err.toString()}`);
3192
- }
3193
- }
3194
- logger.step("Resetting HSM compartment");
3195
- const hsmBridge = createHSMBridge_default(wipeOpts);
3196
- await hsmBridge.resetCompartment(wipeOpts.hsmCompartmentID, runnableOpts);
3197
- logger.success("Wiped backend data");
3198
- }
3199
- var wipeBackend_default = wipeBackend;
3200
- export {
3201
- DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,
3202
- DEFAULT_VAULT_REMOTE_URL,
3203
- GateGroupRequestTypeDefs,
3204
- LIGHT_EVM_CURRENCIES,
3205
- bakeManifest,
3206
- createAPIDevice_default as createAPIDevice,
3207
- createConfigCatEnvironment,
3208
- createDevicesPool_default as createDevicesPool,
3209
- createFaucet_default as createFaucet,
3210
- createInteractions,
3211
- createNetwork,
3212
- createPledge_default as createPledge,
3213
- createSettlement_default as createSettlement,
3214
- deleteConfigCatEnvironment,
3215
- deploy,
3216
- deserializeUnitValue,
3217
- destroy,
3218
- device_exports as device,
3219
- extractSecureChannel,
3220
- feesLevels,
3221
- fetchTokens_default as fetchTokens,
3222
- genSeed_default as genSeed,
3223
- getAccountTypeByCurrency,
3224
- getAccountUnit,
3225
- getAuthTokens,
3226
- getCryptoCurrencyById,
3227
- getCurrencyOrToken,
3228
- getCurrencyUnit,
3229
- getDefaultUsername,
3230
- getGateAccountUnit,
3231
- getMVInstances,
3232
- getTokenUnit,
3233
- getWorkspaceFromGate,
3234
- listCryptoCurrencies,
3235
- performRequest,
3236
- prepareRequest_default as prepareRequest,
3237
- queue,
3238
- recipeManifest,
3239
- reviewAPIRequest_default as reviewAPIRequest,
3240
- send_default as send,
3241
- serializeUnitValue,
3242
- setConfigCatFeatureFlagValues,
3243
- setDeviceAPIEndpoint,
3244
- unwrapConnection,
3245
- upgrade,
3246
- validateManifest,
3247
- vaultCoins,
3248
- wait,
3249
- wipeBackend_default as wipeBackend,
3250
- xpubToExtendedPubKey
3251
- };
3252
- //# sourceMappingURL=index.mjs.map