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