@ledgerhq/vault-common 2.4.4 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +11 -11
- package/CHANGELOG.md +6 -0
- package/lib/index.js +0 -10
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @ledgerhq/vault-common@2.4.
|
|
2
|
+
> @ledgerhq/vault-common@2.4.5 build /home/runner/work/vault-ts/vault-ts/packages/common
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/createHSMBridge.ts, src/index.ts, src/recipeManifest.ts, src/reviewAPIRequest.ts, src/utils.ts, src/crypto/utils.ts, src/types/index.ts
|
|
@@ -9,11 +9,9 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mCJS[39m Build start
|
|
12
|
-
[34mDTS[39m Build start
|
|
13
|
-
[32mCJS[39m [1mlib/createHSMBridge.js [22m[32m331.00 B[39m
|
|
14
12
|
[32mCJS[39m [1mlib/crypto/utils.js [22m[32m335.00 B[39m
|
|
13
|
+
[32mCJS[39m [1mlib/createHSMBridge.js [22m[32m331.00 B[39m
|
|
15
14
|
[32mCJS[39m [1mlib/types/index.js [22m[32m329.00 B[39m
|
|
16
|
-
[32mCJS[39m [1mlib/index.js [22m[32m121.00 KB[39m
|
|
17
15
|
[32mCJS[39m [1mlib/chunk-N6UGATIN.js [22m[32m3.59 KB[39m
|
|
18
16
|
[32mCJS[39m [1mlib/recipeManifest.js [22m[32m349.00 B[39m
|
|
19
17
|
[32mCJS[39m [1mlib/chunk-OZIREBYO.js [22m[32m17.80 KB[39m
|
|
@@ -21,14 +19,13 @@
|
|
|
21
19
|
[32mCJS[39m [1mlib/chunk-BLZRHILW.js [22m[32m37.63 KB[39m
|
|
22
20
|
[32mCJS[39m [1mlib/chunk-TZJ54LD2.js [22m[32m3.39 KB[39m
|
|
23
21
|
[32mCJS[39m [1mlib/utils.js [22m[32m1.20 KB[39m
|
|
24
|
-
[32mCJS[39m [1mlib/
|
|
22
|
+
[32mCJS[39m [1mlib/index.js [22m[32m119.40 KB[39m
|
|
25
23
|
[32mCJS[39m [1mlib/chunk-3L2XDBZ2.js [22m[32m8.65 KB[39m
|
|
24
|
+
[32mCJS[39m [1mlib/chunk-TBOMCYUR.js [22m[32m35.45 KB[39m
|
|
26
25
|
[32mCJS[39m [1mlib/chunk-IAJMQIH7.js [22m[32m396.00 B[39m
|
|
27
26
|
[32mCJS[39m [1mlib/chunk-PZ5AY32C.js [22m[32m314.00 B[39m
|
|
28
27
|
[32mCJS[39m [1mlib/createHSMBridge.js.map [22m[32m293.00 B[39m
|
|
29
28
|
[32mCJS[39m [1mlib/types/index.js.map [22m[32m269.00 B[39m
|
|
30
|
-
[32mCJS[39m [1mlib/crypto/utils.js.map [22m[32m281.00 B[39m
|
|
31
|
-
[32mCJS[39m [1mlib/index.js.map [22m[32m179.10 KB[39m
|
|
32
29
|
[32mCJS[39m [1mlib/chunk-N6UGATIN.js.map [22m[32m7.09 KB[39m
|
|
33
30
|
[32mCJS[39m [1mlib/recipeManifest.js.map [22m[32m288.00 B[39m
|
|
34
31
|
[32mCJS[39m [1mlib/chunk-OZIREBYO.js.map [22m[32m36.53 KB[39m
|
|
@@ -36,12 +33,15 @@
|
|
|
36
33
|
[32mCJS[39m [1mlib/chunk-BLZRHILW.js.map [22m[32m71.66 KB[39m
|
|
37
34
|
[32mCJS[39m [1mlib/chunk-TZJ54LD2.js.map [22m[32m5.19 KB[39m
|
|
38
35
|
[32mCJS[39m [1mlib/utils.js.map [22m[32m398.00 B[39m
|
|
39
|
-
[32mCJS[39m [1mlib/
|
|
36
|
+
[32mCJS[39m [1mlib/index.js.map [22m[32m178.14 KB[39m
|
|
40
37
|
[32mCJS[39m [1mlib/chunk-3L2XDBZ2.js.map [22m[32m20.30 KB[39m
|
|
41
|
-
[32mCJS[39m [1mlib/
|
|
38
|
+
[32mCJS[39m [1mlib/crypto/utils.js.map [22m[32m281.00 B[39m
|
|
39
|
+
[32mCJS[39m [1mlib/chunk-TBOMCYUR.js.map [22m[32m67.04 KB[39m
|
|
42
40
|
[32mCJS[39m [1mlib/chunk-IAJMQIH7.js.map [22m[32m52.69 KB[39m
|
|
43
|
-
[32mCJS[39m
|
|
44
|
-
[
|
|
41
|
+
[32mCJS[39m [1mlib/chunk-PZ5AY32C.js.map [22m[32m479.00 B[39m
|
|
42
|
+
[32mCJS[39m ⚡️ Build success in 833ms
|
|
43
|
+
[34mDTS[39m Build start
|
|
44
|
+
[32mDTS[39m ⚡️ Build success in 11708ms
|
|
45
45
|
[32mDTS[39m [1mlib/createHSMBridge.d.ts [22m[32m984.00 B[39m
|
|
46
46
|
[32mDTS[39m [1mlib/index.d.ts [22m[32m12.96 KB[39m
|
|
47
47
|
[32mDTS[39m [1mlib/reviewAPIRequest.d.ts [22m[32m836.00 B[39m
|
package/CHANGELOG.md
CHANGED
package/lib/index.js
CHANGED
|
@@ -315,16 +315,6 @@ async function bakeManifest(_manifest, pool, options = {}) {
|
|
|
315
315
|
},
|
|
316
316
|
logger
|
|
317
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
318
|
}
|
|
329
319
|
const runner = options.runner || _chunkBLZRHILWjs.createDefaultRunner_default.call(void 0, pool, options);
|
|
330
320
|
const manifestFromGate = await _chunkOZIREBYOjs.recipeManifest.call(void 0, pool, { saveAccountsIndexes: true });
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/vault-ts/vault-ts/packages/common/lib/index.js","../src/bakeManifest.ts","../src/deserializeManifest.ts","../src/fetchTokens.ts","../src/revault-compat.ts","../src/genSeed.ts","../src/utilsComparison.ts","../src/configcat.ts","../src/createDevicesPool.ts","../src/device/index.ts","../src/device/createAPIDevice.ts","../src/device/createHWDevice.ts","../src/device/constants.ts","../src/device/createInteractions.ts","../src/createFaucet.ts","../src/createPledge.ts","../src/createSettlement.ts","../src/deployInstance.ts","../src/constants.ts","../src/destroy.ts","../src/getMVInstances.ts","../src/send.ts","../src/upgradeInstance.ts","../src/validateManifest.ts","../src/wipeBackend.ts"],"names":["genSeed","SILENT_LOGGER","environment","registerData","psdModel","authenticate","ROLE_TO_BYTES","chalk","salt","invariant","interactions","network","notifierURL","duration","sortBy"],"mappings":"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","file":"/home/runner/work/vault-ts/vault-ts/packages/common/lib/index.js","sourcesContent":[null,"import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport chalk from \"chalk\";\nimport orderBy from \"lodash/orderBy\";\nimport sortBy from \"lodash/sortBy\";\n\nimport createDefaultRunner from \"./createDefaultRunner\";\nimport { genKeys } from \"./crypto/utils\";\nimport deserializeManifest from \"./deserializeManifest\";\nimport fetchTokens from \"./fetchTokens\";\nimport { UsersByDevice, UsersByName } from \"./prepareRequest\";\nimport recipeManifest, { GateAccountsByName, RecipeRawData } from \"./recipeManifest\";\nimport { onboardWithRevault } from \"./revault-compat\";\nimport {\n BakeManifestOptions,\n DevicesPool,\n GateTokenCurrency,\n GateTradelinkAssetManagerRequest,\n GateTradelinkExchangeRequest,\n GateUser,\n MRBakeTradelinkEntityOnboardingParams,\n MRBakeTradelinkEntityParams,\n Manifest,\n ManifestAPIUser,\n ManifestAPIV2User,\n ManifestAccount,\n ManifestContractApproval,\n ManifestExchange,\n ManifestGroup,\n ManifestPolicy,\n ManifestTradelink,\n ManifestTradelinkEntity,\n ManifestTradelinkEntityWithAddresses,\n ManifestVaultEntity,\n ManifestWhitelist,\n ManifestWorkspaceRule,\n UserWithDevice,\n} from \"./types\";\nimport { getDefaultUsername, queue } from \"./utils\";\nimport {\n areAccountsDifferent,\n areGroupsDifferent,\n areVaultEntitiesDifferent,\n areWhitelistsDifferent,\n areWorkspaceRulesDifferent,\n} from \"./utilsComparison\";\n\nconst DEFAULT_QUORUM = 2;\n\ntype UserRoleAndDevice = { role: \"admin\" | \"operator\"; device: number };\n\nexport default async function bakeManifest(\n _manifest: Manifest,\n pool: DevicesPool,\n options: BakeManifestOptions = {},\n): Promise<void> {\n const { logger = SILENT_LOGGER, waitForActive } = options;\n const manifest = deserializeManifest(_manifest);\n\n // VG-12511 ability to define salt directly from manifest\n if (_manifest.salt) {\n pool.setSalt(_manifest.salt);\n }\n\n /* istanbul ignore if */\n if (options.revaultOnboarding) {\n logger.step(\"[REVAULT] Onboarding\");\n await onboardWithRevault(\n {\n ...pool.getRevaultCompatOptions(),\n ...options.revaultOnboarding,\n },\n logger,\n );\n } else {\n logger.info(\"╔══════════════════════════════════════════════════════════════════════╗\");\n logger.info(\"║ DEPRECATION NOTICE - LEGACY ONBOARDING HAS BEEN DECOMMISSIONED ║\");\n logger.info(\"║ ║\");\n logger.info(\"║ Onboarding skipped, as the new onboarding options were not provided. ║\");\n logger.info(\"║ Please refer to changelog & announcements to see how you can adapt ║\");\n logger.info(\"║ your code or command. ║\");\n logger.info(\"║ ║\");\n logger.info(\"║ See v2.0 release of vault-cli/vault-common on GitHub. ║\");\n logger.info(\"╚══════════════════════════════════════════════════════════════════════╝\");\n }\n\n const runner = options.runner || createDefaultRunner(pool, options);\n\n // build a manifest from the current gate, in order to check\n // if stuff needs to be EDITED or not\n const manifestFromGate = await recipeManifest(pool, { saveAccountsIndexes: true });\n\n const { rawData } = manifestFromGate;\n const {\n groupsByName,\n whitelistsByName,\n accountsByName,\n usersWithDevice,\n usersWithoutDevice,\n vaultEntitiesByName,\n exchangesByName,\n policiesByName,\n tradelinkCustodiansByName,\n tradelinkHSMCustodiansByName,\n tradelinkExchangesByName,\n tradelinkHSMExchangesByName,\n tradelinkHSMAssetManagersByName,\n tradelinkAssetManagersByName,\n tradelinkOnboardingStatus,\n }: RecipeRawData = rawData;\n const gateManifest = manifestFromGate.manifest;\n\n const __usersByDevice: UsersByDevice = {\n ...usersWithDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.deviceIndex]: curr.user } };\n }, {}),\n };\n\n const __usersByName: UsersByName = {\n ...usersWithDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.user.username]: curr.user } };\n }, {}),\n ...usersWithoutDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.user.username]: curr.user } };\n }, {}),\n };\n\n const __groupsIDsByName: Record<string, number> = {\n ...Object.keys(groupsByName).reduce((acc, curr) => {\n const group = groupsByName[curr];\n /* istanbul ignore if */\n if (!group) return acc;\n return { ...acc, ...{ [curr]: group.id } };\n }, {}),\n };\n const __whitelistsIDsByName: Record<string, number> = {\n ...Object.keys(whitelistsByName).reduce((acc, curr) => {\n const whitelist = whitelistsByName[curr];\n /* istanbul ignore if */\n if (!whitelist) return acc;\n return { ...acc, ...{ [curr]: whitelist.id } };\n }, {}),\n };\n const __policiesIDsByName: Record<string, number> = {\n ...Object.keys(policiesByName).reduce((acc, curr) => {\n const policy = policiesByName[curr];\n /* istanbul ignore if */\n if (!policy) return acc;\n return { ...acc, ...{ [curr]: policy.id } };\n }, {}),\n };\n const __hsmAMsIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMAssetManagersByName).reduce((acc, curr) => {\n const am = tradelinkHSMAssetManagersByName[curr];\n /* istanbul ignore if */\n if (!am) return acc;\n return { ...acc, ...{ [curr]: am.id } };\n }, {}),\n };\n const __hsmExchangesIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMExchangesByName).reduce((acc, curr) => {\n const exchange = tradelinkHSMExchangesByName[curr];\n /* istanbul ignore if */\n if (!exchange) return acc;\n return { ...acc, ...{ [curr]: exchange.id } };\n }, {}),\n };\n const __hsmCustodiansIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMCustodiansByName).reduce((acc, curr) => {\n const custodian = tradelinkHSMCustodiansByName[curr];\n /* istanbul ignore if */\n if (!custodian) return acc;\n return { ...acc, ...{ [curr]: custodian.id } };\n }, {}),\n };\n\n const __accountsByName = { ...accountsByName };\n let tokens: GateTokenCurrency[] = [];\n\n // load tokens if there is at least 1 token account described in manifest\n const shouldLoadTokens =\n manifest.accounts && manifest.accounts.find((account) => \"contractAddress\" in account);\n\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n if (manifest.users) {\n const adminsAndOperators: UserRoleAndDevice[] = [];\n if (manifest.users.admins) {\n adminsAndOperators.push(\n ...manifest.users.admins.map(\n (a): UserRoleAndDevice => ({ role: \"admin\", device: a.device }),\n ),\n );\n }\n\n // FIXME OMG ISTANBUL THERE IS NO ELSE SO WTF?\n /* istanbul ignore else */\n if (manifest.users.operators) {\n adminsAndOperators.push(\n ...manifest.users.operators.map(\n (a): UserRoleAndDevice => ({ role: \"operator\", device: a.device }),\n ),\n );\n }\n\n await queue(\n sortBy(adminsAndOperators, (u) => u.device),\n genCreateAdminsAndOperators(),\n );\n\n /* istanbul ignore else */\n if (manifest.users.api) {\n logger.step(\"LAM users creation\");\n await queue(manifest.users.api, genCreateAPIUser());\n }\n\n if (manifest.users.apiV2) {\n logger.step(\"API users creation\");\n await queue(manifest.users.apiV2, genCreateAPIV2User());\n }\n }\n\n if (manifest.workspaceRules) {\n logger.step(\"Workspace rules creation\");\n await queue(manifest.workspaceRules, createWorkspaceRule);\n }\n\n if (manifest.groups) {\n logger.step(\"Groups\");\n await queue(manifest.groups, createGroup);\n }\n\n if (manifest.whitelists) {\n logger.step(\"Whitelists\");\n await queue(manifest.whitelists, createWhitelist);\n }\n\n if (manifest.tradelink) {\n logger.step(\"TradeLink\");\n\n // approve TRADELINK contracts\n const adminDevices = await pool.getOnboardingAdminDevices();\n await queue(\n adminDevices.map((a) => ({ user: a[1], contractNames: [\"TRADELINK_ADDENDUM\", \"TRADELINK\"] })),\n approveContracts,\n );\n\n await queue(manifest.tradelink.custodians, createTradelinkCustodian);\n await queue(manifest.tradelink.assetManagers, createTradelinkAssetManager);\n await queue(manifest.tradelink.exchanges, createTradelinkExchange);\n await createTradelinkNetwork(manifest.tradelink);\n if (manifest.tradelink.onboarded) {\n await onboardTradelinkNetwork(manifest.tradelink);\n await queue(manifest.tradelink.exchanges, (exchange) =>\n onboardTradelinkEntity(exchange, \"exchanges\"),\n );\n await queue(manifest.tradelink.assetManagers, (am) =>\n onboardTradelinkEntity(am, \"asset_managers\"),\n );\n }\n }\n\n if (manifest.policies) {\n logger.step(\"Policies\");\n await queue(manifest.policies, createVaultPolicy);\n }\n\n if (manifest.accounts) {\n logger.step(\"Accounts\");\n manifest.accounts = orderBy(manifest.accounts, \"contractAddress\", \"desc\");\n await queue(manifest.accounts, createAccount);\n }\n\n if (manifest.entities) {\n logger.step(\"Entities\");\n await queue(manifest.entities, createVaultEntity);\n }\n\n if (manifest.exchanges) {\n logger.step(\"Exchanges\");\n await queue(manifest.exchanges, createVaultExchange);\n }\n\n if (manifest.quorum) {\n logger.step(\"Quorum\");\n await editQuorum(manifest.quorum);\n }\n\n if (manifest.contractApprovals) {\n logger.step(\"Contract Approvals\");\n await queue(manifest.contractApprovals, approveContracts);\n }\n\n logger.success(\"Done\");\n\n async function editQuorum(quorum: number) {\n if (gateManifest.quorum === quorum || (quorum === DEFAULT_QUORUM && !gateManifest.quorum)) {\n logger.info(`Quorum is already set to ${quorum}, skipping`);\n return;\n }\n await runner.editQuorum({ quorum });\n logger.info(`Quorum updated to ${quorum}`);\n }\n\n function genCreateAdminsAndOperators() {\n return async function createGenericUser(user: UserRoleAndDevice) {\n const fn = genCreateUser(user.role);\n return fn(user.device);\n };\n }\n\n async function setViewAll(username: string, gateUserId: number, viewAll: boolean) {\n logger.info(`Setting view-all permission to ${viewAll} for API user ${username}...`);\n const admin = await pool.login(4);\n await admin.network(\"PUT\", `/people/${gateUserId}`, { view_all_override: viewAll });\n logger.info(`API user ${username} has now view-all permission set to ${viewAll}`);\n }\n\n function genCreateAPIV2User() {\n return async function createAPIV2User(apiV2User: ManifestAPIV2User) {\n const username: string = apiV2User.name;\n const existingUser = usersWithoutDevice.find((uwd) => uwd.user.username === username);\n if (existingUser) {\n if ([\"APPROVED\", \"ACTIVE\"].includes(existingUser.user.status)) {\n logger.info(`Skipping registration of API user ${username}`);\n __usersByName[existingUser.user.username] = existingUser.user;\n\n // eventually update the `view_all_override` flag\n const targetViewAll = !!apiV2User.viewAll;\n const existingViewAll = !!existingUser.user.view_all_override;\n const shouldUpdateViewAll = targetViewAll !== existingViewAll;\n if (shouldUpdateViewAll) {\n await setViewAll(username, existingUser.user.id, targetViewAll);\n }\n return;\n }\n }\n const params = {\n user: apiV2User,\n name: username,\n publicKey: apiV2User.publicKey ?? genKeys(username).hexPubKey,\n role: apiV2User.role,\n };\n const userRequest = await runner.createAPIV2User(params);\n const userAccessRequest = await runner.createAPIV2UserAccess(params);\n\n __usersByName[userRequest.user.username] = userRequest.user;\n logger.info(`(+) Created API user: ${username}`);\n\n // VG-18321 workaround inability to assign \"view all\" permission directly on creation\n if (apiV2User.viewAll) {\n await setViewAll(username, userRequest.user.id, true);\n }\n\n // log api key id and secret used for API user authentication\n logger.info(\n chalk`{red.bold IMPORTANT:} {red The API user credentials will not be displayed again so note them somewhere}`,\n );\n logger.info(\n JSON.stringify({\n api_key_id: userAccessRequest.api_key_id,\n api_key_secret: userAccessRequest.api_key_secret,\n }),\n );\n };\n }\n\n function genCreateAPIUser() {\n return async function createUser(apiUser: ManifestAPIUser) {\n const username: string = apiUser.name;\n const existingUser = usersWithDevice.find((ud) => ud.user.username === username);\n\n if (existingUser) {\n if (existingUser.user.status === \"ACTIVE\") {\n logger.info(`Skipping registration of LAM ${username}`);\n __usersByName[existingUser.user.username] = existingUser.user;\n return;\n }\n }\n const { device_id } = await pool.lamAPI.createInvitation(username);\n const params = { user: apiUser, userID: device_id, name: username };\n const request = await runner.createAPIUser(params, manifestFromGate);\n\n __usersByName[request.user.username] = request.user;\n logger.info(`(+) Created LAM api user: ${username}`);\n };\n }\n\n function genCreateUser(role: \"admin\" | \"operator\") {\n return async function createUser(device: number) {\n const name =\n (manifest.customUsernames && manifest.customUsernames[device]) ||\n getDefaultUsername(role, device);\n\n // find if there is already a user with this device\n const userID = await pool.getUserID(device);\n const findByUserID = (ud: UserWithDevice) => ud.user.user_id === userID;\n const existingUser = usersWithDevice.find(findByUserID);\n if (existingUser) {\n logger.info(`Skipping registration of ${role} ${device}`);\n __usersByDevice[device] = existingUser.user;\n __usersByName[existingUser.user.username] = existingUser.user;\n return;\n }\n\n const createUserParams = { role, userID, name, device };\n const request = await runner.createUser(createUserParams);\n\n __usersByDevice[device] = request.user;\n __usersByName[request.user.username] = request.user;\n\n logger.info(`(+) Created ${name}`);\n };\n }\n\n async function createWorkspaceRule(rule: ManifestWorkspaceRule) {\n const existingRule = gateManifest.workspaceRules?.find((r) => r.permission === rule.permission);\n if (!existingRule || areWorkspaceRulesDifferent(rule, existingRule)) {\n // we assume there will always be exactly one step\n const step = rule.steps[0]!;\n logger.info(\n `Configuring workspace rule ${rule.permission} with ${step.quorum} approvals out of ${step.users.length} user(s)`,\n );\n await runner.editWorkspaceRule({ rule, usersByName: __usersByName });\n } else {\n logger.info(`Skipping edition of workspace rule ${rule.permission}`);\n }\n }\n\n async function createGroup(group: ManifestGroup) {\n // find if there is already a group with same name\n let isEdit = false;\n const existingGroup = groupsByName[group.name];\n\n const isApprovingPendingGroup =\n existingGroup && existingGroup.status === \"PENDING\" && group.status !== \"PENDING\";\n\n if (existingGroup) {\n if (existingGroup && existingGroup.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.groups) {\n throw new Error(`No groups in gate manifest`);\n }\n const groupFromGateManifest = gateManifest.groups.find((g) => g.name === group.name);\n /* istanbul ignore if */\n if (!groupFromGateManifest) {\n throw new Error(`Can't find group ${group.name}`);\n }\n isEdit = areGroupsDifferent(groupFromGateManifest, group);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingGroup) {\n logger.info(`Skipping creation of group ${group.name}`);\n return;\n }\n }\n\n const data = {\n group,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n existingGroup,\n };\n\n const existingRequest =\n (!!isApprovingPendingGroup && !!existingGroup && existingGroup.last_request) || null;\n\n const noApproval = shouldNotApprove(group) || shouldRejectRequest(group);\n\n const bakeGroupParams = {\n group,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = isEdit\n ? await runner.editGroup(bakeGroupParams)\n : await runner.createGroup(bakeGroupParams);\n\n __groupsIDsByName[group.name] = (existingGroup && existingGroup.id) || request.group.id;\n\n // TODO: runner implementation\n if (shouldRejectRequest(group)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected group ${group.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} group ${group.name}`);\n }\n }\n\n async function createTradelinkCustodian(tradelinkEntity: ManifestTradelinkEntity) {\n const existingCustodian = tradelinkCustodiansByName[tradelinkEntity.name];\n if (existingCustodian) {\n logger.info(`Skipping creation of tradelink custodian ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"custodians\",\n };\n await runner.createTradelinkEntity<\"CUSTODIAN\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink custodian ${tradelinkEntity.name}`);\n }\n\n async function approveContracts(approval: ManifestContractApproval) {\n const { contractNames, user: deviceIndex } = approval;\n const user = await pool.login(deviceIndex);\n try {\n const unapproved = await user.get<{ contract_name: string; contract_version: string }[]>(\n \"/contracts/unapproved\",\n );\n const contracts = unapproved.filter((c) => contractNames.includes(c.contract_name));\n const promises = contracts.map((c) => {\n logger.info(`Approving contract \"${c.contract_name}\" with user ${deviceIndex}`);\n return user.post(\"/contracts/approve\", c);\n });\n await Promise.all(promises);\n } catch (e) {\n logger.error(`Error approving contracts by user ${deviceIndex}`, e);\n }\n }\n\n async function createTradelinkExchange(tradelinkEntity: ManifestTradelinkEntityWithAddresses) {\n const existingExchange = tradelinkExchangesByName[tradelinkEntity.name];\n if (existingExchange) {\n logger.info(`Skipping creation of tradelink exchange ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"exchanges\",\n };\n await runner.createTradelinkEntity<\"EXCHANGE\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink exchange ${tradelinkEntity.name}`);\n }\n\n async function createTradelinkAssetManager(\n tradelinkEntity: ManifestTradelinkEntityWithAddresses,\n ) {\n const existingCustodian = tradelinkAssetManagersByName[tradelinkEntity.name];\n if (existingCustodian) {\n logger.info(`Skipping creation of tradelink asset manager ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"asset_managers\",\n };\n await runner.createTradelinkEntity<\"ASSET_MANAGER\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink asset manager ${tradelinkEntity.name}`);\n }\n\n function formatTradelinkNetworkParams(\n manifestTLEntity: ManifestTradelinkEntityWithAddresses | ManifestTradelinkEntity,\n ): { id: string; name: string; operators: string[] } {\n const operators: string[] = [];\n if (manifestTLEntity.users.operators) {\n manifestTLEntity.users.operators.forEach((operator) => {\n const user = __usersByDevice[operator];\n if (user) operators.push(user.pub_key);\n });\n }\n if (manifestTLEntity.users.apiV2) {\n manifestTLEntity.users.apiV2.forEach((apiV2) => {\n const user = __usersByName[apiV2];\n if (user) operators.push(user.pub_key);\n });\n }\n\n return {\n id: manifestTLEntity.id,\n name: manifestTLEntity.name,\n operators,\n };\n }\n\n function formatTradelinkNetworkParamsWithAddresses(\n manifestTLEntity: ManifestTradelinkEntityWithAddresses,\n ) {\n const data = formatTradelinkNetworkParams(manifestTLEntity);\n return {\n ...data,\n addresses: manifestTLEntity.addresses,\n };\n }\n\n async function createTradelinkNetwork(tradelink: ManifestTradelink) {\n const custodians = tradelink.custodians.map((custodian) => {\n return formatTradelinkNetworkParams(custodian);\n });\n const exchanges = tradelink.exchanges.map((exchange) => {\n return formatTradelinkNetworkParamsWithAddresses(exchange);\n });\n const assetManagers = tradelink.assetManagers.map((am) => {\n return formatTradelinkNetworkParamsWithAddresses(am);\n });\n\n /* istanbul ignore if */\n if (custodians.length === 0) {\n throw new Error(`Custodian ${tradelink.custodians} not found`);\n }\n\n const bakeTLNetworkParams = { custodians, exchanges, assetManagers };\n await runner.createTradelinkNetwork(bakeTLNetworkParams);\n logger.info(`(+) Created Tradelink network`);\n }\n\n async function onboardTradelinkNetwork(tradelink: ManifestTradelink) {\n if (\n tradelinkOnboardingStatus.status == \"CUSTODIAN_ONBOARDED\" ||\n tradelinkOnboardingStatus.status == \"EXCHANGE_ONBOARDED\" ||\n tradelinkOnboardingStatus.status == \"HSM_READY\"\n ) {\n logger.info(`Skipping onboarding of custodian`);\n return;\n }\n\n const custodian = tradelink.custodians.map((custodian) => {\n return formatTradelinkNetworkParams(custodian);\n })[0];\n\n /* istanbul ignore if */\n if (!custodian) {\n throw new Error(`Custodian ${tradelink.custodians} not found`);\n }\n\n // Today the request is create tradelink as we can not pass any custodian\n const request = await runner.createTradelink({\n type: \"CREATE_TRADELINK\",\n operators: custodian.operators,\n });\n __hsmCustodiansIDsByName[custodian.name] = request.tradelink.id;\n\n logger.info(`(+) ${custodian.name} Tradelink custodian Onboarded`);\n }\n\n async function onboardTradelinkEntity(\n entity: ManifestTradelinkEntityWithAddresses,\n entityType: \"asset_managers\" | \"exchanges\",\n ) {\n if (tradelinkHSMAssetManagersByName[entity.name]?.status === \"ACTIVE\") {\n logger.info(`Skipping onboarding of asset manager ${entity.name}`);\n return;\n }\n if (tradelinkHSMExchangesByName[entity.name]?.status === \"ACTIVE\") {\n logger.info(`Skipping onboarding of exchange ${entity.name}`);\n return;\n }\n\n let approver: GateUser | undefined;\n if (entity.users.apiV2 && entity.users.apiV2.length > 0) {\n const userName = entity.users.apiV2[0] as string;\n approver = __usersByName[userName];\n }\n /* istanbul ignore if */\n if (!approver) {\n throw new Error(`No approver found for ${entity.name}`);\n }\n\n const { name, operators, addresses } = formatTradelinkNetworkParamsWithAddresses(entity);\n\n const bakeParams: MRBakeTradelinkEntityOnboardingParams = {\n tradelinkEntity: { name, operators, addresses },\n type: entityType,\n tradelinkEntityApprover: { name: approver.username, role: approver.role },\n };\n const request = await runner.onboardTradelinkEntity(bakeParams);\n if (entityType === \"asset_managers\")\n __hsmAMsIDsByName[entity.name] = (\n request as GateTradelinkAssetManagerRequest\n ).tradelink_asset_manager.id;\n else\n __hsmExchangesIDsByName[entity.name] = (\n request as GateTradelinkExchangeRequest\n ).tradelink_exchange.id;\n\n logger.info(`(+) ${entity.name} Tradelink ${entityType} Onboarded`);\n }\n\n async function createWhitelist(whitelist: ManifestWhitelist) {\n // find if there is already a whitelist with same name\n let isEdit = false;\n const existingWhitelist = whitelistsByName[whitelist.name];\n\n const isApprovingPendingWhitelist =\n existingWhitelist && existingWhitelist.status === \"PENDING\" && whitelist.status !== \"PENDING\";\n\n if (existingWhitelist && existingWhitelist.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.whitelists) {\n throw new Error(`No whitelists in gate manifest`);\n }\n const whitelistFromGateManifest = gateManifest.whitelists.find(\n (w) => w.name === whitelist.name,\n );\n /* istanbul ignore if */\n if (!whitelistFromGateManifest) {\n throw new Error(`Can't find whitelist ${whitelist.name}`);\n }\n isEdit = areWhitelistsDifferent(whitelistFromGateManifest, whitelist);\n\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingWhitelist) {\n logger.info(`Skipping creation of whitelist ${whitelist.name}`);\n return;\n }\n }\n\n const noApproval = shouldNotApprove(whitelist) || shouldRejectRequest(whitelist);\n\n const existingRequest =\n (!!isApprovingPendingWhitelist && !!existingWhitelist && existingWhitelist.last_request) ||\n null;\n\n const data = { whitelist, existingWhitelist };\n const bakeWLParams = { whitelist, data, existingRequest, noApproval };\n\n const request = isEdit\n ? await runner.editWhitelist(bakeWLParams)\n : await runner.createWhitelist(bakeWLParams);\n\n __whitelistsIDsByName[whitelist.name] =\n (existingWhitelist && existingWhitelist.id) || request.whitelist.id;\n\n // TODO: runner implementation\n if (shouldRejectRequest(whitelist)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected whitelist ${whitelist.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} whitelist ${whitelist.name}`);\n }\n }\n\n function getTradelinkAPIUserFromAssetManager(\n entity: ManifestTradelinkEntityWithAddresses,\n ): ManifestAPIV2User {\n /* istanbul ignore next */\n const entityAM = manifest.tradelink?.assetManagers.filter((am) => am.name === entity.name)[0];\n\n /* istanbul ignore if */\n if (!entityAM) {\n throw new Error(`AssetManager ${entity.name} not found`);\n }\n\n const approverUserName = entityAM.users.apiV2[0];\n\n /* istanbul ignore if */\n if (!approverUserName) {\n throw new Error(`No approver found for ${entity.name}`);\n }\n\n /* istanbul ignore if */\n const user = __usersByName[approverUserName];\n\n /* istanbul ignore if */\n if (!user) {\n throw new Error(`User ${approverUserName} not found`);\n }\n\n return { name: user.username, role: user.role };\n }\n\n async function createAccount(accountDesc: ManifestAccount) {\n let isEdit = false;\n const existingAccount =\n __accountsByName[accountDesc.name] ||\n findAccountByCurrencyAndIndex({ accountDesc, __accountsByName }) ||\n findExistingChildrenAccount({ accountDesc, __accountsByName });\n\n const isApprovingPendingAccount =\n existingAccount && existingAccount.status === \"PENDING\" && accountDesc.status !== \"PENDING\";\n\n if (existingAccount) {\n // special case when targeted account is VIEW_ONLY: it does not\n // appear in manifest file, but we can assume what we want is to\n // edit it\n if (existingAccount.status === \"VIEW_ONLY\") {\n isEdit = true;\n } else if (existingAccount.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.accounts) {\n throw new Error(\"No accounts in gate manifest\");\n }\n const accountFromGateManifest = gateManifest.accounts.find(\n (a) =>\n a.name === accountDesc.name ||\n (\"currency\" in accountDesc &&\n \"index\" in accountDesc &&\n \"currency\" in a &&\n \"index\" in a &&\n accountDesc.currency === a.currency &&\n accountDesc.index === a.index &&\n ((!(\"contractAddress\" in accountDesc) && !(\"contractAddress\" in a)) ||\n (\"contractAddress\" in accountDesc &&\n \"contractAddress\" in a &&\n accountDesc.contractAddress === a.contractAddress))),\n );\n /* istanbul ignore if */\n if (!accountFromGateManifest) {\n throw new Error(`Can't find account ${accountDesc.name}`);\n }\n isEdit = areAccountsDifferent(accountFromGateManifest, accountDesc);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingAccount) {\n logger.info(`Skipping creation of account ${accountDesc.name}`);\n return;\n }\n }\n\n const data = {\n account: accountDesc,\n existingAccount,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n groupsIDsByName: __groupsIDsByName,\n whitelistsIDsByName: __whitelistsIDsByName,\n accountsByName: __accountsByName,\n hsmAssetManagersIDsByName: __hsmAMsIDsByName,\n hsmExchangesIDsByName: __hsmExchangesIDsByName,\n hsmCustodiansIDsByName: __hsmCustodiansIDsByName,\n policiesIDsByName: __policiesIDsByName,\n tokens,\n };\n\n const existingRequest =\n (isApprovingPendingAccount && !!existingAccount && existingAccount.last_request) || null;\n\n const noApproval = shouldNotApprove(accountDesc) || shouldRejectRequest(accountDesc);\n\n // if tradelink account with HMS needs to be activated\n let tradelinkAM: ManifestAPIV2User | undefined;\n if (\"tradelink\" in manifest && \"tradelink_data\" in accountDesc && accountDesc.tradelink_data) {\n // get one asset manager\n /* istanbul ignore next */\n const assetManager = manifest.tradelink?.assetManagers[0];\n /* istanbul ignore if */\n if (!assetManager) {\n throw new Error(`AssetManager not found`);\n }\n tradelinkAM = getTradelinkAPIUserFromAssetManager(assetManager);\n }\n\n const bakeAccountParams = {\n account: accountDesc,\n data,\n existingRequest,\n noApproval,\n waitForActive,\n tradelinkAM,\n };\n\n const request = isEdit\n ? await runner.editAccount(bakeAccountParams)\n : await runner.createAccount(bakeAccountParams);\n\n __accountsByName[accountDesc.name] = request.account;\n\n // TODO: runner implementation\n if (shouldRejectRequest(accountDesc)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected account ${accountDesc.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} account ${accountDesc.name}`);\n }\n\n // account configuration\n if (\"config\" in accountDesc && !!accountDesc.config) {\n const { config } = accountDesc;\n const admin = await pool.login(4);\n const account = __accountsByName[accountDesc.name]!;\n if (config.nftGallery) {\n await admin.post(`/accounts/${account.id}/nfts/activate`, {});\n logger.info(`(+) Enabled NFT Gallery on ${accountDesc.name}`);\n }\n }\n }\n\n async function createVaultEntity(vaultEntity: ManifestVaultEntity) {\n // find if there is already a vault entity with same name\n let isEdit = false;\n const existingVaultEntity = vaultEntitiesByName[vaultEntity.name];\n\n const isApprovingPendingVaultEntity =\n existingVaultEntity &&\n existingVaultEntity.status === \"PENDING\" &&\n vaultEntity.status !== \"PENDING\";\n\n if (existingVaultEntity) {\n if (existingVaultEntity && existingVaultEntity.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.entities) {\n throw new Error(`No entities in gate manifest`);\n }\n const vaultEntityFromGateManifest = gateManifest.entities.find(\n (e) => e.name === vaultEntity.name,\n );\n /* istanbul ignore if */\n if (!vaultEntityFromGateManifest) {\n throw new Error(`Can't find entity ${vaultEntity.name}`);\n }\n isEdit = areVaultEntitiesDifferent(vaultEntityFromGateManifest, vaultEntity);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingVaultEntity) {\n logger.info(`Skipping creation of entity ${vaultEntity.name}`);\n return;\n }\n }\n\n const data = {\n vaultEntity,\n accountsByName: __accountsByName,\n existingVaultEntity,\n };\n\n const existingRequest =\n (!!isApprovingPendingVaultEntity &&\n !!existingVaultEntity &&\n existingVaultEntity.last_request) ||\n null;\n\n const noApproval = shouldNotApprove(vaultEntity) || shouldRejectRequest(vaultEntity);\n\n const bakeVaultEntityParams = {\n vaultEntity,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = isEdit\n ? await runner.editVaultEntity(bakeVaultEntityParams)\n : await runner.createVaultEntity(bakeVaultEntityParams);\n\n // TODO: runner implementation\n if (shouldRejectRequest(vaultEntity)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected entity ${vaultEntity.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} entity ${vaultEntity.name}`);\n }\n }\n\n async function createVaultExchange(exchange: ManifestExchange) {\n const existingExchange = exchangesByName[exchange.name];\n\n const isApprovingPendingExchange =\n existingExchange && existingExchange.status === \"PENDING\" && exchange.status !== \"PENDING\";\n\n if (existingExchange) {\n if (existingExchange && existingExchange.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.exchanges) {\n throw new Error(`No exchanges in gate manifest`);\n }\n const exchangeFromGateManifest = gateManifest.exchanges.find(\n (e) => e.name === exchange.name,\n );\n /* istanbul ignore if */\n if (!exchangeFromGateManifest) {\n throw new Error(`Can't find exchange ${exchange.name}`);\n }\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isApprovingPendingExchange) {\n logger.info(`Skipping creation of exchange ${exchange.name}`);\n return;\n }\n }\n\n const data = {\n exchange,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n groupsIDsByName: __groupsIDsByName,\n existingExchange,\n };\n\n const existingRequest =\n (!!isApprovingPendingExchange && !!existingExchange && existingExchange.last_request) || null;\n\n const noApproval = shouldNotApprove(exchange) || shouldRejectRequest(exchange);\n\n const bakeExchangeParams = {\n exchange,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = await runner.createExchange(bakeExchangeParams);\n\n if (shouldRejectRequest(exchange)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected exchange ${exchange.name}`);\n } else {\n logger.info(`(+) Created exchange ${exchange.name}`);\n }\n }\n\n async function createVaultPolicy(policy: ManifestPolicy) {\n const existingPolicy = policiesByName[policy.name];\n\n const isApprovingPendingPolicy =\n existingPolicy && existingPolicy.status === \"PENDING\" && policy.status !== \"PENDING\";\n\n if (existingPolicy) {\n if (existingPolicy && existingPolicy.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.policies) {\n throw new Error(`No policies in gate manifest`);\n }\n const policyFromGateManifest = gateManifest.policies.find((e) => e.name === policy.name);\n /* istanbul ignore if */\n if (!policyFromGateManifest) {\n throw new Error(`Can't find policy ${policy.name}`);\n }\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isApprovingPendingPolicy) {\n logger.info(`Skipping creation of policy ${policy.name}`);\n return;\n }\n }\n\n const data = {\n policy,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n whitelistsIDsByName: __whitelistsIDsByName,\n groupsIDsByName: __groupsIDsByName,\n existingPolicy,\n };\n\n const existingRequest =\n (!!isApprovingPendingPolicy && !!existingPolicy && existingPolicy.last_request) || null;\n\n const noApproval = shouldNotApprove(policy) || shouldRejectRequest(policy);\n\n const bakePolicyParams = {\n policy,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = await runner.createPolicy(bakePolicyParams);\n\n if (shouldRejectRequest(policy)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected policy ${policy.name}`);\n } else {\n logger.info(`(+) Created policy ${policy.name}`);\n __policiesIDsByName[policy.name] = request.policy.id;\n }\n }\n}\n\nconst shouldNotApprove = (\n manifestEntity:\n | ManifestGroup\n | ManifestWhitelist\n | ManifestAccount\n | ManifestVaultEntity\n | ManifestExchange\n | ManifestPolicy,\n) => manifestEntity.status === \"PENDING\";\n\nconst shouldRejectRequest = (\n manifestEntity:\n | ManifestGroup\n | ManifestWhitelist\n | ManifestAccount\n | ManifestVaultEntity\n | ManifestExchange\n | ManifestPolicy,\n) => manifestEntity.status === \"ABORTED\";\n\nconst findExistingChildrenAccount = ({\n accountDesc,\n __accountsByName,\n}: {\n accountDesc: ManifestAccount;\n __accountsByName: GateAccountsByName;\n}) => {\n if (!(\"parentAccount\" in accountDesc) || !(\"contractAddress\" in accountDesc)) return;\n const accounts = Object.keys(__accountsByName)\n .map((name) => {\n return __accountsByName[name];\n })\n .filter(Boolean);\n const parent = accounts.find((a) => !!a && a.name === accountDesc.parentAccount);\n if (!parent) return;\n\n const children = accounts.filter((a) => !!a && a.parent === parent.id);\n /* istanbul ignore if */\n if (children.length === 0) return;\n\n return children.find((c) => !!c && c.contract_address === accountDesc.contractAddress);\n};\n\nconst findAccountByCurrencyAndIndex = ({\n accountDesc,\n __accountsByName,\n}: {\n accountDesc: ManifestAccount;\n __accountsByName: GateAccountsByName;\n}) => {\n if (!(\"currency\" in accountDesc) || !(\"index\" in accountDesc)) return;\n const accounts = Object.values(__accountsByName);\n return accounts.find(\n (account) =>\n account.currency === accountDesc.currency &&\n account.index === accountDesc.index &&\n (\"contractAddress\" in accountDesc\n ? accountDesc.contractAddress === account.contract_address\n : !account.contract_address),\n );\n};\n","import { DeserializedManifest, Manifest, ManifestAPIUser, ManifestUser } from \"./types\";\n\nconst fromRawUser = (u: number | ManifestUser): ManifestUser =>\n typeof u === \"number\" ? { device: u } : u;\n\nconst fromRawAPIUser = (u: string | ManifestAPIUser): ManifestAPIUser =>\n typeof u === \"string\" ? { name: u } : u;\n\nfunction deserializeManifest(manifest: Manifest): DeserializedManifest {\n const { users, ...rest } = manifest;\n return {\n ...rest,\n ...(users\n ? {\n users: {\n ...(users.operators ? { operators: users.operators.map(fromRawUser) } : {}),\n ...(users.admins ? { admins: users.admins.map(fromRawUser) } : {}),\n ...(users.api ? { api: users.api.map(fromRawAPIUser) } : {}),\n ...(users.apiV2 ? { apiV2: users.apiV2 } : {}),\n },\n }\n : {}),\n };\n}\n\nexport default deserializeManifest;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { GateTokenCurrency, LegacyERC20Token, RunnableOptions, UserContext } from \"./types\";\n\nfunction erc20TokenToGateTokenCurrency(erc20: LegacyERC20Token): GateTokenCurrency {\n const parent_currency =\n erc20.blockchain_name === \"foundation\"\n ? \"ethereum\"\n : erc20.blockchain_name === \"ropsten\"\n ? \"ethereum_ropsten\"\n : erc20.blockchain_name === \"goerli\"\n ? \"ethereum_goerli\"\n : null;\n if (!parent_currency) {\n throw new Error(\n `Can't determine parent_currency from ERC20 token with blockchain_name \"${erc20.blockchain_name}\"`,\n );\n }\n return {\n contract_address: erc20.contract_address,\n family: \"ethereum\",\n name: erc20.name,\n parent_currency,\n ticker: erc20.ticker,\n token_type: \"erc20\",\n units: [\n {\n name: erc20.ticker,\n code: erc20.ticker,\n magnitude: erc20.decimals,\n },\n ],\n __legacy_hsm_account_parameters: erc20.hsm_account_parameters,\n __legacy_hsm_signature: erc20.hsm_signature,\n };\n}\n\nasync function fetchERC20Tokens(\n ctx: UserContext,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<GateTokenCurrency[]> {\n if (process.env.LEGACY_TOKENS) {\n type LegacyRes = { erc20s: LegacyERC20Token[] };\n const res = await ctx.network<LegacyRes>(\"GET\", \"/currencies/erc20s\");\n return res.erc20s.map(erc20TokenToGateTokenCurrency);\n }\n let tokens;\n try {\n type Res = GateTokenCurrency[];\n tokens = await ctx.network<Res>(\"GET\", \"/currencies/tokens\");\n } catch (err) {\n logger.error(\n \"Fetching tokens has failed, if you are targeting a gate < Vault 3.6, set environment variable LEGACY_TOKENS to 1\",\n );\n logger.error(\"e.g: LEGACY_TOKENS=1 ledger-vault <command>\");\n throw err;\n }\n return tokens;\n}\n\nexport default fetchERC20Tokens;\n","import type { Logger as RevaultLogger } from \"@ledgerhq/revault-sdk\";\nimport { EngineOptions, createEngine } from \"@ledgerhq/revault-sdk/engine\";\nimport { onboard } from \"@ledgerhq/revault-sdk/onboarding\";\nimport { Logger } from \"@ledgerhq/vault-utils\";\n\nimport legacyGenSeed from \"./genSeed\";\n\ntype Options = {\n // onboarding target\n workspace: string;\n compartmentId: number;\n hsmScriptsVersion: string;\n // config\n revaultApiUrl: string;\n revaultRootAuthToken: string;\n deviceApiUrl: string;\n salt: string;\n};\n\n/* istanbul ignore next */\nfunction genSeed({ salt, index }: { salt: string; index: number }): string {\n return legacyGenSeed(salt, index);\n}\n\nexport /* istanbul ignore next */ async function onboardWithRevault(\n options: Options,\n logger: Logger,\n): Promise<void> {\n const engineOptions: EngineOptions = {\n salt: options.salt,\n baseUrl: options.revaultApiUrl,\n deviceApiUrl: options.deviceApiUrl,\n genSeed,\n };\n\n const revaultLogger: RevaultLogger = {\n ...logger,\n warning: logger.info,\n debug: (msg: string): void => {\n if (process.env.DEBUG === \"1\") {\n // eslint-disable-next-line no-console\n console.log(\"DEBUG\", msg);\n }\n },\n output: logger.info,\n };\n\n const baseEngine = createEngine(engineOptions, revaultLogger);\n const rootClient = baseEngine.createClient({ rootAuthToken: options.revaultRootAuthToken });\n\n try {\n const onboarding = await rootClient.onboarding.startOnboarding.mutate({\n hsmScriptsVersion: options.hsmScriptsVersion,\n workspaceName: options.workspace,\n compartmentId: options.compartmentId,\n mode: \"GATE_COMPAT\",\n });\n\n const onboardingEngine = baseEngine.withOnboarding(onboarding.id);\n\n await onboard({ engine: onboardingEngine }, revaultLogger);\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"has already started onboarding\")) {\n logger.info(\"Onboarding already started, assuming it's finished, skipping\");\n // we assume the workspace is already onboarded\n return;\n }\n throw err;\n }\n}\n","import { entropyToMnemonic } from \"bip39\";\nimport hex from \"crypto-js/enc-hex\";\nimport sha256 from \"crypto-js/sha256\";\n\nconst hashCode = (str: string): Buffer => {\n return Buffer.from(hex.stringify(sha256(str)), \"hex\");\n};\n\nconst genSeed = (salt = \"\", index = 1, options: { overrideSeeds?: string[] } = {}): string => {\n const { overrideSeeds } = options;\n const overriddenSeed = !!overrideSeeds && overrideSeeds[index - 1];\n if (overriddenSeed) return overriddenSeed;\n const entropy = hashCode(`${salt}_${index}`);\n return entropyToMnemonic(entropy.toString(\"hex\"));\n};\n\nexport default genSeed;\n","import isEqual from \"lodash/isEqual\";\nimport sortBy from \"lodash/sortBy\";\n\nimport {\n ManifestAccount,\n ManifestAccountRuleMultiAuth,\n ManifestAccountRuleMultiAuthStep,\n ManifestAccountRuleThreshold,\n ManifestGroup,\n ManifestVaultEntity,\n ManifestWhitelist,\n ManifestWhitelistAddress,\n ManifestWorkspaceRule,\n} from \"./types\";\n\nexport const areGroupsDifferent = (group1: ManifestGroup, group2: ManifestGroup): boolean => {\n if (group1.users.length !== group2.users.length) return true;\n const sortedUsers1 = sortBy(group1.users, [(user) => user.toString()]);\n const sortedUsers2 = sortBy(group2.users, [(user) => user.toString()]);\n return !isEqual(sortedUsers1, sortedUsers2);\n};\n\nexport const areWorkspaceRulesDifferent = (\n rule1: ManifestWorkspaceRule,\n rule2: ManifestWorkspaceRule,\n): boolean => {\n /* istanbul ignore if */ // should never happen\n if (rule1.permission !== rule2.permission) return true;\n /* istanbul ignore if */ // should never happen\n if (rule1.steps.length !== rule2.steps.length) return true;\n\n return rule1.steps.some((step1, i) => {\n const step2 = rule2.steps[i]!;\n if (step1.quorum !== step2.quorum) return true;\n if (!isEqual(sortBy(step1.users), sortBy(step2.users))) return true;\n return false;\n });\n};\n\nconst formatAddress = (a: ManifestWhitelistAddress) => `${a.currency}-${a.address}`;\n\nexport const areWhitelistsDifferent = (\n whitelist1: ManifestWhitelist,\n whitelist2: ManifestWhitelist,\n): boolean => {\n if (whitelist1.addresses.length !== whitelist2.addresses.length) return true;\n const _addresses1 = whitelist1.addresses.map(formatAddress);\n const _addresses2 = whitelist2.addresses.map(formatAddress);\n return !isEqual(sortBy(_addresses1), sortBy(_addresses2));\n};\n\nfunction sanitizeThreshold(step: ManifestAccountRuleThreshold) {\n return { ...step, min: step.min || 0 };\n}\n\nexport const areAccountsDifferent = (\n account1: ManifestAccount,\n account2: ManifestAccount,\n): boolean => {\n if (account1.name !== account2.name) return true;\n // as Gate do not return tradelink_data, we cannot check Tradelink account are different\n if (account1.tradelink_data || account2.tradelink_data) return false;\n const account1Rules = account1.rules;\n const account2Rules = account2.rules;\n if (!!account1Rules !== !!account2Rules) return true;\n if (!account1Rules || !account2Rules) return false;\n if (account1Rules.length !== account2Rules.length) return true;\n let isDifferent = false;\n\n account1Rules.forEach((rule, index) => {\n const account2Rule = account2Rules[index];\n /* istanbul ignore if */\n if (!account2Rule) throw new Error(`No rule found at index ${index}`);\n if (rule.length !== account2Rule.length) {\n isDifferent = true;\n return;\n }\n rule.forEach((step) => {\n if (step.type === \"WHITELIST\") {\n const same = account2Rule.find(\n (s) => s.type === \"WHITELIST\" && isEqual(sortBy(s.whitelists), sortBy(step.whitelists)),\n );\n if (!same) {\n isDifferent = true;\n return;\n }\n }\n if (step.type === \"THRESHOLD\") {\n const sanitized = sanitizeThreshold(step);\n const same = account2Rule.find((s) => {\n if (s.type === \"THRESHOLD\") {\n const sanitizeStep = sanitizeThreshold(s);\n return sanitizeStep.min === sanitized.min && sanitizeStep.max === sanitized.max;\n }\n return false;\n });\n if (!same) {\n isDifferent = true;\n return;\n }\n }\n if (step.type === \"MULTI_AUTHORIZATIONS\") {\n // @ts-ignore: typescript does't understand Array.find()\n const same: ManifestAccountRuleMultiAuth = account2Rule.find(\n (s) => s.type === \"MULTI_AUTHORIZATIONS\",\n );\n if (!same || areStepsOfMultiAuthDifferent(step.steps, same.steps)) {\n isDifferent = true;\n return;\n }\n }\n });\n });\n return isDifferent;\n};\n\nexport const areVaultEntitiesDifferent = (\n vaultEntity1: ManifestVaultEntity,\n vaultEntity2: ManifestVaultEntity,\n): boolean => {\n if (\n vaultEntity1.accounts &&\n vaultEntity2.accounts &&\n vaultEntity1.accounts.length !== vaultEntity2.accounts.length\n )\n return true;\n return !isEqual(sortBy(vaultEntity1.accounts), sortBy(vaultEntity2.accounts));\n};\n\nconst areStepsOfMultiAuthDifferent = (\n steps1: ManifestAccountRuleMultiAuthStep[],\n steps2: ManifestAccountRuleMultiAuthStep[],\n) => {\n if (steps1.length !== steps2.length) return true;\n let isDifferent = false;\n steps1.forEach((s, index) => {\n const s2 = steps2[index];\n /* istanbul ignore if */\n if (!s2) throw new Error(`No step found at index ${index}`);\n if (s.quorum !== s2.quorum) {\n isDifferent = true;\n return;\n }\n\n if ((\"group\" in s && !(\"group\" in s2)) || (\"group\" in s2 && !(\"group\" in s))) {\n isDifferent = true;\n return;\n }\n\n if (\"group\" in s && \"group\" in s2 && s.group !== s2.group) {\n isDifferent = true;\n return;\n }\n\n if (\"users\" in s && \"users\" in s2) {\n if (s.users.length !== s2.users.length || !isEqual(sortBy(s.users), sortBy(s2.users))) {\n isDifferent = true;\n return;\n }\n }\n });\n return isDifferent;\n};\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport axios from \"axios\";\n\nimport { RunnableOptions } from \"./types\";\n\nexport type ConfigCatConfiguration = {\n apiKey: string | null;\n productId: string | null;\n configId: string | null;\n};\n\nexport type ConfigCatKeys = {\n sdkKey: string;\n productId: string;\n configId: string;\n environmentId: string;\n};\n\ntype ConfigCatFlag = { settingId: string; key: string; settingType: string };\n\nexport const configCatEndpoint = \"https://api.configcat.com/v1\";\n\nexport async function createConfigCatEnvironment(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n): Promise<ConfigCatKeys | null> {\n const {\n data: { environmentId },\n } = await axios.post<{\n environmentId: string;\n }>(\n `${configCatEndpoint}/products/${ccConfig.productId}/environments`,\n {\n name: environmentName,\n color: \"#DADBEE\",\n description: \"Created by ledger-vault CLI\",\n },\n {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n const { data: getCredentialsResponse } = await axios.get<{ primary: string }>(\n `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentId}`,\n {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n return {\n sdkKey: getCredentialsResponse.primary,\n productId: ccConfig.productId!,\n configId: ccConfig.configId!,\n environmentId,\n };\n}\n\nasync function getEnvironmentIDFromEnvironmentName(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n): Promise<string | undefined> {\n const { data: getEnvironmentsResponse } = await axios.get<\n Array<{ environmentId: string; name: string }>\n >(`${configCatEndpoint}/products/${ccConfig.productId}/environments`, {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n });\n const environment = getEnvironmentsResponse?.find(\n (environment) => environment.name === environmentName,\n );\n return environment?.environmentId;\n}\n\nexport async function deleteConfigCatEnvironment(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n) {\n const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);\n if (environmentID === undefined) {\n throw Error(`Unable to find ConfigCat environment '${environmentName}'`);\n }\n\n await axios.delete(`${configCatEndpoint}/environments/${environmentID}`, {\n headers: {\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n });\n}\n\nexport async function setConfigCatFeatureFlagValues(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n flagValues: Record<string, any>,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);\n if (environmentID === undefined) {\n throw Error(`Unable to find ConfigCat environment '${environmentName}'`);\n }\n\n const { data: getFlagsResponse } = await axios.get<Array<ConfigCatFlag>>(\n `${configCatEndpoint}/configs/${ccConfig.configId}/settings`,\n {\n headers: {\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n\n const settingValues: Array<{ settingId: string; value: string }> = getFlagsResponse\n .filter((configCatFlag) => Object.keys(flagValues).includes(configCatFlag.key))\n .map((configCatFlag) => {\n let flagValue = flagValues[configCatFlag.key];\n if (configCatFlag.settingType == \"string\" && typeof flagValue != \"string\") {\n flagValue = JSON.stringify(flagValue);\n }\n return { settingId: configCatFlag.settingId, value: flagValue };\n });\n\n try {\n await axios.post(\n `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentID}/values`,\n { settingValues },\n { headers: { Authorization: `Basic ${ccConfig.apiKey}` } },\n );\n } catch (err) {\n logger.error(`Could not set feature flags`);\n logger.error(err);\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport invariant from \"invariant\";\nimport io, { Socket } from \"socket.io-client\";\n\nimport createNetwork from \"./createNetwork\";\nimport { createRunner } from \"./device\";\nimport createAPIDevice from \"./device/createAPIDevice\";\nimport createHWDevice from \"./device/createHWDevice\";\nimport createInteractions from \"./device/createInteractions\";\nimport { APIDevice } from \"./device/types\";\nimport {\n AdminDevice,\n Connection,\n DevicesPool,\n GateUser,\n Interaction,\n Organization,\n PoolOptions,\n RunnableOptions,\n UserContextRunnable,\n VaultEvent,\n} from \"./types\";\nimport { getWorkspaceFromGate } from \"./utils\";\n\nconst createDevicesPool = (\n options: PoolOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): DevicesPool => {\n const {\n salt = \"\",\n gate,\n apiGateway,\n overrideSeeds,\n lamURL,\n lamAPIKey,\n notifierURL,\n deviceAPISessionID,\n networkDelay,\n readOnlyUser,\n transport = \"software\",\n recordStore,\n deviceAPIURL,\n psdModel = \"STAX\",\n } = options;\n\n let adminDevicesCache: AdminDevice[] = [];\n\n const device =\n transport === \"software\"\n ? createAPIDevice({\n deviceAPISessionID,\n salt,\n overrideSeeds,\n deviceAPIURL,\n psdModel,\n })\n : createHWDevice({ promptToSwitch: transport !== \"nodehid-replayer\" });\n\n const setSalt = (salt: string) => {\n invariant(\n transport === \"software\",\n `Tried to use \\`setSalt\\` with transport not being \"software\"`,\n );\n const apiDevice = device as APIDevice;\n apiDevice.setSalt(salt);\n };\n\n const interactions = createInteractions(device);\n let deviceRun = createRunner(interactions, {\n readOnly: !!readOnlyUser,\n transport,\n recordStore,\n });\n\n const { loginFlow, readOnlyLoginFlow, registerUserFlow, approveFlow, approveFlowWithoutHSM } =\n interactions;\n\n const workspace = getWorkspaceFromGate(gate);\n const network = createNetwork({\n baseURL: gate,\n networkDelay,\n });\n\n const runWithDevice = async (\n deviceIndex: number,\n interactions: Interaction<any>[],\n data: any,\n ) => {\n await device.switchDevice(deviceIndex);\n invariant(deviceRun, \"deviceRun not initialized\");\n return deviceRun(interactions, data);\n };\n\n const getUserID = async (deviceIndex: number) => {\n await device.switchDevice(deviceIndex);\n return device.getUserID();\n };\n\n const registerDevice = async (deviceIndex: number, request: any) => {\n const data = {\n network,\n workspace,\n username: request.user.username,\n role: request.user.role.toLowerCase(),\n // FIXME absolutely wrong name (legacy code)\n member: request,\n urlID: request.url_id,\n };\n return runWithDevice(deviceIndex, registerUserFlow, data);\n };\n\n const tokensByDeviceIndex: Record<string, string> = {};\n const login = async (deviceIndex: number | string) => {\n const isReadOnlyUser = typeof deviceIndex === \"string\" || !!readOnlyUser;\n deviceRun = createRunner(interactions, {\n readOnly: isReadOnlyUser,\n transport,\n recordStore,\n });\n if (typeof deviceIndex === \"number\") {\n await device.switchDevice(deviceIndex);\n }\n\n let _token: string | null = tokensByDeviceIndex[deviceIndex] || null;\n\n const interceptToken = (token: string) => {\n tokensByDeviceIndex[deviceIndex] = token;\n _token = token;\n };\n const injectToken = () => _token;\n if (!_token || process.env.NODE_ENV === \"test\") {\n const network = createNetwork({\n baseURL: gate,\n interceptToken,\n injectToken,\n networkDelay,\n });\n await deviceRun(\n typeof deviceIndex === \"string\"\n ? readOnlyLoginFlow(deviceIndex)\n : readOnlyUser\n ? readOnlyLoginFlow(readOnlyUser)\n : loginFlow,\n { network, workspace },\n );\n if (!_token) {\n throw new Error(\"Could not extract token from login\");\n }\n }\n\n const authNetwork = createNetwork({\n baseURL: gate,\n token: _token,\n networkDelay,\n });\n\n const authRun = async (interactions: Interaction<any>[], data: any) => {\n /* istanbul ignore else */\n if (typeof deviceIndex === \"number\") {\n await runWithDevice(deviceIndex, interactions, data);\n } else {\n throw new Error(`Can't authRun on read-only mode (deviceIdentifier: ${deviceIndex})`);\n }\n };\n\n const post = async (url: string, payload: any) => authNetwork(\"POST\", url, payload);\n\n const rejectRequest = async (requestID: number) => post(`/requests/${requestID}/abort`, {});\n\n const approveRequest = (request: any) =>\n authRun(approveFlow, { request_id: request.id, network: authNetwork });\n const approveRequestWithoutHSM = (request: any) =>\n authRun(approveFlowWithoutHSM, {\n request_id: request.id,\n network: authNetwork,\n });\n\n let socket: typeof Socket | null = null;\n\n return {\n network: authNetwork,\n run: authRun,\n get: async <T>(url: string, requestOptions: any): Promise<T> =>\n authNetwork(\"GET\", url, undefined, requestOptions),\n post,\n rejectRequest,\n approveRequest,\n approveRequestWithoutHSM,\n getToken: () => _token,\n connectSocket: async () => {\n if (!socket) {\n if (!notifierURL) {\n throw new Error(\"No notifierURL specified\");\n }\n /* istanbul ignore if */\n if (!_token) {\n throw new Error(\"No token\");\n }\n socket = createSocket(_token, notifierURL);\n const { pub_key } = await authNetwork(\"GET\", \"/people/me\");\n socket.emit(\"join\", { username: pub_key, workspace });\n logger.success(\"Socket connected\");\n }\n return socket;\n },\n onEvent: (cb: (event: VaultEvent) => void) => {\n if (!socket) {\n throw new Error(\"Call connectSocket() first\");\n }\n socket.on(\"notification\", cb);\n },\n };\n };\n\n const bruteforceDeviceIndex = async (userID: string): Promise<number> => {\n for (let i = 1; i < 100; i++) {\n const found = await getUserID(i);\n /* istanbul ignore else */\n if (found.toUpperCase() === userID.toUpperCase()) return i;\n }\n\n /* istanbul ignore next */\n throw new Error(`Could not find device index for user ${userID}`);\n };\n\n async function getOnboardingAdminDevices(): Promise<AdminDevice[]> {\n if (adminDevicesCache.length > 0) return adminDevicesCache;\n adminDevicesCache = [\n [\"Admin 1\", 4],\n [\"Admin 2\", 5],\n [\"Admin 3\", 6],\n ];\n return adminDevicesCache;\n }\n\n const collectQuorumDevices = async (): Promise<number[]> => {\n const devices: number[] = adminDevicesCache\n .map((adminDevice: AdminDevice) => adminDevice[1])\n .slice(0, 2);\n const admin = await login(devices[0]!);\n const org = await admin.get<Organization>(\"/organization\", {});\n const { quorum } = org;\n\n if (quorum > 2) {\n // fetch all active admins\n const adminsC = await admin.get<Connection<GateUser>>(\n \"/people?status=ACTIVE&role=ADMIN&pageSize=-1\",\n {},\n );\n\n // sort them, and remove the two first ones (we already have their devices)\n let admins = adminsC.edges.map((e) => e.node);\n /* istanbul ignore next */\n admins.sort((a, b) => (a.id > b.id ? 1 : -1));\n admins = admins.slice(2, quorum);\n\n // find and push their device indexes in the devices array\n for (const adm of admins) {\n const deviceIndex = await bruteforceDeviceIndex(adm.user_id);\n devices.push(deviceIndex);\n }\n }\n return devices;\n };\n\n const runWithQuorum = async (iteratee: UserContextRunnable) => {\n const devices = await collectQuorumDevices();\n for (let i = 0; i < devices.length; i++) {\n const device = devices[i];\n /* istanbul ignore if */\n if (!device) throw new Error(\"Invalid device\");\n const admin = await login(device);\n try {\n await iteratee(admin);\n } catch (err) {\n /* istanbul ignore next */\n const reqAlreadyExistsErr = \"REQUEST_ALREADY_APPROVED_OR_ABORTED_BY_THIS_MEMBER_EXCEPTION\";\n /* istanbul ignore next */\n // @ts-expect-error\n if (err.name === reqAlreadyExistsErr) continue;\n /* istanbul ignore next */\n throw err;\n }\n }\n };\n\n const apiNetwork = createNetwork({\n baseURL: lamURL || \"\",\n networkDelay,\n });\n const headersAPILam = lamAPIKey ? { headers: { \"X-Ledger-API-Key\": lamAPIKey } } : {};\n\n const createInvitation = async (name: string): Promise<{ device_id: string }> => {\n try {\n const r = await apiNetwork(\"POST\", `/api_users/${name}`, {}, headersAPILam);\n return r;\n } catch (e: unknown) {\n return apiNetwork(\"GET\", `/api_users/${name}`, {}, headersAPILam);\n }\n };\n\n const registerUser = async (name: string, uuid: string): Promise<void> => {\n return apiNetwork(\"POST\", `/api_users/${name}/register/${uuid}`, {}, headersAPILam);\n };\n\n const lamAPI = {\n createInvitation,\n registerUser,\n };\n\n const createSocket = (token: string, notifierURL: string): typeof Socket => {\n const client: typeof Socket = io(notifierURL, {\n transportOptions: {\n polling: {\n extraHeaders: {\n Cookie: `ledger_workspace=${workspace}; ledger_token=${token}`,\n },\n },\n },\n });\n return client;\n };\n\n /* istanbul ignore next */\n const getRevaultCompatOptions = () => {\n return {\n workspace,\n deviceApiUrl:\n deviceAPIURL ?? process.env.VAULT_DEVICE_API_URL ?? \"https://localhost:8443/device-api\",\n salt,\n };\n };\n\n return {\n getRevaultCompatOptions,\n workspace,\n network,\n gate,\n apiGateway,\n login,\n lamAPI,\n getUserID,\n bruteforceDeviceIndex,\n setSalt,\n registerDevice,\n runWithDevice,\n runWithQuorum,\n device,\n interactions,\n getOnboardingAdminDevices,\n psdModel,\n };\n};\n\nexport default createDevicesPool;\n","import { RecordStore, openTransportReplayer } from \"@ledgerhq/hw-transport-mocker\";\nimport { registerTransportModule, withDevicePolling } from \"@ledgerhq/live-common-stub\";\nimport { listen } from \"@ledgerhq/logs\";\nimport { from } from \"rxjs\";\n\nimport { Interaction, Interactions, TransportType } from \"../types\";\n\n/* istanbul ignore next */\nconst mockTransport: Promise<any> = Promise.resolve({\n open: () => Promise.resolve(),\n close: () => Promise.resolve(),\n});\n\n// always logs apdu for now\nlisten(\n /* istanbul ignore next */ (log) => {\n if (log.type === \"apdu\" && process.env.DEBUG === \"1\") {\n console.log(`${log.type}: ${log.message ? log.message : \"\"}`); // eslint-disable-line no-console\n }\n },\n);\n\n/* istanbul ignore next */\nregisterTransportModule({\n id: \"software\",\n open: (id: string) => {\n if (id !== \"software\") return;\n return mockTransport;\n },\n disconnect: () => null,\n});\n\ntype CreateRunnerOptions = {\n readOnly?: boolean;\n transport: TransportType;\n recordStore?: RecordStore | null;\n acceptPollingError?: (err: unknown) => boolean;\n};\n\nexport type Runner = (\n _interactions: Interaction<any>[],\n _data?: any,\n options?: {\n onStepStart?: (responseKey: string) => void;\n onStepDone?: <D>(res: { responseKey: string; value: D }) => void;\n },\n) => Promise<Record<string, unknown>>;\n\nexport const createRunner =\n (\n allInteractions: Interactions,\n { readOnly, transport, recordStore, acceptPollingError }: CreateRunnerOptions = {\n transport: \"software\",\n },\n ): Runner =>\n async <T>(\n _interactions: Interaction<any>[],\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n _data?: any,\n options?: {\n onStepStart?: (responseKey: string) => void;\n onStepDone?: <D>(res: { responseKey: string; value: D }) => void;\n },\n ): Promise<T> => {\n const interactions = [...(readOnly ? [] : [allInteractions.getU2FPublicKey]), ..._interactions];\n const responses = { ..._data };\n for (let i = 0; i < interactions.length; i++) {\n const interaction = interactions[i];\n /* istanbul ignore if */\n if (!interaction) throw new Error(\"Invalid interaction\");\n if (recordStore) {\n const replayer = await openTransportReplayer(recordStore);\n const response = await interaction.action({\n ...responses,\n transport: replayer,\n });\n\n responses[interaction.responseKey] = response;\n } else {\n /* istanbul ignore next */\n const obs = withDevicePolling(transport)(\n (transport: any) => {\n return from(interaction.action({ ...responses, transport }));\n },\n // always throw on error:\n // eslint-disable-next-line\n acceptPollingError ?? (() => false),\n );\n /* istanbul ignore next */\n if (options?.onStepStart) {\n options.onStepStart(interaction.responseKey);\n }\n const res = await obs.toPromise();\n responses[interaction.responseKey] = res;\n /* istanbul ignore next */\n if (options?.onStepDone) {\n options.onStepDone({ responseKey: interaction.responseKey, value: res });\n }\n }\n }\n return responses;\n };\n","import { nanoid } from \"nanoid\";\n\nimport createNetwork from \"../createNetwork\";\nimport genSeed from \"../genSeed\";\nimport { PsdModel } from \"../types\";\nimport {\n APIDevice,\n Device,\n DeviceAuthenticate,\n DeviceGenAttestationCert,\n DeviceGetPubKey,\n DeviceRegister,\n DeviceRegisterData,\n DeviceValidateVaultOp,\n FinalizePairingHandshake,\n InitiatePairingHandshake,\n PairingData,\n StartKPatternAsResponder,\n} from \"./types\";\n\nconst ROLE_TO_BYTES = {\n shared_owner: Buffer.from([2]),\n admin: Buffer.from([1]),\n operator: Buffer.from([0]),\n};\n\nconst ENDPOINTS = {\n GET_PUBLIC_KEY: \"/get-public-key\",\n GET_ATTESTATION: \"/get-attestation\",\n OPEN_SESSION: \"/open-session\",\n AUTHENTICATE: \"/authenticate\",\n REGISTER: \"/register\",\n VALIDATE_VAULT_OPERATION: \"/validate-vault-operation\",\n GENERATE_KEY_FRAGMENTS: \"/generate-key-fragments\",\n REGISTER_DATA: \"/u2f-register-data\",\n GET_CURRENT_DEVICE: \"/current-device\",\n // NEW SECURE CHANNEL ENDPOINTS\n INITIATE_PAIRING_HANDSHAKE: \"/initiate-pairing-handshake\",\n GENERATE_SEED_COMPONENT: \"/generate-key-component\",\n FINALIZE_PAIRING_HANDSHAKE: \"/finalize-pairing-handshake\",\n START_K_PATTERN_AS_RESPONDER: \"/start-k-pattern-as-responder\",\n START_KK_PATTERN_AS_RESPONDER: \"/start-kk-pattern-as-responder\",\n VALIDATE_OP: \"/validate-op\",\n};\n\nconst deviceTypeBufferFromPsdModel = {\n BLUE: Buffer.from([0]),\n STAX: Buffer.from([2]),\n};\n\nconst pathArrayToString = (path: number[]): string => {\n /* istanbul ignore if */\n if (!path[0] || !path[1]) throw new Error(\"Invalid path array\");\n return `${path[0] & 0xfffffff}'/${path[1] & 0xfffffff}'`; // eslint-disable-line no-bitwise\n};\n\nlet __DEVICE_API_NETWORK__ = createNetwork({\n baseURL: process.env.VAULT_DEVICE_API_URL || \"https://localhost:8443/device-api\",\n});\n\nexport function setDeviceAPIEndpoint(url: string): void {\n __DEVICE_API_NETWORK__ = createNetwork({ baseURL: url });\n}\n\ntype CreateAPIDeviceOptions = {\n deviceAPISessionID?: string;\n salt?: string;\n overrideSeeds?: string[];\n deviceAPIURL?: string;\n psdModel?: PsdModel;\n};\n\nfunction createAPIDevice(options: CreateAPIDeviceOptions = {}): APIDevice {\n const { deviceAPISessionID, overrideSeeds, deviceAPIURL, psdModel = \"STAX\" } = options;\n let { salt = \"\" } = options;\n // singleton used to store the current device seed\n // this way we keep the \"switch device\" logic (which\n // is handy because it works a lot like IRL), but\n // it has the advantage of having state held by client\n // and not by device-api (allow multi users)\n let __CURRENT_SEED__ = \"\";\n\n let __PSD_MODEL__ = psdModel;\n\n // used to persist data on device-api side\n const __DEVICE_API_SESSION_ID__ = deviceAPISessionID || nanoid();\n\n const setSeed = (seed: string): void => {\n __CURRENT_SEED__ = seed;\n };\n\n const setSalt = (newSalt: string) => {\n salt = newSalt;\n };\n\n const deviceNetwork = (\n method: \"POST\" | \"GET\",\n url: string,\n _data: any = {},\n requestOptions: any = {},\n ) => {\n const data = {\n seed: __CURRENT_SEED__,\n sessionID: __DEVICE_API_SESSION_ID__,\n psd_model: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n ..._data,\n version: 3, // This is used to choose Noise Channel mode\n };\n const deviceAPINetwork = deviceAPIURL\n ? createNetwork({ baseURL: deviceAPIURL })\n : __DEVICE_API_NETWORK__;\n return deviceAPINetwork(method, url, data, requestOptions);\n };\n\n const getPublicKey: DeviceGetPubKey = async (transport, path, secp256k1 = true) => {\n const data: {\n pubKey: string;\n signature: string;\n attestation: string;\n } = await deviceNetwork(\"POST\", ENDPOINTS.GET_PUBLIC_KEY, {\n path: pathArrayToString(path),\n secp256k1,\n });\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\"),\n };\n };\n\n const registerData: DeviceRegisterData = async (transport, challenge) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.REGISTER_DATA, {\n challenge: challenge.toString(\"hex\"),\n });\n\n // @ts-ignore\n return Buffer.from(data, \"hex\");\n };\n\n const switchDevice = async (id: number) => {\n setSeed(genSeed(salt, id, { overrideSeeds }));\n };\n\n const register: DeviceRegister = async (\n transport,\n challenge,\n application,\n name,\n userRole,\n registerData,\n ) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.REGISTER, {\n challenge: challenge.toString(\"hex\"),\n name,\n role: ROLE_TO_BYTES[userRole].toString(\"hex\"),\n hsm_data: registerData,\n type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n application,\n });\n // @ts-ignore\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0];\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0];\n /* istanbul ignore if */\n if (typeof keyHandleLength === \"undefined\") throw new Error(\"Invalid key handle length\");\n const keyHandle = response.slice(i, (i += keyHandleLength));\n // const attestationSignature = lastResponse.slice(i, ++i)[0];\n // const signature = lastResponse.slice(i).toString(\"hex\");\n return {\n u2f_register: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const getAttestationCertificate: DeviceGenAttestationCert = async () => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.GET_ATTESTATION);\n return Buffer.from(data, \"hex\");\n };\n\n const validateVaultOperation: DeviceValidateVaultOp = async (transport, path, channel) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.VALIDATE_OP, {\n path: pathArrayToString(path),\n actions: channel.w_actions,\n });\n return data;\n };\n\n const authenticate: DeviceAuthenticate = async (\n transport,\n challenge,\n application,\n keyHandle,\n userName,\n role,\n workspace,\n ) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.AUTHENTICATE, {\n challenge: challenge.toString(\"hex\"),\n application,\n key_handle: keyHandle.toString(\"hex\"),\n name: userName,\n // @ts-ignore\n role: ROLE_TO_BYTES[role.toLowerCase()].toString(\"hex\"),\n type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n workspaceName: workspace,\n });\n\n // we add a fake status response because transport on device does it\n const response = Buffer.concat([Buffer.from(data, \"hex\"), Buffer.from(\"9000\", \"hex\")]);\n\n const userPresence = response.slice(0, 1);\n const counter = response.slice(1, 5);\n const signature = response.slice(5, response.length - 2).toString(\"hex\");\n return {\n userPresence,\n counter,\n signature,\n rawResponse: response.slice(0, response.length - 2).toString(\"hex\"),\n };\n };\n\n const getUserID = async (): Promise<string> => {\n const { blue_device_id } = await deviceNetwork(\"POST\", \"/blue-device-id\");\n return blue_device_id.toUpperCase();\n };\n\n // NEW SECURE CHANNEL ENDPOINTS\n\n const initiatePairingHandshake: InitiatePairingHandshake = async (): Promise<any> => {\n return deviceNetwork(\"POST\", ENDPOINTS.INITIATE_PAIRING_HANDSHAKE, {});\n };\n\n const finalizePairingHandshake: FinalizePairingHandshake = async (\n transport: any,\n handshake: string,\n ciphertext: string,\n ): Promise<string> => {\n const data = {\n handshake,\n ciphertext,\n };\n return deviceNetwork(\"POST\", ENDPOINTS.FINALIZE_PAIRING_HANDSHAKE, data);\n };\n\n const startKpatternAsResponder: StartKPatternAsResponder = async (\n transport: any,\n scriptID: number,\n handshake: string,\n handshakeAttestation: {\n attestation_pub: string;\n certificate: string;\n code_hash: string;\n signature: string;\n },\n partitionID: string,\n ): Promise<string> => {\n const data = {\n script_id: scriptID,\n handshake,\n // FIXME apparently for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionID,\n };\n return deviceNetwork(\"POST\", ENDPOINTS.START_K_PATTERN_AS_RESPONDER, data);\n };\n\n const hasPartitionID = async () => {\n const partitionIDs = await deviceNetwork(\"POST\", \"/get-partition-ids\");\n return partitionIDs.length > 0;\n };\n\n const getConfidentialityKey = () => deviceNetwork(\"POST\", \"/get-confidentiality-key\");\n\n const updatePSDPartitionPairing = (p: { pairingData: PairingData }) =>\n deviceNetwork(\"POST\", \"/set-partition-key\", p.pairingData);\n\n return {\n getPsdModel: () => __PSD_MODEL__,\n setPsdModel: (psdModel: PsdModel) => (__PSD_MODEL__ = psdModel),\n setSeed,\n setSalt,\n authenticate,\n getAttestationCertificate,\n getPublicKey,\n getUserID,\n register,\n registerData,\n switchDevice,\n // NEW SECURE CHANNEL ENDPOINTS\n validateVaultOperation,\n initiatePairingHandshake,\n finalizePairingHandshake,\n startKpatternAsResponder,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n };\n}\n\nexport const isAPIDevice = (device: Device): device is APIDevice => {\n return \"getPsdModel\" in device;\n};\n\nexport default createAPIDevice;\n","import { TransportStatusError } from \"@ledgerhq/hw-transport\";\nimport chalk from \"chalk\";\nimport invariant from \"invariant\";\nimport readline from \"readline\";\n\nimport { PAGINATED_STATUS, STREAMING_NEXT_ACTION, STREAMING_RESPONSE } from \"./constants\";\nimport {\n Device,\n DeviceAuthenticate,\n DeviceGenAttestationCert,\n DeviceGetPubKey,\n DeviceRegister,\n DeviceRegisterData,\n DeviceValidateVaultOp,\n FinalizePairingHandshake,\n InitiatePairingHandshake,\n StartKPatternAsResponder,\n} from \"./types\";\n\nconst STATUS_LENGTH = 2;\nconst MAX_CHUNK_LENGTH = 250;\nconst removeStatus = (result: Buffer): Buffer => result.slice(0, result.length - STATUS_LENGTH);\n\ntype Transport = {\n send: (\n cla: number,\n ins: number,\n p1: number,\n p2: number,\n data: Buffer,\n successStatus?: number[],\n ) => Promise<Buffer>;\n};\n\nconst buildAttestation = (\n cliMsg: string,\n attestation: {\n attestation_pub: string;\n certificate: string;\n code_hash: string;\n signature: string;\n },\n partitionID: string,\n) => {\n const sigBuffer = Buffer.from(attestation.signature, \"base64\");\n const sigLengthBuffer = Buffer.alloc(1);\n sigLengthBuffer.writeUIntBE(sigBuffer.length, 0, 1);\n\n const certBuffer = Buffer.from(attestation.certificate, \"base64\");\n const certLengthBuffer = Buffer.alloc(1);\n certLengthBuffer.writeUIntBE(certBuffer.length, 0, 1);\n\n const pubBuffer = Buffer.from(attestation.attestation_pub, \"base64\");\n\n const certChainData = Buffer.concat([\n sigLengthBuffer,\n sigBuffer,\n pubBuffer,\n certLengthBuffer,\n certBuffer,\n ]);\n\n const cliMsgBuffer = Buffer.from(cliMsg, \"hex\");\n const cliMsgLengthBuffer = Buffer.alloc(1);\n cliMsgLengthBuffer.writeUIntBE(cliMsgBuffer.length, 0, 1);\n\n const pidBuffer = Buffer.from(partitionID, \"hex\");\n const pidLengthBuffer = Buffer.alloc(1);\n pidLengthBuffer.writeUIntBE(pidBuffer.length, 0, 1);\n\n const certChainDataLengthBuff = Buffer.alloc(2);\n certChainDataLengthBuff.writeUInt16BE(certChainData.length, 0);\n\n const data = Buffer.concat([\n cliMsgLengthBuffer,\n cliMsgBuffer,\n certChainDataLengthBuff,\n certChainData,\n pidLengthBuffer,\n pidBuffer,\n ]);\n\n const totalLengthBuffer = Buffer.alloc(2);\n totalLengthBuffer.writeUInt16BE(data.length, 0);\n\n const toSend = Buffer.concat([totalLengthBuffer, data]);\n\n return toSend;\n};\n\n/* istanbul ignore next: (obviously not run in tests) */\nfunction promptUserInput(query: any) {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) =>\n rl.question(query, (ans) => {\n rl.close();\n resolve(ans);\n }),\n );\n}\nconst splits = (chunk: number, buffer: Buffer): Buffer[] => {\n const chunks = [];\n for (let i = 0, size = chunk; i < buffer.length; i += size, size = chunk) {\n chunks.push(buffer.slice(i, i + size));\n }\n return chunks;\n};\n\nexport const sendByChunk = async (\n transport: Transport,\n command: number[], // eg [0xe0, 0x45, 0x00, 0x00]\n data: Buffer,\n chunkSize: number = MAX_CHUNK_LENGTH,\n): Promise<Buffer> => {\n const chunks = splits(chunkSize, data);\n let response = Buffer.from([]);\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n const apdu = [...command];\n // command is 0xe0,0x45,0x00,0x00 for first chunk, then it's 0xe0,0x45, 0x80,0x00\n apdu[2] = i && 0x80;\n response = await transport.send(\n apdu[0] as number,\n apdu[1] as number,\n apdu[2] as number,\n apdu[3] as number,\n chunk as Buffer,\n [0x9000, ...PAGINATED_STATUS],\n );\n }\n return response;\n};\n\nconst ROLE_TO_BYTES: Record<string, Buffer> = {\n shared_owner: Buffer.from([2]),\n admin: Buffer.from([1]),\n operator: Buffer.from([0]),\n};\n\ntype CreateHWDeviceOptions = {\n promptToSwitch?: boolean; // wether we prompt the user to enter to continue, false for tests\n};\n\nfunction createHWDevice(options: CreateHWDeviceOptions): Device {\n const { promptToSwitch } = options;\n\n const getPublicKey: DeviceGetPubKey = async (\n transport,\n path,\n /* istanbul ignore next */\n secp256k1 = true,\n ) => {\n const data = Buffer.concat([\n Buffer.from([path.length]),\n ...path.map((derivation) => {\n const buf = Buffer.alloc(4);\n buf.writeUInt32BE(derivation, 0);\n return buf;\n }),\n ]);\n let curve = 0x01;\n if (!secp256k1) {\n curve = 0x02;\n }\n const response = await transport.send(0xe0, 0x40, curve, 0x00, data);\n const pubKeyLength = response.readInt8(0);\n const pubKey = response\n .slice(1, pubKeyLength + 1)\n .toString(\"hex\")\n .toUpperCase();\n const signature = response.slice(pubKeyLength + 1, response.length - STATUS_LENGTH);\n return { pubKey, signature };\n };\n /* istanbul ignore next */\n const registerData: DeviceRegisterData = async (transport, challenge) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n\n const response = await transport.send(0xe0, 0x03, 0x00, 0x00, challenge);\n return removeStatus(response);\n };\n /* istanbul ignore next */\n const register: DeviceRegister = async (\n transport,\n challenge,\n application,\n userName,\n userRole,\n ) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n const applicationBuf = Buffer.from(application, \"hex\");\n invariant(applicationBuf.length === 32, \"application hex is expected to have 32 bytes\");\n\n const userNameBuff = Buffer.from(userName);\n\n const keyHandleData = Buffer.concat([\n ROLE_TO_BYTES[userRole.toLowerCase()] as Buffer,\n Buffer.from([userNameBuff.length]),\n userNameBuff,\n ]);\n\n const keyHandleDataLength = Buffer.alloc(2);\n keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);\n\n const data = Buffer.concat([challenge, applicationBuf, keyHandleDataLength, keyHandleData]);\n\n const response = await sendByChunk(transport, [0xe0, 0x01, 0x00, 0x00], data);\n\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0];\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0] as number;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2f_register: removeStatus(response),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n /* istanbul ignore next */\n const getAttestationCertificate: DeviceGenAttestationCert = async (transport) => {\n const response = await transport.send(0xe0, 0x41, 0x00, 0x00);\n return removeStatus(response);\n };\n\n const authenticate: DeviceAuthenticate = async (\n transport,\n challenge,\n application,\n keyHandle,\n name,\n role,\n workspace,\n ) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n const applicationBuf = Buffer.from(application, \"hex\");\n invariant(applicationBuf.length === 32, \"application hex is expected to have 32 bytes\");\n\n const nameBuf = Buffer.from(name);\n const workspaceBuf = Buffer.from(workspace);\n\n const roleBuf = ROLE_TO_BYTES[role.toLowerCase()] as Buffer;\n\n const keyHandleData = Buffer.concat([\n roleBuf,\n Buffer.from([nameBuf.length]),\n nameBuf,\n Buffer.from([workspaceBuf.length]),\n workspaceBuf,\n ]);\n\n const keyHandleDataLength = Buffer.alloc(2);\n keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);\n\n const data = Buffer.concat([\n challenge,\n applicationBuf,\n Buffer.from([keyHandle.length]),\n keyHandle,\n keyHandleDataLength,\n keyHandleData,\n ]);\n\n const response = await sendByChunk(transport, [0xe0, 0x02, 0x00, 0x00], data);\n\n const userPresence = response.slice(0, 1);\n const counter = response.slice(1, 5);\n const signature = response.slice(5, response.length - STATUS_LENGTH).toString(\"hex\");\n return {\n userPresence,\n counter,\n signature,\n rawResponse: removeStatus(response).toString(\"hex\"),\n };\n };\n\n const switchDevice = async () => {\n /* istanbul ignore next */\n if (promptToSwitch) {\n await promptUserInput(\n chalk`{red.green [SWITCH DEVICE]} press {red ENTER} when the device is plugged on.`,\n );\n }\n };\n\n // TODO implement new noise channel endpoints\n // TODO remove istanbul ignore statements when done\n /* istanbul ignore next */\n const initiatePairingHandshake: InitiatePairingHandshake = async (transport) => {\n const res = await transport.send(0xe0, 0x46, 0x00, 0x00);\n const pubKeyLength = res.readInt8(0);\n const pubKey = res\n .slice(1, pubKeyLength + 1)\n .toString(\"hex\")\n .toUpperCase();\n const attestation = res.slice(pubKeyLength + 1, res.length - STATUS_LENGTH);\n return { pubKey, attestation };\n };\n\n const validateVaultOperation: DeviceValidateVaultOp = async (transport, path, channel) => {\n const paths = Buffer.concat([\n Buffer.from([path.length]),\n ...path.map((derivation) => {\n const buf = Buffer.alloc(4);\n buf.writeUInt32BE(derivation, 0);\n return buf;\n }),\n ]);\n\n let nextActionId = 1;\n let finalResponse;\n\n while (!finalResponse) {\n const screen = Buffer.from(channel.w_actions[nextActionId - 1]!, \"base64\");\n const length = Buffer.alloc(2);\n length.writeUInt16BE(screen.length, 0);\n const data = Buffer.concat([paths, length, screen]);\n const response = await sendByChunk(transport, [0xe0, 0x45, 0x00, 0x00], data);\n const responseType = response.readInt8(0);\n let responseStatus = response.readUInt16BE(response.length - 2);\n\n /* istanbul ignore else */\n if (responseType === STREAMING_NEXT_ACTION) {\n nextActionId = response.readUIntBE(1, 2);\n } else if (responseType === STREAMING_RESPONSE) {\n finalResponse = response.slice(3, response.length - 2);\n // FIXME we should cover this in tests\n /* istanbul ignore next */\n while (responseStatus !== 0x9000) {\n const resp = await transport.send(0x00, 0xc0, 0x00, 0x00);\n responseStatus = resp.readUInt16BE(resp.length - 2);\n finalResponse = Buffer.concat([finalResponse, removeStatus(resp)]);\n }\n } else {\n throw Error(`${responseType}`);\n }\n }\n return finalResponse.toString(\"base64\");\n };\n\n /* istanbul ignore next */\n const finalizePairingHandshake: FinalizePairingHandshake = async (\n transport,\n handshake,\n ciphertext,\n handshakeAttestation,\n ) => {\n const response = await sendByChunk(\n transport,\n [0xe0, 0x47, 0x00, 0x02],\n buildAttestation(handshake, handshakeAttestation, ciphertext),\n );\n return removeStatus(response).toString(\"hex\");\n };\n\n const startKpatternAsResponder: StartKPatternAsResponder = async (\n transport,\n scriptID,\n handshake,\n handshakeAttestation,\n partitionID,\n ) => {\n const data = buildAttestation(handshake, handshakeAttestation, partitionID);\n const response = await sendByChunk(transport, [0xe0, 0x49, 0x00, scriptID], data);\n return removeStatus(response).toString(\"hex\");\n };\n\n /* istanbul ignore next */\n const getUserID = () => Promise.resolve(\"\");\n\n const hasPartitionID = async ({ transport }: any) => {\n const apdu = [0xe0, 0x51, 0x00, 0x00, 0xb5];\n const res = await transport.exchange(Buffer.from(apdu));\n const hex = Buffer.from(res).toString(\"hex\");\n\n // 6f45 => this error means that there is no partition ID on the device\n // if no error, it means there is (at least) one\n //\n // 6a87 => TEMPORARY hack because Stax apparently does not implement\n // the \"get partition id\" APDU, so it returns \"incorrect length\"\n // easier to just assume that in this case there is no partition,\n // and try to insert one anyway (done on revault onboarding)\n //\n return hex !== \"6f45\" && hex !== \"6a87\";\n };\n\n const getConfidentialityKey = async ({ transport }: { transport: any }) => {\n const apdu = [0xe0, 0x4d, 0x00, 0x00];\n const bufWithStatus = await transport.send(...apdu);\n const status = bufWithStatus.readUInt16BE(bufWithStatus.length - 2);\n /* istanbul ignore if */\n if (status !== 0x9000) {\n // @ts-ignore\n throw new TransportStatusError(status);\n }\n const buf = removeStatus(bufWithStatus);\n const keyLength = buf[0]!;\n const key = buf.slice(1, 1 + keyLength);\n const sig = buf.slice(1 + keyLength + 1);\n const res = {\n confidentiality_key_curve25519: key.toString(\"hex\"),\n confidentiality_key_signature: sig.toString(\"hex\"),\n };\n return res;\n };\n\n type PairingData = {\n blob: string;\n };\n\n const updatePSDPartitionPairing = async ({\n transport,\n pairingData,\n }: {\n transport: any;\n pairingData: PairingData;\n }) => {\n const psdPayload = Buffer.from(pairingData.blob, \"base64\");\n const length = Buffer.alloc(2);\n length.writeUInt16BE(psdPayload.length, 0);\n const final = Buffer.concat([length, psdPayload]);\n await sendByChunk(transport, [0xe0, 0x4e, 0x00, 0x00], final);\n };\n\n return {\n authenticate,\n switchDevice,\n getAttestationCertificate,\n getPublicKey,\n register,\n registerData,\n initiatePairingHandshake,\n validateVaultOperation,\n finalizePairingHandshake,\n startKpatternAsResponder,\n getUserID,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n };\n}\n\nexport default createHWDevice;\n","// @flow\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const U2F_PATH = [0x80564c54, 0x80553246];\nexport const CONFIDENTIALITY_PATH = [0x80564c54, 0x80434e46];\nexport const VALIDATION_PATH = [0x80564c54, 0x8056414c];\nexport const MATCHER_SESSION = 0x01;\nexport const ACCOUNT_MANAGER_SESSION = 0x02;\nexport const U2F_TIMEOUT = \"U2F_5\";\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\nexport const INVALID_DATA = 27264;\nexport const DEVICE_REJECT_ERROR_CODE = 27013;\nexport const INVALID_OR_MISSING_ATTESTATION = 0x6801;\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n","// DISCLAIMER\n//\n// This is blackbox legacy code extract from ledger-vault-front source\n// It's not intended to be clear, but it's working.\nimport { GateUser, GetU2FPubKeyInteraction, Interaction, Interactions } from \"../types\";\nimport { extractSecureChannel } from \"../utils\";\nimport {\n ACCOUNT_MANAGER_SESSION,\n APPID_VAULT_ADMINISTRATOR,\n CONFIDENTIALITY_PATH,\n U2F_PATH,\n VALIDATION_PATH,\n} from \"./constants\";\nimport { isAPIDevice } from \"./createAPIDevice\";\nimport { Device, PairingData } from \"./types\";\n\nfunction noValidChannel(channels: any) {\n const keys = Object.keys(channels);\n return keys.length === 0 || (keys.length === 1 && keys[0] === \"success\");\n}\n\nfunction buildCertif(attestation: Buffer, signature: string) {\n const certLen = attestation.readInt8(32 + 65 + 1);\n return {\n code_hash: attestation.slice(0, 32).toString(\"base64\"),\n attestation_pub: attestation.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestation.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n signature: Buffer.from(signature, \"hex\").toString(\"base64\"),\n };\n}\n\nexport default function createInteractions(device: Device): Interactions {\n const {\n getPublicKey,\n getAttestationCertificate,\n register,\n registerData,\n authenticate,\n validateVaultOperation,\n initiatePairingHandshake,\n finalizePairingHandshake,\n startKpatternAsResponder,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n } = device;\n\n const initPairing: Interaction<any> = {\n responseKey: \"psd_ephemeral_pubkey\",\n action: ({ transport }: any) => initiatePairingHandshake(transport),\n };\n\n const getU2FPublicKey: GetU2FPubKeyInteraction = {\n responseKey: \"u2f_key\",\n action: ({ transport }) => getPublicKey(transport, U2F_PATH, false),\n };\n\n const getSecureChannel: Interaction<any> = {\n responseKey: \"secure_channels\",\n action: async ({ request_id, network }: any) =>\n network(\"GET\", `/requests/${request_id}/challenge`, {}),\n };\n\n const doStartKpatternAsResponder: Interaction<any> = {\n responseKey: \"start-k-pattern\",\n action: async ({ transport, secure_channels, u2f_key }: any) => {\n if (noValidChannel(secure_channels)) {\n throw new Error(\"Request finished\");\n }\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n if (!channel) {\n throw new Error(\"No channel for device\");\n }\n await startKpatternAsResponder(\n transport,\n ACCOUNT_MANAGER_SESSION,\n channel.handshake,\n channel.handshake_attestation,\n channel.partition_id,\n );\n },\n };\n\n const validateDevice: Interaction<any> = {\n responseKey: \"validate_device\",\n action: ({ transport, secure_channels, u2f_key }: any) => {\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n return validateVaultOperation(transport, VALIDATION_PATH, channel);\n },\n };\n\n const validatePayload: Interaction<any> = {\n responseKey: \"validate_payload\",\n action: ({ secure_channels, u2f_key, validate_device }: any) => {\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n\n const handshake_attestation = {\n code_hash: channel.handshake_attestation.code_hash,\n attestation_pub: channel.handshake_attestation.attestation_pub,\n certificate: channel.handshake_attestation.certificate,\n signature: channel.handshake_attestation.signature,\n };\n\n return Promise.resolve({\n public_key: u2f_key.pubKey,\n challenge_response: validate_device,\n handshake: Buffer.from(channel.handshake, \"hex\").toString(\"base64\"),\n handshake_attestation,\n });\n },\n };\n\n const postApproval: Interaction<any> = {\n responseKey: \"post\",\n action: async ({ request_id, u2f_key: { pubKey }, validate_payload, network }: any) =>\n network(\"POST\", `/requests/${request_id}/approve`, {\n requestId: request_id,\n ...validate_payload,\n public_key: pubKey.toString(\"hex\"),\n }),\n };\n\n const postSimpleApproval: Interaction<any> = {\n responseKey: \"post\",\n action: async ({ request_id, u2f_key: { pubKey }, network }: any) =>\n network(\"POST\", `/requests/${request_id}/approve`, {\n requestId: request_id,\n public_key: pubKey.toString(\"hex\"),\n }),\n };\n\n const getConfidentialityPublicKey: Interaction<any> = {\n responseKey: \"confidentiality_key\",\n action: ({ transport }: { transport: any }) => getPublicKey(transport, CONFIDENTIALITY_PATH),\n };\n\n const getAttestation: Interaction<any> = {\n responseKey: \"attestation\",\n action: ({ transport }) => getAttestationCertificate(transport),\n };\n\n const getU2FChallenge: Interaction<any> = {\n responseKey: \"u2f_challenge\",\n action: async ({ u2f_key: { pubKey }, network }: any) => {\n const challenge = await network(\"GET\", `/u2f/authentications/${pubKey}/challenge`);\n if (!challenge.key_handle) {\n throw new Error(\"Unknown device\");\n }\n return challenge;\n },\n };\n\n const getValidationPublicKey: Interaction<any> = {\n responseKey: \"validation_key\",\n action: ({ transport }) => getPublicKey(transport, VALIDATION_PATH),\n };\n\n const u2fAuthenticate: Interaction<any> = {\n device: true,\n responseKey: \"u2f_authenticate\",\n action: ({ transport, u2f_challenge: { challenge, key_handle, role, name }, workspace }: any) =>\n authenticate(\n transport,\n Buffer.from(challenge, \"base64\"),\n APPID_VAULT_ADMINISTRATOR,\n Buffer.from(key_handle, \"hex\"),\n name,\n role,\n workspace,\n ),\n };\n\n const readOnlyLogin: (username: string) => Interaction<any> = (username: string) => ({\n responseKey: \"u2f_sign\",\n action: async ({ network }: any) => {\n const gateUsers: GateUser[] = await network(\"GET\", \"/dev/people/pub_keys\");\n\n const currentUser = gateUsers.find((u) => u.username === username);\n\n if (!currentUser) {\n throw new Error(`Can't find user ${username}`);\n }\n\n return network(\"POST\", `/dev/authentication/view_only/${currentUser.pub_key}`);\n },\n });\n\n const postU2FSignature: Interaction<any> = {\n responseKey: \"u2f_sign\",\n action: ({ u2f_authenticate, network }: any) =>\n network(\"POST\", `/u2f/authentications/authenticate`, {\n authentication: u2f_authenticate.rawResponse,\n }),\n };\n\n const finalizePairing: Interaction<any> = {\n responseKey: \"pairing_payload\",\n action: ({ onboardingRegisterChallenge, transport, u2f_key }) => {\n const secure_channel = extractSecureChannel(\n onboardingRegisterChallenge,\n u2f_key.pubKey.toUpperCase(),\n );\n return finalizePairingHandshake(\n transport,\n secure_channel.handshake,\n secure_channel.ciphertext,\n secure_channel.handshake_attestation!,\n );\n },\n };\n\n const onboardingRegisterDevice: Interaction<any> = {\n responseKey: \"u2f_register\",\n action: async ({ u2f_key, onboardingRegisterChallenge, role, username, transport }) => {\n const secure_channel = extractSecureChannel(\n onboardingRegisterChallenge,\n u2f_key.pubKey.toUpperCase(),\n );\n return register(\n transport,\n Buffer.from(secure_channel.challenge, \"hex\"),\n APPID_VAULT_ADMINISTRATOR,\n username,\n role,\n secure_channel.u2f_register_data,\n );\n },\n };\n\n const onboardingRegisterData: Interaction<any> = {\n responseKey: \"register_data\",\n action: ({ transport, u2f_key, onboardingRegisterChallenge }) => {\n // either the channel is in a map indexed by public key (onboarding)\n // or directly in registerChallenge (register operator)\n /* istanbul ignore next */\n const register_challenge =\n onboardingRegisterChallenge[u2f_key.pubKey.toString(\"hex\").toUpperCase()] ||\n onboardingRegisterChallenge;\n\n return registerData(transport, Buffer.from(register_challenge.challenge, \"hex\"));\n },\n };\n\n const onboardingPostResult: Interaction<any> = {\n responseKey: \"register_input\",\n action: ({\n register_data,\n pairing_payload,\n validation_key,\n u2f_register,\n attestation,\n u2f_key,\n }) => {\n const attestationOffset = 67 + u2f_register.u2f_register.readInt8(66);\n const registration_payload = Buffer.concat([\n u2f_register.u2f_register.slice(0, attestationOffset),\n Buffer.from([attestation.length]),\n attestation,\n u2f_register.u2f_register.slice(attestationOffset),\n ]);\n const data = {\n public_key: u2f_key.pubKey.toString(\"hex\"),\n key_handle: u2f_register.keyHandle.toString(\"hex\"),\n validation_key: validation_key.pubKey.toString(\"hex\"),\n register_data: register_data.toString(\"hex\"),\n u2f_register: registration_payload.toString(\"hex\"),\n certificate: buildCertif(attestation, validation_key.signature),\n pairing_payload,\n };\n return Promise.resolve(data);\n },\n };\n\n const postUserRegistration: Interaction<any> = {\n responseKey: \"result\",\n action: ({ urlID, register_input, member, network }: any) =>\n network(\"POST\", `/requests/registration/${urlID}/authenticate`, {\n ...register_input,\n name: member.user.username,\n }),\n };\n\n const operatorGetChallenge: Interaction<any> = {\n responseKey: \"onboardingRegisterChallenge\",\n action: ({\n u2f_key,\n confidentiality_key,\n attestation,\n urlID,\n network,\n psd_ephemeral_pubkey,\n }: any) => {\n const data = {\n public_key: u2f_key.pubKey,\n confidentiality_key: confidentiality_key.pubKey,\n ...(isAPIDevice(device) && device.getPsdModel() === \"STAX\"\n ? { device_type: \"02\" }\n : /* istanbul ignore next */\n {}),\n ...{\n psd_ephemeral_key: psd_ephemeral_pubkey.pubKey,\n psd_ephemeral_key_attestation: buildCertif(attestation, psd_ephemeral_pubkey.attestation),\n },\n };\n return network(\"POST\", `/requests/registration/${urlID}/challenge`, data);\n },\n };\n\n const ensurePartitionPairing: Interaction<any> = {\n responseKey: \"hasPairing\",\n action: async ({ transport, network }) => {\n // check if there is a partition ID on device\n if (!(await hasPartitionID({ transport }))) {\n // if not, we ask device for its confidentiality key\n const payload = await getConfidentialityKey({ transport });\n // we then initiate a \"prepare psd update\" flow with the hsm...\n const url = \"/authentications/prepare-psd-key-update\";\n const pairingData = await network<PairingData>(\"POST\", url, payload);\n // ...and inject back the data into the device\n await updatePSDPartitionPairing({ transport, pairingData });\n }\n\n // else, all is alright\n return true;\n },\n };\n\n const validateOperation = [\n getU2FPublicKey,\n doStartKpatternAsResponder,\n validateDevice,\n validatePayload,\n ];\n\n const approveFlow = [getSecureChannel, ...validateOperation, postApproval];\n const approveFlowWithoutHSM = [postSimpleApproval];\n\n const loginFlow = [\n getU2FPublicKey,\n getU2FChallenge,\n u2fAuthenticate,\n postU2FSignature,\n ensurePartitionPairing,\n ];\n\n const readOnlyLoginFlow = (username: string) => [readOnlyLogin(username)];\n\n const registerUserFlow = [\n getAttestation,\n getU2FPublicKey,\n getConfidentialityPublicKey,\n initPairing,\n operatorGetChallenge,\n finalizePairing,\n onboardingRegisterDevice,\n getValidationPublicKey,\n onboardingRegisterData,\n onboardingPostResult,\n postUserRegistration,\n ];\n\n return {\n approveFlow,\n approveFlowWithoutHSM,\n doStartKpatternAsResponder,\n getSecureChannel,\n getU2FChallenge,\n getU2FPublicKey,\n initPairing,\n loginFlow,\n readOnlyLoginFlow,\n registerUserFlow,\n u2fAuthenticate,\n validateOperation,\n ensurePartitionPairing,\n finalizePairing,\n getAttestation,\n getConfidentialityPublicKey,\n getValidationPublicKey,\n operatorGetChallenge,\n postU2FSignature,\n postUserRegistration,\n validateDevice,\n validatePayload,\n postApproval,\n postSimpleApproval,\n };\n}\n","import createNetwork from \"./createNetwork\";\n\ntype CreateFaucetOptions = {\n pralineBTCTestnetURL?: string;\n};\n\ntype FaucetOptions = FaucetImplementationOptions & {\n currency: string;\n};\n\ntype Faucet = (opts: FaucetOptions) => Promise<TxHash>;\n\ntype FaucetImplementationOptions = {\n recipient: string;\n amount: string; // amount in base unit (e.g: \"1\" for \"1 BTC\")\n};\n\ntype TxHash = string;\n\ntype FaucetImplementation = (o: FaucetImplementationOptions) => Promise<TxHash>;\n\ntype PralineFaucetResponse = {\n tx: string;\n minedBlocks: string[];\n};\n\nconst createPralineFaucet =\n (url: string): FaucetImplementation =>\n async (opts) => {\n const network = createNetwork({\n baseURL: url,\n });\n const { recipient, amount } = opts;\n const endpoint = `/chain/faucet/${recipient}/${amount}`;\n const res = await network<PralineFaucetResponse>(\"POST\", endpoint);\n return res.tx;\n };\n\nfunction createFaucet(opts: CreateFaucetOptions): Faucet {\n const implems: Record<string, FaucetImplementation> = {};\n\n /* istanbul ignore else */\n if (opts.pralineBTCTestnetURL) {\n implems[\"bitcoin_testnet\"] = createPralineFaucet(opts.pralineBTCTestnetURL);\n }\n\n const faucet: Faucet = (opts: FaucetOptions) => {\n const { currency, ...rest } = opts;\n const implem = implems[currency];\n if (!implem) {\n throw new Error(`Unsupported faucet currency ${opts.currency}`);\n }\n return implem(rest);\n };\n\n return faucet;\n}\n\nexport default createFaucet;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { authenticate, decodeChallenge, getTradelinkPledge, signAndApprove } from \"./apiUser\";\nimport createNetwork from \"./createNetwork\";\nimport fetchTokens from \"./fetchTokens\";\nimport {\n APIRequestResponse,\n DevicesPool,\n GateAccount,\n GateTokenCurrency,\n ManifestAPIV2User,\n ManifestAccount,\n RunnableOptions,\n} from \"./types\";\nimport { APIPostPledgeIncrementData } from \"./types/tradelink\";\nimport { getAccountUnit, getWorkspaceFromGate, serializeUnitValue } from \"./utils\";\n\nexport type CreatePledgeOptions = {\n pool: DevicesPool;\n account: ManifestAccount;\n accountsByName: Record<string, GateAccount>;\n exchange: string;\n amount: string;\n apiUser: ManifestAPIV2User;\n gate: string;\n apiGateway: string;\n};\n\nasync function createPledge(\n {\n pool,\n account,\n accountsByName,\n exchange,\n amount,\n apiUser,\n gate,\n apiGateway,\n }: CreatePledgeOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<APIRequestResponse> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n const apiNetwork = createNetwork({\n baseURL: apiGateway,\n });\n logger.info(`Authenticate for ${apiUser.name}`);\n const workspace = getWorkspaceFromGate(gate);\n const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);\n\n const pledge = await getTradelinkPledge({\n apiNetwork,\n workspace,\n gateAccount,\n bearerToken,\n exchange,\n });\n\n logger.info(\"Create pledge\");\n\n let tokens: GateTokenCurrency[] = [];\n\n // if account is a token one, we need to fetch tokens list from the Gate to properly grab unit\n const shouldLoadTokens = !!gateAccount.contract_address;\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n const unit = getAccountUnit(account, tokens);\n const serializedAmount = serializeUnitValue(unit, amount);\n\n const data: APIPostPledgeIncrementData = {\n type: \"CREATE_PLEDGE_INCREMENT\",\n data: {\n pledge_subaccount_id: pledge.pledge_subaccount_id,\n pledge_data: {\n amount: serializedAmount,\n account_name: account.name,\n currency: gateAccount.currency,\n exchange_name: exchange,\n },\n },\n };\n\n if (shouldLoadTokens) {\n data.data.pledge_data.contract_address = gateAccount.contract_address;\n }\n\n const pledgeRequestResp = await apiNetwork<APIRequestResponse>(\"POST\", \"/requests\", data, {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n logger.info(`Approving pledge ${pledgeRequestResp.id}`);\n logger.info(\"Decode challenge\");\n const apiChallenge = await decodeChallenge({\n apiNetwork,\n workspace,\n bearerToken,\n requestID: pledgeRequestResp.id,\n reviewType: \"APPROVE\",\n });\n logger.info(apiChallenge.decodedChallenge);\n logger.info(\"Sign and approve\");\n return await signAndApprove({\n apiNetwork,\n workspace,\n bearerToken,\n requestID: pledgeRequestResp.id,\n apiUser,\n challenge: apiChallenge.challenge,\n reviewType: \"APPROVE\",\n });\n}\n\nexport default createPledge;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport { authenticate, getTradelinkPledge, getTradelinkRecipient } from \"./apiUser\";\nimport createNetwork from \"./createNetwork\";\nimport {\n APIBitcoinLikeSend,\n APIEstimateFeesResponse,\n APIEthereumLikeSend,\n APIGenericSend,\n DevicesPool,\n GateAccount,\n ManifestAPIV2User,\n ManifestAccount,\n RunnableOptions,\n} from \"./types\";\nimport { APISettlementData } from \"./types/tradelink\";\nimport { getWorkspaceFromGate } from \"./utils\";\n\nexport type CreateSettlementOptions = {\n pool: DevicesPool;\n account: ManifestAccount;\n accountsByName: Record<string, GateAccount>;\n exchange: string;\n percentage: number;\n apiUser: ManifestAPIV2User;\n gate: string;\n apiGateway: string;\n};\n\nasync function createSettlement(\n {\n pool,\n account,\n accountsByName,\n exchange,\n percentage,\n apiUser,\n gate,\n apiGateway,\n }: CreateSettlementOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<any> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n const apiNetwork = createNetwork({\n baseURL: apiGateway,\n });\n logger.info(`Authenticate for ${apiUser.name}`);\n const workspace = getWorkspaceFromGate(gate);\n const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);\n const pledge = await getTradelinkPledge({\n apiNetwork,\n workspace,\n gateAccount,\n bearerToken,\n exchange,\n });\n const recipient = await getTradelinkRecipient({ pool, gateAccount, pledge });\n\n const settlementAmount = Math.floor((pledge.amount * percentage) / 100);\n\n let txIntent: APIGenericSend | APIEthereumLikeSend | APIBitcoinLikeSend = {\n account_id: gateAccount.id,\n fees_strategy: {\n type: \"SPEED\",\n data: {\n speed: \"FAST\",\n },\n },\n transaction_data: {\n account_name: gateAccount.name,\n amount: String(settlementAmount),\n max_fees: \"0\",\n recipient,\n },\n };\n if (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") {\n // get max fees\n txIntent = {\n ...txIntent,\n transaction_data: {\n ...txIntent.transaction_data,\n currency: gateAccount.currency as\n | \"ethereum\"\n | \"ethereum_sepolia\"\n | \"polygon\"\n | \"ethereum_holesky\",\n },\n transaction_type: \"ETHEREUM_LIKE_SEND\",\n };\n } else {\n throw new Error(\"Unsupported account type for outbound transactions\");\n }\n\n // get max fees\n const maxFeesResp = await apiNetwork<APIEstimateFeesResponse>(\n \"POST\",\n \"/transactions/estimate-fees\",\n {\n type: \"CREATE_TRANSACTION\",\n data: txIntent,\n },\n {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n },\n );\n\n // update tx intent with max fees\n txIntent.transaction_data.max_fees = maxFeesResp.max_fees;\n\n const settlementIntent: APISettlementData = {\n id: String(uuidv4()),\n from_pledge_id: pledge.id,\n inbound_transaction_intent: null,\n outbound_transaction_intent: txIntent,\n meta: {},\n };\n\n logger.info(\"Post settlement\");\n // post settlement\n return await apiNetwork<any>(\"POST\", \"/settlements\", settlementIntent, {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n}\n\nexport default createSettlement;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport duration from \"humanize-duration\";\nimport isEqual from \"lodash/isEqual\";\nimport io from \"socket.io-client\";\n\nimport { DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES, DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { DeployOptions, DeploymentStepID, MVInstanceDeployment, RunnableOptions } from \"./types\";\nimport { wait } from \"./utils\";\n\ntype SimplifiedDeploymentState = {\n error: Error | null;\n totalPods: number;\n totalHealthyPods: number;\n currentStep: DeploymentStepID;\n status: \"BUSY\" | \"SUCCESS\" | \"ERROR\";\n};\n\nconst toSimplifiedState = (deployment: MVInstanceDeployment): SimplifiedDeploymentState => {\n return {\n error: deployment.error,\n status: deployment.status,\n currentStep: deployment.currentStep,\n totalPods: deployment.instance.pods.length,\n totalHealthyPods: deployment.instance.pods.filter(\n // TODO centralize types with vault-remote\n (p: any) => p.status === \"HEALTHY\",\n ).length,\n };\n};\n\nexport default async function deploy(\n opts: DeployOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<{ url: string }> {\n const {\n remoteURL = DEFAULT_VAULT_REMOTE_URL,\n watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,\n useHTTPPolling = false,\n ...payload\n } = opts;\n\n const now = Date.now();\n\n logger.step(`Deploying ${payload.name}`);\n\n const url = remoteURL.replace(/https:\\/\\/(.*?)\\./, `https://${payload.name}.`);\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n const deployment: MVInstanceDeployment = await network(\"POST\", \"/deploy\", payload);\n const gateURL = `https://${deployment.instance.name}.${deployment.instance.host}/gate/minivault`;\n const gateNetwork = createNetwork({ baseURL: gateURL });\n\n let state = toSimplifiedState(deployment);\n\n const logState = () => {\n const step = deployment.steps.find((s) => s.key === state.currentStep)!;\n logger.info(`[${step.labelCurrent}] (${state.totalHealthyPods}/${state.totalPods})`);\n };\n\n let resolve: ((value: void | PromiseLike<void>) => void) | null = null,\n reject: ((err: Error) => void) | null = null;\n\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const deployTimeout = setTimeout(\n () => reject!(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),\n watchTimeoutMinutes * 60 * 1000,\n );\n\n const onState = (p: MVInstanceDeployment) => {\n const newState = toSimplifiedState(p);\n if (!isEqual(state, newState)) {\n state = newState;\n logState();\n }\n if (newState.error) {\n reject!(newState.error);\n } else if (newState.status === \"SUCCESS\") {\n const elapsed = Date.now() - now;\n const displayDuration = duration(elapsed, { round: true });\n logger.success(`Successfully deployed instance in ${displayDuration}`);\n logger.success(url);\n clearTimeout(deployTimeout);\n resolve!();\n }\n };\n\n async function pollGate() {\n try {\n logger.info(`Polling onboarding state on ${gateURL}/onboarding/state ...`);\n await gateNetwork(\"GET\", \"/onboarding/state\");\n resolve!();\n } catch {\n await wait(3e3);\n pollGate();\n }\n }\n\n if (useHTTPPolling) {\n logger.info(\"Using HTTP polling strategy (will not output deployment steps)\");\n // important to wait before starting to poll the Gate, else it can have nasty side-effects\n // with local domain resolution (seems that the \"DNS not found\" is kind of cached)\n logger.info(\"Waiting 30 seconds for domain to be deployed...\");\n await wait(30e3);\n logger.info(\"Waiting for Gate to be ready...\");\n pollGate();\n } else {\n logState();\n const socket = io(remoteURL, { transports: [\"polling\"] });\n socket.on(\"deployment-state\", onState);\n socket.emit(\"join-deployment\", deployment.id);\n socket.on(\"disconnect\", () => {\n reject!(new Error(\"Socket disconnected!\"));\n });\n }\n\n await promise;\n\n return { url };\n}\n","export const DEFAULT_VAULT_REMOTE_URL = \"https://remote.minivault.ledger-sbx.com\";\nexport const DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES = 10; // 10 min timeout for deployments\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { DestroyOptions, RunnableOptions } from \"./types\";\n\nexport default async function destroy(\n opts: DestroyOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const { remoteURL = DEFAULT_VAULT_REMOTE_URL, name } = opts;\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n await network(\"DELETE\", `/instances/${name}`);\n logger.success(`Successfully destroyed instance ${name}`);\n\n if (opts.wait) {\n logger.info(\"Waiting for namespace to disappear...\");\n while (await isInstanceAlive(name)) {\n await new Promise((r) => setTimeout(r, 2e3));\n }\n logger.info(\"Done\");\n }\n\n async function isInstanceAlive(name: string) {\n try {\n await network(\"GET\", `/instances/${name}`);\n return true;\n } catch {\n return false;\n }\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { MVInstance, RunnableOptions } from \"./types\";\n\nexport default async function getMVInstances(\n opts: { remoteURL?: string },\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<MVInstance[]> {\n const { remoteURL = DEFAULT_VAULT_REMOTE_URL } = opts;\n\n logger.info(\"Fetching instances...\");\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n const instances = await network(\"GET\", \"/instances\");\n\n return instances;\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport BigNumber from \"bignumber.js\";\n\nimport fetchTokens from \"./fetchTokens\";\nimport {\n DevicesPool,\n GateAccount,\n GateFees,\n GateFeesEIP1559,\n GateTokenCurrency,\n GateTransaction,\n GateTransactionRequest,\n GateWhitelist,\n ManifestAccount,\n ManifestAccountRuleMultiAuth,\n ManifestAccountRuleMultiAuthStep,\n ManifestAccountRuleThreshold,\n ManifestAccountRuleWhitelist,\n ManifestGroup,\n ManifestTransaction,\n ManifestWhitelistAddress,\n RunnableOptions,\n UTXOsPickingStrategy,\n UserContext,\n UserDevice,\n} from \"./types\";\nimport { getAccountUnit, serializeUnitValue } from \"./utils\";\n\ntype SendOptions = {\n pool: DevicesPool;\n transaction: ManifestTransaction;\n account: ManifestAccount;\n groups?: ManifestGroup[];\n accountsByName: Record<string, GateAccount>;\n whitelistsByName: Record<string, GateWhitelist>;\n noApproval?: boolean;\n utxosPickingStrategy?: UTXOsPickingStrategy;\n contractPayload?: string;\n};\n\nconst SUPPORTED_ACCOUNT_TYPES = [\n \"Bitcoin\",\n \"Ethereum\",\n \"Tezos\",\n \"Polkadot\",\n \"Stellar\",\n \"Erc20\",\n \"Solana\",\n];\n\nasync function send(\n {\n pool,\n transaction,\n account,\n accountsByName,\n whitelistsByName,\n noApproval,\n groups = [],\n }: SendOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<GateTransaction> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n\n if (\n transaction.feesLevel === \"CUSTOM\" &&\n gateAccount.account_type !== \"Ethereum\" &&\n gateAccount.account_type !== \"Erc20\"\n ) {\n throw new Error(\"Custom fees are only supported for Ethereum and ERC20 accounts\");\n }\n\n if (transaction.feesLevel === \"CUSTOM\" && (!transaction.gasPrice || !transaction.gasLimit)) {\n throw new Error(\"Gas price and gas limit are required when using custom fees\");\n }\n\n if (SUPPORTED_ACCOUNT_TYPES.indexOf(gateAccount.account_type) === -1) {\n throw new Error(`Account type ${gateAccount.account_type} is not supported YET`);\n }\n\n let tokens: GateTokenCurrency[] = [];\n\n // if account is a token one, we need to fetch tokens list from the Gate to properly grab unit\n const shouldLoadTokens = !!gateAccount.contract_address;\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n const unit = getAccountUnit(account, tokens);\n const serializedAmount = serializeUnitValue(unit, transaction.amount);\n\n const matchingRule = getMatchingMultiAuthRule({\n account,\n transaction,\n whitelistsByName,\n });\n\n const { steps } = matchingRule;\n\n // users that have approved\n const usedUsers: UserDevice[] = [];\n\n // log with first available user in creator step\n const firstStep = steps[0];\n /* istanbul ignore if */\n if (!firstStep) throw new Error(`No creator step found`);\n const creator = await loginWithStepUser({ pool, groups, step: firstStep, usedUsers });\n\n const feesPayload = {\n amount: serializedAmount,\n recipient: transaction.recipient,\n fees_level: transaction.feesLevel || \"NORMAL\",\n ...(transaction.transactionType ? { type: transaction.transactionType } : {}),\n };\n\n if (gateAccount.account_type === \"Bitcoin\" && transaction.utxosPickingStrategy) {\n Object.assign(feesPayload, {\n utxo_picking_strategy: transaction.utxosPickingStrategy,\n });\n }\n\n type GateFeesResponse = GateFees | GateFeesEIP1559;\n\n let fees: GateFees = {\n fees_per_byte: \"0\",\n fees: transaction.feesLevel || \"NORMAL\",\n gas_limit: transaction.gasLimit!,\n gas_price: transaction.gasPrice!,\n };\n\n let feesEIP1559: GateFeesEIP1559 = {\n base_fees: transaction.feesLevel || \"NORMAL\",\n gas_limit: transaction.gasLimit!,\n max_fees: transaction.maxFees!,\n max_fees_buffer_factor: transaction.maxFeesBufferFactor!,\n priority_fees: transaction.priorityFees!,\n };\n\n if (transaction.feesLevel !== \"CUSTOM\") {\n const feesResponse = await creator.network<GateFeesResponse>(\n \"POST\",\n `/accounts/${gateAccount.id}/transactions/fees`,\n feesPayload,\n );\n\n \"priority_fees\" in feesResponse ? (feesEIP1559 = feesResponse) : (fees = feesResponse);\n }\n\n let txFees = {};\n if (\n (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") &&\n !feesEIP1559.priority_fees\n ) {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.gas_price).times(fees.gas_limit).toFixed(),\n gas_price: new BigNumber(fees.gas_price).toFixed(),\n gas_limit: new BigNumber(fees.gas_limit).toFixed(),\n };\n }\n\n if (\n (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") &&\n feesEIP1559.priority_fees\n ) {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(feesEIP1559.max_fees).toFixed(),\n gas_limit: new BigNumber(feesEIP1559.gas_limit).toFixed(),\n priority_fees: new BigNumber(feesEIP1559.priority_fees).toFixed(),\n max_fees_buffer_factor: new BigNumber(feesEIP1559.max_fees_buffer_factor).toFixed(),\n };\n }\n\n if (gateAccount.account_type === \"Bitcoin\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n fees_per_byte: new BigNumber(fees.fees_per_byte).toFixed(),\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n\n if (transaction.utxosPickingStrategy) {\n Object.assign(txFees, {\n utxo_picking_strategy: transaction.utxosPickingStrategy,\n });\n }\n }\n if (gateAccount.account_type === \"Tezos\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n gas_limit: new BigNumber(fees.gas_limit).toFixed(),\n max_fees: new BigNumber(fees.fees).toFixed(),\n storage_limit: fees.storage_limit,\n };\n }\n if (gateAccount.account_type === \"Polkadot\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n if (gateAccount.account_type === \"Stellar\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n\n if (gateAccount.account_type === \"Solana\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n\n const payload = {\n type: \"CREATE_TRANSACTION\",\n account_id: gateAccount.id,\n transaction: {\n ...(transaction.transactionType ? { type: transaction.transactionType } : {}),\n recipient: transaction.recipient,\n amount: serializedAmount,\n ...txFees,\n },\n };\n\n if (transaction.contractPayload) {\n Object.assign(payload.transaction, {\n contract_payload: transaction.contractPayload,\n });\n }\n\n if (transaction.title || transaction.comment) {\n Object.assign(payload.transaction, {\n note: {\n title: transaction.title || \"\",\n content: transaction.comment || \"\",\n },\n });\n }\n\n logger.info(\"Creating transaction request\");\n const request = await creator.post<GateTransactionRequest>(\"/requests\", payload);\n\n if (noApproval) {\n logger.info(\"Transaction created (skipped approval)\");\n } else {\n logger.info(\"Approving request\");\n await creator.approveRequest(request);\n\n // successively go through each step\n for (let i = 1; i < steps.length; i++) {\n const step = steps[i];\n /* istanbul ignore if */\n if (!step) throw new Error(`No step at index ${i}`);\n // fill the quorum of approvals\n for (let j = 0; j < step.quorum; j++) {\n const approver = await loginWithStepUser({ pool, step, usedUsers, groups });\n logger.info(\"Approving request\");\n await approver.approveRequest(request);\n }\n }\n\n logger.success(\"Transaction created & approved\");\n }\n\n const tx = await creator.network<GateTransaction>(\"GET\", `/transactions/${request.target_id}`);\n\n return tx;\n}\n\n// TODO add polkadot\nconst TX_TYPE_TO_PRESET = {\n DELEGATE: \"TEZOS_DELEGATION\",\n UNDELEGATE: \"TEZOS_DELEGATION\",\n};\nexport function getMatchingMultiAuthRule({\n account,\n transaction,\n whitelistsByName,\n}: {\n account: ManifestAccount;\n transaction: ManifestTransaction;\n whitelistsByName: Record<string, GateWhitelist>;\n}): ManifestAccountRuleMultiAuth {\n let multiAuthRule;\n /* istanbul ignore if */\n if (!account.rules) {\n throw new Error(`No rules defined in account '${account.name}'`);\n }\n\n // VG-8212 we first filter rulesSet according to transactionType\n const rulesSet = account.rules.filter((rules) => {\n // if there is no txType we remove all rules that contain a txType preset\n // like TEZOS_DELEGATION\n if (\n !transaction.transactionType ||\n !Object.keys(TX_TYPE_TO_PRESET).includes(transaction.transactionType)\n ) {\n return !rules.some((r) => Object.values(TX_TYPE_TO_PRESET).includes(r.type));\n }\n\n // otherwise we only take rules with the preset\n // corresponding to txType\n return rules.some(\n // @ts-ignore\n (r) => r.type === TX_TYPE_TO_PRESET[transaction.transactionType],\n );\n });\n rulesSet.find((rules) => {\n // @ts-ignore\n const multiAuth: ManifestAccountRuleMultiAuth = rules.find(\n (r) => r.type === \"MULTI_AUTHORIZATIONS\",\n );\n // @ts-ignore\n const threshold: ManifestAccountRuleThreshold = rules.find((r) => r.type === \"THRESHOLD\");\n // @ts-ignore\n const whitelist: ManifestAccountRuleWhitelist = rules.find((r) => r.type === \"WHITELIST\");\n\n if (threshold) {\n const amount = new BigNumber(transaction.amount);\n if (threshold.min && amount.isLessThan(threshold.min)) {\n return false;\n }\n /* istanbul ignore else */\n if (threshold.max && amount.isGreaterThan(threshold.max)) {\n return false;\n }\n }\n\n if (whitelist) {\n const allowedAddresses = whitelist.whitelists.reduce(\n (acc: ManifestWhitelistAddress[], curr: string) => {\n const w = whitelistsByName[curr];\n /* istanbul ignore next */\n if (!w) return [];\n return [...acc, ...w.addresses];\n },\n [],\n );\n\n if (!allowedAddresses.find((a) => a.address === transaction.recipient)) {\n throw new Error(`Can't find ${transaction.recipient} in account whitelist(s)`);\n }\n }\n\n multiAuthRule = multiAuth;\n return true;\n });\n\n if (!multiAuthRule) {\n throw new Error(`Can't find matching rule for transaction`);\n }\n\n return multiAuthRule;\n}\n\nfunction loginWithStepUser({\n pool,\n step,\n usedUsers,\n groups,\n}: {\n pool: DevicesPool;\n step: ManifestAccountRuleMultiAuthStep;\n usedUsers: UserDevice[];\n groups: ManifestGroup[];\n}): Promise<UserContext> {\n const user =\n \"group\" in step\n ? groups!.find((g) => g.name === step.group)!.users.find((u) => usedUsers.indexOf(u) === -1)\n : step.users.find((u) => usedUsers.indexOf(u) === -1);\n if (!user) {\n throw new Error(`No available user to approve`);\n }\n if (typeof user === \"string\") {\n throw new Error(`Sending with API user is not supported YET`);\n }\n usedUsers.push(user);\n return pool.login(user);\n}\n\nexport default send;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport duration from \"humanize-duration\";\nimport io from \"socket.io-client\";\n\nimport { DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES, DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { RunnableOptions, UpgradeOptions } from \"./types\";\n\ntype LoggerType = \"step\" | \"info\" | \"success\" | \"error\";\n\nexport type SocketLog = {\n msg: string;\n type: LoggerType;\n};\n\nexport default async function upgrade(\n opts: UpgradeOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const {\n remoteURL = DEFAULT_VAULT_REMOTE_URL,\n watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,\n ...payload\n } = opts;\n\n const now = Date.now();\n\n logger.step(`Upgrading ${payload.name}`);\n\n const socket = io(remoteURL);\n const network = createNetwork({ baseURL: remoteURL });\n const { jobID } = await network<{ jobID: string }>(\"PUT\", \"/api/upgrade\", payload);\n\n let resolve: ((value: void | PromiseLike<void>) => void) | null = null;\n let reject: ((err: Error) => void) | null = null;\n\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const upgradeTimeout = setTimeout(\n () => reject!(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),\n watchTimeoutMinutes * 60 * 1000,\n );\n\n socket.on(\"job-log\", (msg: SocketLog) => logger[msg.type](`(remote log)> ${msg.msg}`));\n\n socket.on(\"job-success\", () => {\n const elapsed = Date.now() - now;\n const displayDuration = duration(elapsed, { round: true });\n logger.success(`Successfully upgraded instance in ${displayDuration}`);\n clearTimeout(upgradeTimeout);\n resolve!();\n });\n\n socket.on(\"job-error\", (errMsg: string) => reject!(new Error(errMsg)));\n\n socket.emit(\"join-job\", jobID);\n\n await promise;\n}\n","import sortBy from \"lodash/sortBy\";\n\nimport { getCryptoCurrencyById } from \"./currencies\";\nimport deserializeManifest from \"./deserializeManifest\";\nimport { Manifest } from \"./types\";\n\nexport default function validateManifest(_manifest: Manifest): void {\n const manifest = deserializeManifest(_manifest);\n if (manifest.users) {\n const allRegularUsers = sortBy(\n [...(manifest.users.operators || []), ...(manifest.users.admins || [])],\n (u) => u.device,\n );\n if (allRegularUsers.length) {\n allRegularUsers.forEach((u, i) => {\n if (i > 0) {\n const user = allRegularUsers[i];\n /* istanbul ignore if */\n if (!user) throw new Error(`Can't find user with index ${i}`);\n const prevUser = allRegularUsers[i - 1];\n /* istanbul ignore if */\n if (!prevUser) throw new Error(`Can't find user with index ${i - 1}`);\n if (user.device !== prevUser.device + 1) {\n throw new Error(\"Users indexes are not contiguous\");\n }\n } else {\n if (u.device !== 10) {\n throw new Error(`Users devices indexes must start at 10`);\n }\n }\n });\n }\n }\n\n if (manifest.groups) {\n manifest.groups.forEach((group) => {\n group.users.forEach((id) => {\n if (\n !manifest.users ||\n (typeof id === \"number\" &&\n (!manifest.users.operators ||\n !manifest.users.operators.find((i) => i.device === id))) ||\n (typeof id === \"string\" &&\n (!manifest.users.api || !manifest.users.api.find((i) => i.name === id)) &&\n (!manifest.users.apiV2 || !manifest.users.apiV2.find((i) => i.name === id)))\n ) {\n throw new Error(`Group ${group.name} is referring to unknown operator ${id}`);\n }\n });\n });\n }\n if (manifest.accounts) {\n manifest.accounts.forEach((account) => {\n if (!(\"currency\" in account) && !(\"contractAddress\" in account)) {\n throw new Error(\n // @ts-ignore\n `No currency or contractAddress in account \"${account.name}\"`,\n );\n }\n /* istanbul ignore else */\n if (\"currency\" in account) {\n try {\n getCryptoCurrencyById(account.currency);\n } catch (e) {\n throw Error(`Invalid account currency: \"${account.currency}\"`);\n }\n }\n if (\"policy\" in account) {\n if (account.rules && account.rules.length) {\n throw new Error(\"Account cannot have both rules and policy\");\n }\n if (!manifest.policies || !manifest.policies.find((p) => p.name === account.policy)) {\n throw new Error(`Account \"${account.name}\": policy \"${account.policy}\" does not exist`);\n }\n }\n if (account.rules && account.rules.length) {\n account.rules.forEach((rule) => {\n rule.forEach((r, j) => {\n if (r.type === \"MULTI_AUTHORIZATIONS\") {\n r.steps.forEach((step, i) => {\n if (\"group\" in step && \"users\" in step) {\n throw new Error(\n `Can't have both 'users' and 'group' in step ${\n i + 1\n } of MULTI_AUTHORIZATIONS rule of rules set ${j + 1} of account \"${\n account.name\n }\"`,\n );\n } else if (\"group\" in step) {\n /* istanbul ignore else */\n if (\n !manifest.groups ||\n !manifest.groups.find((group) => group.name === step.group)\n ) {\n throw new Error(\n `Account \"${account.name}\": Group \"${step.group}\" does not exist`,\n );\n }\n } else if (\"users\" in step) {\n step.users.forEach((userId) => {\n if (\n !manifest.users ||\n (typeof userId === \"number\" &&\n (!manifest.users.operators ||\n !manifest.users.operators.find((o) => o.device === userId))) ||\n (typeof userId === \"string\" &&\n (!manifest.users.api ||\n !manifest.users.api.find((o) => o.name === userId)) &&\n (!manifest.users.apiV2 ||\n !manifest.users.apiV2.find((o) => o.name === userId)))\n ) {\n throw new Error(\n `Account \"${account.name}\": User with id ${userId} does not exist`,\n );\n }\n });\n } else {\n throw new Error(\n `No group or users in step ${i + 1} of MULTI_AUTHORIZATIONS rule of rules set ${\n j + 1\n } of account \"${account.name}\"`,\n );\n }\n });\n }\n if (r.type === \"WHITELIST\") {\n r.whitelists.forEach((whitelist) => {\n /* istanbul ignore else */\n if (\n !manifest.whitelists ||\n !manifest.whitelists.find((w) => w.name === whitelist)\n ) {\n throw new Error(\n `Account \"${account.name}\": whitelist \"${whitelist}\" does not exist`,\n );\n }\n });\n }\n });\n });\n } else {\n /* istanbul ignore else */\n if (!manifest.users || !manifest.users.operators || !manifest.users.operators.length) {\n throw new Error(\"Need at least 1 operator\");\n }\n }\n\n if (account.tradelink_data) {\n const accountTradelinkData = account.tradelink_data;\n\n if (!manifest.tradelink) {\n throw new Error(\"Account has tradelink_data but manifest does not\");\n }\n\n const { exchanges, assetManagers, custodians } = manifest.tradelink;\n\n accountTradelinkData.exchanges.forEach((exchange) => {\n if (!exchanges.find((e) => e.name === exchange.name)) {\n throw new Error(\n `Account \"${account.name}\": exchange \"${exchange.name}\" does not exist`,\n );\n }\n });\n\n if (!assetManagers.find((e) => e.name === accountTradelinkData.asset_manager.name)) {\n throw new Error(\n `Account \"${account.name}\": asset manager \"${account.tradelink_data.asset_manager.name}\" does not exist`,\n );\n }\n\n if (!custodians.find((e) => e.name === accountTradelinkData.custodian.name)) {\n throw new Error(\n `Account \"${account.name}\": custodian \"${account.tradelink_data.custodian.name}\" does not exist`,\n );\n }\n }\n });\n }\n if (manifest.entities) {\n manifest.entities.forEach((entity) => {\n entity.accounts?.forEach((accountName) => {\n if (!manifest.accounts || !manifest.accounts.find(({ name }) => accountName === name)) {\n throw new Error(\n `Entity ${entity.name} is referring to an unknown account ${accountName}`,\n );\n }\n });\n });\n }\n\n /* istanbul ignore else */\n if (manifest.whitelists) {\n manifest.whitelists.forEach((whitelist) => {\n whitelist.addresses.map((address) => {\n try {\n getCryptoCurrencyById(address.currency);\n } catch (e) {\n throw Error(`Invalid whitelist currency: \"${address.currency}\"`);\n }\n });\n });\n }\n\n if (manifest.tradelink) {\n manifest.tradelink.exchanges.forEach((exchange) => {\n /* istanbul ignore next */\n const operators = exchange.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = exchange.users?.apiV2 || [];\n\n if (!operators.length && !apiV2.length) {\n throw new Error(`Exchange ${exchange.name} has no users`);\n }\n\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(`Exchange ${exchange.name} refers to unknown operator ${operator}`);\n }\n });\n\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Exchange ${exchange.name} refers to unknown API User ${api}`);\n }\n });\n });\n\n manifest.tradelink.assetManagers.forEach((assetManager) => {\n /* istanbul ignore next */\n const operators = assetManager.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = assetManager.users?.apiV2 || [];\n if (!operators.length && !apiV2.length) {\n throw new Error(`Asset Manager ${assetManager.name} has no users`);\n }\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(\n `Asset Manager ${assetManager.name} refers to unknown operator ${operator}`,\n );\n }\n });\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Asset Manager ${assetManager.name} refers to unknown API User ${api}`);\n }\n });\n });\n\n manifest.tradelink.custodians.forEach((custodian) => {\n /* istanbul ignore next */\n const operators = custodian.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = custodian.users?.apiV2 || [];\n if (!operators.length && !apiV2.length) {\n throw new Error(`Custodian ${custodian.name} has no users`);\n }\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(`Custodian ${custodian.name} refers to unknown operator ${operator}`);\n }\n });\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Custodian ${custodian.name} refers to unknown API User ${api}`);\n }\n });\n });\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport createHSMBridge from \"./createHSMBridge\";\nimport createNetwork from \"./createNetwork\";\nimport { RunnableOptions, WipeOptions } from \"./types\";\n\nasync function wipeBackend(\n wipeOpts: WipeOptions,\n runnableOpts: RunnableOptions = {},\n): Promise<void> {\n const { logger = SILENT_LOGGER } = runnableOpts;\n\n if (!wipeOpts.hsmEndpoint) {\n throw new Error(\"HSM endpoint is empty, please provide one\");\n }\n\n logger.step(\"Wiping backend data\");\n const gateNetworkOptions = { baseURL: wipeOpts.gate };\n const gateNetwork = createNetwork(gateNetworkOptions);\n\n try {\n await gateNetwork(\"POST\", \"/maintenance/wipe\");\n } catch {\n logger.info(\n \"[WARN] POST for /maintenance/wipe did not worked, attempting legacy GET (see VG-8670)\",\n );\n await gateNetwork(\"GET\", \"/maintenance/wipe\");\n logger.info(\"[WARN] legacy GET /maintenance/wipe successful\");\n }\n\n if (wipeOpts.lam) {\n const lamNetworkOptions = { baseURL: wipeOpts.lam };\n const headersAPILam = wipeOpts.lamAPIKey\n ? { headers: { \"X-Ledger-API-Key\": wipeOpts.lamAPIKey } }\n : {};\n const lamNetwork = createNetwork(lamNetworkOptions);\n\n logger.step(\"Wiping LAM data\");\n try {\n const lamWipeResponse = await lamNetwork(\n \"DELETE\",\n \"/api_users/maintenance/wipe\",\n {},\n headersAPILam,\n );\n if (!lamWipeResponse) {\n logger.info(\"[WARN] LAM wipe did not work as expected!\");\n }\n } catch (err) {\n // @ts-expect-error\n logger.info(`Error while wiping the LAM: ${err.toString()}`);\n }\n }\n\n logger.step(\"Resetting HSM compartment\");\n const hsmBridge = createHSMBridge(wipeOpts);\n await hsmBridge.resetCompartment(wipeOpts.hsmCompartmentID, runnableOpts);\n\n logger.success(\"Wiped backend data\");\n}\n\nexport default wipeBackend;\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/vault-ts/vault-ts/packages/common/lib/index.js","../src/bakeManifest.ts","../src/deserializeManifest.ts","../src/fetchTokens.ts","../src/revault-compat.ts","../src/genSeed.ts","../src/utilsComparison.ts","../src/configcat.ts","../src/createDevicesPool.ts","../src/device/index.ts","../src/device/createAPIDevice.ts","../src/device/createHWDevice.ts","../src/device/constants.ts","../src/device/createInteractions.ts","../src/createFaucet.ts","../src/createPledge.ts","../src/createSettlement.ts","../src/deployInstance.ts","../src/constants.ts","../src/destroy.ts","../src/getMVInstances.ts","../src/send.ts","../src/upgradeInstance.ts","../src/validateManifest.ts","../src/wipeBackend.ts"],"names":["genSeed","SILENT_LOGGER","environment","registerData","psdModel","authenticate","ROLE_TO_BYTES","chalk","salt","invariant","interactions","network","notifierURL","duration","sortBy"],"mappings":"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AAA;AACH,EAAA;AAGyB,EAAA;AACH,IAAA;AACE,MAAA;AACd,QAAA;AACF,UAAA;AACU,QAAA;AACE,UAAA;AACd,QAAA;AACD,MAAA;AACF,IAAA;AACH,EAAA;AAEwB,EAAA;AACH,IAAA;AAEC,MAAA;AAEJ,MAAA;AAEC,MAAA;AACG,QAAA;AAClB,MAAA;AAEmB,MAAA;AAEH,QAAA;AACI,UAAA;AAClB,QAAA;AACD,MAAA;AAEc,MAAA;AAEC,QAAA;AACI,UAAA;AAClB,QAAA;AACD,MAAA;AACF,IAAA;AAEkB,IAAA;AAEC,MAAA;AAEJ,MAAA;AACC,MAAA;AACG,QAAA;AAClB,MAAA;AACmB,MAAA;AAEH,QAAA;AACF,UAAA;AACR,YAAA;AACF,UAAA;AACF,QAAA;AACD,MAAA;AACc,MAAA;AAEC,QAAA;AACI,UAAA;AAClB,QAAA;AACD,MAAA;AACF,IAAA;AAEkB,IAAA;AAEC,MAAA;AAEJ,MAAA;AACC,MAAA;AACG,QAAA;AAClB,MAAA;AACmB,MAAA;AAEH,QAAA;AACI,UAAA;AAClB,QAAA;AACD,MAAA;AACc,MAAA;AAEC,QAAA;AACI,UAAA;AAClB,QAAA;AACD,MAAA;AACF,IAAA;AACH,EAAA;AACF;AvBs0F4B;AACA;AwB1lGnBb;AAOP;AAGiBA,EAAAA;AAEH,EAAA;AACI,IAAA;AAClB,EAAA;AAEY,EAAA;AACN,EAAA;AACc,EAAA;AAEhB,EAAA;AACgB,IAAA;AACZ,EAAA;AACC,IAAA;AACL,MAAA;AACF,IAAA;AACkB,IAAA;AACN,IAAA;AACd,EAAA;AAEkB,EAAA;AACV,IAAA;AACgB,IAAA;AAGH,IAAA;AAEP,IAAA;AACR,IAAA;AACI,MAAA;AACJ,QAAA;AACA,QAAA;AACC,QAAA;AACD,QAAA;AACF,MAAA;AACsB,MAAA;AACR,QAAA;AACd,MAAA;AACY,IAAA;AAEA,MAAA;AACd,IAAA;AACF,EAAA;AAEY,EAAA;AACM,EAAA;AACF,EAAA;AAED,EAAA;AACjB;AAEe;AxBykGa;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/vault-ts/vault-ts/packages/common/lib/index.js","sourcesContent":[null,"import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport chalk from \"chalk\";\nimport orderBy from \"lodash/orderBy\";\nimport sortBy from \"lodash/sortBy\";\n\nimport createDefaultRunner from \"./createDefaultRunner\";\nimport { genKeys } from \"./crypto/utils\";\nimport deserializeManifest from \"./deserializeManifest\";\nimport fetchTokens from \"./fetchTokens\";\nimport { UsersByDevice, UsersByName } from \"./prepareRequest\";\nimport recipeManifest, { GateAccountsByName, RecipeRawData } from \"./recipeManifest\";\nimport { onboardWithRevault } from \"./revault-compat\";\nimport {\n BakeManifestOptions,\n DevicesPool,\n GateTokenCurrency,\n GateTradelinkAssetManagerRequest,\n GateTradelinkExchangeRequest,\n GateUser,\n MRBakeTradelinkEntityOnboardingParams,\n MRBakeTradelinkEntityParams,\n Manifest,\n ManifestAPIUser,\n ManifestAPIV2User,\n ManifestAccount,\n ManifestContractApproval,\n ManifestExchange,\n ManifestGroup,\n ManifestPolicy,\n ManifestTradelink,\n ManifestTradelinkEntity,\n ManifestTradelinkEntityWithAddresses,\n ManifestVaultEntity,\n ManifestWhitelist,\n ManifestWorkspaceRule,\n UserWithDevice,\n} from \"./types\";\nimport { getDefaultUsername, queue } from \"./utils\";\nimport {\n areAccountsDifferent,\n areGroupsDifferent,\n areVaultEntitiesDifferent,\n areWhitelistsDifferent,\n areWorkspaceRulesDifferent,\n} from \"./utilsComparison\";\n\nconst DEFAULT_QUORUM = 2;\n\ntype UserRoleAndDevice = { role: \"admin\" | \"operator\"; device: number };\n\nexport default async function bakeManifest(\n _manifest: Manifest,\n pool: DevicesPool,\n options: BakeManifestOptions = {},\n): Promise<void> {\n const { logger = SILENT_LOGGER, waitForActive } = options;\n const manifest = deserializeManifest(_manifest);\n\n // VG-12511 ability to define salt directly from manifest\n if (_manifest.salt) {\n pool.setSalt(_manifest.salt);\n }\n\n /* istanbul ignore if */\n if (options.revaultOnboarding) {\n logger.step(\"[REVAULT] Onboarding\");\n await onboardWithRevault(\n {\n ...pool.getRevaultCompatOptions(),\n ...options.revaultOnboarding,\n },\n logger,\n );\n }\n\n const runner = options.runner || createDefaultRunner(pool, options);\n\n // build a manifest from the current gate, in order to check\n // if stuff needs to be EDITED or not\n const manifestFromGate = await recipeManifest(pool, { saveAccountsIndexes: true });\n\n const { rawData } = manifestFromGate;\n const {\n groupsByName,\n whitelistsByName,\n accountsByName,\n usersWithDevice,\n usersWithoutDevice,\n vaultEntitiesByName,\n exchangesByName,\n policiesByName,\n tradelinkCustodiansByName,\n tradelinkHSMCustodiansByName,\n tradelinkExchangesByName,\n tradelinkHSMExchangesByName,\n tradelinkHSMAssetManagersByName,\n tradelinkAssetManagersByName,\n tradelinkOnboardingStatus,\n }: RecipeRawData = rawData;\n const gateManifest = manifestFromGate.manifest;\n\n const __usersByDevice: UsersByDevice = {\n ...usersWithDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.deviceIndex]: curr.user } };\n }, {}),\n };\n\n const __usersByName: UsersByName = {\n ...usersWithDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.user.username]: curr.user } };\n }, {}),\n ...usersWithoutDevice.reduce((acc, curr) => {\n return { ...acc, ...{ [curr.user.username]: curr.user } };\n }, {}),\n };\n\n const __groupsIDsByName: Record<string, number> = {\n ...Object.keys(groupsByName).reduce((acc, curr) => {\n const group = groupsByName[curr];\n /* istanbul ignore if */\n if (!group) return acc;\n return { ...acc, ...{ [curr]: group.id } };\n }, {}),\n };\n const __whitelistsIDsByName: Record<string, number> = {\n ...Object.keys(whitelistsByName).reduce((acc, curr) => {\n const whitelist = whitelistsByName[curr];\n /* istanbul ignore if */\n if (!whitelist) return acc;\n return { ...acc, ...{ [curr]: whitelist.id } };\n }, {}),\n };\n const __policiesIDsByName: Record<string, number> = {\n ...Object.keys(policiesByName).reduce((acc, curr) => {\n const policy = policiesByName[curr];\n /* istanbul ignore if */\n if (!policy) return acc;\n return { ...acc, ...{ [curr]: policy.id } };\n }, {}),\n };\n const __hsmAMsIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMAssetManagersByName).reduce((acc, curr) => {\n const am = tradelinkHSMAssetManagersByName[curr];\n /* istanbul ignore if */\n if (!am) return acc;\n return { ...acc, ...{ [curr]: am.id } };\n }, {}),\n };\n const __hsmExchangesIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMExchangesByName).reduce((acc, curr) => {\n const exchange = tradelinkHSMExchangesByName[curr];\n /* istanbul ignore if */\n if (!exchange) return acc;\n return { ...acc, ...{ [curr]: exchange.id } };\n }, {}),\n };\n const __hsmCustodiansIDsByName: Record<string, number> = {\n ...Object.keys(tradelinkHSMCustodiansByName).reduce((acc, curr) => {\n const custodian = tradelinkHSMCustodiansByName[curr];\n /* istanbul ignore if */\n if (!custodian) return acc;\n return { ...acc, ...{ [curr]: custodian.id } };\n }, {}),\n };\n\n const __accountsByName = { ...accountsByName };\n let tokens: GateTokenCurrency[] = [];\n\n // load tokens if there is at least 1 token account described in manifest\n const shouldLoadTokens =\n manifest.accounts && manifest.accounts.find((account) => \"contractAddress\" in account);\n\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n if (manifest.users) {\n const adminsAndOperators: UserRoleAndDevice[] = [];\n if (manifest.users.admins) {\n adminsAndOperators.push(\n ...manifest.users.admins.map(\n (a): UserRoleAndDevice => ({ role: \"admin\", device: a.device }),\n ),\n );\n }\n\n // FIXME OMG ISTANBUL THERE IS NO ELSE SO WTF?\n /* istanbul ignore else */\n if (manifest.users.operators) {\n adminsAndOperators.push(\n ...manifest.users.operators.map(\n (a): UserRoleAndDevice => ({ role: \"operator\", device: a.device }),\n ),\n );\n }\n\n await queue(\n sortBy(adminsAndOperators, (u) => u.device),\n genCreateAdminsAndOperators(),\n );\n\n /* istanbul ignore else */\n if (manifest.users.api) {\n logger.step(\"LAM users creation\");\n await queue(manifest.users.api, genCreateAPIUser());\n }\n\n if (manifest.users.apiV2) {\n logger.step(\"API users creation\");\n await queue(manifest.users.apiV2, genCreateAPIV2User());\n }\n }\n\n if (manifest.workspaceRules) {\n logger.step(\"Workspace rules creation\");\n await queue(manifest.workspaceRules, createWorkspaceRule);\n }\n\n if (manifest.groups) {\n logger.step(\"Groups\");\n await queue(manifest.groups, createGroup);\n }\n\n if (manifest.whitelists) {\n logger.step(\"Whitelists\");\n await queue(manifest.whitelists, createWhitelist);\n }\n\n if (manifest.tradelink) {\n logger.step(\"TradeLink\");\n\n // approve TRADELINK contracts\n const adminDevices = await pool.getOnboardingAdminDevices();\n await queue(\n adminDevices.map((a) => ({ user: a[1], contractNames: [\"TRADELINK_ADDENDUM\", \"TRADELINK\"] })),\n approveContracts,\n );\n\n await queue(manifest.tradelink.custodians, createTradelinkCustodian);\n await queue(manifest.tradelink.assetManagers, createTradelinkAssetManager);\n await queue(manifest.tradelink.exchanges, createTradelinkExchange);\n await createTradelinkNetwork(manifest.tradelink);\n if (manifest.tradelink.onboarded) {\n await onboardTradelinkNetwork(manifest.tradelink);\n await queue(manifest.tradelink.exchanges, (exchange) =>\n onboardTradelinkEntity(exchange, \"exchanges\"),\n );\n await queue(manifest.tradelink.assetManagers, (am) =>\n onboardTradelinkEntity(am, \"asset_managers\"),\n );\n }\n }\n\n if (manifest.policies) {\n logger.step(\"Policies\");\n await queue(manifest.policies, createVaultPolicy);\n }\n\n if (manifest.accounts) {\n logger.step(\"Accounts\");\n manifest.accounts = orderBy(manifest.accounts, \"contractAddress\", \"desc\");\n await queue(manifest.accounts, createAccount);\n }\n\n if (manifest.entities) {\n logger.step(\"Entities\");\n await queue(manifest.entities, createVaultEntity);\n }\n\n if (manifest.exchanges) {\n logger.step(\"Exchanges\");\n await queue(manifest.exchanges, createVaultExchange);\n }\n\n if (manifest.quorum) {\n logger.step(\"Quorum\");\n await editQuorum(manifest.quorum);\n }\n\n if (manifest.contractApprovals) {\n logger.step(\"Contract Approvals\");\n await queue(manifest.contractApprovals, approveContracts);\n }\n\n logger.success(\"Done\");\n\n async function editQuorum(quorum: number) {\n if (gateManifest.quorum === quorum || (quorum === DEFAULT_QUORUM && !gateManifest.quorum)) {\n logger.info(`Quorum is already set to ${quorum}, skipping`);\n return;\n }\n await runner.editQuorum({ quorum });\n logger.info(`Quorum updated to ${quorum}`);\n }\n\n function genCreateAdminsAndOperators() {\n return async function createGenericUser(user: UserRoleAndDevice) {\n const fn = genCreateUser(user.role);\n return fn(user.device);\n };\n }\n\n async function setViewAll(username: string, gateUserId: number, viewAll: boolean) {\n logger.info(`Setting view-all permission to ${viewAll} for API user ${username}...`);\n const admin = await pool.login(4);\n await admin.network(\"PUT\", `/people/${gateUserId}`, { view_all_override: viewAll });\n logger.info(`API user ${username} has now view-all permission set to ${viewAll}`);\n }\n\n function genCreateAPIV2User() {\n return async function createAPIV2User(apiV2User: ManifestAPIV2User) {\n const username: string = apiV2User.name;\n const existingUser = usersWithoutDevice.find((uwd) => uwd.user.username === username);\n if (existingUser) {\n if ([\"APPROVED\", \"ACTIVE\"].includes(existingUser.user.status)) {\n logger.info(`Skipping registration of API user ${username}`);\n __usersByName[existingUser.user.username] = existingUser.user;\n\n // eventually update the `view_all_override` flag\n const targetViewAll = !!apiV2User.viewAll;\n const existingViewAll = !!existingUser.user.view_all_override;\n const shouldUpdateViewAll = targetViewAll !== existingViewAll;\n if (shouldUpdateViewAll) {\n await setViewAll(username, existingUser.user.id, targetViewAll);\n }\n return;\n }\n }\n const params = {\n user: apiV2User,\n name: username,\n publicKey: apiV2User.publicKey ?? genKeys(username).hexPubKey,\n role: apiV2User.role,\n };\n const userRequest = await runner.createAPIV2User(params);\n const userAccessRequest = await runner.createAPIV2UserAccess(params);\n\n __usersByName[userRequest.user.username] = userRequest.user;\n logger.info(`(+) Created API user: ${username}`);\n\n // VG-18321 workaround inability to assign \"view all\" permission directly on creation\n if (apiV2User.viewAll) {\n await setViewAll(username, userRequest.user.id, true);\n }\n\n // log api key id and secret used for API user authentication\n logger.info(\n chalk`{red.bold IMPORTANT:} {red The API user credentials will not be displayed again so note them somewhere}`,\n );\n logger.info(\n JSON.stringify({\n api_key_id: userAccessRequest.api_key_id,\n api_key_secret: userAccessRequest.api_key_secret,\n }),\n );\n };\n }\n\n function genCreateAPIUser() {\n return async function createUser(apiUser: ManifestAPIUser) {\n const username: string = apiUser.name;\n const existingUser = usersWithDevice.find((ud) => ud.user.username === username);\n\n if (existingUser) {\n if (existingUser.user.status === \"ACTIVE\") {\n logger.info(`Skipping registration of LAM ${username}`);\n __usersByName[existingUser.user.username] = existingUser.user;\n return;\n }\n }\n const { device_id } = await pool.lamAPI.createInvitation(username);\n const params = { user: apiUser, userID: device_id, name: username };\n const request = await runner.createAPIUser(params, manifestFromGate);\n\n __usersByName[request.user.username] = request.user;\n logger.info(`(+) Created LAM api user: ${username}`);\n };\n }\n\n function genCreateUser(role: \"admin\" | \"operator\") {\n return async function createUser(device: number) {\n const name =\n (manifest.customUsernames && manifest.customUsernames[device]) ||\n getDefaultUsername(role, device);\n\n // find if there is already a user with this device\n const userID = await pool.getUserID(device);\n const findByUserID = (ud: UserWithDevice) => ud.user.user_id === userID;\n const existingUser = usersWithDevice.find(findByUserID);\n if (existingUser) {\n logger.info(`Skipping registration of ${role} ${device}`);\n __usersByDevice[device] = existingUser.user;\n __usersByName[existingUser.user.username] = existingUser.user;\n return;\n }\n\n const createUserParams = { role, userID, name, device };\n const request = await runner.createUser(createUserParams);\n\n __usersByDevice[device] = request.user;\n __usersByName[request.user.username] = request.user;\n\n logger.info(`(+) Created ${name}`);\n };\n }\n\n async function createWorkspaceRule(rule: ManifestWorkspaceRule) {\n const existingRule = gateManifest.workspaceRules?.find((r) => r.permission === rule.permission);\n if (!existingRule || areWorkspaceRulesDifferent(rule, existingRule)) {\n // we assume there will always be exactly one step\n const step = rule.steps[0]!;\n logger.info(\n `Configuring workspace rule ${rule.permission} with ${step.quorum} approvals out of ${step.users.length} user(s)`,\n );\n await runner.editWorkspaceRule({ rule, usersByName: __usersByName });\n } else {\n logger.info(`Skipping edition of workspace rule ${rule.permission}`);\n }\n }\n\n async function createGroup(group: ManifestGroup) {\n // find if there is already a group with same name\n let isEdit = false;\n const existingGroup = groupsByName[group.name];\n\n const isApprovingPendingGroup =\n existingGroup && existingGroup.status === \"PENDING\" && group.status !== \"PENDING\";\n\n if (existingGroup) {\n if (existingGroup && existingGroup.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.groups) {\n throw new Error(`No groups in gate manifest`);\n }\n const groupFromGateManifest = gateManifest.groups.find((g) => g.name === group.name);\n /* istanbul ignore if */\n if (!groupFromGateManifest) {\n throw new Error(`Can't find group ${group.name}`);\n }\n isEdit = areGroupsDifferent(groupFromGateManifest, group);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingGroup) {\n logger.info(`Skipping creation of group ${group.name}`);\n return;\n }\n }\n\n const data = {\n group,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n existingGroup,\n };\n\n const existingRequest =\n (!!isApprovingPendingGroup && !!existingGroup && existingGroup.last_request) || null;\n\n const noApproval = shouldNotApprove(group) || shouldRejectRequest(group);\n\n const bakeGroupParams = {\n group,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = isEdit\n ? await runner.editGroup(bakeGroupParams)\n : await runner.createGroup(bakeGroupParams);\n\n __groupsIDsByName[group.name] = (existingGroup && existingGroup.id) || request.group.id;\n\n // TODO: runner implementation\n if (shouldRejectRequest(group)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected group ${group.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} group ${group.name}`);\n }\n }\n\n async function createTradelinkCustodian(tradelinkEntity: ManifestTradelinkEntity) {\n const existingCustodian = tradelinkCustodiansByName[tradelinkEntity.name];\n if (existingCustodian) {\n logger.info(`Skipping creation of tradelink custodian ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"custodians\",\n };\n await runner.createTradelinkEntity<\"CUSTODIAN\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink custodian ${tradelinkEntity.name}`);\n }\n\n async function approveContracts(approval: ManifestContractApproval) {\n const { contractNames, user: deviceIndex } = approval;\n const user = await pool.login(deviceIndex);\n try {\n const unapproved = await user.get<{ contract_name: string; contract_version: string }[]>(\n \"/contracts/unapproved\",\n );\n const contracts = unapproved.filter((c) => contractNames.includes(c.contract_name));\n const promises = contracts.map((c) => {\n logger.info(`Approving contract \"${c.contract_name}\" with user ${deviceIndex}`);\n return user.post(\"/contracts/approve\", c);\n });\n await Promise.all(promises);\n } catch (e) {\n logger.error(`Error approving contracts by user ${deviceIndex}`, e);\n }\n }\n\n async function createTradelinkExchange(tradelinkEntity: ManifestTradelinkEntityWithAddresses) {\n const existingExchange = tradelinkExchangesByName[tradelinkEntity.name];\n if (existingExchange) {\n logger.info(`Skipping creation of tradelink exchange ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"exchanges\",\n };\n await runner.createTradelinkEntity<\"EXCHANGE\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink exchange ${tradelinkEntity.name}`);\n }\n\n async function createTradelinkAssetManager(\n tradelinkEntity: ManifestTradelinkEntityWithAddresses,\n ) {\n const existingCustodian = tradelinkAssetManagersByName[tradelinkEntity.name];\n if (existingCustodian) {\n logger.info(`Skipping creation of tradelink asset manager ${tradelinkEntity.name}`);\n return;\n }\n\n const bakeTLCustodianParams: MRBakeTradelinkEntityParams = {\n tradelinkEntity,\n type: \"asset_managers\",\n };\n await runner.createTradelinkEntity<\"ASSET_MANAGER\">(bakeTLCustodianParams);\n logger.info(`(+) Created Tradelink asset manager ${tradelinkEntity.name}`);\n }\n\n function formatTradelinkNetworkParams(\n manifestTLEntity: ManifestTradelinkEntityWithAddresses | ManifestTradelinkEntity,\n ): { id: string; name: string; operators: string[] } {\n const operators: string[] = [];\n if (manifestTLEntity.users.operators) {\n manifestTLEntity.users.operators.forEach((operator) => {\n const user = __usersByDevice[operator];\n if (user) operators.push(user.pub_key);\n });\n }\n if (manifestTLEntity.users.apiV2) {\n manifestTLEntity.users.apiV2.forEach((apiV2) => {\n const user = __usersByName[apiV2];\n if (user) operators.push(user.pub_key);\n });\n }\n\n return {\n id: manifestTLEntity.id,\n name: manifestTLEntity.name,\n operators,\n };\n }\n\n function formatTradelinkNetworkParamsWithAddresses(\n manifestTLEntity: ManifestTradelinkEntityWithAddresses,\n ) {\n const data = formatTradelinkNetworkParams(manifestTLEntity);\n return {\n ...data,\n addresses: manifestTLEntity.addresses,\n };\n }\n\n async function createTradelinkNetwork(tradelink: ManifestTradelink) {\n const custodians = tradelink.custodians.map((custodian) => {\n return formatTradelinkNetworkParams(custodian);\n });\n const exchanges = tradelink.exchanges.map((exchange) => {\n return formatTradelinkNetworkParamsWithAddresses(exchange);\n });\n const assetManagers = tradelink.assetManagers.map((am) => {\n return formatTradelinkNetworkParamsWithAddresses(am);\n });\n\n /* istanbul ignore if */\n if (custodians.length === 0) {\n throw new Error(`Custodian ${tradelink.custodians} not found`);\n }\n\n const bakeTLNetworkParams = { custodians, exchanges, assetManagers };\n await runner.createTradelinkNetwork(bakeTLNetworkParams);\n logger.info(`(+) Created Tradelink network`);\n }\n\n async function onboardTradelinkNetwork(tradelink: ManifestTradelink) {\n if (\n tradelinkOnboardingStatus.status == \"CUSTODIAN_ONBOARDED\" ||\n tradelinkOnboardingStatus.status == \"EXCHANGE_ONBOARDED\" ||\n tradelinkOnboardingStatus.status == \"HSM_READY\"\n ) {\n logger.info(`Skipping onboarding of custodian`);\n return;\n }\n\n const custodian = tradelink.custodians.map((custodian) => {\n return formatTradelinkNetworkParams(custodian);\n })[0];\n\n /* istanbul ignore if */\n if (!custodian) {\n throw new Error(`Custodian ${tradelink.custodians} not found`);\n }\n\n // Today the request is create tradelink as we can not pass any custodian\n const request = await runner.createTradelink({\n type: \"CREATE_TRADELINK\",\n operators: custodian.operators,\n });\n __hsmCustodiansIDsByName[custodian.name] = request.tradelink.id;\n\n logger.info(`(+) ${custodian.name} Tradelink custodian Onboarded`);\n }\n\n async function onboardTradelinkEntity(\n entity: ManifestTradelinkEntityWithAddresses,\n entityType: \"asset_managers\" | \"exchanges\",\n ) {\n if (tradelinkHSMAssetManagersByName[entity.name]?.status === \"ACTIVE\") {\n logger.info(`Skipping onboarding of asset manager ${entity.name}`);\n return;\n }\n if (tradelinkHSMExchangesByName[entity.name]?.status === \"ACTIVE\") {\n logger.info(`Skipping onboarding of exchange ${entity.name}`);\n return;\n }\n\n let approver: GateUser | undefined;\n if (entity.users.apiV2 && entity.users.apiV2.length > 0) {\n const userName = entity.users.apiV2[0] as string;\n approver = __usersByName[userName];\n }\n /* istanbul ignore if */\n if (!approver) {\n throw new Error(`No approver found for ${entity.name}`);\n }\n\n const { name, operators, addresses } = formatTradelinkNetworkParamsWithAddresses(entity);\n\n const bakeParams: MRBakeTradelinkEntityOnboardingParams = {\n tradelinkEntity: { name, operators, addresses },\n type: entityType,\n tradelinkEntityApprover: { name: approver.username, role: approver.role },\n };\n const request = await runner.onboardTradelinkEntity(bakeParams);\n if (entityType === \"asset_managers\")\n __hsmAMsIDsByName[entity.name] = (\n request as GateTradelinkAssetManagerRequest\n ).tradelink_asset_manager.id;\n else\n __hsmExchangesIDsByName[entity.name] = (\n request as GateTradelinkExchangeRequest\n ).tradelink_exchange.id;\n\n logger.info(`(+) ${entity.name} Tradelink ${entityType} Onboarded`);\n }\n\n async function createWhitelist(whitelist: ManifestWhitelist) {\n // find if there is already a whitelist with same name\n let isEdit = false;\n const existingWhitelist = whitelistsByName[whitelist.name];\n\n const isApprovingPendingWhitelist =\n existingWhitelist && existingWhitelist.status === \"PENDING\" && whitelist.status !== \"PENDING\";\n\n if (existingWhitelist && existingWhitelist.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.whitelists) {\n throw new Error(`No whitelists in gate manifest`);\n }\n const whitelistFromGateManifest = gateManifest.whitelists.find(\n (w) => w.name === whitelist.name,\n );\n /* istanbul ignore if */\n if (!whitelistFromGateManifest) {\n throw new Error(`Can't find whitelist ${whitelist.name}`);\n }\n isEdit = areWhitelistsDifferent(whitelistFromGateManifest, whitelist);\n\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingWhitelist) {\n logger.info(`Skipping creation of whitelist ${whitelist.name}`);\n return;\n }\n }\n\n const noApproval = shouldNotApprove(whitelist) || shouldRejectRequest(whitelist);\n\n const existingRequest =\n (!!isApprovingPendingWhitelist && !!existingWhitelist && existingWhitelist.last_request) ||\n null;\n\n const data = { whitelist, existingWhitelist };\n const bakeWLParams = { whitelist, data, existingRequest, noApproval };\n\n const request = isEdit\n ? await runner.editWhitelist(bakeWLParams)\n : await runner.createWhitelist(bakeWLParams);\n\n __whitelistsIDsByName[whitelist.name] =\n (existingWhitelist && existingWhitelist.id) || request.whitelist.id;\n\n // TODO: runner implementation\n if (shouldRejectRequest(whitelist)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected whitelist ${whitelist.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} whitelist ${whitelist.name}`);\n }\n }\n\n function getTradelinkAPIUserFromAssetManager(\n entity: ManifestTradelinkEntityWithAddresses,\n ): ManifestAPIV2User {\n /* istanbul ignore next */\n const entityAM = manifest.tradelink?.assetManagers.filter((am) => am.name === entity.name)[0];\n\n /* istanbul ignore if */\n if (!entityAM) {\n throw new Error(`AssetManager ${entity.name} not found`);\n }\n\n const approverUserName = entityAM.users.apiV2[0];\n\n /* istanbul ignore if */\n if (!approverUserName) {\n throw new Error(`No approver found for ${entity.name}`);\n }\n\n /* istanbul ignore if */\n const user = __usersByName[approverUserName];\n\n /* istanbul ignore if */\n if (!user) {\n throw new Error(`User ${approverUserName} not found`);\n }\n\n return { name: user.username, role: user.role };\n }\n\n async function createAccount(accountDesc: ManifestAccount) {\n let isEdit = false;\n const existingAccount =\n __accountsByName[accountDesc.name] ||\n findAccountByCurrencyAndIndex({ accountDesc, __accountsByName }) ||\n findExistingChildrenAccount({ accountDesc, __accountsByName });\n\n const isApprovingPendingAccount =\n existingAccount && existingAccount.status === \"PENDING\" && accountDesc.status !== \"PENDING\";\n\n if (existingAccount) {\n // special case when targeted account is VIEW_ONLY: it does not\n // appear in manifest file, but we can assume what we want is to\n // edit it\n if (existingAccount.status === \"VIEW_ONLY\") {\n isEdit = true;\n } else if (existingAccount.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.accounts) {\n throw new Error(\"No accounts in gate manifest\");\n }\n const accountFromGateManifest = gateManifest.accounts.find(\n (a) =>\n a.name === accountDesc.name ||\n (\"currency\" in accountDesc &&\n \"index\" in accountDesc &&\n \"currency\" in a &&\n \"index\" in a &&\n accountDesc.currency === a.currency &&\n accountDesc.index === a.index &&\n ((!(\"contractAddress\" in accountDesc) && !(\"contractAddress\" in a)) ||\n (\"contractAddress\" in accountDesc &&\n \"contractAddress\" in a &&\n accountDesc.contractAddress === a.contractAddress))),\n );\n /* istanbul ignore if */\n if (!accountFromGateManifest) {\n throw new Error(`Can't find account ${accountDesc.name}`);\n }\n isEdit = areAccountsDifferent(accountFromGateManifest, accountDesc);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingAccount) {\n logger.info(`Skipping creation of account ${accountDesc.name}`);\n return;\n }\n }\n\n const data = {\n account: accountDesc,\n existingAccount,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n groupsIDsByName: __groupsIDsByName,\n whitelistsIDsByName: __whitelistsIDsByName,\n accountsByName: __accountsByName,\n hsmAssetManagersIDsByName: __hsmAMsIDsByName,\n hsmExchangesIDsByName: __hsmExchangesIDsByName,\n hsmCustodiansIDsByName: __hsmCustodiansIDsByName,\n policiesIDsByName: __policiesIDsByName,\n tokens,\n };\n\n const existingRequest =\n (isApprovingPendingAccount && !!existingAccount && existingAccount.last_request) || null;\n\n const noApproval = shouldNotApprove(accountDesc) || shouldRejectRequest(accountDesc);\n\n // if tradelink account with HMS needs to be activated\n let tradelinkAM: ManifestAPIV2User | undefined;\n if (\"tradelink\" in manifest && \"tradelink_data\" in accountDesc && accountDesc.tradelink_data) {\n // get one asset manager\n /* istanbul ignore next */\n const assetManager = manifest.tradelink?.assetManagers[0];\n /* istanbul ignore if */\n if (!assetManager) {\n throw new Error(`AssetManager not found`);\n }\n tradelinkAM = getTradelinkAPIUserFromAssetManager(assetManager);\n }\n\n const bakeAccountParams = {\n account: accountDesc,\n data,\n existingRequest,\n noApproval,\n waitForActive,\n tradelinkAM,\n };\n\n const request = isEdit\n ? await runner.editAccount(bakeAccountParams)\n : await runner.createAccount(bakeAccountParams);\n\n __accountsByName[accountDesc.name] = request.account;\n\n // TODO: runner implementation\n if (shouldRejectRequest(accountDesc)) {\n const admin = await pool.login(4);\n admin.rejectRequest(request.id);\n logger.info(`(+) Rejected account ${accountDesc.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} account ${accountDesc.name}`);\n }\n\n // account configuration\n if (\"config\" in accountDesc && !!accountDesc.config) {\n const { config } = accountDesc;\n const admin = await pool.login(4);\n const account = __accountsByName[accountDesc.name]!;\n if (config.nftGallery) {\n await admin.post(`/accounts/${account.id}/nfts/activate`, {});\n logger.info(`(+) Enabled NFT Gallery on ${accountDesc.name}`);\n }\n }\n }\n\n async function createVaultEntity(vaultEntity: ManifestVaultEntity) {\n // find if there is already a vault entity with same name\n let isEdit = false;\n const existingVaultEntity = vaultEntitiesByName[vaultEntity.name];\n\n const isApprovingPendingVaultEntity =\n existingVaultEntity &&\n existingVaultEntity.status === \"PENDING\" &&\n vaultEntity.status !== \"PENDING\";\n\n if (existingVaultEntity) {\n if (existingVaultEntity && existingVaultEntity.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.entities) {\n throw new Error(`No entities in gate manifest`);\n }\n const vaultEntityFromGateManifest = gateManifest.entities.find(\n (e) => e.name === vaultEntity.name,\n );\n /* istanbul ignore if */\n if (!vaultEntityFromGateManifest) {\n throw new Error(`Can't find entity ${vaultEntity.name}`);\n }\n isEdit = areVaultEntitiesDifferent(vaultEntityFromGateManifest, vaultEntity);\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isEdit && !isApprovingPendingVaultEntity) {\n logger.info(`Skipping creation of entity ${vaultEntity.name}`);\n return;\n }\n }\n\n const data = {\n vaultEntity,\n accountsByName: __accountsByName,\n existingVaultEntity,\n };\n\n const existingRequest =\n (!!isApprovingPendingVaultEntity &&\n !!existingVaultEntity &&\n existingVaultEntity.last_request) ||\n null;\n\n const noApproval = shouldNotApprove(vaultEntity) || shouldRejectRequest(vaultEntity);\n\n const bakeVaultEntityParams = {\n vaultEntity,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = isEdit\n ? await runner.editVaultEntity(bakeVaultEntityParams)\n : await runner.createVaultEntity(bakeVaultEntityParams);\n\n // TODO: runner implementation\n if (shouldRejectRequest(vaultEntity)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected entity ${vaultEntity.name}`);\n } else {\n logger.info(`(+) ${isEdit ? \"Edited\" : \"Created\"} entity ${vaultEntity.name}`);\n }\n }\n\n async function createVaultExchange(exchange: ManifestExchange) {\n const existingExchange = exchangesByName[exchange.name];\n\n const isApprovingPendingExchange =\n existingExchange && existingExchange.status === \"PENDING\" && exchange.status !== \"PENDING\";\n\n if (existingExchange) {\n if (existingExchange && existingExchange.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.exchanges) {\n throw new Error(`No exchanges in gate manifest`);\n }\n const exchangeFromGateManifest = gateManifest.exchanges.find(\n (e) => e.name === exchange.name,\n );\n /* istanbul ignore if */\n if (!exchangeFromGateManifest) {\n throw new Error(`Can't find exchange ${exchange.name}`);\n }\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isApprovingPendingExchange) {\n logger.info(`Skipping creation of exchange ${exchange.name}`);\n return;\n }\n }\n\n const data = {\n exchange,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n groupsIDsByName: __groupsIDsByName,\n existingExchange,\n };\n\n const existingRequest =\n (!!isApprovingPendingExchange && !!existingExchange && existingExchange.last_request) || null;\n\n const noApproval = shouldNotApprove(exchange) || shouldRejectRequest(exchange);\n\n const bakeExchangeParams = {\n exchange,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = await runner.createExchange(bakeExchangeParams);\n\n if (shouldRejectRequest(exchange)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected exchange ${exchange.name}`);\n } else {\n logger.info(`(+) Created exchange ${exchange.name}`);\n }\n }\n\n async function createVaultPolicy(policy: ManifestPolicy) {\n const existingPolicy = policiesByName[policy.name];\n\n const isApprovingPendingPolicy =\n existingPolicy && existingPolicy.status === \"PENDING\" && policy.status !== \"PENDING\";\n\n if (existingPolicy) {\n if (existingPolicy && existingPolicy.status === \"ACTIVE\") {\n /* istanbul ignore if */\n if (!gateManifest.policies) {\n throw new Error(`No policies in gate manifest`);\n }\n const policyFromGateManifest = gateManifest.policies.find((e) => e.name === policy.name);\n /* istanbul ignore if */\n if (!policyFromGateManifest) {\n throw new Error(`Can't find policy ${policy.name}`);\n }\n }\n // For some reason (wtf?) istanbul reporter think that we should have\n // a else here, while it makes no sense\n /* istanbul ignore else */\n if (!isApprovingPendingPolicy) {\n logger.info(`Skipping creation of policy ${policy.name}`);\n return;\n }\n }\n\n const data = {\n policy,\n usersByDevice: __usersByDevice,\n usersByName: __usersByName,\n whitelistsIDsByName: __whitelistsIDsByName,\n groupsIDsByName: __groupsIDsByName,\n existingPolicy,\n };\n\n const existingRequest =\n (!!isApprovingPendingPolicy && !!existingPolicy && existingPolicy.last_request) || null;\n\n const noApproval = shouldNotApprove(policy) || shouldRejectRequest(policy);\n\n const bakePolicyParams = {\n policy,\n data,\n noApproval,\n existingRequest,\n };\n\n const request = await runner.createPolicy(bakePolicyParams);\n\n if (shouldRejectRequest(policy)) {\n const admin = await pool.login(4);\n await admin.rejectRequest(request.id);\n logger.info(`(+) Rejected policy ${policy.name}`);\n } else {\n logger.info(`(+) Created policy ${policy.name}`);\n __policiesIDsByName[policy.name] = request.policy.id;\n }\n }\n}\n\nconst shouldNotApprove = (\n manifestEntity:\n | ManifestGroup\n | ManifestWhitelist\n | ManifestAccount\n | ManifestVaultEntity\n | ManifestExchange\n | ManifestPolicy,\n) => manifestEntity.status === \"PENDING\";\n\nconst shouldRejectRequest = (\n manifestEntity:\n | ManifestGroup\n | ManifestWhitelist\n | ManifestAccount\n | ManifestVaultEntity\n | ManifestExchange\n | ManifestPolicy,\n) => manifestEntity.status === \"ABORTED\";\n\nconst findExistingChildrenAccount = ({\n accountDesc,\n __accountsByName,\n}: {\n accountDesc: ManifestAccount;\n __accountsByName: GateAccountsByName;\n}) => {\n if (!(\"parentAccount\" in accountDesc) || !(\"contractAddress\" in accountDesc)) return;\n const accounts = Object.keys(__accountsByName)\n .map((name) => {\n return __accountsByName[name];\n })\n .filter(Boolean);\n const parent = accounts.find((a) => !!a && a.name === accountDesc.parentAccount);\n if (!parent) return;\n\n const children = accounts.filter((a) => !!a && a.parent === parent.id);\n /* istanbul ignore if */\n if (children.length === 0) return;\n\n return children.find((c) => !!c && c.contract_address === accountDesc.contractAddress);\n};\n\nconst findAccountByCurrencyAndIndex = ({\n accountDesc,\n __accountsByName,\n}: {\n accountDesc: ManifestAccount;\n __accountsByName: GateAccountsByName;\n}) => {\n if (!(\"currency\" in accountDesc) || !(\"index\" in accountDesc)) return;\n const accounts = Object.values(__accountsByName);\n return accounts.find(\n (account) =>\n account.currency === accountDesc.currency &&\n account.index === accountDesc.index &&\n (\"contractAddress\" in accountDesc\n ? accountDesc.contractAddress === account.contract_address\n : !account.contract_address),\n );\n};\n","import { DeserializedManifest, Manifest, ManifestAPIUser, ManifestUser } from \"./types\";\n\nconst fromRawUser = (u: number | ManifestUser): ManifestUser =>\n typeof u === \"number\" ? { device: u } : u;\n\nconst fromRawAPIUser = (u: string | ManifestAPIUser): ManifestAPIUser =>\n typeof u === \"string\" ? { name: u } : u;\n\nfunction deserializeManifest(manifest: Manifest): DeserializedManifest {\n const { users, ...rest } = manifest;\n return {\n ...rest,\n ...(users\n ? {\n users: {\n ...(users.operators ? { operators: users.operators.map(fromRawUser) } : {}),\n ...(users.admins ? { admins: users.admins.map(fromRawUser) } : {}),\n ...(users.api ? { api: users.api.map(fromRawAPIUser) } : {}),\n ...(users.apiV2 ? { apiV2: users.apiV2 } : {}),\n },\n }\n : {}),\n };\n}\n\nexport default deserializeManifest;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { GateTokenCurrency, LegacyERC20Token, RunnableOptions, UserContext } from \"./types\";\n\nfunction erc20TokenToGateTokenCurrency(erc20: LegacyERC20Token): GateTokenCurrency {\n const parent_currency =\n erc20.blockchain_name === \"foundation\"\n ? \"ethereum\"\n : erc20.blockchain_name === \"ropsten\"\n ? \"ethereum_ropsten\"\n : erc20.blockchain_name === \"goerli\"\n ? \"ethereum_goerli\"\n : null;\n if (!parent_currency) {\n throw new Error(\n `Can't determine parent_currency from ERC20 token with blockchain_name \"${erc20.blockchain_name}\"`,\n );\n }\n return {\n contract_address: erc20.contract_address,\n family: \"ethereum\",\n name: erc20.name,\n parent_currency,\n ticker: erc20.ticker,\n token_type: \"erc20\",\n units: [\n {\n name: erc20.ticker,\n code: erc20.ticker,\n magnitude: erc20.decimals,\n },\n ],\n __legacy_hsm_account_parameters: erc20.hsm_account_parameters,\n __legacy_hsm_signature: erc20.hsm_signature,\n };\n}\n\nasync function fetchERC20Tokens(\n ctx: UserContext,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<GateTokenCurrency[]> {\n if (process.env.LEGACY_TOKENS) {\n type LegacyRes = { erc20s: LegacyERC20Token[] };\n const res = await ctx.network<LegacyRes>(\"GET\", \"/currencies/erc20s\");\n return res.erc20s.map(erc20TokenToGateTokenCurrency);\n }\n let tokens;\n try {\n type Res = GateTokenCurrency[];\n tokens = await ctx.network<Res>(\"GET\", \"/currencies/tokens\");\n } catch (err) {\n logger.error(\n \"Fetching tokens has failed, if you are targeting a gate < Vault 3.6, set environment variable LEGACY_TOKENS to 1\",\n );\n logger.error(\"e.g: LEGACY_TOKENS=1 ledger-vault <command>\");\n throw err;\n }\n return tokens;\n}\n\nexport default fetchERC20Tokens;\n","import type { Logger as RevaultLogger } from \"@ledgerhq/revault-sdk\";\nimport { EngineOptions, createEngine } from \"@ledgerhq/revault-sdk/engine\";\nimport { onboard } from \"@ledgerhq/revault-sdk/onboarding\";\nimport { Logger } from \"@ledgerhq/vault-utils\";\n\nimport legacyGenSeed from \"./genSeed\";\n\ntype Options = {\n // onboarding target\n workspace: string;\n compartmentId: number;\n hsmScriptsVersion: string;\n // config\n revaultApiUrl: string;\n revaultRootAuthToken: string;\n deviceApiUrl: string;\n salt: string;\n};\n\n/* istanbul ignore next */\nfunction genSeed({ salt, index }: { salt: string; index: number }): string {\n return legacyGenSeed(salt, index);\n}\n\nexport /* istanbul ignore next */ async function onboardWithRevault(\n options: Options,\n logger: Logger,\n): Promise<void> {\n const engineOptions: EngineOptions = {\n salt: options.salt,\n baseUrl: options.revaultApiUrl,\n deviceApiUrl: options.deviceApiUrl,\n genSeed,\n };\n\n const revaultLogger: RevaultLogger = {\n ...logger,\n warning: logger.info,\n debug: (msg: string): void => {\n if (process.env.DEBUG === \"1\") {\n // eslint-disable-next-line no-console\n console.log(\"DEBUG\", msg);\n }\n },\n output: logger.info,\n };\n\n const baseEngine = createEngine(engineOptions, revaultLogger);\n const rootClient = baseEngine.createClient({ rootAuthToken: options.revaultRootAuthToken });\n\n try {\n const onboarding = await rootClient.onboarding.startOnboarding.mutate({\n hsmScriptsVersion: options.hsmScriptsVersion,\n workspaceName: options.workspace,\n compartmentId: options.compartmentId,\n mode: \"GATE_COMPAT\",\n });\n\n const onboardingEngine = baseEngine.withOnboarding(onboarding.id);\n\n await onboard({ engine: onboardingEngine }, revaultLogger);\n } catch (err) {\n if (err instanceof Error && err.message.includes(\"has already started onboarding\")) {\n logger.info(\"Onboarding already started, assuming it's finished, skipping\");\n // we assume the workspace is already onboarded\n return;\n }\n throw err;\n }\n}\n","import { entropyToMnemonic } from \"bip39\";\nimport hex from \"crypto-js/enc-hex\";\nimport sha256 from \"crypto-js/sha256\";\n\nconst hashCode = (str: string): Buffer => {\n return Buffer.from(hex.stringify(sha256(str)), \"hex\");\n};\n\nconst genSeed = (salt = \"\", index = 1, options: { overrideSeeds?: string[] } = {}): string => {\n const { overrideSeeds } = options;\n const overriddenSeed = !!overrideSeeds && overrideSeeds[index - 1];\n if (overriddenSeed) return overriddenSeed;\n const entropy = hashCode(`${salt}_${index}`);\n return entropyToMnemonic(entropy.toString(\"hex\"));\n};\n\nexport default genSeed;\n","import isEqual from \"lodash/isEqual\";\nimport sortBy from \"lodash/sortBy\";\n\nimport {\n ManifestAccount,\n ManifestAccountRuleMultiAuth,\n ManifestAccountRuleMultiAuthStep,\n ManifestAccountRuleThreshold,\n ManifestGroup,\n ManifestVaultEntity,\n ManifestWhitelist,\n ManifestWhitelistAddress,\n ManifestWorkspaceRule,\n} from \"./types\";\n\nexport const areGroupsDifferent = (group1: ManifestGroup, group2: ManifestGroup): boolean => {\n if (group1.users.length !== group2.users.length) return true;\n const sortedUsers1 = sortBy(group1.users, [(user) => user.toString()]);\n const sortedUsers2 = sortBy(group2.users, [(user) => user.toString()]);\n return !isEqual(sortedUsers1, sortedUsers2);\n};\n\nexport const areWorkspaceRulesDifferent = (\n rule1: ManifestWorkspaceRule,\n rule2: ManifestWorkspaceRule,\n): boolean => {\n /* istanbul ignore if */ // should never happen\n if (rule1.permission !== rule2.permission) return true;\n /* istanbul ignore if */ // should never happen\n if (rule1.steps.length !== rule2.steps.length) return true;\n\n return rule1.steps.some((step1, i) => {\n const step2 = rule2.steps[i]!;\n if (step1.quorum !== step2.quorum) return true;\n if (!isEqual(sortBy(step1.users), sortBy(step2.users))) return true;\n return false;\n });\n};\n\nconst formatAddress = (a: ManifestWhitelistAddress) => `${a.currency}-${a.address}`;\n\nexport const areWhitelistsDifferent = (\n whitelist1: ManifestWhitelist,\n whitelist2: ManifestWhitelist,\n): boolean => {\n if (whitelist1.addresses.length !== whitelist2.addresses.length) return true;\n const _addresses1 = whitelist1.addresses.map(formatAddress);\n const _addresses2 = whitelist2.addresses.map(formatAddress);\n return !isEqual(sortBy(_addresses1), sortBy(_addresses2));\n};\n\nfunction sanitizeThreshold(step: ManifestAccountRuleThreshold) {\n return { ...step, min: step.min || 0 };\n}\n\nexport const areAccountsDifferent = (\n account1: ManifestAccount,\n account2: ManifestAccount,\n): boolean => {\n if (account1.name !== account2.name) return true;\n // as Gate do not return tradelink_data, we cannot check Tradelink account are different\n if (account1.tradelink_data || account2.tradelink_data) return false;\n const account1Rules = account1.rules;\n const account2Rules = account2.rules;\n if (!!account1Rules !== !!account2Rules) return true;\n if (!account1Rules || !account2Rules) return false;\n if (account1Rules.length !== account2Rules.length) return true;\n let isDifferent = false;\n\n account1Rules.forEach((rule, index) => {\n const account2Rule = account2Rules[index];\n /* istanbul ignore if */\n if (!account2Rule) throw new Error(`No rule found at index ${index}`);\n if (rule.length !== account2Rule.length) {\n isDifferent = true;\n return;\n }\n rule.forEach((step) => {\n if (step.type === \"WHITELIST\") {\n const same = account2Rule.find(\n (s) => s.type === \"WHITELIST\" && isEqual(sortBy(s.whitelists), sortBy(step.whitelists)),\n );\n if (!same) {\n isDifferent = true;\n return;\n }\n }\n if (step.type === \"THRESHOLD\") {\n const sanitized = sanitizeThreshold(step);\n const same = account2Rule.find((s) => {\n if (s.type === \"THRESHOLD\") {\n const sanitizeStep = sanitizeThreshold(s);\n return sanitizeStep.min === sanitized.min && sanitizeStep.max === sanitized.max;\n }\n return false;\n });\n if (!same) {\n isDifferent = true;\n return;\n }\n }\n if (step.type === \"MULTI_AUTHORIZATIONS\") {\n // @ts-ignore: typescript does't understand Array.find()\n const same: ManifestAccountRuleMultiAuth = account2Rule.find(\n (s) => s.type === \"MULTI_AUTHORIZATIONS\",\n );\n if (!same || areStepsOfMultiAuthDifferent(step.steps, same.steps)) {\n isDifferent = true;\n return;\n }\n }\n });\n });\n return isDifferent;\n};\n\nexport const areVaultEntitiesDifferent = (\n vaultEntity1: ManifestVaultEntity,\n vaultEntity2: ManifestVaultEntity,\n): boolean => {\n if (\n vaultEntity1.accounts &&\n vaultEntity2.accounts &&\n vaultEntity1.accounts.length !== vaultEntity2.accounts.length\n )\n return true;\n return !isEqual(sortBy(vaultEntity1.accounts), sortBy(vaultEntity2.accounts));\n};\n\nconst areStepsOfMultiAuthDifferent = (\n steps1: ManifestAccountRuleMultiAuthStep[],\n steps2: ManifestAccountRuleMultiAuthStep[],\n) => {\n if (steps1.length !== steps2.length) return true;\n let isDifferent = false;\n steps1.forEach((s, index) => {\n const s2 = steps2[index];\n /* istanbul ignore if */\n if (!s2) throw new Error(`No step found at index ${index}`);\n if (s.quorum !== s2.quorum) {\n isDifferent = true;\n return;\n }\n\n if ((\"group\" in s && !(\"group\" in s2)) || (\"group\" in s2 && !(\"group\" in s))) {\n isDifferent = true;\n return;\n }\n\n if (\"group\" in s && \"group\" in s2 && s.group !== s2.group) {\n isDifferent = true;\n return;\n }\n\n if (\"users\" in s && \"users\" in s2) {\n if (s.users.length !== s2.users.length || !isEqual(sortBy(s.users), sortBy(s2.users))) {\n isDifferent = true;\n return;\n }\n }\n });\n return isDifferent;\n};\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport axios from \"axios\";\n\nimport { RunnableOptions } from \"./types\";\n\nexport type ConfigCatConfiguration = {\n apiKey: string | null;\n productId: string | null;\n configId: string | null;\n};\n\nexport type ConfigCatKeys = {\n sdkKey: string;\n productId: string;\n configId: string;\n environmentId: string;\n};\n\ntype ConfigCatFlag = { settingId: string; key: string; settingType: string };\n\nexport const configCatEndpoint = \"https://api.configcat.com/v1\";\n\nexport async function createConfigCatEnvironment(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n): Promise<ConfigCatKeys | null> {\n const {\n data: { environmentId },\n } = await axios.post<{\n environmentId: string;\n }>(\n `${configCatEndpoint}/products/${ccConfig.productId}/environments`,\n {\n name: environmentName,\n color: \"#DADBEE\",\n description: \"Created by ledger-vault CLI\",\n },\n {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n const { data: getCredentialsResponse } = await axios.get<{ primary: string }>(\n `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentId}`,\n {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n return {\n sdkKey: getCredentialsResponse.primary,\n productId: ccConfig.productId!,\n configId: ccConfig.configId!,\n environmentId,\n };\n}\n\nasync function getEnvironmentIDFromEnvironmentName(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n): Promise<string | undefined> {\n const { data: getEnvironmentsResponse } = await axios.get<\n Array<{ environmentId: string; name: string }>\n >(`${configCatEndpoint}/products/${ccConfig.productId}/environments`, {\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n });\n const environment = getEnvironmentsResponse?.find(\n (environment) => environment.name === environmentName,\n );\n return environment?.environmentId;\n}\n\nexport async function deleteConfigCatEnvironment(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n) {\n const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);\n if (environmentID === undefined) {\n throw Error(`Unable to find ConfigCat environment '${environmentName}'`);\n }\n\n await axios.delete(`${configCatEndpoint}/environments/${environmentID}`, {\n headers: {\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n });\n}\n\nexport async function setConfigCatFeatureFlagValues(\n ccConfig: ConfigCatConfiguration,\n environmentName: string,\n flagValues: Record<string, any>,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const environmentID = await getEnvironmentIDFromEnvironmentName(ccConfig, environmentName);\n if (environmentID === undefined) {\n throw Error(`Unable to find ConfigCat environment '${environmentName}'`);\n }\n\n const { data: getFlagsResponse } = await axios.get<Array<ConfigCatFlag>>(\n `${configCatEndpoint}/configs/${ccConfig.configId}/settings`,\n {\n headers: {\n Authorization: `Basic ${ccConfig.apiKey}`,\n },\n },\n );\n\n const settingValues: Array<{ settingId: string; value: string }> = getFlagsResponse\n .filter((configCatFlag) => Object.keys(flagValues).includes(configCatFlag.key))\n .map((configCatFlag) => {\n let flagValue = flagValues[configCatFlag.key];\n if (configCatFlag.settingType == \"string\" && typeof flagValue != \"string\") {\n flagValue = JSON.stringify(flagValue);\n }\n return { settingId: configCatFlag.settingId, value: flagValue };\n });\n\n try {\n await axios.post(\n `${configCatEndpoint}/configs/${ccConfig.configId}/environments/${environmentID}/values`,\n { settingValues },\n { headers: { Authorization: `Basic ${ccConfig.apiKey}` } },\n );\n } catch (err) {\n logger.error(`Could not set feature flags`);\n logger.error(err);\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport invariant from \"invariant\";\nimport io, { Socket } from \"socket.io-client\";\n\nimport createNetwork from \"./createNetwork\";\nimport { createRunner } from \"./device\";\nimport createAPIDevice from \"./device/createAPIDevice\";\nimport createHWDevice from \"./device/createHWDevice\";\nimport createInteractions from \"./device/createInteractions\";\nimport { APIDevice } from \"./device/types\";\nimport {\n AdminDevice,\n Connection,\n DevicesPool,\n GateUser,\n Interaction,\n Organization,\n PoolOptions,\n RunnableOptions,\n UserContextRunnable,\n VaultEvent,\n} from \"./types\";\nimport { getWorkspaceFromGate } from \"./utils\";\n\nconst createDevicesPool = (\n options: PoolOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): DevicesPool => {\n const {\n salt = \"\",\n gate,\n apiGateway,\n overrideSeeds,\n lamURL,\n lamAPIKey,\n notifierURL,\n deviceAPISessionID,\n networkDelay,\n readOnlyUser,\n transport = \"software\",\n recordStore,\n deviceAPIURL,\n psdModel = \"STAX\",\n } = options;\n\n let adminDevicesCache: AdminDevice[] = [];\n\n const device =\n transport === \"software\"\n ? createAPIDevice({\n deviceAPISessionID,\n salt,\n overrideSeeds,\n deviceAPIURL,\n psdModel,\n })\n : createHWDevice({ promptToSwitch: transport !== \"nodehid-replayer\" });\n\n const setSalt = (salt: string) => {\n invariant(\n transport === \"software\",\n `Tried to use \\`setSalt\\` with transport not being \"software\"`,\n );\n const apiDevice = device as APIDevice;\n apiDevice.setSalt(salt);\n };\n\n const interactions = createInteractions(device);\n let deviceRun = createRunner(interactions, {\n readOnly: !!readOnlyUser,\n transport,\n recordStore,\n });\n\n const { loginFlow, readOnlyLoginFlow, registerUserFlow, approveFlow, approveFlowWithoutHSM } =\n interactions;\n\n const workspace = getWorkspaceFromGate(gate);\n const network = createNetwork({\n baseURL: gate,\n networkDelay,\n });\n\n const runWithDevice = async (\n deviceIndex: number,\n interactions: Interaction<any>[],\n data: any,\n ) => {\n await device.switchDevice(deviceIndex);\n invariant(deviceRun, \"deviceRun not initialized\");\n return deviceRun(interactions, data);\n };\n\n const getUserID = async (deviceIndex: number) => {\n await device.switchDevice(deviceIndex);\n return device.getUserID();\n };\n\n const registerDevice = async (deviceIndex: number, request: any) => {\n const data = {\n network,\n workspace,\n username: request.user.username,\n role: request.user.role.toLowerCase(),\n // FIXME absolutely wrong name (legacy code)\n member: request,\n urlID: request.url_id,\n };\n return runWithDevice(deviceIndex, registerUserFlow, data);\n };\n\n const tokensByDeviceIndex: Record<string, string> = {};\n const login = async (deviceIndex: number | string) => {\n const isReadOnlyUser = typeof deviceIndex === \"string\" || !!readOnlyUser;\n deviceRun = createRunner(interactions, {\n readOnly: isReadOnlyUser,\n transport,\n recordStore,\n });\n if (typeof deviceIndex === \"number\") {\n await device.switchDevice(deviceIndex);\n }\n\n let _token: string | null = tokensByDeviceIndex[deviceIndex] || null;\n\n const interceptToken = (token: string) => {\n tokensByDeviceIndex[deviceIndex] = token;\n _token = token;\n };\n const injectToken = () => _token;\n if (!_token || process.env.NODE_ENV === \"test\") {\n const network = createNetwork({\n baseURL: gate,\n interceptToken,\n injectToken,\n networkDelay,\n });\n await deviceRun(\n typeof deviceIndex === \"string\"\n ? readOnlyLoginFlow(deviceIndex)\n : readOnlyUser\n ? readOnlyLoginFlow(readOnlyUser)\n : loginFlow,\n { network, workspace },\n );\n if (!_token) {\n throw new Error(\"Could not extract token from login\");\n }\n }\n\n const authNetwork = createNetwork({\n baseURL: gate,\n token: _token,\n networkDelay,\n });\n\n const authRun = async (interactions: Interaction<any>[], data: any) => {\n /* istanbul ignore else */\n if (typeof deviceIndex === \"number\") {\n await runWithDevice(deviceIndex, interactions, data);\n } else {\n throw new Error(`Can't authRun on read-only mode (deviceIdentifier: ${deviceIndex})`);\n }\n };\n\n const post = async (url: string, payload: any) => authNetwork(\"POST\", url, payload);\n\n const rejectRequest = async (requestID: number) => post(`/requests/${requestID}/abort`, {});\n\n const approveRequest = (request: any) =>\n authRun(approveFlow, { request_id: request.id, network: authNetwork });\n const approveRequestWithoutHSM = (request: any) =>\n authRun(approveFlowWithoutHSM, {\n request_id: request.id,\n network: authNetwork,\n });\n\n let socket: typeof Socket | null = null;\n\n return {\n network: authNetwork,\n run: authRun,\n get: async <T>(url: string, requestOptions: any): Promise<T> =>\n authNetwork(\"GET\", url, undefined, requestOptions),\n post,\n rejectRequest,\n approveRequest,\n approveRequestWithoutHSM,\n getToken: () => _token,\n connectSocket: async () => {\n if (!socket) {\n if (!notifierURL) {\n throw new Error(\"No notifierURL specified\");\n }\n /* istanbul ignore if */\n if (!_token) {\n throw new Error(\"No token\");\n }\n socket = createSocket(_token, notifierURL);\n const { pub_key } = await authNetwork(\"GET\", \"/people/me\");\n socket.emit(\"join\", { username: pub_key, workspace });\n logger.success(\"Socket connected\");\n }\n return socket;\n },\n onEvent: (cb: (event: VaultEvent) => void) => {\n if (!socket) {\n throw new Error(\"Call connectSocket() first\");\n }\n socket.on(\"notification\", cb);\n },\n };\n };\n\n const bruteforceDeviceIndex = async (userID: string): Promise<number> => {\n for (let i = 1; i < 100; i++) {\n const found = await getUserID(i);\n /* istanbul ignore else */\n if (found.toUpperCase() === userID.toUpperCase()) return i;\n }\n\n /* istanbul ignore next */\n throw new Error(`Could not find device index for user ${userID}`);\n };\n\n async function getOnboardingAdminDevices(): Promise<AdminDevice[]> {\n if (adminDevicesCache.length > 0) return adminDevicesCache;\n adminDevicesCache = [\n [\"Admin 1\", 4],\n [\"Admin 2\", 5],\n [\"Admin 3\", 6],\n ];\n return adminDevicesCache;\n }\n\n const collectQuorumDevices = async (): Promise<number[]> => {\n const devices: number[] = adminDevicesCache\n .map((adminDevice: AdminDevice) => adminDevice[1])\n .slice(0, 2);\n const admin = await login(devices[0]!);\n const org = await admin.get<Organization>(\"/organization\", {});\n const { quorum } = org;\n\n if (quorum > 2) {\n // fetch all active admins\n const adminsC = await admin.get<Connection<GateUser>>(\n \"/people?status=ACTIVE&role=ADMIN&pageSize=-1\",\n {},\n );\n\n // sort them, and remove the two first ones (we already have their devices)\n let admins = adminsC.edges.map((e) => e.node);\n /* istanbul ignore next */\n admins.sort((a, b) => (a.id > b.id ? 1 : -1));\n admins = admins.slice(2, quorum);\n\n // find and push their device indexes in the devices array\n for (const adm of admins) {\n const deviceIndex = await bruteforceDeviceIndex(adm.user_id);\n devices.push(deviceIndex);\n }\n }\n return devices;\n };\n\n const runWithQuorum = async (iteratee: UserContextRunnable) => {\n const devices = await collectQuorumDevices();\n for (let i = 0; i < devices.length; i++) {\n const device = devices[i];\n /* istanbul ignore if */\n if (!device) throw new Error(\"Invalid device\");\n const admin = await login(device);\n try {\n await iteratee(admin);\n } catch (err) {\n /* istanbul ignore next */\n const reqAlreadyExistsErr = \"REQUEST_ALREADY_APPROVED_OR_ABORTED_BY_THIS_MEMBER_EXCEPTION\";\n /* istanbul ignore next */\n // @ts-expect-error\n if (err.name === reqAlreadyExistsErr) continue;\n /* istanbul ignore next */\n throw err;\n }\n }\n };\n\n const apiNetwork = createNetwork({\n baseURL: lamURL || \"\",\n networkDelay,\n });\n const headersAPILam = lamAPIKey ? { headers: { \"X-Ledger-API-Key\": lamAPIKey } } : {};\n\n const createInvitation = async (name: string): Promise<{ device_id: string }> => {\n try {\n const r = await apiNetwork(\"POST\", `/api_users/${name}`, {}, headersAPILam);\n return r;\n } catch (e: unknown) {\n return apiNetwork(\"GET\", `/api_users/${name}`, {}, headersAPILam);\n }\n };\n\n const registerUser = async (name: string, uuid: string): Promise<void> => {\n return apiNetwork(\"POST\", `/api_users/${name}/register/${uuid}`, {}, headersAPILam);\n };\n\n const lamAPI = {\n createInvitation,\n registerUser,\n };\n\n const createSocket = (token: string, notifierURL: string): typeof Socket => {\n const client: typeof Socket = io(notifierURL, {\n transportOptions: {\n polling: {\n extraHeaders: {\n Cookie: `ledger_workspace=${workspace}; ledger_token=${token}`,\n },\n },\n },\n });\n return client;\n };\n\n /* istanbul ignore next */\n const getRevaultCompatOptions = () => {\n return {\n workspace,\n deviceApiUrl:\n deviceAPIURL ?? process.env.VAULT_DEVICE_API_URL ?? \"https://localhost:8443/device-api\",\n salt,\n };\n };\n\n return {\n getRevaultCompatOptions,\n workspace,\n network,\n gate,\n apiGateway,\n login,\n lamAPI,\n getUserID,\n bruteforceDeviceIndex,\n setSalt,\n registerDevice,\n runWithDevice,\n runWithQuorum,\n device,\n interactions,\n getOnboardingAdminDevices,\n psdModel,\n };\n};\n\nexport default createDevicesPool;\n","import { RecordStore, openTransportReplayer } from \"@ledgerhq/hw-transport-mocker\";\nimport { registerTransportModule, withDevicePolling } from \"@ledgerhq/live-common-stub\";\nimport { listen } from \"@ledgerhq/logs\";\nimport { from } from \"rxjs\";\n\nimport { Interaction, Interactions, TransportType } from \"../types\";\n\n/* istanbul ignore next */\nconst mockTransport: Promise<any> = Promise.resolve({\n open: () => Promise.resolve(),\n close: () => Promise.resolve(),\n});\n\n// always logs apdu for now\nlisten(\n /* istanbul ignore next */ (log) => {\n if (log.type === \"apdu\" && process.env.DEBUG === \"1\") {\n console.log(`${log.type}: ${log.message ? log.message : \"\"}`); // eslint-disable-line no-console\n }\n },\n);\n\n/* istanbul ignore next */\nregisterTransportModule({\n id: \"software\",\n open: (id: string) => {\n if (id !== \"software\") return;\n return mockTransport;\n },\n disconnect: () => null,\n});\n\ntype CreateRunnerOptions = {\n readOnly?: boolean;\n transport: TransportType;\n recordStore?: RecordStore | null;\n acceptPollingError?: (err: unknown) => boolean;\n};\n\nexport type Runner = (\n _interactions: Interaction<any>[],\n _data?: any,\n options?: {\n onStepStart?: (responseKey: string) => void;\n onStepDone?: <D>(res: { responseKey: string; value: D }) => void;\n },\n) => Promise<Record<string, unknown>>;\n\nexport const createRunner =\n (\n allInteractions: Interactions,\n { readOnly, transport, recordStore, acceptPollingError }: CreateRunnerOptions = {\n transport: \"software\",\n },\n ): Runner =>\n async <T>(\n _interactions: Interaction<any>[],\n // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types\n _data?: any,\n options?: {\n onStepStart?: (responseKey: string) => void;\n onStepDone?: <D>(res: { responseKey: string; value: D }) => void;\n },\n ): Promise<T> => {\n const interactions = [...(readOnly ? [] : [allInteractions.getU2FPublicKey]), ..._interactions];\n const responses = { ..._data };\n for (let i = 0; i < interactions.length; i++) {\n const interaction = interactions[i];\n /* istanbul ignore if */\n if (!interaction) throw new Error(\"Invalid interaction\");\n if (recordStore) {\n const replayer = await openTransportReplayer(recordStore);\n const response = await interaction.action({\n ...responses,\n transport: replayer,\n });\n\n responses[interaction.responseKey] = response;\n } else {\n /* istanbul ignore next */\n const obs = withDevicePolling(transport)(\n (transport: any) => {\n return from(interaction.action({ ...responses, transport }));\n },\n // always throw on error:\n // eslint-disable-next-line\n acceptPollingError ?? (() => false),\n );\n /* istanbul ignore next */\n if (options?.onStepStart) {\n options.onStepStart(interaction.responseKey);\n }\n const res = await obs.toPromise();\n responses[interaction.responseKey] = res;\n /* istanbul ignore next */\n if (options?.onStepDone) {\n options.onStepDone({ responseKey: interaction.responseKey, value: res });\n }\n }\n }\n return responses;\n };\n","import { nanoid } from \"nanoid\";\n\nimport createNetwork from \"../createNetwork\";\nimport genSeed from \"../genSeed\";\nimport { PsdModel } from \"../types\";\nimport {\n APIDevice,\n Device,\n DeviceAuthenticate,\n DeviceGenAttestationCert,\n DeviceGetPubKey,\n DeviceRegister,\n DeviceRegisterData,\n DeviceValidateVaultOp,\n FinalizePairingHandshake,\n InitiatePairingHandshake,\n PairingData,\n StartKPatternAsResponder,\n} from \"./types\";\n\nconst ROLE_TO_BYTES = {\n shared_owner: Buffer.from([2]),\n admin: Buffer.from([1]),\n operator: Buffer.from([0]),\n};\n\nconst ENDPOINTS = {\n GET_PUBLIC_KEY: \"/get-public-key\",\n GET_ATTESTATION: \"/get-attestation\",\n OPEN_SESSION: \"/open-session\",\n AUTHENTICATE: \"/authenticate\",\n REGISTER: \"/register\",\n VALIDATE_VAULT_OPERATION: \"/validate-vault-operation\",\n GENERATE_KEY_FRAGMENTS: \"/generate-key-fragments\",\n REGISTER_DATA: \"/u2f-register-data\",\n GET_CURRENT_DEVICE: \"/current-device\",\n // NEW SECURE CHANNEL ENDPOINTS\n INITIATE_PAIRING_HANDSHAKE: \"/initiate-pairing-handshake\",\n GENERATE_SEED_COMPONENT: \"/generate-key-component\",\n FINALIZE_PAIRING_HANDSHAKE: \"/finalize-pairing-handshake\",\n START_K_PATTERN_AS_RESPONDER: \"/start-k-pattern-as-responder\",\n START_KK_PATTERN_AS_RESPONDER: \"/start-kk-pattern-as-responder\",\n VALIDATE_OP: \"/validate-op\",\n};\n\nconst deviceTypeBufferFromPsdModel = {\n BLUE: Buffer.from([0]),\n STAX: Buffer.from([2]),\n};\n\nconst pathArrayToString = (path: number[]): string => {\n /* istanbul ignore if */\n if (!path[0] || !path[1]) throw new Error(\"Invalid path array\");\n return `${path[0] & 0xfffffff}'/${path[1] & 0xfffffff}'`; // eslint-disable-line no-bitwise\n};\n\nlet __DEVICE_API_NETWORK__ = createNetwork({\n baseURL: process.env.VAULT_DEVICE_API_URL || \"https://localhost:8443/device-api\",\n});\n\nexport function setDeviceAPIEndpoint(url: string): void {\n __DEVICE_API_NETWORK__ = createNetwork({ baseURL: url });\n}\n\ntype CreateAPIDeviceOptions = {\n deviceAPISessionID?: string;\n salt?: string;\n overrideSeeds?: string[];\n deviceAPIURL?: string;\n psdModel?: PsdModel;\n};\n\nfunction createAPIDevice(options: CreateAPIDeviceOptions = {}): APIDevice {\n const { deviceAPISessionID, overrideSeeds, deviceAPIURL, psdModel = \"STAX\" } = options;\n let { salt = \"\" } = options;\n // singleton used to store the current device seed\n // this way we keep the \"switch device\" logic (which\n // is handy because it works a lot like IRL), but\n // it has the advantage of having state held by client\n // and not by device-api (allow multi users)\n let __CURRENT_SEED__ = \"\";\n\n let __PSD_MODEL__ = psdModel;\n\n // used to persist data on device-api side\n const __DEVICE_API_SESSION_ID__ = deviceAPISessionID || nanoid();\n\n const setSeed = (seed: string): void => {\n __CURRENT_SEED__ = seed;\n };\n\n const setSalt = (newSalt: string) => {\n salt = newSalt;\n };\n\n const deviceNetwork = (\n method: \"POST\" | \"GET\",\n url: string,\n _data: any = {},\n requestOptions: any = {},\n ) => {\n const data = {\n seed: __CURRENT_SEED__,\n sessionID: __DEVICE_API_SESSION_ID__,\n psd_model: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n ..._data,\n version: 3, // This is used to choose Noise Channel mode\n };\n const deviceAPINetwork = deviceAPIURL\n ? createNetwork({ baseURL: deviceAPIURL })\n : __DEVICE_API_NETWORK__;\n return deviceAPINetwork(method, url, data, requestOptions);\n };\n\n const getPublicKey: DeviceGetPubKey = async (transport, path, secp256k1 = true) => {\n const data: {\n pubKey: string;\n signature: string;\n attestation: string;\n } = await deviceNetwork(\"POST\", ENDPOINTS.GET_PUBLIC_KEY, {\n path: pathArrayToString(path),\n secp256k1,\n });\n return {\n pubKey: data.pubKey,\n signature: Buffer.from(data.attestation, \"hex\"),\n };\n };\n\n const registerData: DeviceRegisterData = async (transport, challenge) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.REGISTER_DATA, {\n challenge: challenge.toString(\"hex\"),\n });\n\n // @ts-ignore\n return Buffer.from(data, \"hex\");\n };\n\n const switchDevice = async (id: number) => {\n setSeed(genSeed(salt, id, { overrideSeeds }));\n };\n\n const register: DeviceRegister = async (\n transport,\n challenge,\n application,\n name,\n userRole,\n registerData,\n ) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.REGISTER, {\n challenge: challenge.toString(\"hex\"),\n name,\n role: ROLE_TO_BYTES[userRole].toString(\"hex\"),\n hsm_data: registerData,\n type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n application,\n });\n // @ts-ignore\n const response = Buffer.from(data, \"hex\");\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0];\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0];\n /* istanbul ignore if */\n if (typeof keyHandleLength === \"undefined\") throw new Error(\"Invalid key handle length\");\n const keyHandle = response.slice(i, (i += keyHandleLength));\n // const attestationSignature = lastResponse.slice(i, ++i)[0];\n // const signature = lastResponse.slice(i).toString(\"hex\");\n return {\n u2f_register: response.slice(0, response.length - 2),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n const getAttestationCertificate: DeviceGenAttestationCert = async () => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.GET_ATTESTATION);\n return Buffer.from(data, \"hex\");\n };\n\n const validateVaultOperation: DeviceValidateVaultOp = async (transport, path, channel) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.VALIDATE_OP, {\n path: pathArrayToString(path),\n actions: channel.w_actions,\n });\n return data;\n };\n\n const authenticate: DeviceAuthenticate = async (\n transport,\n challenge,\n application,\n keyHandle,\n userName,\n role,\n workspace,\n ) => {\n const data = await deviceNetwork(\"POST\", ENDPOINTS.AUTHENTICATE, {\n challenge: challenge.toString(\"hex\"),\n application,\n key_handle: keyHandle.toString(\"hex\"),\n name: userName,\n // @ts-ignore\n role: ROLE_TO_BYTES[role.toLowerCase()].toString(\"hex\"),\n type: deviceTypeBufferFromPsdModel[__PSD_MODEL__].toString(\"hex\"),\n workspaceName: workspace,\n });\n\n // we add a fake status response because transport on device does it\n const response = Buffer.concat([Buffer.from(data, \"hex\"), Buffer.from(\"9000\", \"hex\")]);\n\n const userPresence = response.slice(0, 1);\n const counter = response.slice(1, 5);\n const signature = response.slice(5, response.length - 2).toString(\"hex\");\n return {\n userPresence,\n counter,\n signature,\n rawResponse: response.slice(0, response.length - 2).toString(\"hex\"),\n };\n };\n\n const getUserID = async (): Promise<string> => {\n const { blue_device_id } = await deviceNetwork(\"POST\", \"/blue-device-id\");\n return blue_device_id.toUpperCase();\n };\n\n // NEW SECURE CHANNEL ENDPOINTS\n\n const initiatePairingHandshake: InitiatePairingHandshake = async (): Promise<any> => {\n return deviceNetwork(\"POST\", ENDPOINTS.INITIATE_PAIRING_HANDSHAKE, {});\n };\n\n const finalizePairingHandshake: FinalizePairingHandshake = async (\n transport: any,\n handshake: string,\n ciphertext: string,\n ): Promise<string> => {\n const data = {\n handshake,\n ciphertext,\n };\n return deviceNetwork(\"POST\", ENDPOINTS.FINALIZE_PAIRING_HANDSHAKE, data);\n };\n\n const startKpatternAsResponder: StartKPatternAsResponder = async (\n transport: any,\n scriptID: number,\n handshake: string,\n handshakeAttestation: {\n attestation_pub: string;\n certificate: string;\n code_hash: string;\n signature: string;\n },\n partitionID: string,\n ): Promise<string> => {\n const data = {\n script_id: scriptID,\n handshake,\n // FIXME apparently for device-api it's normal to have empty string as handshake_attestation\n handshake_attestation: \"\",\n partition_id: partitionID,\n };\n return deviceNetwork(\"POST\", ENDPOINTS.START_K_PATTERN_AS_RESPONDER, data);\n };\n\n const hasPartitionID = async () => {\n const partitionIDs = await deviceNetwork(\"POST\", \"/get-partition-ids\");\n return partitionIDs.length > 0;\n };\n\n const getConfidentialityKey = () => deviceNetwork(\"POST\", \"/get-confidentiality-key\");\n\n const updatePSDPartitionPairing = (p: { pairingData: PairingData }) =>\n deviceNetwork(\"POST\", \"/set-partition-key\", p.pairingData);\n\n return {\n getPsdModel: () => __PSD_MODEL__,\n setPsdModel: (psdModel: PsdModel) => (__PSD_MODEL__ = psdModel),\n setSeed,\n setSalt,\n authenticate,\n getAttestationCertificate,\n getPublicKey,\n getUserID,\n register,\n registerData,\n switchDevice,\n // NEW SECURE CHANNEL ENDPOINTS\n validateVaultOperation,\n initiatePairingHandshake,\n finalizePairingHandshake,\n startKpatternAsResponder,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n };\n}\n\nexport const isAPIDevice = (device: Device): device is APIDevice => {\n return \"getPsdModel\" in device;\n};\n\nexport default createAPIDevice;\n","import { TransportStatusError } from \"@ledgerhq/hw-transport\";\nimport chalk from \"chalk\";\nimport invariant from \"invariant\";\nimport readline from \"readline\";\n\nimport { PAGINATED_STATUS, STREAMING_NEXT_ACTION, STREAMING_RESPONSE } from \"./constants\";\nimport {\n Device,\n DeviceAuthenticate,\n DeviceGenAttestationCert,\n DeviceGetPubKey,\n DeviceRegister,\n DeviceRegisterData,\n DeviceValidateVaultOp,\n FinalizePairingHandshake,\n InitiatePairingHandshake,\n StartKPatternAsResponder,\n} from \"./types\";\n\nconst STATUS_LENGTH = 2;\nconst MAX_CHUNK_LENGTH = 250;\nconst removeStatus = (result: Buffer): Buffer => result.slice(0, result.length - STATUS_LENGTH);\n\ntype Transport = {\n send: (\n cla: number,\n ins: number,\n p1: number,\n p2: number,\n data: Buffer,\n successStatus?: number[],\n ) => Promise<Buffer>;\n};\n\nconst buildAttestation = (\n cliMsg: string,\n attestation: {\n attestation_pub: string;\n certificate: string;\n code_hash: string;\n signature: string;\n },\n partitionID: string,\n) => {\n const sigBuffer = Buffer.from(attestation.signature, \"base64\");\n const sigLengthBuffer = Buffer.alloc(1);\n sigLengthBuffer.writeUIntBE(sigBuffer.length, 0, 1);\n\n const certBuffer = Buffer.from(attestation.certificate, \"base64\");\n const certLengthBuffer = Buffer.alloc(1);\n certLengthBuffer.writeUIntBE(certBuffer.length, 0, 1);\n\n const pubBuffer = Buffer.from(attestation.attestation_pub, \"base64\");\n\n const certChainData = Buffer.concat([\n sigLengthBuffer,\n sigBuffer,\n pubBuffer,\n certLengthBuffer,\n certBuffer,\n ]);\n\n const cliMsgBuffer = Buffer.from(cliMsg, \"hex\");\n const cliMsgLengthBuffer = Buffer.alloc(1);\n cliMsgLengthBuffer.writeUIntBE(cliMsgBuffer.length, 0, 1);\n\n const pidBuffer = Buffer.from(partitionID, \"hex\");\n const pidLengthBuffer = Buffer.alloc(1);\n pidLengthBuffer.writeUIntBE(pidBuffer.length, 0, 1);\n\n const certChainDataLengthBuff = Buffer.alloc(2);\n certChainDataLengthBuff.writeUInt16BE(certChainData.length, 0);\n\n const data = Buffer.concat([\n cliMsgLengthBuffer,\n cliMsgBuffer,\n certChainDataLengthBuff,\n certChainData,\n pidLengthBuffer,\n pidBuffer,\n ]);\n\n const totalLengthBuffer = Buffer.alloc(2);\n totalLengthBuffer.writeUInt16BE(data.length, 0);\n\n const toSend = Buffer.concat([totalLengthBuffer, data]);\n\n return toSend;\n};\n\n/* istanbul ignore next: (obviously not run in tests) */\nfunction promptUserInput(query: any) {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) =>\n rl.question(query, (ans) => {\n rl.close();\n resolve(ans);\n }),\n );\n}\nconst splits = (chunk: number, buffer: Buffer): Buffer[] => {\n const chunks = [];\n for (let i = 0, size = chunk; i < buffer.length; i += size, size = chunk) {\n chunks.push(buffer.slice(i, i + size));\n }\n return chunks;\n};\n\nexport const sendByChunk = async (\n transport: Transport,\n command: number[], // eg [0xe0, 0x45, 0x00, 0x00]\n data: Buffer,\n chunkSize: number = MAX_CHUNK_LENGTH,\n): Promise<Buffer> => {\n const chunks = splits(chunkSize, data);\n let response = Buffer.from([]);\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n const apdu = [...command];\n // command is 0xe0,0x45,0x00,0x00 for first chunk, then it's 0xe0,0x45, 0x80,0x00\n apdu[2] = i && 0x80;\n response = await transport.send(\n apdu[0] as number,\n apdu[1] as number,\n apdu[2] as number,\n apdu[3] as number,\n chunk as Buffer,\n [0x9000, ...PAGINATED_STATUS],\n );\n }\n return response;\n};\n\nconst ROLE_TO_BYTES: Record<string, Buffer> = {\n shared_owner: Buffer.from([2]),\n admin: Buffer.from([1]),\n operator: Buffer.from([0]),\n};\n\ntype CreateHWDeviceOptions = {\n promptToSwitch?: boolean; // wether we prompt the user to enter to continue, false for tests\n};\n\nfunction createHWDevice(options: CreateHWDeviceOptions): Device {\n const { promptToSwitch } = options;\n\n const getPublicKey: DeviceGetPubKey = async (\n transport,\n path,\n /* istanbul ignore next */\n secp256k1 = true,\n ) => {\n const data = Buffer.concat([\n Buffer.from([path.length]),\n ...path.map((derivation) => {\n const buf = Buffer.alloc(4);\n buf.writeUInt32BE(derivation, 0);\n return buf;\n }),\n ]);\n let curve = 0x01;\n if (!secp256k1) {\n curve = 0x02;\n }\n const response = await transport.send(0xe0, 0x40, curve, 0x00, data);\n const pubKeyLength = response.readInt8(0);\n const pubKey = response\n .slice(1, pubKeyLength + 1)\n .toString(\"hex\")\n .toUpperCase();\n const signature = response.slice(pubKeyLength + 1, response.length - STATUS_LENGTH);\n return { pubKey, signature };\n };\n /* istanbul ignore next */\n const registerData: DeviceRegisterData = async (transport, challenge) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n\n const response = await transport.send(0xe0, 0x03, 0x00, 0x00, challenge);\n return removeStatus(response);\n };\n /* istanbul ignore next */\n const register: DeviceRegister = async (\n transport,\n challenge,\n application,\n userName,\n userRole,\n ) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n const applicationBuf = Buffer.from(application, \"hex\");\n invariant(applicationBuf.length === 32, \"application hex is expected to have 32 bytes\");\n\n const userNameBuff = Buffer.from(userName);\n\n const keyHandleData = Buffer.concat([\n ROLE_TO_BYTES[userRole.toLowerCase()] as Buffer,\n Buffer.from([userNameBuff.length]),\n userNameBuff,\n ]);\n\n const keyHandleDataLength = Buffer.alloc(2);\n keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);\n\n const data = Buffer.concat([challenge, applicationBuf, keyHandleDataLength, keyHandleData]);\n\n const response = await sendByChunk(transport, [0xe0, 0x01, 0x00, 0x00], data);\n\n let i = 0;\n const rfu = response.slice(i, (i += 1))[0];\n const pubKey = response.slice(i, (i += 65)).toString(\"hex\");\n const keyHandleLength = response.slice(i, ++i)[0] as number;\n const keyHandle = response.slice(i, (i += keyHandleLength));\n return {\n u2f_register: removeStatus(response),\n keyHandle,\n rfu,\n pubKey,\n };\n };\n\n /* istanbul ignore next */\n const getAttestationCertificate: DeviceGenAttestationCert = async (transport) => {\n const response = await transport.send(0xe0, 0x41, 0x00, 0x00);\n return removeStatus(response);\n };\n\n const authenticate: DeviceAuthenticate = async (\n transport,\n challenge,\n application,\n keyHandle,\n name,\n role,\n workspace,\n ) => {\n invariant(challenge.length === 32, \"challenge hex is expected to have 32 bytes\");\n const applicationBuf = Buffer.from(application, \"hex\");\n invariant(applicationBuf.length === 32, \"application hex is expected to have 32 bytes\");\n\n const nameBuf = Buffer.from(name);\n const workspaceBuf = Buffer.from(workspace);\n\n const roleBuf = ROLE_TO_BYTES[role.toLowerCase()] as Buffer;\n\n const keyHandleData = Buffer.concat([\n roleBuf,\n Buffer.from([nameBuf.length]),\n nameBuf,\n Buffer.from([workspaceBuf.length]),\n workspaceBuf,\n ]);\n\n const keyHandleDataLength = Buffer.alloc(2);\n keyHandleDataLength.writeUInt16BE(keyHandleData.length, 0);\n\n const data = Buffer.concat([\n challenge,\n applicationBuf,\n Buffer.from([keyHandle.length]),\n keyHandle,\n keyHandleDataLength,\n keyHandleData,\n ]);\n\n const response = await sendByChunk(transport, [0xe0, 0x02, 0x00, 0x00], data);\n\n const userPresence = response.slice(0, 1);\n const counter = response.slice(1, 5);\n const signature = response.slice(5, response.length - STATUS_LENGTH).toString(\"hex\");\n return {\n userPresence,\n counter,\n signature,\n rawResponse: removeStatus(response).toString(\"hex\"),\n };\n };\n\n const switchDevice = async () => {\n /* istanbul ignore next */\n if (promptToSwitch) {\n await promptUserInput(\n chalk`{red.green [SWITCH DEVICE]} press {red ENTER} when the device is plugged on.`,\n );\n }\n };\n\n // TODO implement new noise channel endpoints\n // TODO remove istanbul ignore statements when done\n /* istanbul ignore next */\n const initiatePairingHandshake: InitiatePairingHandshake = async (transport) => {\n const res = await transport.send(0xe0, 0x46, 0x00, 0x00);\n const pubKeyLength = res.readInt8(0);\n const pubKey = res\n .slice(1, pubKeyLength + 1)\n .toString(\"hex\")\n .toUpperCase();\n const attestation = res.slice(pubKeyLength + 1, res.length - STATUS_LENGTH);\n return { pubKey, attestation };\n };\n\n const validateVaultOperation: DeviceValidateVaultOp = async (transport, path, channel) => {\n const paths = Buffer.concat([\n Buffer.from([path.length]),\n ...path.map((derivation) => {\n const buf = Buffer.alloc(4);\n buf.writeUInt32BE(derivation, 0);\n return buf;\n }),\n ]);\n\n let nextActionId = 1;\n let finalResponse;\n\n while (!finalResponse) {\n const screen = Buffer.from(channel.w_actions[nextActionId - 1]!, \"base64\");\n const length = Buffer.alloc(2);\n length.writeUInt16BE(screen.length, 0);\n const data = Buffer.concat([paths, length, screen]);\n const response = await sendByChunk(transport, [0xe0, 0x45, 0x00, 0x00], data);\n const responseType = response.readInt8(0);\n let responseStatus = response.readUInt16BE(response.length - 2);\n\n /* istanbul ignore else */\n if (responseType === STREAMING_NEXT_ACTION) {\n nextActionId = response.readUIntBE(1, 2);\n } else if (responseType === STREAMING_RESPONSE) {\n finalResponse = response.slice(3, response.length - 2);\n // FIXME we should cover this in tests\n /* istanbul ignore next */\n while (responseStatus !== 0x9000) {\n const resp = await transport.send(0x00, 0xc0, 0x00, 0x00);\n responseStatus = resp.readUInt16BE(resp.length - 2);\n finalResponse = Buffer.concat([finalResponse, removeStatus(resp)]);\n }\n } else {\n throw Error(`${responseType}`);\n }\n }\n return finalResponse.toString(\"base64\");\n };\n\n /* istanbul ignore next */\n const finalizePairingHandshake: FinalizePairingHandshake = async (\n transport,\n handshake,\n ciphertext,\n handshakeAttestation,\n ) => {\n const response = await sendByChunk(\n transport,\n [0xe0, 0x47, 0x00, 0x02],\n buildAttestation(handshake, handshakeAttestation, ciphertext),\n );\n return removeStatus(response).toString(\"hex\");\n };\n\n const startKpatternAsResponder: StartKPatternAsResponder = async (\n transport,\n scriptID,\n handshake,\n handshakeAttestation,\n partitionID,\n ) => {\n const data = buildAttestation(handshake, handshakeAttestation, partitionID);\n const response = await sendByChunk(transport, [0xe0, 0x49, 0x00, scriptID], data);\n return removeStatus(response).toString(\"hex\");\n };\n\n /* istanbul ignore next */\n const getUserID = () => Promise.resolve(\"\");\n\n const hasPartitionID = async ({ transport }: any) => {\n const apdu = [0xe0, 0x51, 0x00, 0x00, 0xb5];\n const res = await transport.exchange(Buffer.from(apdu));\n const hex = Buffer.from(res).toString(\"hex\");\n\n // 6f45 => this error means that there is no partition ID on the device\n // if no error, it means there is (at least) one\n //\n // 6a87 => TEMPORARY hack because Stax apparently does not implement\n // the \"get partition id\" APDU, so it returns \"incorrect length\"\n // easier to just assume that in this case there is no partition,\n // and try to insert one anyway (done on revault onboarding)\n //\n return hex !== \"6f45\" && hex !== \"6a87\";\n };\n\n const getConfidentialityKey = async ({ transport }: { transport: any }) => {\n const apdu = [0xe0, 0x4d, 0x00, 0x00];\n const bufWithStatus = await transport.send(...apdu);\n const status = bufWithStatus.readUInt16BE(bufWithStatus.length - 2);\n /* istanbul ignore if */\n if (status !== 0x9000) {\n // @ts-ignore\n throw new TransportStatusError(status);\n }\n const buf = removeStatus(bufWithStatus);\n const keyLength = buf[0]!;\n const key = buf.slice(1, 1 + keyLength);\n const sig = buf.slice(1 + keyLength + 1);\n const res = {\n confidentiality_key_curve25519: key.toString(\"hex\"),\n confidentiality_key_signature: sig.toString(\"hex\"),\n };\n return res;\n };\n\n type PairingData = {\n blob: string;\n };\n\n const updatePSDPartitionPairing = async ({\n transport,\n pairingData,\n }: {\n transport: any;\n pairingData: PairingData;\n }) => {\n const psdPayload = Buffer.from(pairingData.blob, \"base64\");\n const length = Buffer.alloc(2);\n length.writeUInt16BE(psdPayload.length, 0);\n const final = Buffer.concat([length, psdPayload]);\n await sendByChunk(transport, [0xe0, 0x4e, 0x00, 0x00], final);\n };\n\n return {\n authenticate,\n switchDevice,\n getAttestationCertificate,\n getPublicKey,\n register,\n registerData,\n initiatePairingHandshake,\n validateVaultOperation,\n finalizePairingHandshake,\n startKpatternAsResponder,\n getUserID,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n };\n}\n\nexport default createHWDevice;\n","// @flow\n\nconst all61xxStatus = [];\nfor (let i = 0x6100; i <= 0x61ff; i++) {\n all61xxStatus.push(i);\n}\n\nexport const PAGINATED_STATUS = all61xxStatus;\nexport const U2F_PATH = [0x80564c54, 0x80553246];\nexport const CONFIDENTIALITY_PATH = [0x80564c54, 0x80434e46];\nexport const VALIDATION_PATH = [0x80564c54, 0x8056414c];\nexport const MATCHER_SESSION = 0x01;\nexport const ACCOUNT_MANAGER_SESSION = 0x02;\nexport const U2F_TIMEOUT = \"U2F_5\";\nexport const STREAMING_RESPONSE = 0x02;\nexport const STREAMING_NEXT_ACTION = 0x01;\nexport const INVALID_DATA = 27264;\nexport const DEVICE_REJECT_ERROR_CODE = 27013;\nexport const INVALID_OR_MISSING_ATTESTATION = 0x6801;\nexport const APPID_VAULT_ADMINISTRATOR =\n \"ad5be1a1fe011ce7f53ae081a22ae000a42021f3f94106a3bac9f76e8230e4b9\";\n","// DISCLAIMER\n//\n// This is blackbox legacy code extract from ledger-vault-front source\n// It's not intended to be clear, but it's working.\nimport { GateUser, GetU2FPubKeyInteraction, Interaction, Interactions } from \"../types\";\nimport { extractSecureChannel } from \"../utils\";\nimport {\n ACCOUNT_MANAGER_SESSION,\n APPID_VAULT_ADMINISTRATOR,\n CONFIDENTIALITY_PATH,\n U2F_PATH,\n VALIDATION_PATH,\n} from \"./constants\";\nimport { isAPIDevice } from \"./createAPIDevice\";\nimport { Device, PairingData } from \"./types\";\n\nfunction noValidChannel(channels: any) {\n const keys = Object.keys(channels);\n return keys.length === 0 || (keys.length === 1 && keys[0] === \"success\");\n}\n\nfunction buildCertif(attestation: Buffer, signature: string) {\n const certLen = attestation.readInt8(32 + 65 + 1);\n return {\n code_hash: attestation.slice(0, 32).toString(\"base64\"),\n attestation_pub: attestation.slice(32, 32 + 65).toString(\"base64\"),\n certificate: attestation.slice(32 + 65, 32 + 65 + 2 + certLen).toString(\"base64\"),\n signature: Buffer.from(signature, \"hex\").toString(\"base64\"),\n };\n}\n\nexport default function createInteractions(device: Device): Interactions {\n const {\n getPublicKey,\n getAttestationCertificate,\n register,\n registerData,\n authenticate,\n validateVaultOperation,\n initiatePairingHandshake,\n finalizePairingHandshake,\n startKpatternAsResponder,\n hasPartitionID,\n getConfidentialityKey,\n updatePSDPartitionPairing,\n } = device;\n\n const initPairing: Interaction<any> = {\n responseKey: \"psd_ephemeral_pubkey\",\n action: ({ transport }: any) => initiatePairingHandshake(transport),\n };\n\n const getU2FPublicKey: GetU2FPubKeyInteraction = {\n responseKey: \"u2f_key\",\n action: ({ transport }) => getPublicKey(transport, U2F_PATH, false),\n };\n\n const getSecureChannel: Interaction<any> = {\n responseKey: \"secure_channels\",\n action: async ({ request_id, network }: any) =>\n network(\"GET\", `/requests/${request_id}/challenge`, {}),\n };\n\n const doStartKpatternAsResponder: Interaction<any> = {\n responseKey: \"start-k-pattern\",\n action: async ({ transport, secure_channels, u2f_key }: any) => {\n if (noValidChannel(secure_channels)) {\n throw new Error(\"Request finished\");\n }\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n if (!channel) {\n throw new Error(\"No channel for device\");\n }\n await startKpatternAsResponder(\n transport,\n ACCOUNT_MANAGER_SESSION,\n channel.handshake,\n channel.handshake_attestation,\n channel.partition_id,\n );\n },\n };\n\n const validateDevice: Interaction<any> = {\n responseKey: \"validate_device\",\n action: ({ transport, secure_channels, u2f_key }: any) => {\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n return validateVaultOperation(transport, VALIDATION_PATH, channel);\n },\n };\n\n const validatePayload: Interaction<any> = {\n responseKey: \"validate_payload\",\n action: ({ secure_channels, u2f_key, validate_device }: any) => {\n const channel = secure_channels[u2f_key.pubKey.toUpperCase()];\n\n const handshake_attestation = {\n code_hash: channel.handshake_attestation.code_hash,\n attestation_pub: channel.handshake_attestation.attestation_pub,\n certificate: channel.handshake_attestation.certificate,\n signature: channel.handshake_attestation.signature,\n };\n\n return Promise.resolve({\n public_key: u2f_key.pubKey,\n challenge_response: validate_device,\n handshake: Buffer.from(channel.handshake, \"hex\").toString(\"base64\"),\n handshake_attestation,\n });\n },\n };\n\n const postApproval: Interaction<any> = {\n responseKey: \"post\",\n action: async ({ request_id, u2f_key: { pubKey }, validate_payload, network }: any) =>\n network(\"POST\", `/requests/${request_id}/approve`, {\n requestId: request_id,\n ...validate_payload,\n public_key: pubKey.toString(\"hex\"),\n }),\n };\n\n const postSimpleApproval: Interaction<any> = {\n responseKey: \"post\",\n action: async ({ request_id, u2f_key: { pubKey }, network }: any) =>\n network(\"POST\", `/requests/${request_id}/approve`, {\n requestId: request_id,\n public_key: pubKey.toString(\"hex\"),\n }),\n };\n\n const getConfidentialityPublicKey: Interaction<any> = {\n responseKey: \"confidentiality_key\",\n action: ({ transport }: { transport: any }) => getPublicKey(transport, CONFIDENTIALITY_PATH),\n };\n\n const getAttestation: Interaction<any> = {\n responseKey: \"attestation\",\n action: ({ transport }) => getAttestationCertificate(transport),\n };\n\n const getU2FChallenge: Interaction<any> = {\n responseKey: \"u2f_challenge\",\n action: async ({ u2f_key: { pubKey }, network }: any) => {\n const challenge = await network(\"GET\", `/u2f/authentications/${pubKey}/challenge`);\n if (!challenge.key_handle) {\n throw new Error(\"Unknown device\");\n }\n return challenge;\n },\n };\n\n const getValidationPublicKey: Interaction<any> = {\n responseKey: \"validation_key\",\n action: ({ transport }) => getPublicKey(transport, VALIDATION_PATH),\n };\n\n const u2fAuthenticate: Interaction<any> = {\n device: true,\n responseKey: \"u2f_authenticate\",\n action: ({ transport, u2f_challenge: { challenge, key_handle, role, name }, workspace }: any) =>\n authenticate(\n transport,\n Buffer.from(challenge, \"base64\"),\n APPID_VAULT_ADMINISTRATOR,\n Buffer.from(key_handle, \"hex\"),\n name,\n role,\n workspace,\n ),\n };\n\n const readOnlyLogin: (username: string) => Interaction<any> = (username: string) => ({\n responseKey: \"u2f_sign\",\n action: async ({ network }: any) => {\n const gateUsers: GateUser[] = await network(\"GET\", \"/dev/people/pub_keys\");\n\n const currentUser = gateUsers.find((u) => u.username === username);\n\n if (!currentUser) {\n throw new Error(`Can't find user ${username}`);\n }\n\n return network(\"POST\", `/dev/authentication/view_only/${currentUser.pub_key}`);\n },\n });\n\n const postU2FSignature: Interaction<any> = {\n responseKey: \"u2f_sign\",\n action: ({ u2f_authenticate, network }: any) =>\n network(\"POST\", `/u2f/authentications/authenticate`, {\n authentication: u2f_authenticate.rawResponse,\n }),\n };\n\n const finalizePairing: Interaction<any> = {\n responseKey: \"pairing_payload\",\n action: ({ onboardingRegisterChallenge, transport, u2f_key }) => {\n const secure_channel = extractSecureChannel(\n onboardingRegisterChallenge,\n u2f_key.pubKey.toUpperCase(),\n );\n return finalizePairingHandshake(\n transport,\n secure_channel.handshake,\n secure_channel.ciphertext,\n secure_channel.handshake_attestation!,\n );\n },\n };\n\n const onboardingRegisterDevice: Interaction<any> = {\n responseKey: \"u2f_register\",\n action: async ({ u2f_key, onboardingRegisterChallenge, role, username, transport }) => {\n const secure_channel = extractSecureChannel(\n onboardingRegisterChallenge,\n u2f_key.pubKey.toUpperCase(),\n );\n return register(\n transport,\n Buffer.from(secure_channel.challenge, \"hex\"),\n APPID_VAULT_ADMINISTRATOR,\n username,\n role,\n secure_channel.u2f_register_data,\n );\n },\n };\n\n const onboardingRegisterData: Interaction<any> = {\n responseKey: \"register_data\",\n action: ({ transport, u2f_key, onboardingRegisterChallenge }) => {\n // either the channel is in a map indexed by public key (onboarding)\n // or directly in registerChallenge (register operator)\n /* istanbul ignore next */\n const register_challenge =\n onboardingRegisterChallenge[u2f_key.pubKey.toString(\"hex\").toUpperCase()] ||\n onboardingRegisterChallenge;\n\n return registerData(transport, Buffer.from(register_challenge.challenge, \"hex\"));\n },\n };\n\n const onboardingPostResult: Interaction<any> = {\n responseKey: \"register_input\",\n action: ({\n register_data,\n pairing_payload,\n validation_key,\n u2f_register,\n attestation,\n u2f_key,\n }) => {\n const attestationOffset = 67 + u2f_register.u2f_register.readInt8(66);\n const registration_payload = Buffer.concat([\n u2f_register.u2f_register.slice(0, attestationOffset),\n Buffer.from([attestation.length]),\n attestation,\n u2f_register.u2f_register.slice(attestationOffset),\n ]);\n const data = {\n public_key: u2f_key.pubKey.toString(\"hex\"),\n key_handle: u2f_register.keyHandle.toString(\"hex\"),\n validation_key: validation_key.pubKey.toString(\"hex\"),\n register_data: register_data.toString(\"hex\"),\n u2f_register: registration_payload.toString(\"hex\"),\n certificate: buildCertif(attestation, validation_key.signature),\n pairing_payload,\n };\n return Promise.resolve(data);\n },\n };\n\n const postUserRegistration: Interaction<any> = {\n responseKey: \"result\",\n action: ({ urlID, register_input, member, network }: any) =>\n network(\"POST\", `/requests/registration/${urlID}/authenticate`, {\n ...register_input,\n name: member.user.username,\n }),\n };\n\n const operatorGetChallenge: Interaction<any> = {\n responseKey: \"onboardingRegisterChallenge\",\n action: ({\n u2f_key,\n confidentiality_key,\n attestation,\n urlID,\n network,\n psd_ephemeral_pubkey,\n }: any) => {\n const data = {\n public_key: u2f_key.pubKey,\n confidentiality_key: confidentiality_key.pubKey,\n ...(isAPIDevice(device) && device.getPsdModel() === \"STAX\"\n ? { device_type: \"02\" }\n : /* istanbul ignore next */\n {}),\n ...{\n psd_ephemeral_key: psd_ephemeral_pubkey.pubKey,\n psd_ephemeral_key_attestation: buildCertif(attestation, psd_ephemeral_pubkey.attestation),\n },\n };\n return network(\"POST\", `/requests/registration/${urlID}/challenge`, data);\n },\n };\n\n const ensurePartitionPairing: Interaction<any> = {\n responseKey: \"hasPairing\",\n action: async ({ transport, network }) => {\n // check if there is a partition ID on device\n if (!(await hasPartitionID({ transport }))) {\n // if not, we ask device for its confidentiality key\n const payload = await getConfidentialityKey({ transport });\n // we then initiate a \"prepare psd update\" flow with the hsm...\n const url = \"/authentications/prepare-psd-key-update\";\n const pairingData = await network<PairingData>(\"POST\", url, payload);\n // ...and inject back the data into the device\n await updatePSDPartitionPairing({ transport, pairingData });\n }\n\n // else, all is alright\n return true;\n },\n };\n\n const validateOperation = [\n getU2FPublicKey,\n doStartKpatternAsResponder,\n validateDevice,\n validatePayload,\n ];\n\n const approveFlow = [getSecureChannel, ...validateOperation, postApproval];\n const approveFlowWithoutHSM = [postSimpleApproval];\n\n const loginFlow = [\n getU2FPublicKey,\n getU2FChallenge,\n u2fAuthenticate,\n postU2FSignature,\n ensurePartitionPairing,\n ];\n\n const readOnlyLoginFlow = (username: string) => [readOnlyLogin(username)];\n\n const registerUserFlow = [\n getAttestation,\n getU2FPublicKey,\n getConfidentialityPublicKey,\n initPairing,\n operatorGetChallenge,\n finalizePairing,\n onboardingRegisterDevice,\n getValidationPublicKey,\n onboardingRegisterData,\n onboardingPostResult,\n postUserRegistration,\n ];\n\n return {\n approveFlow,\n approveFlowWithoutHSM,\n doStartKpatternAsResponder,\n getSecureChannel,\n getU2FChallenge,\n getU2FPublicKey,\n initPairing,\n loginFlow,\n readOnlyLoginFlow,\n registerUserFlow,\n u2fAuthenticate,\n validateOperation,\n ensurePartitionPairing,\n finalizePairing,\n getAttestation,\n getConfidentialityPublicKey,\n getValidationPublicKey,\n operatorGetChallenge,\n postU2FSignature,\n postUserRegistration,\n validateDevice,\n validatePayload,\n postApproval,\n postSimpleApproval,\n };\n}\n","import createNetwork from \"./createNetwork\";\n\ntype CreateFaucetOptions = {\n pralineBTCTestnetURL?: string;\n};\n\ntype FaucetOptions = FaucetImplementationOptions & {\n currency: string;\n};\n\ntype Faucet = (opts: FaucetOptions) => Promise<TxHash>;\n\ntype FaucetImplementationOptions = {\n recipient: string;\n amount: string; // amount in base unit (e.g: \"1\" for \"1 BTC\")\n};\n\ntype TxHash = string;\n\ntype FaucetImplementation = (o: FaucetImplementationOptions) => Promise<TxHash>;\n\ntype PralineFaucetResponse = {\n tx: string;\n minedBlocks: string[];\n};\n\nconst createPralineFaucet =\n (url: string): FaucetImplementation =>\n async (opts) => {\n const network = createNetwork({\n baseURL: url,\n });\n const { recipient, amount } = opts;\n const endpoint = `/chain/faucet/${recipient}/${amount}`;\n const res = await network<PralineFaucetResponse>(\"POST\", endpoint);\n return res.tx;\n };\n\nfunction createFaucet(opts: CreateFaucetOptions): Faucet {\n const implems: Record<string, FaucetImplementation> = {};\n\n /* istanbul ignore else */\n if (opts.pralineBTCTestnetURL) {\n implems[\"bitcoin_testnet\"] = createPralineFaucet(opts.pralineBTCTestnetURL);\n }\n\n const faucet: Faucet = (opts: FaucetOptions) => {\n const { currency, ...rest } = opts;\n const implem = implems[currency];\n if (!implem) {\n throw new Error(`Unsupported faucet currency ${opts.currency}`);\n }\n return implem(rest);\n };\n\n return faucet;\n}\n\nexport default createFaucet;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { authenticate, decodeChallenge, getTradelinkPledge, signAndApprove } from \"./apiUser\";\nimport createNetwork from \"./createNetwork\";\nimport fetchTokens from \"./fetchTokens\";\nimport {\n APIRequestResponse,\n DevicesPool,\n GateAccount,\n GateTokenCurrency,\n ManifestAPIV2User,\n ManifestAccount,\n RunnableOptions,\n} from \"./types\";\nimport { APIPostPledgeIncrementData } from \"./types/tradelink\";\nimport { getAccountUnit, getWorkspaceFromGate, serializeUnitValue } from \"./utils\";\n\nexport type CreatePledgeOptions = {\n pool: DevicesPool;\n account: ManifestAccount;\n accountsByName: Record<string, GateAccount>;\n exchange: string;\n amount: string;\n apiUser: ManifestAPIV2User;\n gate: string;\n apiGateway: string;\n};\n\nasync function createPledge(\n {\n pool,\n account,\n accountsByName,\n exchange,\n amount,\n apiUser,\n gate,\n apiGateway,\n }: CreatePledgeOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<APIRequestResponse> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n const apiNetwork = createNetwork({\n baseURL: apiGateway,\n });\n logger.info(`Authenticate for ${apiUser.name}`);\n const workspace = getWorkspaceFromGate(gate);\n const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);\n\n const pledge = await getTradelinkPledge({\n apiNetwork,\n workspace,\n gateAccount,\n bearerToken,\n exchange,\n });\n\n logger.info(\"Create pledge\");\n\n let tokens: GateTokenCurrency[] = [];\n\n // if account is a token one, we need to fetch tokens list from the Gate to properly grab unit\n const shouldLoadTokens = !!gateAccount.contract_address;\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n const unit = getAccountUnit(account, tokens);\n const serializedAmount = serializeUnitValue(unit, amount);\n\n const data: APIPostPledgeIncrementData = {\n type: \"CREATE_PLEDGE_INCREMENT\",\n data: {\n pledge_subaccount_id: pledge.pledge_subaccount_id,\n pledge_data: {\n amount: serializedAmount,\n account_name: account.name,\n currency: gateAccount.currency,\n exchange_name: exchange,\n },\n },\n };\n\n if (shouldLoadTokens) {\n data.data.pledge_data.contract_address = gateAccount.contract_address;\n }\n\n const pledgeRequestResp = await apiNetwork<APIRequestResponse>(\"POST\", \"/requests\", data, {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n\n logger.info(`Approving pledge ${pledgeRequestResp.id}`);\n logger.info(\"Decode challenge\");\n const apiChallenge = await decodeChallenge({\n apiNetwork,\n workspace,\n bearerToken,\n requestID: pledgeRequestResp.id,\n reviewType: \"APPROVE\",\n });\n logger.info(apiChallenge.decodedChallenge);\n logger.info(\"Sign and approve\");\n return await signAndApprove({\n apiNetwork,\n workspace,\n bearerToken,\n requestID: pledgeRequestResp.id,\n apiUser,\n challenge: apiChallenge.challenge,\n reviewType: \"APPROVE\",\n });\n}\n\nexport default createPledge;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport { v4 as uuidv4 } from \"uuid\";\n\nimport { authenticate, getTradelinkPledge, getTradelinkRecipient } from \"./apiUser\";\nimport createNetwork from \"./createNetwork\";\nimport {\n APIBitcoinLikeSend,\n APIEstimateFeesResponse,\n APIEthereumLikeSend,\n APIGenericSend,\n DevicesPool,\n GateAccount,\n ManifestAPIV2User,\n ManifestAccount,\n RunnableOptions,\n} from \"./types\";\nimport { APISettlementData } from \"./types/tradelink\";\nimport { getWorkspaceFromGate } from \"./utils\";\n\nexport type CreateSettlementOptions = {\n pool: DevicesPool;\n account: ManifestAccount;\n accountsByName: Record<string, GateAccount>;\n exchange: string;\n percentage: number;\n apiUser: ManifestAPIV2User;\n gate: string;\n apiGateway: string;\n};\n\nasync function createSettlement(\n {\n pool,\n account,\n accountsByName,\n exchange,\n percentage,\n apiUser,\n gate,\n apiGateway,\n }: CreateSettlementOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<any> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n const apiNetwork = createNetwork({\n baseURL: apiGateway,\n });\n logger.info(`Authenticate for ${apiUser.name}`);\n const workspace = getWorkspaceFromGate(gate);\n const bearerToken = await authenticate(pool, apiNetwork, workspace, apiUser, logger);\n const pledge = await getTradelinkPledge({\n apiNetwork,\n workspace,\n gateAccount,\n bearerToken,\n exchange,\n });\n const recipient = await getTradelinkRecipient({ pool, gateAccount, pledge });\n\n const settlementAmount = Math.floor((pledge.amount * percentage) / 100);\n\n let txIntent: APIGenericSend | APIEthereumLikeSend | APIBitcoinLikeSend = {\n account_id: gateAccount.id,\n fees_strategy: {\n type: \"SPEED\",\n data: {\n speed: \"FAST\",\n },\n },\n transaction_data: {\n account_name: gateAccount.name,\n amount: String(settlementAmount),\n max_fees: \"0\",\n recipient,\n },\n };\n if (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") {\n // get max fees\n txIntent = {\n ...txIntent,\n transaction_data: {\n ...txIntent.transaction_data,\n currency: gateAccount.currency as\n | \"ethereum\"\n | \"ethereum_sepolia\"\n | \"polygon\"\n | \"ethereum_holesky\",\n },\n transaction_type: \"ETHEREUM_LIKE_SEND\",\n };\n } else {\n throw new Error(\"Unsupported account type for outbound transactions\");\n }\n\n // get max fees\n const maxFeesResp = await apiNetwork<APIEstimateFeesResponse>(\n \"POST\",\n \"/transactions/estimate-fees\",\n {\n type: \"CREATE_TRANSACTION\",\n data: txIntent,\n },\n {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n },\n );\n\n // update tx intent with max fees\n txIntent.transaction_data.max_fees = maxFeesResp.max_fees;\n\n const settlementIntent: APISettlementData = {\n id: String(uuidv4()),\n from_pledge_id: pledge.id,\n inbound_transaction_intent: null,\n outbound_transaction_intent: txIntent,\n meta: {},\n };\n\n logger.info(\"Post settlement\");\n // post settlement\n return await apiNetwork<any>(\"POST\", \"/settlements\", settlementIntent, {\n headers: {\n \"X-Ledger-Workspace\": workspace,\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${bearerToken}`,\n },\n });\n}\n\nexport default createSettlement;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport duration from \"humanize-duration\";\nimport isEqual from \"lodash/isEqual\";\nimport io from \"socket.io-client\";\n\nimport { DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES, DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { DeployOptions, DeploymentStepID, MVInstanceDeployment, RunnableOptions } from \"./types\";\nimport { wait } from \"./utils\";\n\ntype SimplifiedDeploymentState = {\n error: Error | null;\n totalPods: number;\n totalHealthyPods: number;\n currentStep: DeploymentStepID;\n status: \"BUSY\" | \"SUCCESS\" | \"ERROR\";\n};\n\nconst toSimplifiedState = (deployment: MVInstanceDeployment): SimplifiedDeploymentState => {\n return {\n error: deployment.error,\n status: deployment.status,\n currentStep: deployment.currentStep,\n totalPods: deployment.instance.pods.length,\n totalHealthyPods: deployment.instance.pods.filter(\n // TODO centralize types with vault-remote\n (p: any) => p.status === \"HEALTHY\",\n ).length,\n };\n};\n\nexport default async function deploy(\n opts: DeployOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<{ url: string }> {\n const {\n remoteURL = DEFAULT_VAULT_REMOTE_URL,\n watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,\n useHTTPPolling = false,\n ...payload\n } = opts;\n\n const now = Date.now();\n\n logger.step(`Deploying ${payload.name}`);\n\n const url = remoteURL.replace(/https:\\/\\/(.*?)\\./, `https://${payload.name}.`);\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n const deployment: MVInstanceDeployment = await network(\"POST\", \"/deploy\", payload);\n const gateURL = `https://${deployment.instance.name}.${deployment.instance.host}/gate/minivault`;\n const gateNetwork = createNetwork({ baseURL: gateURL });\n\n let state = toSimplifiedState(deployment);\n\n const logState = () => {\n const step = deployment.steps.find((s) => s.key === state.currentStep)!;\n logger.info(`[${step.labelCurrent}] (${state.totalHealthyPods}/${state.totalPods})`);\n };\n\n let resolve: ((value: void | PromiseLike<void>) => void) | null = null,\n reject: ((err: Error) => void) | null = null;\n\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const deployTimeout = setTimeout(\n () => reject!(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),\n watchTimeoutMinutes * 60 * 1000,\n );\n\n const onState = (p: MVInstanceDeployment) => {\n const newState = toSimplifiedState(p);\n if (!isEqual(state, newState)) {\n state = newState;\n logState();\n }\n if (newState.error) {\n reject!(newState.error);\n } else if (newState.status === \"SUCCESS\") {\n const elapsed = Date.now() - now;\n const displayDuration = duration(elapsed, { round: true });\n logger.success(`Successfully deployed instance in ${displayDuration}`);\n logger.success(url);\n clearTimeout(deployTimeout);\n resolve!();\n }\n };\n\n async function pollGate() {\n try {\n logger.info(`Polling onboarding state on ${gateURL}/onboarding/state ...`);\n await gateNetwork(\"GET\", \"/onboarding/state\");\n resolve!();\n } catch {\n await wait(3e3);\n pollGate();\n }\n }\n\n if (useHTTPPolling) {\n logger.info(\"Using HTTP polling strategy (will not output deployment steps)\");\n // important to wait before starting to poll the Gate, else it can have nasty side-effects\n // with local domain resolution (seems that the \"DNS not found\" is kind of cached)\n logger.info(\"Waiting 30 seconds for domain to be deployed...\");\n await wait(30e3);\n logger.info(\"Waiting for Gate to be ready...\");\n pollGate();\n } else {\n logState();\n const socket = io(remoteURL, { transports: [\"polling\"] });\n socket.on(\"deployment-state\", onState);\n socket.emit(\"join-deployment\", deployment.id);\n socket.on(\"disconnect\", () => {\n reject!(new Error(\"Socket disconnected!\"));\n });\n }\n\n await promise;\n\n return { url };\n}\n","export const DEFAULT_VAULT_REMOTE_URL = \"https://remote.minivault.ledger-sbx.com\";\nexport const DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES = 10; // 10 min timeout for deployments\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { DestroyOptions, RunnableOptions } from \"./types\";\n\nexport default async function destroy(\n opts: DestroyOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const { remoteURL = DEFAULT_VAULT_REMOTE_URL, name } = opts;\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n await network(\"DELETE\", `/instances/${name}`);\n logger.success(`Successfully destroyed instance ${name}`);\n\n if (opts.wait) {\n logger.info(\"Waiting for namespace to disappear...\");\n while (await isInstanceAlive(name)) {\n await new Promise((r) => setTimeout(r, 2e3));\n }\n logger.info(\"Done\");\n }\n\n async function isInstanceAlive(name: string) {\n try {\n await network(\"GET\", `/instances/${name}`);\n return true;\n } catch {\n return false;\n }\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport { DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { MVInstance, RunnableOptions } from \"./types\";\n\nexport default async function getMVInstances(\n opts: { remoteURL?: string },\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<MVInstance[]> {\n const { remoteURL = DEFAULT_VAULT_REMOTE_URL } = opts;\n\n logger.info(\"Fetching instances...\");\n const network = createNetwork({ baseURL: `${remoteURL}/api` });\n const instances = await network(\"GET\", \"/instances\");\n\n return instances;\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport BigNumber from \"bignumber.js\";\n\nimport fetchTokens from \"./fetchTokens\";\nimport {\n DevicesPool,\n GateAccount,\n GateFees,\n GateFeesEIP1559,\n GateTokenCurrency,\n GateTransaction,\n GateTransactionRequest,\n GateWhitelist,\n ManifestAccount,\n ManifestAccountRuleMultiAuth,\n ManifestAccountRuleMultiAuthStep,\n ManifestAccountRuleThreshold,\n ManifestAccountRuleWhitelist,\n ManifestGroup,\n ManifestTransaction,\n ManifestWhitelistAddress,\n RunnableOptions,\n UTXOsPickingStrategy,\n UserContext,\n UserDevice,\n} from \"./types\";\nimport { getAccountUnit, serializeUnitValue } from \"./utils\";\n\ntype SendOptions = {\n pool: DevicesPool;\n transaction: ManifestTransaction;\n account: ManifestAccount;\n groups?: ManifestGroup[];\n accountsByName: Record<string, GateAccount>;\n whitelistsByName: Record<string, GateWhitelist>;\n noApproval?: boolean;\n utxosPickingStrategy?: UTXOsPickingStrategy;\n contractPayload?: string;\n};\n\nconst SUPPORTED_ACCOUNT_TYPES = [\n \"Bitcoin\",\n \"Ethereum\",\n \"Tezos\",\n \"Polkadot\",\n \"Stellar\",\n \"Erc20\",\n \"Solana\",\n];\n\nasync function send(\n {\n pool,\n transaction,\n account,\n accountsByName,\n whitelistsByName,\n noApproval,\n groups = [],\n }: SendOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<GateTransaction> {\n const gateAccount = accountsByName[account.name];\n /* istanbul ignore if */\n if (!gateAccount) throw new Error(`Can't find account with name ${account.name}`);\n\n if (\n transaction.feesLevel === \"CUSTOM\" &&\n gateAccount.account_type !== \"Ethereum\" &&\n gateAccount.account_type !== \"Erc20\"\n ) {\n throw new Error(\"Custom fees are only supported for Ethereum and ERC20 accounts\");\n }\n\n if (transaction.feesLevel === \"CUSTOM\" && (!transaction.gasPrice || !transaction.gasLimit)) {\n throw new Error(\"Gas price and gas limit are required when using custom fees\");\n }\n\n if (SUPPORTED_ACCOUNT_TYPES.indexOf(gateAccount.account_type) === -1) {\n throw new Error(`Account type ${gateAccount.account_type} is not supported YET`);\n }\n\n let tokens: GateTokenCurrency[] = [];\n\n // if account is a token one, we need to fetch tokens list from the Gate to properly grab unit\n const shouldLoadTokens = !!gateAccount.contract_address;\n if (shouldLoadTokens) {\n const admin = await pool.login(4);\n tokens = await fetchTokens(admin, { logger });\n }\n\n const unit = getAccountUnit(account, tokens);\n const serializedAmount = serializeUnitValue(unit, transaction.amount);\n\n const matchingRule = getMatchingMultiAuthRule({\n account,\n transaction,\n whitelistsByName,\n });\n\n const { steps } = matchingRule;\n\n // users that have approved\n const usedUsers: UserDevice[] = [];\n\n // log with first available user in creator step\n const firstStep = steps[0];\n /* istanbul ignore if */\n if (!firstStep) throw new Error(`No creator step found`);\n const creator = await loginWithStepUser({ pool, groups, step: firstStep, usedUsers });\n\n const feesPayload = {\n amount: serializedAmount,\n recipient: transaction.recipient,\n fees_level: transaction.feesLevel || \"NORMAL\",\n ...(transaction.transactionType ? { type: transaction.transactionType } : {}),\n };\n\n if (gateAccount.account_type === \"Bitcoin\" && transaction.utxosPickingStrategy) {\n Object.assign(feesPayload, {\n utxo_picking_strategy: transaction.utxosPickingStrategy,\n });\n }\n\n type GateFeesResponse = GateFees | GateFeesEIP1559;\n\n let fees: GateFees = {\n fees_per_byte: \"0\",\n fees: transaction.feesLevel || \"NORMAL\",\n gas_limit: transaction.gasLimit!,\n gas_price: transaction.gasPrice!,\n };\n\n let feesEIP1559: GateFeesEIP1559 = {\n base_fees: transaction.feesLevel || \"NORMAL\",\n gas_limit: transaction.gasLimit!,\n max_fees: transaction.maxFees!,\n max_fees_buffer_factor: transaction.maxFeesBufferFactor!,\n priority_fees: transaction.priorityFees!,\n };\n\n if (transaction.feesLevel !== \"CUSTOM\") {\n const feesResponse = await creator.network<GateFeesResponse>(\n \"POST\",\n `/accounts/${gateAccount.id}/transactions/fees`,\n feesPayload,\n );\n\n \"priority_fees\" in feesResponse ? (feesEIP1559 = feesResponse) : (fees = feesResponse);\n }\n\n let txFees = {};\n if (\n (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") &&\n !feesEIP1559.priority_fees\n ) {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.gas_price).times(fees.gas_limit).toFixed(),\n gas_price: new BigNumber(fees.gas_price).toFixed(),\n gas_limit: new BigNumber(fees.gas_limit).toFixed(),\n };\n }\n\n if (\n (gateAccount.account_type === \"Ethereum\" || gateAccount.account_type === \"Erc20\") &&\n feesEIP1559.priority_fees\n ) {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(feesEIP1559.max_fees).toFixed(),\n gas_limit: new BigNumber(feesEIP1559.gas_limit).toFixed(),\n priority_fees: new BigNumber(feesEIP1559.priority_fees).toFixed(),\n max_fees_buffer_factor: new BigNumber(feesEIP1559.max_fees_buffer_factor).toFixed(),\n };\n }\n\n if (gateAccount.account_type === \"Bitcoin\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n fees_per_byte: new BigNumber(fees.fees_per_byte).toFixed(),\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n\n if (transaction.utxosPickingStrategy) {\n Object.assign(txFees, {\n utxo_picking_strategy: transaction.utxosPickingStrategy,\n });\n }\n }\n if (gateAccount.account_type === \"Tezos\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n gas_limit: new BigNumber(fees.gas_limit).toFixed(),\n max_fees: new BigNumber(fees.fees).toFixed(),\n storage_limit: fees.storage_limit,\n };\n }\n if (gateAccount.account_type === \"Polkadot\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n if (gateAccount.account_type === \"Stellar\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n\n if (gateAccount.account_type === \"Solana\") {\n txFees = {\n fees_level: feesPayload.fees_level,\n max_fees: new BigNumber(fees.fees).toFixed(),\n };\n }\n\n const payload = {\n type: \"CREATE_TRANSACTION\",\n account_id: gateAccount.id,\n transaction: {\n ...(transaction.transactionType ? { type: transaction.transactionType } : {}),\n recipient: transaction.recipient,\n amount: serializedAmount,\n ...txFees,\n },\n };\n\n if (transaction.contractPayload) {\n Object.assign(payload.transaction, {\n contract_payload: transaction.contractPayload,\n });\n }\n\n if (transaction.title || transaction.comment) {\n Object.assign(payload.transaction, {\n note: {\n title: transaction.title || \"\",\n content: transaction.comment || \"\",\n },\n });\n }\n\n logger.info(\"Creating transaction request\");\n const request = await creator.post<GateTransactionRequest>(\"/requests\", payload);\n\n if (noApproval) {\n logger.info(\"Transaction created (skipped approval)\");\n } else {\n logger.info(\"Approving request\");\n await creator.approveRequest(request);\n\n // successively go through each step\n for (let i = 1; i < steps.length; i++) {\n const step = steps[i];\n /* istanbul ignore if */\n if (!step) throw new Error(`No step at index ${i}`);\n // fill the quorum of approvals\n for (let j = 0; j < step.quorum; j++) {\n const approver = await loginWithStepUser({ pool, step, usedUsers, groups });\n logger.info(\"Approving request\");\n await approver.approveRequest(request);\n }\n }\n\n logger.success(\"Transaction created & approved\");\n }\n\n const tx = await creator.network<GateTransaction>(\"GET\", `/transactions/${request.target_id}`);\n\n return tx;\n}\n\n// TODO add polkadot\nconst TX_TYPE_TO_PRESET = {\n DELEGATE: \"TEZOS_DELEGATION\",\n UNDELEGATE: \"TEZOS_DELEGATION\",\n};\nexport function getMatchingMultiAuthRule({\n account,\n transaction,\n whitelistsByName,\n}: {\n account: ManifestAccount;\n transaction: ManifestTransaction;\n whitelistsByName: Record<string, GateWhitelist>;\n}): ManifestAccountRuleMultiAuth {\n let multiAuthRule;\n /* istanbul ignore if */\n if (!account.rules) {\n throw new Error(`No rules defined in account '${account.name}'`);\n }\n\n // VG-8212 we first filter rulesSet according to transactionType\n const rulesSet = account.rules.filter((rules) => {\n // if there is no txType we remove all rules that contain a txType preset\n // like TEZOS_DELEGATION\n if (\n !transaction.transactionType ||\n !Object.keys(TX_TYPE_TO_PRESET).includes(transaction.transactionType)\n ) {\n return !rules.some((r) => Object.values(TX_TYPE_TO_PRESET).includes(r.type));\n }\n\n // otherwise we only take rules with the preset\n // corresponding to txType\n return rules.some(\n // @ts-ignore\n (r) => r.type === TX_TYPE_TO_PRESET[transaction.transactionType],\n );\n });\n rulesSet.find((rules) => {\n // @ts-ignore\n const multiAuth: ManifestAccountRuleMultiAuth = rules.find(\n (r) => r.type === \"MULTI_AUTHORIZATIONS\",\n );\n // @ts-ignore\n const threshold: ManifestAccountRuleThreshold = rules.find((r) => r.type === \"THRESHOLD\");\n // @ts-ignore\n const whitelist: ManifestAccountRuleWhitelist = rules.find((r) => r.type === \"WHITELIST\");\n\n if (threshold) {\n const amount = new BigNumber(transaction.amount);\n if (threshold.min && amount.isLessThan(threshold.min)) {\n return false;\n }\n /* istanbul ignore else */\n if (threshold.max && amount.isGreaterThan(threshold.max)) {\n return false;\n }\n }\n\n if (whitelist) {\n const allowedAddresses = whitelist.whitelists.reduce(\n (acc: ManifestWhitelistAddress[], curr: string) => {\n const w = whitelistsByName[curr];\n /* istanbul ignore next */\n if (!w) return [];\n return [...acc, ...w.addresses];\n },\n [],\n );\n\n if (!allowedAddresses.find((a) => a.address === transaction.recipient)) {\n throw new Error(`Can't find ${transaction.recipient} in account whitelist(s)`);\n }\n }\n\n multiAuthRule = multiAuth;\n return true;\n });\n\n if (!multiAuthRule) {\n throw new Error(`Can't find matching rule for transaction`);\n }\n\n return multiAuthRule;\n}\n\nfunction loginWithStepUser({\n pool,\n step,\n usedUsers,\n groups,\n}: {\n pool: DevicesPool;\n step: ManifestAccountRuleMultiAuthStep;\n usedUsers: UserDevice[];\n groups: ManifestGroup[];\n}): Promise<UserContext> {\n const user =\n \"group\" in step\n ? groups!.find((g) => g.name === step.group)!.users.find((u) => usedUsers.indexOf(u) === -1)\n : step.users.find((u) => usedUsers.indexOf(u) === -1);\n if (!user) {\n throw new Error(`No available user to approve`);\n }\n if (typeof user === \"string\") {\n throw new Error(`Sending with API user is not supported YET`);\n }\n usedUsers.push(user);\n return pool.login(user);\n}\n\nexport default send;\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\nimport duration from \"humanize-duration\";\nimport io from \"socket.io-client\";\n\nimport { DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES, DEFAULT_VAULT_REMOTE_URL } from \"./constants\";\nimport createNetwork from \"./createNetwork\";\nimport { RunnableOptions, UpgradeOptions } from \"./types\";\n\ntype LoggerType = \"step\" | \"info\" | \"success\" | \"error\";\n\nexport type SocketLog = {\n msg: string;\n type: LoggerType;\n};\n\nexport default async function upgrade(\n opts: UpgradeOptions,\n { logger = SILENT_LOGGER }: RunnableOptions = {},\n): Promise<void> {\n const {\n remoteURL = DEFAULT_VAULT_REMOTE_URL,\n watchTimeoutMinutes = DEFAULT_SPAWN_WATCH_TIMEOUT_MINUTES,\n ...payload\n } = opts;\n\n const now = Date.now();\n\n logger.step(`Upgrading ${payload.name}`);\n\n const socket = io(remoteURL);\n const network = createNetwork({ baseURL: remoteURL });\n const { jobID } = await network<{ jobID: string }>(\"PUT\", \"/api/upgrade\", payload);\n\n let resolve: ((value: void | PromiseLike<void>) => void) | null = null;\n let reject: ((err: Error) => void) | null = null;\n\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n\n const upgradeTimeout = setTimeout(\n () => reject!(new Error(`Waited for ${watchTimeoutMinutes}min, too long, exiting`)),\n watchTimeoutMinutes * 60 * 1000,\n );\n\n socket.on(\"job-log\", (msg: SocketLog) => logger[msg.type](`(remote log)> ${msg.msg}`));\n\n socket.on(\"job-success\", () => {\n const elapsed = Date.now() - now;\n const displayDuration = duration(elapsed, { round: true });\n logger.success(`Successfully upgraded instance in ${displayDuration}`);\n clearTimeout(upgradeTimeout);\n resolve!();\n });\n\n socket.on(\"job-error\", (errMsg: string) => reject!(new Error(errMsg)));\n\n socket.emit(\"join-job\", jobID);\n\n await promise;\n}\n","import sortBy from \"lodash/sortBy\";\n\nimport { getCryptoCurrencyById } from \"./currencies\";\nimport deserializeManifest from \"./deserializeManifest\";\nimport { Manifest } from \"./types\";\n\nexport default function validateManifest(_manifest: Manifest): void {\n const manifest = deserializeManifest(_manifest);\n if (manifest.users) {\n const allRegularUsers = sortBy(\n [...(manifest.users.operators || []), ...(manifest.users.admins || [])],\n (u) => u.device,\n );\n if (allRegularUsers.length) {\n allRegularUsers.forEach((u, i) => {\n if (i > 0) {\n const user = allRegularUsers[i];\n /* istanbul ignore if */\n if (!user) throw new Error(`Can't find user with index ${i}`);\n const prevUser = allRegularUsers[i - 1];\n /* istanbul ignore if */\n if (!prevUser) throw new Error(`Can't find user with index ${i - 1}`);\n if (user.device !== prevUser.device + 1) {\n throw new Error(\"Users indexes are not contiguous\");\n }\n } else {\n if (u.device !== 10) {\n throw new Error(`Users devices indexes must start at 10`);\n }\n }\n });\n }\n }\n\n if (manifest.groups) {\n manifest.groups.forEach((group) => {\n group.users.forEach((id) => {\n if (\n !manifest.users ||\n (typeof id === \"number\" &&\n (!manifest.users.operators ||\n !manifest.users.operators.find((i) => i.device === id))) ||\n (typeof id === \"string\" &&\n (!manifest.users.api || !manifest.users.api.find((i) => i.name === id)) &&\n (!manifest.users.apiV2 || !manifest.users.apiV2.find((i) => i.name === id)))\n ) {\n throw new Error(`Group ${group.name} is referring to unknown operator ${id}`);\n }\n });\n });\n }\n if (manifest.accounts) {\n manifest.accounts.forEach((account) => {\n if (!(\"currency\" in account) && !(\"contractAddress\" in account)) {\n throw new Error(\n // @ts-ignore\n `No currency or contractAddress in account \"${account.name}\"`,\n );\n }\n /* istanbul ignore else */\n if (\"currency\" in account) {\n try {\n getCryptoCurrencyById(account.currency);\n } catch (e) {\n throw Error(`Invalid account currency: \"${account.currency}\"`);\n }\n }\n if (\"policy\" in account) {\n if (account.rules && account.rules.length) {\n throw new Error(\"Account cannot have both rules and policy\");\n }\n if (!manifest.policies || !manifest.policies.find((p) => p.name === account.policy)) {\n throw new Error(`Account \"${account.name}\": policy \"${account.policy}\" does not exist`);\n }\n }\n if (account.rules && account.rules.length) {\n account.rules.forEach((rule) => {\n rule.forEach((r, j) => {\n if (r.type === \"MULTI_AUTHORIZATIONS\") {\n r.steps.forEach((step, i) => {\n if (\"group\" in step && \"users\" in step) {\n throw new Error(\n `Can't have both 'users' and 'group' in step ${\n i + 1\n } of MULTI_AUTHORIZATIONS rule of rules set ${j + 1} of account \"${\n account.name\n }\"`,\n );\n } else if (\"group\" in step) {\n /* istanbul ignore else */\n if (\n !manifest.groups ||\n !manifest.groups.find((group) => group.name === step.group)\n ) {\n throw new Error(\n `Account \"${account.name}\": Group \"${step.group}\" does not exist`,\n );\n }\n } else if (\"users\" in step) {\n step.users.forEach((userId) => {\n if (\n !manifest.users ||\n (typeof userId === \"number\" &&\n (!manifest.users.operators ||\n !manifest.users.operators.find((o) => o.device === userId))) ||\n (typeof userId === \"string\" &&\n (!manifest.users.api ||\n !manifest.users.api.find((o) => o.name === userId)) &&\n (!manifest.users.apiV2 ||\n !manifest.users.apiV2.find((o) => o.name === userId)))\n ) {\n throw new Error(\n `Account \"${account.name}\": User with id ${userId} does not exist`,\n );\n }\n });\n } else {\n throw new Error(\n `No group or users in step ${i + 1} of MULTI_AUTHORIZATIONS rule of rules set ${\n j + 1\n } of account \"${account.name}\"`,\n );\n }\n });\n }\n if (r.type === \"WHITELIST\") {\n r.whitelists.forEach((whitelist) => {\n /* istanbul ignore else */\n if (\n !manifest.whitelists ||\n !manifest.whitelists.find((w) => w.name === whitelist)\n ) {\n throw new Error(\n `Account \"${account.name}\": whitelist \"${whitelist}\" does not exist`,\n );\n }\n });\n }\n });\n });\n } else {\n /* istanbul ignore else */\n if (!manifest.users || !manifest.users.operators || !manifest.users.operators.length) {\n throw new Error(\"Need at least 1 operator\");\n }\n }\n\n if (account.tradelink_data) {\n const accountTradelinkData = account.tradelink_data;\n\n if (!manifest.tradelink) {\n throw new Error(\"Account has tradelink_data but manifest does not\");\n }\n\n const { exchanges, assetManagers, custodians } = manifest.tradelink;\n\n accountTradelinkData.exchanges.forEach((exchange) => {\n if (!exchanges.find((e) => e.name === exchange.name)) {\n throw new Error(\n `Account \"${account.name}\": exchange \"${exchange.name}\" does not exist`,\n );\n }\n });\n\n if (!assetManagers.find((e) => e.name === accountTradelinkData.asset_manager.name)) {\n throw new Error(\n `Account \"${account.name}\": asset manager \"${account.tradelink_data.asset_manager.name}\" does not exist`,\n );\n }\n\n if (!custodians.find((e) => e.name === accountTradelinkData.custodian.name)) {\n throw new Error(\n `Account \"${account.name}\": custodian \"${account.tradelink_data.custodian.name}\" does not exist`,\n );\n }\n }\n });\n }\n if (manifest.entities) {\n manifest.entities.forEach((entity) => {\n entity.accounts?.forEach((accountName) => {\n if (!manifest.accounts || !manifest.accounts.find(({ name }) => accountName === name)) {\n throw new Error(\n `Entity ${entity.name} is referring to an unknown account ${accountName}`,\n );\n }\n });\n });\n }\n\n /* istanbul ignore else */\n if (manifest.whitelists) {\n manifest.whitelists.forEach((whitelist) => {\n whitelist.addresses.map((address) => {\n try {\n getCryptoCurrencyById(address.currency);\n } catch (e) {\n throw Error(`Invalid whitelist currency: \"${address.currency}\"`);\n }\n });\n });\n }\n\n if (manifest.tradelink) {\n manifest.tradelink.exchanges.forEach((exchange) => {\n /* istanbul ignore next */\n const operators = exchange.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = exchange.users?.apiV2 || [];\n\n if (!operators.length && !apiV2.length) {\n throw new Error(`Exchange ${exchange.name} has no users`);\n }\n\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(`Exchange ${exchange.name} refers to unknown operator ${operator}`);\n }\n });\n\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Exchange ${exchange.name} refers to unknown API User ${api}`);\n }\n });\n });\n\n manifest.tradelink.assetManagers.forEach((assetManager) => {\n /* istanbul ignore next */\n const operators = assetManager.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = assetManager.users?.apiV2 || [];\n if (!operators.length && !apiV2.length) {\n throw new Error(`Asset Manager ${assetManager.name} has no users`);\n }\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(\n `Asset Manager ${assetManager.name} refers to unknown operator ${operator}`,\n );\n }\n });\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Asset Manager ${assetManager.name} refers to unknown API User ${api}`);\n }\n });\n });\n\n manifest.tradelink.custodians.forEach((custodian) => {\n /* istanbul ignore next */\n const operators = custodian.users?.operators || [];\n /* istanbul ignore next */\n const apiV2 = custodian.users?.apiV2 || [];\n if (!operators.length && !apiV2.length) {\n throw new Error(`Custodian ${custodian.name} has no users`);\n }\n operators.forEach((operator) => {\n /* istanbul ignore next */\n if (!manifest.users?.operators?.find((o) => o.device === operator)) {\n throw new Error(`Custodian ${custodian.name} refers to unknown operator ${operator}`);\n }\n });\n apiV2.forEach((api) => {\n /* istanbul ignore next */\n if (!manifest.users?.apiV2?.find((o) => o.name === api)) {\n throw new Error(`Custodian ${custodian.name} refers to unknown API User ${api}`);\n }\n });\n });\n }\n}\n","import { SILENT_LOGGER } from \"@ledgerhq/vault-utils\";\n\nimport createHSMBridge from \"./createHSMBridge\";\nimport createNetwork from \"./createNetwork\";\nimport { RunnableOptions, WipeOptions } from \"./types\";\n\nasync function wipeBackend(\n wipeOpts: WipeOptions,\n runnableOpts: RunnableOptions = {},\n): Promise<void> {\n const { logger = SILENT_LOGGER } = runnableOpts;\n\n if (!wipeOpts.hsmEndpoint) {\n throw new Error(\"HSM endpoint is empty, please provide one\");\n }\n\n logger.step(\"Wiping backend data\");\n const gateNetworkOptions = { baseURL: wipeOpts.gate };\n const gateNetwork = createNetwork(gateNetworkOptions);\n\n try {\n await gateNetwork(\"POST\", \"/maintenance/wipe\");\n } catch {\n logger.info(\n \"[WARN] POST for /maintenance/wipe did not worked, attempting legacy GET (see VG-8670)\",\n );\n await gateNetwork(\"GET\", \"/maintenance/wipe\");\n logger.info(\"[WARN] legacy GET /maintenance/wipe successful\");\n }\n\n if (wipeOpts.lam) {\n const lamNetworkOptions = { baseURL: wipeOpts.lam };\n const headersAPILam = wipeOpts.lamAPIKey\n ? { headers: { \"X-Ledger-API-Key\": wipeOpts.lamAPIKey } }\n : {};\n const lamNetwork = createNetwork(lamNetworkOptions);\n\n logger.step(\"Wiping LAM data\");\n try {\n const lamWipeResponse = await lamNetwork(\n \"DELETE\",\n \"/api_users/maintenance/wipe\",\n {},\n headersAPILam,\n );\n if (!lamWipeResponse) {\n logger.info(\"[WARN] LAM wipe did not work as expected!\");\n }\n } catch (err) {\n // @ts-expect-error\n logger.info(`Error while wiping the LAM: ${err.toString()}`);\n }\n }\n\n logger.step(\"Resetting HSM compartment\");\n const hsmBridge = createHSMBridge(wipeOpts);\n await hsmBridge.resetCompartment(wipeOpts.hsmCompartmentID, runnableOpts);\n\n logger.success(\"Wiped backend data\");\n}\n\nexport default wipeBackend;\n"]}
|