@dynamic-labs-wallet/browser 1.0.92 → 1.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs +273 -60
- package/index.esm.js +274 -63
- package/package.json +4 -3
- package/src/client.d.ts +42 -8
- package/src/client.d.ts.map +1 -1
- package/src/ed25519.d.ts +17 -0
- package/src/ed25519.d.ts.map +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.d.ts.map +1 -1
- package/src/mpc/mpc.d.ts +13 -6
- package/src/mpc/mpc.d.ts.map +1 -1
- package/src/mpc/types.d.ts +5 -5
- package/src/mpc/types.d.ts.map +1 -1
- package/src/services/logger.d.ts +11 -2
- package/src/services/logger.d.ts.map +1 -1
- package/src/types.d.ts +9 -1
- package/src/types.d.ts.map +1 -1
- package/src/utils.d.ts +19 -0
- package/src/utils.d.ts.map +1 -1
package/index.cjs
CHANGED
|
@@ -9,6 +9,8 @@ var loadArgon2idWasm = require('argon2id');
|
|
|
9
9
|
var axios = require('axios');
|
|
10
10
|
var gte = require('semver/functions/gte');
|
|
11
11
|
var PQueue = require('p-queue');
|
|
12
|
+
var ed25519 = require('@noble/curves/ed25519');
|
|
13
|
+
var utils = require('@noble/curves/abstract/utils');
|
|
12
14
|
|
|
13
15
|
function _extends() {
|
|
14
16
|
_extends = Object.assign || function assign(target) {
|
|
@@ -78,12 +80,17 @@ function _extends() {
|
|
|
78
80
|
return undefined;
|
|
79
81
|
}
|
|
80
82
|
};
|
|
81
|
-
const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = core.MPC_RELAY_PROD_API_URL })=>{
|
|
83
|
+
const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = core.MPC_RELAY_PROD_API_URL, variant = core.DEFAULT_ED25519_VARIANT })=>{
|
|
82
84
|
switch(signingAlgorithm){
|
|
83
85
|
case primitives.SigningAlgorithm.ECDSA:
|
|
84
86
|
return new web.Ecdsa(baseRelayUrl);
|
|
85
87
|
case primitives.SigningAlgorithm.ED25519:
|
|
86
|
-
|
|
88
|
+
// `Ed25519` (non-exportable) and `ExportableEd25519` are distinct protocol
|
|
89
|
+
// classes with incompatible keyshare formats. Downstream `instanceof`
|
|
90
|
+
// dispatch (createWalletAccount / importPrivateKey / derive*) always tests
|
|
91
|
+
// `ExportableEd25519` before `Ed25519` — most-specific-first — so it stays
|
|
92
|
+
// correct even if `ExportableEd25519` were a subclass of `Ed25519`.
|
|
93
|
+
return variant === 'ed25519Standard' ? new web.Ed25519(baseRelayUrl) : new web.ExportableEd25519(baseRelayUrl);
|
|
87
94
|
case primitives.SigningAlgorithm.BIP340:
|
|
88
95
|
return new web.BIP340(baseRelayUrl);
|
|
89
96
|
case primitives.SigningAlgorithm.EDBLS12_377:
|
|
@@ -92,11 +99,12 @@ const getMPCSignatureScheme = ({ signingAlgorithm, baseRelayUrl = core.MPC_RELAY
|
|
|
92
99
|
throw new Error(`Unsupported signing algorithm: ${signingAlgorithm}`);
|
|
93
100
|
}
|
|
94
101
|
};
|
|
95
|
-
const getMPCSigner = ({ chainName, baseRelayUrl, bitcoinConfig })=>{
|
|
102
|
+
const getMPCSigner = ({ chainName, baseRelayUrl, bitcoinConfig, variant })=>{
|
|
96
103
|
const chainConfig = core.getMPCChainConfig(chainName, bitcoinConfig);
|
|
97
104
|
const signatureScheme = getMPCSignatureScheme({
|
|
98
105
|
signingAlgorithm: chainConfig.signingAlgorithm,
|
|
99
|
-
baseRelayUrl
|
|
106
|
+
baseRelayUrl,
|
|
107
|
+
variant
|
|
100
108
|
});
|
|
101
109
|
return signatureScheme;
|
|
102
110
|
};
|
|
@@ -1486,6 +1494,37 @@ const NON_RETRYABLE_CEREMONY_ERROR_MESSAGES = [
|
|
|
1486
1494
|
ERROR_INCORRECT_PASSWORD,
|
|
1487
1495
|
ERROR_MULTIPLE_WALLETS_PER_CHAIN
|
|
1488
1496
|
];
|
|
1497
|
+
/**
|
|
1498
|
+
* True when the backend rejected a create because the user already has a wallet
|
|
1499
|
+
* on that chain — an expected business-rule outcome, not a system failure.
|
|
1500
|
+
*
|
|
1501
|
+
* The string never appears on `error.message`: the backend returns it in the 400
|
|
1502
|
+
* response body as `{ existingWalletId, error: '<this string>' }`, so an
|
|
1503
|
+
* `AxiosError` carries axios's own "Request failed with status code 400" message
|
|
1504
|
+
* and `handleAxiosError` maps the status to "Invalid request". `WalletApiError`
|
|
1505
|
+
* does preserve the body value, but on `code` (via `extractServerErrorCode`),
|
|
1506
|
+
* not `message`. A `.message` comparison therefore never matches, which is why
|
|
1507
|
+
* the intended downgrade at the keyGen catch site had never fired in production.
|
|
1508
|
+
*
|
|
1509
|
+
* `WalletApiError` is matched on `name` as well as `instanceof`, for the reason
|
|
1510
|
+
* documented on `getWalletApiErrorStatus` in services/logger.ts: core is inlined
|
|
1511
|
+
* into more than one bundle, so a second copy of the class defeats `instanceof`.
|
|
1512
|
+
* A foreign copy has no `response.data` and the generic "Invalid request"
|
|
1513
|
+
* message, so an instanceof-only check misses every branch below.
|
|
1514
|
+
*/ const isMultipleWalletsPerChainRejection = (error)=>{
|
|
1515
|
+
var _error_response;
|
|
1516
|
+
const isWalletApiError = error instanceof core.WalletApiError || error instanceof Error && error.name === 'WalletApiError';
|
|
1517
|
+
if (isWalletApiError && error.code === ERROR_MULTIPLE_WALLETS_PER_CHAIN) return true;
|
|
1518
|
+
// Raw AxiosError: the rejection reaches the keyGen catch before
|
|
1519
|
+
// `handleAxiosError` has converted it, so read the response body directly.
|
|
1520
|
+
const body = error == null ? void 0 : (_error_response = error.response) == null ? void 0 : _error_response.data;
|
|
1521
|
+
if (body && typeof body === 'object' && body['error'] === ERROR_MULTIPLE_WALLETS_PER_CHAIN) {
|
|
1522
|
+
return true;
|
|
1523
|
+
}
|
|
1524
|
+
// SSE transport assigns the body onto a plain Error.
|
|
1525
|
+
if ((error == null ? void 0 : error.error) === ERROR_MULTIPLE_WALLETS_PER_CHAIN) return true;
|
|
1526
|
+
return error instanceof Error && error.message === ERROR_MULTIPLE_WALLETS_PER_CHAIN;
|
|
1527
|
+
};
|
|
1489
1528
|
/**
|
|
1490
1529
|
* Marks an error as non-retryable in place so `retryPromise` will skip
|
|
1491
1530
|
* remaining attempts. Used at ceremony retry boundaries.
|
|
@@ -2281,14 +2320,33 @@ const classifyOne = (error)=>{
|
|
|
2281
2320
|
return fallback;
|
|
2282
2321
|
};
|
|
2283
2322
|
const MAX_DESCRIBED_LENGTH = 200;
|
|
2323
|
+
/**
|
|
2324
|
+
* Keys safe to echo when a thrown object exposes no recognised message field.
|
|
2325
|
+
* Deliberately an allowlist: the alternative — serialising the whole object —
|
|
2326
|
+
* cannot be made safe, because a thrown MPC signer rejection or API body may
|
|
2327
|
+
* carry key-share material, a raw transaction body or an auth token.
|
|
2328
|
+
*/ const SAFE_DETAIL_KEYS = [
|
|
2329
|
+
'status',
|
|
2330
|
+
'statusCode',
|
|
2331
|
+
'code',
|
|
2332
|
+
'error_code',
|
|
2333
|
+
'errorCode',
|
|
2334
|
+
'name',
|
|
2335
|
+
'type'
|
|
2336
|
+
];
|
|
2337
|
+
const isScalar = (value)=>typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
2284
2338
|
/**
|
|
2285
2339
|
* Builds a human-readable message for a thrown value that is not an `Error` —
|
|
2286
2340
|
* most often an API response body, e.g.
|
|
2287
2341
|
* `{ existingWalletId, error: 'Multiple wallets per chain not allowed' }`.
|
|
2288
2342
|
*
|
|
2289
2343
|
* `String(value)` would collapse that to `[object Object]`, losing the only
|
|
2290
|
-
* diagnostic content it had, so
|
|
2291
|
-
*
|
|
2344
|
+
* diagnostic content it had, so a recognised message field is preferred.
|
|
2345
|
+
*
|
|
2346
|
+
* Never serialises an unrecognised object wholesale. Where no message field
|
|
2347
|
+
* exists, only `SAFE_DETAIL_KEYS` scalars are echoed, and failing that a fixed
|
|
2348
|
+
* placeholder — so a value carrying secrets can never reach the logging backend
|
|
2349
|
+
* just because it lacked a `message`.
|
|
2292
2350
|
*/ const describeNonError = (value)=>{
|
|
2293
2351
|
if (typeof value === 'string') return value.slice(0, MAX_DESCRIBED_LENGTH);
|
|
2294
2352
|
if (value !== null && typeof value === 'object') {
|
|
@@ -2299,11 +2357,8 @@ const MAX_DESCRIBED_LENGTH = 200;
|
|
|
2299
2357
|
record['detail']
|
|
2300
2358
|
].find((candidate)=>typeof candidate === 'string' && candidate.length > 0);
|
|
2301
2359
|
if (named) return named.slice(0, MAX_DESCRIBED_LENGTH);
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
} catch (e) {
|
|
2305
|
-
// Circular or non-serialisable — fall through to String().
|
|
2306
|
-
}
|
|
2360
|
+
const safeDetails = SAFE_DETAIL_KEYS.filter((key)=>isScalar(record[key])).map((key)=>`${key}=${String(record[key])}`);
|
|
2361
|
+
return safeDetails.length > 0 ? safeDetails.join(' ').slice(0, MAX_DESCRIBED_LENGTH) : 'Non-Error value thrown with no recognised message field';
|
|
2307
2362
|
}
|
|
2308
2363
|
return String(value).slice(0, MAX_DESCRIBED_LENGTH);
|
|
2309
2364
|
};
|
|
@@ -2319,27 +2374,38 @@ const MAX_DESCRIBED_LENGTH = 200;
|
|
|
2319
2374
|
* under `operationError` instead, and a real `Error` is synthesised so the log
|
|
2320
2375
|
* still gets a stack and an `error.kind`.
|
|
2321
2376
|
*
|
|
2377
|
+
* A non-`Error` is recorded as its bounded `describeNonError` description only,
|
|
2378
|
+
* never the raw value — the same rule `logError` applies when it logs a cause as
|
|
2379
|
+
* `{ name, message }` — while `operationError` remains its own attribute so "the
|
|
2380
|
+
* thrown value was not an Error" stays queryable.
|
|
2381
|
+
*
|
|
2322
2382
|
* Returned as a plain object so this is unit-testable without constructing a
|
|
2323
2383
|
* wallet client (which pulls the MPC WASM bundle).
|
|
2324
2384
|
*/ const buildOperationFailureLog = (error)=>{
|
|
2325
2385
|
// Classify the original value: an Error's `cause` chain carries the severity,
|
|
2326
2386
|
// and normalising first would discard it.
|
|
2327
2387
|
const level = resolveLogLevel(error, 'error');
|
|
2328
|
-
|
|
2388
|
+
if (error instanceof Error) return {
|
|
2329
2389
|
level,
|
|
2330
2390
|
context: undefined,
|
|
2331
2391
|
error
|
|
2332
|
-
}
|
|
2392
|
+
};
|
|
2393
|
+
const described = describeNonError(error);
|
|
2394
|
+
return {
|
|
2333
2395
|
level,
|
|
2334
2396
|
context: {
|
|
2335
|
-
operationError:
|
|
2397
|
+
operationError: described
|
|
2336
2398
|
},
|
|
2337
|
-
error: new Error(
|
|
2399
|
+
error: new Error(described)
|
|
2338
2400
|
};
|
|
2339
2401
|
};
|
|
2340
2402
|
const logError = ({ message, error, context, level = 'error' })=>{
|
|
2341
2403
|
if (error instanceof axios.AxiosError) {
|
|
2342
|
-
|
|
2404
|
+
// Forward the caller's severity hint. It used to be dropped here, so a call
|
|
2405
|
+
// site that knew its failure was expected still got logged at `error`.
|
|
2406
|
+
core.handleAxiosError(error, message, context, {
|
|
2407
|
+
level
|
|
2408
|
+
});
|
|
2343
2409
|
return;
|
|
2344
2410
|
}
|
|
2345
2411
|
const resolvedLevel = resolveLogLevel(error, level);
|
|
@@ -3377,28 +3443,34 @@ class DynamicWalletClient {
|
|
|
3377
3443
|
}, this.getTraceContext(traceContext))));
|
|
3378
3444
|
throw new Error('Timed out waiting for wallet creation ceremony to complete');
|
|
3379
3445
|
}
|
|
3380
|
-
async clientInitializeKeyGen({ chainName, thresholdSignatureScheme, bitcoinConfig }) {
|
|
3446
|
+
async clientInitializeKeyGen({ chainName, thresholdSignatureScheme, bitcoinConfig, variant }) {
|
|
3381
3447
|
// Get the mpc signer
|
|
3382
3448
|
const mpcSigner = getMPCSigner({
|
|
3383
3449
|
chainName,
|
|
3384
3450
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3385
|
-
bitcoinConfig
|
|
3451
|
+
bitcoinConfig,
|
|
3452
|
+
variant
|
|
3386
3453
|
});
|
|
3387
3454
|
const clientThreshold = core.getClientThreshold(thresholdSignatureScheme);
|
|
3388
3455
|
const keygenInitResults = await Promise.all(Array(clientThreshold).fill(null).map(()=>mpcSigner.initKeygen()));
|
|
3389
3456
|
return keygenInitResults;
|
|
3390
3457
|
}
|
|
3391
|
-
async derivePublicKey({ chainName, keyShare, derivationPath, bitcoinConfig }) {
|
|
3458
|
+
async derivePublicKey({ chainName, keyShare, derivationPath, bitcoinConfig, variant }) {
|
|
3392
3459
|
const mpcSigner = getMPCSigner({
|
|
3393
3460
|
chainName,
|
|
3394
3461
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3395
|
-
bitcoinConfig
|
|
3462
|
+
bitcoinConfig,
|
|
3463
|
+
variant
|
|
3396
3464
|
});
|
|
3397
3465
|
let publicKey;
|
|
3398
3466
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
3399
3467
|
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
3400
3468
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
3401
3469
|
publicKey = await mpcSigner.getPubkey(keyShare);
|
|
3470
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
3471
|
+
// Non-exportable Ed25519 (raw-scalar-imported wallets). Derivation is not
|
|
3472
|
+
// standardized for ed25519, so the root pubkey is taken with an undefined path.
|
|
3473
|
+
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
3402
3474
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
3403
3475
|
publicKey = await mpcSigner.deriveTweakPubkey(keyShare, derivationPath);
|
|
3404
3476
|
}
|
|
@@ -3727,7 +3799,7 @@ class DynamicWalletClient {
|
|
|
3727
3799
|
// rejection (the user already has a wallet for this chain), not a system
|
|
3728
3800
|
// failure — log it at warn so it doesn't flood error dashboards. All
|
|
3729
3801
|
// other keygen failures stay at error.
|
|
3730
|
-
const isExpectedRejection = error
|
|
3802
|
+
const isExpectedRejection = isMultipleWalletsPerChainRejection(error);
|
|
3731
3803
|
// On the HTTP transport, tag the failure with the cross-repo join keys and
|
|
3732
3804
|
// a stable errorKind so it lines up with the redcoast worker ceremony logs.
|
|
3733
3805
|
const usedHttpTransport = this.featureFlags[core.FEATURE_FLAGS.ENABLE_HTTP_WAAS_TRANSPORT] === true && !!idempotencyKey;
|
|
@@ -3762,7 +3834,7 @@ class DynamicWalletClient {
|
|
|
3762
3834
|
shouldRetry: (error)=>!isNonRetryableCeremonyError(error)
|
|
3763
3835
|
});
|
|
3764
3836
|
}
|
|
3765
|
-
async runImportRawPrivateKeyAttempt({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, onError, onCeremonyComplete, traceContext, legacyWalletId, password, signedSessionId }) {
|
|
3837
|
+
async runImportRawPrivateKeyAttempt({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, onError, onCeremonyComplete, traceContext, legacyWalletId, password, signedSessionId, isRawScalarImport }) {
|
|
3766
3838
|
const dynamicRequestId = uuid.v4();
|
|
3767
3839
|
try {
|
|
3768
3840
|
this.assertPasswordRequired(password);
|
|
@@ -3770,15 +3842,21 @@ class DynamicWalletClient {
|
|
|
3770
3842
|
password,
|
|
3771
3843
|
signedSessionId
|
|
3772
3844
|
});
|
|
3845
|
+
// A raw-scalar import must run on the non-exportable `Ed25519` protocol for
|
|
3846
|
+
// the whole ceremony (init keygen, import, pubkey derivation) so the produced
|
|
3847
|
+
// key shares are consistent. Non-raw imports keep the default ('ed25519Exportable').
|
|
3848
|
+
const variant = isRawScalarImport ? 'ed25519Standard' : undefined;
|
|
3773
3849
|
const mpcSigner = getMPCSigner({
|
|
3774
3850
|
chainName,
|
|
3775
3851
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3776
|
-
bitcoinConfig
|
|
3852
|
+
bitcoinConfig,
|
|
3853
|
+
variant
|
|
3777
3854
|
});
|
|
3778
3855
|
const clientKeygenInitResults = await this.clientInitializeKeyGen({
|
|
3779
3856
|
chainName,
|
|
3780
3857
|
thresholdSignatureScheme,
|
|
3781
|
-
bitcoinConfig
|
|
3858
|
+
bitcoinConfig,
|
|
3859
|
+
variant
|
|
3782
3860
|
});
|
|
3783
3861
|
const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
|
|
3784
3862
|
this.logger.info('[DynamicWaasWalletClient] Client key generation initialized', _extends({
|
|
@@ -3796,7 +3874,9 @@ class DynamicWalletClient {
|
|
|
3796
3874
|
bitcoinConfig,
|
|
3797
3875
|
onError,
|
|
3798
3876
|
onCeremonyComplete,
|
|
3799
|
-
legacyWalletId
|
|
3877
|
+
legacyWalletId,
|
|
3878
|
+
// Persist the protocol variant server-side so later sign/reshare read it back.
|
|
3879
|
+
variant
|
|
3800
3880
|
});
|
|
3801
3881
|
this.logger.info('[DynamicWaasWalletClient] Server key generation initialized', _extends({
|
|
3802
3882
|
roomId,
|
|
@@ -3814,7 +3894,9 @@ class DynamicWalletClient {
|
|
|
3814
3894
|
...serverKeygenIds,
|
|
3815
3895
|
...otherClientKeygenIds
|
|
3816
3896
|
];
|
|
3817
|
-
const importerKeygenResult =
|
|
3897
|
+
const importerKeygenResult = isRawScalarImport ? // uses the 32 bytes directly as the signing scalar instead of
|
|
3898
|
+
// SHA-512-expanding them as an RFC-8032 seed.
|
|
3899
|
+
await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds, true) : await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds);
|
|
3818
3900
|
return importerKeygenResult;
|
|
3819
3901
|
} else {
|
|
3820
3902
|
const recipientKeygenResult = await mpcSigner.importPrivateKeyRecipient(roomId, threshold, currentInit, [
|
|
@@ -3831,7 +3913,8 @@ class DynamicWalletClient {
|
|
|
3831
3913
|
chainName,
|
|
3832
3914
|
keyShare: clientKeygenResult,
|
|
3833
3915
|
derivationPath,
|
|
3834
|
-
bitcoinConfig
|
|
3916
|
+
bitcoinConfig,
|
|
3917
|
+
variant
|
|
3835
3918
|
});
|
|
3836
3919
|
this.logger.info('[DynamicWaasWalletClient] Completed import of raw private key', _extends({
|
|
3837
3920
|
rawPublicKey,
|
|
@@ -3866,6 +3949,8 @@ class DynamicWalletClient {
|
|
|
3866
3949
|
if (typeof message !== 'string') {
|
|
3867
3950
|
message = `0x${Buffer.from(message).toString('hex')}`;
|
|
3868
3951
|
}
|
|
3952
|
+
// Note: the server signing party (wallet-service) selects the ed25519
|
|
3953
|
+
// protocol variant from its own persisted EAC, so it is NOT threaded here.
|
|
3869
3954
|
const serializedContext = context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined;
|
|
3870
3955
|
const useHttpTransport = this.featureFlags[core.FEATURE_FLAGS.ENABLE_HTTP_WAAS_TRANSPORT] === true && this.featureFlags[core.FEATURE_FLAGS.ENABLE_HTTP_WAAS_TRANSPORT_SIGNMESSAGE] === true;
|
|
3871
3956
|
const params = {
|
|
@@ -3929,7 +4014,7 @@ class DynamicWalletClient {
|
|
|
3929
4014
|
}
|
|
3930
4015
|
return signatureBytes;
|
|
3931
4016
|
}
|
|
3932
|
-
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext, bitcoinConfig }) {
|
|
4017
|
+
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext, bitcoinConfig, variant }) {
|
|
3933
4018
|
try {
|
|
3934
4019
|
const chainConfig = core.getMPCChainConfig(chainName, bitcoinConfig);
|
|
3935
4020
|
const signingAlgo = chainConfig.signingAlgorithm;
|
|
@@ -3942,7 +4027,7 @@ class DynamicWalletClient {
|
|
|
3942
4027
|
});
|
|
3943
4028
|
const tweak = this.convertTweakForBIP340(bitcoinConfig == null ? void 0 : bitcoinConfig.tweak);
|
|
3944
4029
|
this.logger.info('Forward MPC enabled, signing message with forward MPC (new)', this.getTraceContext(traceContext));
|
|
3945
|
-
const { signature: signatureBytes } = await this.apiClient.forwardMPCClient.signMessage({
|
|
4030
|
+
const { signature: signatureBytes } = await this.apiClient.forwardMPCClient.signMessage(_extends({
|
|
3946
4031
|
keyshare: keyShare,
|
|
3947
4032
|
message: messageToSign,
|
|
3948
4033
|
relayDomain: this.baseMPCRelayApiUrl || '',
|
|
@@ -3954,7 +4039,9 @@ class DynamicWalletClient {
|
|
|
3954
4039
|
traceContext,
|
|
3955
4040
|
userId: this.userId,
|
|
3956
4041
|
environmentId: this.environmentId
|
|
3957
|
-
}
|
|
4042
|
+
}, variant === 'ed25519Standard' ? {
|
|
4043
|
+
ed25519Variant: 'standard'
|
|
4044
|
+
} : {}));
|
|
3958
4045
|
if (!(signatureBytes instanceof Uint8Array)) {
|
|
3959
4046
|
throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
|
|
3960
4047
|
}
|
|
@@ -3974,15 +4061,22 @@ class DynamicWalletClient {
|
|
|
3974
4061
|
throw error;
|
|
3975
4062
|
}
|
|
3976
4063
|
}
|
|
3977
|
-
async clientSign({ chainName, message, roomId: initialRoomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext, bitcoinConfig, refreshRoom }) {
|
|
4064
|
+
async clientSign({ chainName, message, roomId: initialRoomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext, bitcoinConfig, variant, refreshRoom }) {
|
|
3978
4065
|
// Reassigned when forward MPC fails and we mint a fresh room for the
|
|
3979
4066
|
// relay-based fallback below.
|
|
3980
4067
|
let roomId = initialRoomId;
|
|
4068
|
+
// Non-exportable Ed25519 (raw-scalar import) derives its address at the
|
|
4069
|
+
// root — see derivePrivateKeyFromExport's identical guard. Unlike
|
|
4070
|
+
// ExportableEd25519 (which ignores this arg), Ed25519.sign() *applies* a
|
|
4071
|
+
// non-undefined path, so forwarding the wallet's stored derivation path
|
|
4072
|
+
// here would sign under a derived child key instead of the wallet's own.
|
|
4073
|
+
const effectiveDerivationPath = variant === 'ed25519Standard' ? undefined : derivationPath;
|
|
3981
4074
|
try {
|
|
3982
4075
|
const mpcSigner = getMPCSigner({
|
|
3983
4076
|
chainName,
|
|
3984
4077
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3985
|
-
bitcoinConfig
|
|
4078
|
+
bitcoinConfig,
|
|
4079
|
+
variant
|
|
3986
4080
|
});
|
|
3987
4081
|
const formattedMessage = isFormatted ? new web.MessageHash(message) : formatMessage(chainName, message);
|
|
3988
4082
|
this.logger.debug('[DynamicWaasWalletClient] Starting client sign', _extends({
|
|
@@ -4006,12 +4100,13 @@ class DynamicWalletClient {
|
|
|
4006
4100
|
message,
|
|
4007
4101
|
roomId,
|
|
4008
4102
|
keyShare,
|
|
4009
|
-
derivationPath,
|
|
4103
|
+
derivationPath: effectiveDerivationPath,
|
|
4010
4104
|
formattedMessage,
|
|
4011
4105
|
dynamicRequestId,
|
|
4012
4106
|
isFormatted,
|
|
4013
4107
|
traceContext,
|
|
4014
|
-
bitcoinConfig
|
|
4108
|
+
bitcoinConfig,
|
|
4109
|
+
variant
|
|
4015
4110
|
});
|
|
4016
4111
|
} catch (error) {
|
|
4017
4112
|
const errorInfo = core.classifyForwardMpcError(error);
|
|
@@ -4055,7 +4150,7 @@ class DynamicWalletClient {
|
|
|
4055
4150
|
if (!('sign' in mpcSigner)) {
|
|
4056
4151
|
throw new TypeError(`Message signing is not supported for chain ${chainName} — use signTransaction instead`);
|
|
4057
4152
|
}
|
|
4058
|
-
const signature = await mpcSigner.sign(roomId, keyShare, messageToSign,
|
|
4153
|
+
const signature = await mpcSigner.sign(roomId, keyShare, messageToSign, effectiveDerivationPath, tweak);
|
|
4059
4154
|
return signature;
|
|
4060
4155
|
} catch (error) {
|
|
4061
4156
|
logError({
|
|
@@ -4139,7 +4234,8 @@ class DynamicWalletClient {
|
|
|
4139
4234
|
chainName,
|
|
4140
4235
|
localShares,
|
|
4141
4236
|
recordedKeygenIds,
|
|
4142
|
-
bitcoinConfig
|
|
4237
|
+
bitcoinConfig,
|
|
4238
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
4143
4239
|
});
|
|
4144
4240
|
localKeygenIds = comparison.localKeygenIds;
|
|
4145
4241
|
staleness = comparison.fresh ? 'fresh' : 'stale';
|
|
@@ -4205,11 +4301,14 @@ class DynamicWalletClient {
|
|
|
4205
4301
|
* the server recorded for the wallet's Dynamic backups. Shared by the
|
|
4206
4302
|
* stale-share heal (verifyAndRecoverStaleShare) and the backup preflight
|
|
4207
4303
|
* (ensureLocalSharesAreFresh).
|
|
4208
|
-
*/ async compareShareGenerations({ accountAddress, chainName, localShares, recordedKeygenIds, bitcoinConfig }) {
|
|
4304
|
+
*/ async compareShareGenerations({ accountAddress, chainName, localShares, recordedKeygenIds, bitcoinConfig, variant }) {
|
|
4209
4305
|
const mpcSigner = getMPCSigner({
|
|
4210
4306
|
chainName,
|
|
4211
4307
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4212
|
-
bitcoinConfig: bitcoinConfig != null ? bitcoinConfig : this.getBitcoinConfigForChain(chainName, accountAddress)
|
|
4308
|
+
bitcoinConfig: bitcoinConfig != null ? bitcoinConfig : this.getBitcoinConfigForChain(chainName, accountAddress),
|
|
4309
|
+
// Raw-scalar (non-exportable Ed25519) wallets: build the matching signer so
|
|
4310
|
+
// getKeygenIdForShare deserializes the Ed25519 share (else "Invalid discriminant").
|
|
4311
|
+
variant
|
|
4213
4312
|
});
|
|
4214
4313
|
const localKeygenIds = await Promise.all(localShares.map((share)=>this.getKeygenIdForShare(mpcSigner, share)));
|
|
4215
4314
|
const recordedSet = new Set(recordedKeygenIds);
|
|
@@ -4367,6 +4466,7 @@ class DynamicWalletClient {
|
|
|
4367
4466
|
dynamicRequestId,
|
|
4368
4467
|
traceContext,
|
|
4369
4468
|
bitcoinConfig,
|
|
4469
|
+
variant: wallet.variant,
|
|
4370
4470
|
// On forward-MPC fallback, re-run the server sign with no roomId so a
|
|
4371
4471
|
// brand-new server-joined room is created for the relay ceremony.
|
|
4372
4472
|
refreshRoom: async ()=>{
|
|
@@ -4666,7 +4766,11 @@ class DynamicWalletClient {
|
|
|
4666
4766
|
const mpcSigner = getMPCSigner({
|
|
4667
4767
|
chainName,
|
|
4668
4768
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4669
|
-
bitcoinConfig
|
|
4769
|
+
bitcoinConfig,
|
|
4770
|
+
// Raw-scalar (non-exportable Ed25519) wallets must refresh on the same
|
|
4771
|
+
// protocol their shares were created with, or the ceremony fails to
|
|
4772
|
+
// deserialize them ("Invalid discriminant").
|
|
4773
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
4670
4774
|
});
|
|
4671
4775
|
// Ensure client key shares exist before hitting the API
|
|
4672
4776
|
const clientKeyShares = await this.ensureClientShare(accountAddress);
|
|
@@ -4848,11 +4952,26 @@ class DynamicWalletClient {
|
|
|
4848
4952
|
}
|
|
4849
4953
|
return mpcSigner.exportID(share);
|
|
4850
4954
|
}
|
|
4851
|
-
|
|
4955
|
+
/**
|
|
4956
|
+
* The ed25519 protocol variant for an existing wallet, read from the wallet map
|
|
4957
|
+
* (sourced from `walletProperties.settings.variant`, persisted at import).
|
|
4958
|
+
*
|
|
4959
|
+
* It is per-wallet protocol metadata — like `thresholdSignatureScheme` or
|
|
4960
|
+
* `addressType` — and the single source of truth for which signer to build when
|
|
4961
|
+
* operating on a wallet's key shares. Wallets imported from a raw ed25519 scalar
|
|
4962
|
+
* (e.g. Fireblocks Embedded Wallets) are `'ed25519Standard'` (non-exportable `Ed25519`);
|
|
4963
|
+
* everything else is `undefined` → the default exportable protocol. Using the
|
|
4964
|
+
* wrong one fails to deserialize the key share with "Invalid discriminant".
|
|
4965
|
+
*/ ed25519VariantForWallet(accountAddress) {
|
|
4966
|
+
var _this_getWalletFromMap;
|
|
4967
|
+
return (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.variant;
|
|
4968
|
+
}
|
|
4969
|
+
async getExportId({ chainName, clientKeyShare, bitcoinConfig, accountAddress }) {
|
|
4852
4970
|
const mpcSigner = getMPCSigner({
|
|
4853
4971
|
chainName,
|
|
4854
4972
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4855
|
-
bitcoinConfig
|
|
4973
|
+
bitcoinConfig,
|
|
4974
|
+
variant: accountAddress ? this.ed25519VariantForWallet(accountAddress) : undefined
|
|
4856
4975
|
});
|
|
4857
4976
|
try {
|
|
4858
4977
|
const exportId = await this.getKeygenIdForShare(mpcSigner, clientKeyShare);
|
|
@@ -4900,7 +5019,8 @@ class DynamicWalletClient {
|
|
|
4900
5019
|
return this.getExportId({
|
|
4901
5020
|
chainName,
|
|
4902
5021
|
clientKeyShare,
|
|
4903
|
-
bitcoinConfig
|
|
5022
|
+
bitcoinConfig,
|
|
5023
|
+
accountAddress
|
|
4904
5024
|
});
|
|
4905
5025
|
}
|
|
4906
5026
|
/**
|
|
@@ -4941,7 +5061,8 @@ class DynamicWalletClient {
|
|
|
4941
5061
|
const existingClientKeygenIds = await Promise.all(existingClientKeyShares.map(async (keyShare)=>await this.getExportId({
|
|
4942
5062
|
chainName,
|
|
4943
5063
|
clientKeyShare: keyShare,
|
|
4944
|
-
bitcoinConfig
|
|
5064
|
+
bitcoinConfig,
|
|
5065
|
+
accountAddress
|
|
4945
5066
|
})));
|
|
4946
5067
|
return {
|
|
4947
5068
|
newClientInitKeygenResults,
|
|
@@ -5010,7 +5131,8 @@ class DynamicWalletClient {
|
|
|
5010
5131
|
const existingClientKeygenId = await this.getExportId({
|
|
5011
5132
|
chainName,
|
|
5012
5133
|
clientKeyShare: existingClientShare,
|
|
5013
|
-
bitcoinConfig
|
|
5134
|
+
bitcoinConfig,
|
|
5135
|
+
accountAddress
|
|
5014
5136
|
});
|
|
5015
5137
|
// Gate matches internalRefresh/Reshare — SSE error rejects awaitTerminal.
|
|
5016
5138
|
let newShareSetId;
|
|
@@ -5619,7 +5741,11 @@ class DynamicWalletClient {
|
|
|
5619
5741
|
const mpcSigner = getMPCSigner({
|
|
5620
5742
|
chainName,
|
|
5621
5743
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
5622
|
-
bitcoinConfig
|
|
5744
|
+
bitcoinConfig,
|
|
5745
|
+
// Raw-scalar (non-exportable Ed25519) wallets must reshare on the same
|
|
5746
|
+
// protocol their shares were created with, or the ceremony fails to
|
|
5747
|
+
// deserialize them ("Invalid discriminant").
|
|
5748
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
5623
5749
|
});
|
|
5624
5750
|
let existingReshareResults;
|
|
5625
5751
|
let newReshareResults;
|
|
@@ -5983,6 +6109,9 @@ class DynamicWalletClient {
|
|
|
5983
6109
|
if (mpcSigner instanceof web.ExportableEd25519) {
|
|
5984
6110
|
return new web.ExportableEd25519KeygenResult(extractedPubkey, secretShare);
|
|
5985
6111
|
}
|
|
6112
|
+
if (mpcSigner instanceof web.Ed25519) {
|
|
6113
|
+
return new web.Ed25519KeygenResult(extractedPubkey, secretShare);
|
|
6114
|
+
}
|
|
5986
6115
|
return new web.BIP340KeygenResult(extractedPubkey, secretShare);
|
|
5987
6116
|
}
|
|
5988
6117
|
async exportKey({ accountAddress, chainName, bitcoinConfig, password = undefined, signedSessionId, mfaToken, elevatedAccessToken, traceContext }) {
|
|
@@ -6013,13 +6142,15 @@ class DynamicWalletClient {
|
|
|
6013
6142
|
const mpcSigner = getMPCSigner({
|
|
6014
6143
|
chainName,
|
|
6015
6144
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
6016
|
-
bitcoinConfig
|
|
6145
|
+
bitcoinConfig,
|
|
6146
|
+
variant: wallet.variant
|
|
6017
6147
|
});
|
|
6018
6148
|
const reconstructedKeyShare = await this.getReconstructedKeyShare(accountAddress, mpcSigner);
|
|
6019
6149
|
const exportId = await this.getExportId({
|
|
6020
6150
|
chainName,
|
|
6021
6151
|
clientKeyShare: reconstructedKeyShare,
|
|
6022
|
-
bitcoinConfig
|
|
6152
|
+
bitcoinConfig,
|
|
6153
|
+
accountAddress: wallet.accountAddress
|
|
6023
6154
|
});
|
|
6024
6155
|
const data = await this.apiClient.exportKey({
|
|
6025
6156
|
walletId: wallet.walletId,
|
|
@@ -6162,20 +6293,35 @@ class DynamicWalletClient {
|
|
|
6162
6293
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
6163
6294
|
return mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
6164
6295
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
6296
|
+
// ExportableEd25519's exportFullPrivateKey already returns the RFC-8032
|
|
6297
|
+
// form (hex of seed||pubkey); pass it straight through.
|
|
6165
6298
|
return keyExportRaw;
|
|
6299
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
6300
|
+
// Non-exportable Ed25519's exportFullPrivateKey returns an `spriv`
|
|
6301
|
+
// (extended private key), NOT a raw hex key — it must be parsed to the
|
|
6302
|
+
// actual private scalar. Returning the spriv verbatim makes the chain
|
|
6303
|
+
// client hex-decode it into an empty buffer (blank export field).
|
|
6304
|
+
//
|
|
6305
|
+
// This protocol is only used for raw-scalar import, whose public key is
|
|
6306
|
+
// the ROOT scalar (`A = s·B`, derived at import via derivePubkey(_, undefined)).
|
|
6307
|
+
// The chain-config derivation path stored on the wallet (e.g. Solana's
|
|
6308
|
+
// [44,501,0,0,0]) must NOT be applied here — deriving a child scalar would
|
|
6309
|
+
// export a key that doesn't correspond to the wallet's address.
|
|
6310
|
+
return mpcSigner.derivePrivateKeyFromSpriv(keyExportRaw);
|
|
6166
6311
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
6167
6312
|
return mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
6168
6313
|
}
|
|
6169
6314
|
return undefined;
|
|
6170
6315
|
}
|
|
6171
|
-
async offlineExportKey({ chainName, keyShares, derivationPath }) {
|
|
6316
|
+
async offlineExportKey({ chainName, keyShares, derivationPath, variant }) {
|
|
6172
6317
|
try {
|
|
6173
6318
|
if (!keyShares || keyShares.length < 2) {
|
|
6174
6319
|
throw new Error(`Must provide at least min threshold of key shares`);
|
|
6175
6320
|
}
|
|
6176
6321
|
const mpcSigner = getMPCSigner({
|
|
6177
6322
|
chainName,
|
|
6178
|
-
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
6323
|
+
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
6324
|
+
variant
|
|
6179
6325
|
});
|
|
6180
6326
|
const walletKeyShares = keyShares.map((keyShare)=>{
|
|
6181
6327
|
if (!('pubkey' in keyShare)) {
|
|
@@ -6194,6 +6340,10 @@ class DynamicWalletClient {
|
|
|
6194
6340
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
6195
6341
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
6196
6342
|
derivedPrivateKey = keyExportRaw;
|
|
6343
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
6344
|
+
// Non-exportable Ed25519 (raw-scalar) returns an `spriv`, not raw hex —
|
|
6345
|
+
// parse it to the root scalar (see derivePrivateKeyFromExport).
|
|
6346
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromSpriv(keyExportRaw);
|
|
6197
6347
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
6198
6348
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
6199
6349
|
}
|
|
@@ -6399,7 +6549,7 @@ class DynamicWalletClient {
|
|
|
6399
6549
|
* @param thresholdSignatureScheme - The TSS scheme used
|
|
6400
6550
|
* @param derivationPath - Optional derivation path (will be computed from chainConfig if not provided)
|
|
6401
6551
|
* @param additionalProps - Any chain-specific additional properties to merge
|
|
6402
|
-
*/ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, shareSetId, shareSetType, otherShareSets, additionalProps = {} }) {
|
|
6552
|
+
*/ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, shareSetId, shareSetType, otherShareSets, variant, additionalProps = {} }) {
|
|
6403
6553
|
this.updateWalletMap(accountAddress, _extends({
|
|
6404
6554
|
accountAddress,
|
|
6405
6555
|
walletId,
|
|
@@ -6413,6 +6563,8 @@ class DynamicWalletClient {
|
|
|
6413
6563
|
shareSetType
|
|
6414
6564
|
} : {}, otherShareSets ? {
|
|
6415
6565
|
otherShareSets
|
|
6566
|
+
} : {}, variant ? {
|
|
6567
|
+
variant
|
|
6416
6568
|
} : {}, {
|
|
6417
6569
|
clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
|
|
6418
6570
|
}, additionalProps));
|
|
@@ -6632,7 +6784,7 @@ class DynamicWalletClient {
|
|
|
6632
6784
|
tokenSource: googleDriveTokenSource
|
|
6633
6785
|
} : {});
|
|
6634
6786
|
}
|
|
6635
|
-
async publishDelegatedShare({ walletId, shareSetId, delegatedShare, signedSessionId, dynamicRequestId, chainName, bitcoinConfig, isPasswordEncrypted }) {
|
|
6787
|
+
async publishDelegatedShare({ walletId, shareSetId, delegatedShare, signedSessionId, dynamicRequestId, chainName, accountAddress, bitcoinConfig, isPasswordEncrypted }) {
|
|
6636
6788
|
var _publicKey_key, _publicKey_key1, _publicKey_key2;
|
|
6637
6789
|
const publicKey = await this.apiClient.getDelegatedEncryptionKey({
|
|
6638
6790
|
environmentId: this.environmentId
|
|
@@ -6657,7 +6809,8 @@ class DynamicWalletClient {
|
|
|
6657
6809
|
chainName,
|
|
6658
6810
|
clientKeyShare: delegatedShare,
|
|
6659
6811
|
bitcoinConfig,
|
|
6660
|
-
walletId
|
|
6812
|
+
walletId,
|
|
6813
|
+
accountAddress
|
|
6661
6814
|
});
|
|
6662
6815
|
return {
|
|
6663
6816
|
location: core.BackupLocation.DELEGATED,
|
|
@@ -6823,6 +6976,7 @@ class DynamicWalletClient {
|
|
|
6823
6976
|
signedSessionId: resolvedSignedSessionId,
|
|
6824
6977
|
dynamicRequestId,
|
|
6825
6978
|
chainName: walletData.chainName,
|
|
6979
|
+
accountAddress,
|
|
6826
6980
|
bitcoinConfig,
|
|
6827
6981
|
isPasswordEncrypted
|
|
6828
6982
|
}), {
|
|
@@ -6991,7 +7145,8 @@ class DynamicWalletClient {
|
|
|
6991
7145
|
accountAddress,
|
|
6992
7146
|
chainName,
|
|
6993
7147
|
localShares,
|
|
6994
|
-
recordedKeygenIds
|
|
7148
|
+
recordedKeygenIds,
|
|
7149
|
+
variant: walletData.variant
|
|
6995
7150
|
});
|
|
6996
7151
|
if (fresh) return;
|
|
6997
7152
|
this.logger.warn('[storeEncryptedBackupByWallet] Stale local shares detected; refreshing local storage', {
|
|
@@ -8416,9 +8571,17 @@ class DynamicWalletClient {
|
|
|
8416
8571
|
const user = await this.getUserWithEnvCheck(dynamicRequestId);
|
|
8417
8572
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
8418
8573
|
for (const vc of waasWallets != null ? waasWallets : []){
|
|
8574
|
+
var _props_settings, _this_getWalletFromMap;
|
|
8419
8575
|
const addr = vc.address;
|
|
8420
8576
|
const props = vc.walletProperties;
|
|
8421
|
-
|
|
8577
|
+
var _props_settings_variant, _ref;
|
|
8578
|
+
// Resolve the ed25519 protocol variant: server-surfaced (walletProperties
|
|
8579
|
+
// .settings or top-level) first, then any locally-seeded value (set at raw
|
|
8580
|
+
// import). Required on a fresh session (no local seed) so recover/sign
|
|
8581
|
+
// rebuild the non-exportable Ed25519 signer for raw-scalar wallets instead
|
|
8582
|
+
// of defaulting to exportable → "Invalid discriminant".
|
|
8583
|
+
const resolvedVariant = (_ref = (_props_settings_variant = props == null ? void 0 : (_props_settings = props.settings) == null ? void 0 : _props_settings.variant) != null ? _props_settings_variant : props == null ? void 0 : props.variant) != null ? _ref : (_this_getWalletFromMap = this.getWalletFromMap(addr)) == null ? void 0 : _this_getWalletFromMap.variant;
|
|
8584
|
+
this.updateWalletMap(addr, _extends({
|
|
8422
8585
|
walletId: vc.id,
|
|
8423
8586
|
chainName: core.verifiedCredentialNameToChainEnum[vc.chain],
|
|
8424
8587
|
accountAddress: addr,
|
|
@@ -8431,7 +8594,9 @@ class DynamicWalletClient {
|
|
|
8431
8594
|
shareSetId: props == null ? void 0 : props.shareSetId,
|
|
8432
8595
|
shareSetType: props == null ? void 0 : props.shareSetType,
|
|
8433
8596
|
otherShareSets: props == null ? void 0 : props.otherShareSets
|
|
8434
|
-
}
|
|
8597
|
+
}, resolvedVariant ? {
|
|
8598
|
+
variant: resolvedVariant
|
|
8599
|
+
} : {}));
|
|
8435
8600
|
}
|
|
8436
8601
|
if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
8437
8602
|
accountAddress,
|
|
@@ -8809,8 +8974,8 @@ class DynamicWalletClient {
|
|
|
8809
8974
|
this.userId = user.id;
|
|
8810
8975
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
8811
8976
|
const wallets = waasWallets.map((vc)=>{
|
|
8812
|
-
var _this_getWalletFromMap, _vc_walletProperties, _vc_walletProperties1, _vc_walletProperties2, _vc_walletProperties3, _vc_walletProperties4, _vc_walletProperties5, _vc_walletProperties6;
|
|
8813
|
-
var _this_getWalletFromMap_derivationPath;
|
|
8977
|
+
var _this_getWalletFromMap, _vc_walletProperties, _vc_walletProperties1, _vc_walletProperties2, _vc_walletProperties3, _vc_walletProperties4, _vc_walletProperties5, _vc_walletProperties6, _vc_walletProperties_settings, _vc_walletProperties7, _vc_walletProperties8, _this_getWalletFromMap1;
|
|
8978
|
+
var _this_getWalletFromMap_derivationPath, _vc_walletProperties_settings_variant, _ref;
|
|
8814
8979
|
return {
|
|
8815
8980
|
walletId: vc.id,
|
|
8816
8981
|
chainName: core.verifiedCredentialNameToChainEnum[vc.chain],
|
|
@@ -8828,14 +8993,18 @@ class DynamicWalletClient {
|
|
|
8828
8993
|
// On-sign-on settings (shouldRefreshOnNextSignOn / reshareOnNextSignOn /
|
|
8829
8994
|
// revokeOnNextSignOn). Surfaced so the SDK can self-drive the on-sign-on
|
|
8830
8995
|
// orchestration instead of the host reading these flags.
|
|
8831
|
-
settings: (_vc_walletProperties6 = vc.walletProperties) == null ? void 0 : _vc_walletProperties6.settings
|
|
8996
|
+
settings: (_vc_walletProperties6 = vc.walletProperties) == null ? void 0 : _vc_walletProperties6.settings,
|
|
8997
|
+
// Prefer the server-persisted variant (surfaced via walletProperties.settings),
|
|
8998
|
+
// then the locally-known value (set at raw import) so same-session
|
|
8999
|
+
// import → sign works before the server has surfaced it.
|
|
9000
|
+
variant: (_ref = (_vc_walletProperties_settings_variant = (_vc_walletProperties7 = vc.walletProperties) == null ? void 0 : (_vc_walletProperties_settings = _vc_walletProperties7.settings) == null ? void 0 : _vc_walletProperties_settings.variant) != null ? _vc_walletProperties_settings_variant : (_vc_walletProperties8 = vc.walletProperties) == null ? void 0 : _vc_walletProperties8.variant) != null ? _ref : (_this_getWalletFromMap1 = this.getWalletFromMap(vc.address)) == null ? void 0 : _this_getWalletFromMap1.variant
|
|
8832
9001
|
};
|
|
8833
9002
|
});
|
|
8834
9003
|
const existingWalletMap = this.walletMap;
|
|
8835
9004
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
8836
9005
|
const normalizedAddress = normalizeAddress(wallet.accountAddress);
|
|
8837
9006
|
const existingWallet = existingWalletMap[normalizedAddress];
|
|
8838
|
-
acc[normalizedAddress] = {
|
|
9007
|
+
acc[normalizedAddress] = _extends({
|
|
8839
9008
|
walletId: wallet.walletId,
|
|
8840
9009
|
chainName: wallet.chainName,
|
|
8841
9010
|
accountAddress: wallet.accountAddress,
|
|
@@ -8848,7 +9017,9 @@ class DynamicWalletClient {
|
|
|
8848
9017
|
shareSetType: wallet.shareSetType,
|
|
8849
9018
|
businessAccountId: wallet.businessAccountId,
|
|
8850
9019
|
otherShareSets: wallet.otherShareSets
|
|
8851
|
-
}
|
|
9020
|
+
}, wallet.variant ? {
|
|
9021
|
+
variant: wallet.variant
|
|
9022
|
+
} : {});
|
|
8852
9023
|
return acc;
|
|
8853
9024
|
}, {});
|
|
8854
9025
|
this.logger.info('[walletMap] getWallets: walletMap updated from API', {
|
|
@@ -9302,6 +9473,46 @@ DynamicWalletClient.roomsPersistDirty = false;
|
|
|
9302
9473
|
// rooms back into the new user's storage (TOCTOU on the create→merge gap).
|
|
9303
9474
|
DynamicWalletClient.roomsGeneration = 0;
|
|
9304
9475
|
|
|
9476
|
+
/**
|
|
9477
|
+
* Derive the 32-byte ed25519 public key from a **raw** 32-byte signing scalar
|
|
9478
|
+
* (`A = s·B`).
|
|
9479
|
+
*
|
|
9480
|
+
* This is NOT the same as deriving a public key from an RFC-8032 seed: the
|
|
9481
|
+
* standard ed25519 key-generation step takes a 32-byte seed and SHA-512-expands
|
|
9482
|
+
* it (then clamps) to obtain the scalar. A scalar exported from an external MPC
|
|
9483
|
+
* system (e.g. Fireblocks Embedded Wallets) has no preimage seed, so it must be
|
|
9484
|
+
* used directly as the scalar. Using `Keypair.fromSecretKey` / `keyPairFromSeed`
|
|
9485
|
+
* on such bytes would re-expand them and produce the wrong public key/address.
|
|
9486
|
+
*
|
|
9487
|
+
* @param scalar Raw 32-byte ed25519 scalar, little-endian, as hex (with or
|
|
9488
|
+
* without a `0x` prefix) or as raw bytes.
|
|
9489
|
+
* @returns The compressed 32-byte ed25519 public key.
|
|
9490
|
+
*/ const getEd25519PublicKeyFromRawScalar = (scalar)=>{
|
|
9491
|
+
let bytes;
|
|
9492
|
+
if (typeof scalar === 'string') {
|
|
9493
|
+
const hex = scalar.replace(/^0x/, '');
|
|
9494
|
+
// Validate before hexToBytes so malformed input fails with a clear, own
|
|
9495
|
+
// error rather than an opaque/implementation-specific one from the decoder.
|
|
9496
|
+
if (!/^[0-9a-fA-F]+$/.test(hex)) {
|
|
9497
|
+
throw new Error('Invalid raw ed25519 scalar: expected a hex string');
|
|
9498
|
+
}
|
|
9499
|
+
bytes = utils.hexToBytes(hex);
|
|
9500
|
+
} else {
|
|
9501
|
+
bytes = scalar;
|
|
9502
|
+
}
|
|
9503
|
+
if (bytes.length !== 32) {
|
|
9504
|
+
throw new Error(`Invalid raw ed25519 scalar length: ${bytes.length}, expected 32`);
|
|
9505
|
+
}
|
|
9506
|
+
// Interpret the scalar little-endian and reduce mod the curve order. We do NOT
|
|
9507
|
+
// clamp here: the caller provides the actual signing scalar `s`, and the public
|
|
9508
|
+
// key is exactly `s·B`.
|
|
9509
|
+
const reduced = utils.bytesToNumberLE(bytes) % ed25519.ed25519.CURVE.n;
|
|
9510
|
+
if (reduced === 0n) {
|
|
9511
|
+
throw new Error('Invalid raw ed25519 scalar: reduces to zero mod the curve order');
|
|
9512
|
+
}
|
|
9513
|
+
return ed25519.ed25519.ExtendedPoint.BASE.multiply(reduced).toRawBytes();
|
|
9514
|
+
};
|
|
9515
|
+
|
|
9305
9516
|
Object.defineProperty(exports, "Logger", {
|
|
9306
9517
|
enumerable: true,
|
|
9307
9518
|
get: function () { return core.Logger; }
|
|
@@ -9416,6 +9627,7 @@ exports.getBitcoinAddressTypeFromDerivationPath = getBitcoinAddressTypeFromDeriv
|
|
|
9416
9627
|
exports.getClientKeyShareBackupInfo = getClientKeyShareBackupInfo;
|
|
9417
9628
|
exports.getClientKeyShareExportFileName = getClientKeyShareExportFileName;
|
|
9418
9629
|
exports.getDelegatedShareSet = getDelegatedShareSet;
|
|
9630
|
+
exports.getEd25519PublicKeyFromRawScalar = getEd25519PublicKeyFromRawScalar;
|
|
9419
9631
|
exports.getGoogleOAuthAccountId = getGoogleOAuthAccountId;
|
|
9420
9632
|
exports.getHttpStatus = getHttpStatus;
|
|
9421
9633
|
exports.getICloudBackup = getICloudBackup;
|
|
@@ -9429,6 +9641,7 @@ exports.isBrowser = isBrowser;
|
|
|
9429
9641
|
exports.isHeavyQueueOperation = isHeavyQueueOperation;
|
|
9430
9642
|
exports.isHexString = isHexString;
|
|
9431
9643
|
exports.isICloudAuthenticated = isICloudAuthenticated;
|
|
9644
|
+
exports.isMultipleWalletsPerChainRejection = isMultipleWalletsPerChainRejection;
|
|
9432
9645
|
exports.isNonRetryableCeremonyError = isNonRetryableCeremonyError;
|
|
9433
9646
|
exports.isPublicKeyMismatchError = isPublicKeyMismatchError;
|
|
9434
9647
|
exports.isRecoverQueueOperation = isRecoverQueueOperation;
|