@dynamic-labs-wallet/browser 1.0.92 → 1.0.93
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 +201 -47
- package/index.esm.js +203 -50
- 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/types.d.ts +9 -1
- package/src/types.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
|
};
|
|
@@ -3377,28 +3385,34 @@ class DynamicWalletClient {
|
|
|
3377
3385
|
}, this.getTraceContext(traceContext))));
|
|
3378
3386
|
throw new Error('Timed out waiting for wallet creation ceremony to complete');
|
|
3379
3387
|
}
|
|
3380
|
-
async clientInitializeKeyGen({ chainName, thresholdSignatureScheme, bitcoinConfig }) {
|
|
3388
|
+
async clientInitializeKeyGen({ chainName, thresholdSignatureScheme, bitcoinConfig, variant }) {
|
|
3381
3389
|
// Get the mpc signer
|
|
3382
3390
|
const mpcSigner = getMPCSigner({
|
|
3383
3391
|
chainName,
|
|
3384
3392
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3385
|
-
bitcoinConfig
|
|
3393
|
+
bitcoinConfig,
|
|
3394
|
+
variant
|
|
3386
3395
|
});
|
|
3387
3396
|
const clientThreshold = core.getClientThreshold(thresholdSignatureScheme);
|
|
3388
3397
|
const keygenInitResults = await Promise.all(Array(clientThreshold).fill(null).map(()=>mpcSigner.initKeygen()));
|
|
3389
3398
|
return keygenInitResults;
|
|
3390
3399
|
}
|
|
3391
|
-
async derivePublicKey({ chainName, keyShare, derivationPath, bitcoinConfig }) {
|
|
3400
|
+
async derivePublicKey({ chainName, keyShare, derivationPath, bitcoinConfig, variant }) {
|
|
3392
3401
|
const mpcSigner = getMPCSigner({
|
|
3393
3402
|
chainName,
|
|
3394
3403
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3395
|
-
bitcoinConfig
|
|
3404
|
+
bitcoinConfig,
|
|
3405
|
+
variant
|
|
3396
3406
|
});
|
|
3397
3407
|
let publicKey;
|
|
3398
3408
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
3399
3409
|
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
3400
3410
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
3401
3411
|
publicKey = await mpcSigner.getPubkey(keyShare);
|
|
3412
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
3413
|
+
// Non-exportable Ed25519 (raw-scalar-imported wallets). Derivation is not
|
|
3414
|
+
// standardized for ed25519, so the root pubkey is taken with an undefined path.
|
|
3415
|
+
publicKey = await mpcSigner.derivePubkey(keyShare, derivationPath);
|
|
3402
3416
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
3403
3417
|
publicKey = await mpcSigner.deriveTweakPubkey(keyShare, derivationPath);
|
|
3404
3418
|
}
|
|
@@ -3762,7 +3776,7 @@ class DynamicWalletClient {
|
|
|
3762
3776
|
shouldRetry: (error)=>!isNonRetryableCeremonyError(error)
|
|
3763
3777
|
});
|
|
3764
3778
|
}
|
|
3765
|
-
async runImportRawPrivateKeyAttempt({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, onError, onCeremonyComplete, traceContext, legacyWalletId, password, signedSessionId }) {
|
|
3779
|
+
async runImportRawPrivateKeyAttempt({ chainName, privateKey, thresholdSignatureScheme, bitcoinConfig, onError, onCeremonyComplete, traceContext, legacyWalletId, password, signedSessionId, isRawScalarImport }) {
|
|
3766
3780
|
const dynamicRequestId = uuid.v4();
|
|
3767
3781
|
try {
|
|
3768
3782
|
this.assertPasswordRequired(password);
|
|
@@ -3770,15 +3784,21 @@ class DynamicWalletClient {
|
|
|
3770
3784
|
password,
|
|
3771
3785
|
signedSessionId
|
|
3772
3786
|
});
|
|
3787
|
+
// A raw-scalar import must run on the non-exportable `Ed25519` protocol for
|
|
3788
|
+
// the whole ceremony (init keygen, import, pubkey derivation) so the produced
|
|
3789
|
+
// key shares are consistent. Non-raw imports keep the default ('ed25519Exportable').
|
|
3790
|
+
const variant = isRawScalarImport ? 'ed25519Standard' : undefined;
|
|
3773
3791
|
const mpcSigner = getMPCSigner({
|
|
3774
3792
|
chainName,
|
|
3775
3793
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3776
|
-
bitcoinConfig
|
|
3794
|
+
bitcoinConfig,
|
|
3795
|
+
variant
|
|
3777
3796
|
});
|
|
3778
3797
|
const clientKeygenInitResults = await this.clientInitializeKeyGen({
|
|
3779
3798
|
chainName,
|
|
3780
3799
|
thresholdSignatureScheme,
|
|
3781
|
-
bitcoinConfig
|
|
3800
|
+
bitcoinConfig,
|
|
3801
|
+
variant
|
|
3782
3802
|
});
|
|
3783
3803
|
const clientKeygenIds = clientKeygenInitResults.map((result)=>result.keygenId);
|
|
3784
3804
|
this.logger.info('[DynamicWaasWalletClient] Client key generation initialized', _extends({
|
|
@@ -3796,7 +3816,9 @@ class DynamicWalletClient {
|
|
|
3796
3816
|
bitcoinConfig,
|
|
3797
3817
|
onError,
|
|
3798
3818
|
onCeremonyComplete,
|
|
3799
|
-
legacyWalletId
|
|
3819
|
+
legacyWalletId,
|
|
3820
|
+
// Persist the protocol variant server-side so later sign/reshare read it back.
|
|
3821
|
+
variant
|
|
3800
3822
|
});
|
|
3801
3823
|
this.logger.info('[DynamicWaasWalletClient] Server key generation initialized', _extends({
|
|
3802
3824
|
roomId,
|
|
@@ -3814,7 +3836,9 @@ class DynamicWalletClient {
|
|
|
3814
3836
|
...serverKeygenIds,
|
|
3815
3837
|
...otherClientKeygenIds
|
|
3816
3838
|
];
|
|
3817
|
-
const importerKeygenResult =
|
|
3839
|
+
const importerKeygenResult = isRawScalarImport ? // uses the 32 bytes directly as the signing scalar instead of
|
|
3840
|
+
// SHA-512-expanding them as an RFC-8032 seed.
|
|
3841
|
+
await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds, true) : await mpcSigner.importPrivateKeyImporter(roomId, threshold, privateKey, currentInit, otherKeyGenIds);
|
|
3818
3842
|
return importerKeygenResult;
|
|
3819
3843
|
} else {
|
|
3820
3844
|
const recipientKeygenResult = await mpcSigner.importPrivateKeyRecipient(roomId, threshold, currentInit, [
|
|
@@ -3831,7 +3855,8 @@ class DynamicWalletClient {
|
|
|
3831
3855
|
chainName,
|
|
3832
3856
|
keyShare: clientKeygenResult,
|
|
3833
3857
|
derivationPath,
|
|
3834
|
-
bitcoinConfig
|
|
3858
|
+
bitcoinConfig,
|
|
3859
|
+
variant
|
|
3835
3860
|
});
|
|
3836
3861
|
this.logger.info('[DynamicWaasWalletClient] Completed import of raw private key', _extends({
|
|
3837
3862
|
rawPublicKey,
|
|
@@ -3866,6 +3891,8 @@ class DynamicWalletClient {
|
|
|
3866
3891
|
if (typeof message !== 'string') {
|
|
3867
3892
|
message = `0x${Buffer.from(message).toString('hex')}`;
|
|
3868
3893
|
}
|
|
3894
|
+
// Note: the server signing party (wallet-service) selects the ed25519
|
|
3895
|
+
// protocol variant from its own persisted EAC, so it is NOT threaded here.
|
|
3869
3896
|
const serializedContext = context ? JSON.parse(JSON.stringify(context, (_key, value)=>typeof value === 'bigint' ? value.toString() : value)) : undefined;
|
|
3870
3897
|
const useHttpTransport = this.featureFlags[core.FEATURE_FLAGS.ENABLE_HTTP_WAAS_TRANSPORT] === true && this.featureFlags[core.FEATURE_FLAGS.ENABLE_HTTP_WAAS_TRANSPORT_SIGNMESSAGE] === true;
|
|
3871
3898
|
const params = {
|
|
@@ -3929,7 +3956,7 @@ class DynamicWalletClient {
|
|
|
3929
3956
|
}
|
|
3930
3957
|
return signatureBytes;
|
|
3931
3958
|
}
|
|
3932
|
-
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext, bitcoinConfig }) {
|
|
3959
|
+
async forwardMPCClientSign({ chainName, message, roomId, keyShare, derivationPath, formattedMessage, dynamicRequestId, isFormatted, traceContext, bitcoinConfig, variant }) {
|
|
3933
3960
|
try {
|
|
3934
3961
|
const chainConfig = core.getMPCChainConfig(chainName, bitcoinConfig);
|
|
3935
3962
|
const signingAlgo = chainConfig.signingAlgorithm;
|
|
@@ -3942,7 +3969,7 @@ class DynamicWalletClient {
|
|
|
3942
3969
|
});
|
|
3943
3970
|
const tweak = this.convertTweakForBIP340(bitcoinConfig == null ? void 0 : bitcoinConfig.tweak);
|
|
3944
3971
|
this.logger.info('Forward MPC enabled, signing message with forward MPC (new)', this.getTraceContext(traceContext));
|
|
3945
|
-
const { signature: signatureBytes } = await this.apiClient.forwardMPCClient.signMessage({
|
|
3972
|
+
const { signature: signatureBytes } = await this.apiClient.forwardMPCClient.signMessage(_extends({
|
|
3946
3973
|
keyshare: keyShare,
|
|
3947
3974
|
message: messageToSign,
|
|
3948
3975
|
relayDomain: this.baseMPCRelayApiUrl || '',
|
|
@@ -3954,7 +3981,9 @@ class DynamicWalletClient {
|
|
|
3954
3981
|
traceContext,
|
|
3955
3982
|
userId: this.userId,
|
|
3956
3983
|
environmentId: this.environmentId
|
|
3957
|
-
}
|
|
3984
|
+
}, variant === 'ed25519Standard' ? {
|
|
3985
|
+
ed25519Variant: 'standard'
|
|
3986
|
+
} : {}));
|
|
3958
3987
|
if (!(signatureBytes instanceof Uint8Array)) {
|
|
3959
3988
|
throw new TypeError(`Invalid signature format: expected Uint8Array, got ${typeof signatureBytes}`);
|
|
3960
3989
|
}
|
|
@@ -3974,15 +4003,22 @@ class DynamicWalletClient {
|
|
|
3974
4003
|
throw error;
|
|
3975
4004
|
}
|
|
3976
4005
|
}
|
|
3977
|
-
async clientSign({ chainName, message, roomId: initialRoomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext, bitcoinConfig, refreshRoom }) {
|
|
4006
|
+
async clientSign({ chainName, message, roomId: initialRoomId, keyShare, derivationPath, isFormatted, dynamicRequestId, traceContext, bitcoinConfig, variant, refreshRoom }) {
|
|
3978
4007
|
// Reassigned when forward MPC fails and we mint a fresh room for the
|
|
3979
4008
|
// relay-based fallback below.
|
|
3980
4009
|
let roomId = initialRoomId;
|
|
4010
|
+
// Non-exportable Ed25519 (raw-scalar import) derives its address at the
|
|
4011
|
+
// root — see derivePrivateKeyFromExport's identical guard. Unlike
|
|
4012
|
+
// ExportableEd25519 (which ignores this arg), Ed25519.sign() *applies* a
|
|
4013
|
+
// non-undefined path, so forwarding the wallet's stored derivation path
|
|
4014
|
+
// here would sign under a derived child key instead of the wallet's own.
|
|
4015
|
+
const effectiveDerivationPath = variant === 'ed25519Standard' ? undefined : derivationPath;
|
|
3981
4016
|
try {
|
|
3982
4017
|
const mpcSigner = getMPCSigner({
|
|
3983
4018
|
chainName,
|
|
3984
4019
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
3985
|
-
bitcoinConfig
|
|
4020
|
+
bitcoinConfig,
|
|
4021
|
+
variant
|
|
3986
4022
|
});
|
|
3987
4023
|
const formattedMessage = isFormatted ? new web.MessageHash(message) : formatMessage(chainName, message);
|
|
3988
4024
|
this.logger.debug('[DynamicWaasWalletClient] Starting client sign', _extends({
|
|
@@ -4006,12 +4042,13 @@ class DynamicWalletClient {
|
|
|
4006
4042
|
message,
|
|
4007
4043
|
roomId,
|
|
4008
4044
|
keyShare,
|
|
4009
|
-
derivationPath,
|
|
4045
|
+
derivationPath: effectiveDerivationPath,
|
|
4010
4046
|
formattedMessage,
|
|
4011
4047
|
dynamicRequestId,
|
|
4012
4048
|
isFormatted,
|
|
4013
4049
|
traceContext,
|
|
4014
|
-
bitcoinConfig
|
|
4050
|
+
bitcoinConfig,
|
|
4051
|
+
variant
|
|
4015
4052
|
});
|
|
4016
4053
|
} catch (error) {
|
|
4017
4054
|
const errorInfo = core.classifyForwardMpcError(error);
|
|
@@ -4055,7 +4092,7 @@ class DynamicWalletClient {
|
|
|
4055
4092
|
if (!('sign' in mpcSigner)) {
|
|
4056
4093
|
throw new TypeError(`Message signing is not supported for chain ${chainName} — use signTransaction instead`);
|
|
4057
4094
|
}
|
|
4058
|
-
const signature = await mpcSigner.sign(roomId, keyShare, messageToSign,
|
|
4095
|
+
const signature = await mpcSigner.sign(roomId, keyShare, messageToSign, effectiveDerivationPath, tweak);
|
|
4059
4096
|
return signature;
|
|
4060
4097
|
} catch (error) {
|
|
4061
4098
|
logError({
|
|
@@ -4139,7 +4176,8 @@ class DynamicWalletClient {
|
|
|
4139
4176
|
chainName,
|
|
4140
4177
|
localShares,
|
|
4141
4178
|
recordedKeygenIds,
|
|
4142
|
-
bitcoinConfig
|
|
4179
|
+
bitcoinConfig,
|
|
4180
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
4143
4181
|
});
|
|
4144
4182
|
localKeygenIds = comparison.localKeygenIds;
|
|
4145
4183
|
staleness = comparison.fresh ? 'fresh' : 'stale';
|
|
@@ -4205,11 +4243,14 @@ class DynamicWalletClient {
|
|
|
4205
4243
|
* the server recorded for the wallet's Dynamic backups. Shared by the
|
|
4206
4244
|
* stale-share heal (verifyAndRecoverStaleShare) and the backup preflight
|
|
4207
4245
|
* (ensureLocalSharesAreFresh).
|
|
4208
|
-
*/ async compareShareGenerations({ accountAddress, chainName, localShares, recordedKeygenIds, bitcoinConfig }) {
|
|
4246
|
+
*/ async compareShareGenerations({ accountAddress, chainName, localShares, recordedKeygenIds, bitcoinConfig, variant }) {
|
|
4209
4247
|
const mpcSigner = getMPCSigner({
|
|
4210
4248
|
chainName,
|
|
4211
4249
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4212
|
-
bitcoinConfig: bitcoinConfig != null ? bitcoinConfig : this.getBitcoinConfigForChain(chainName, accountAddress)
|
|
4250
|
+
bitcoinConfig: bitcoinConfig != null ? bitcoinConfig : this.getBitcoinConfigForChain(chainName, accountAddress),
|
|
4251
|
+
// Raw-scalar (non-exportable Ed25519) wallets: build the matching signer so
|
|
4252
|
+
// getKeygenIdForShare deserializes the Ed25519 share (else "Invalid discriminant").
|
|
4253
|
+
variant
|
|
4213
4254
|
});
|
|
4214
4255
|
const localKeygenIds = await Promise.all(localShares.map((share)=>this.getKeygenIdForShare(mpcSigner, share)));
|
|
4215
4256
|
const recordedSet = new Set(recordedKeygenIds);
|
|
@@ -4367,6 +4408,7 @@ class DynamicWalletClient {
|
|
|
4367
4408
|
dynamicRequestId,
|
|
4368
4409
|
traceContext,
|
|
4369
4410
|
bitcoinConfig,
|
|
4411
|
+
variant: wallet.variant,
|
|
4370
4412
|
// On forward-MPC fallback, re-run the server sign with no roomId so a
|
|
4371
4413
|
// brand-new server-joined room is created for the relay ceremony.
|
|
4372
4414
|
refreshRoom: async ()=>{
|
|
@@ -4666,7 +4708,11 @@ class DynamicWalletClient {
|
|
|
4666
4708
|
const mpcSigner = getMPCSigner({
|
|
4667
4709
|
chainName,
|
|
4668
4710
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4669
|
-
bitcoinConfig
|
|
4711
|
+
bitcoinConfig,
|
|
4712
|
+
// Raw-scalar (non-exportable Ed25519) wallets must refresh on the same
|
|
4713
|
+
// protocol their shares were created with, or the ceremony fails to
|
|
4714
|
+
// deserialize them ("Invalid discriminant").
|
|
4715
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
4670
4716
|
});
|
|
4671
4717
|
// Ensure client key shares exist before hitting the API
|
|
4672
4718
|
const clientKeyShares = await this.ensureClientShare(accountAddress);
|
|
@@ -4848,11 +4894,26 @@ class DynamicWalletClient {
|
|
|
4848
4894
|
}
|
|
4849
4895
|
return mpcSigner.exportID(share);
|
|
4850
4896
|
}
|
|
4851
|
-
|
|
4897
|
+
/**
|
|
4898
|
+
* The ed25519 protocol variant for an existing wallet, read from the wallet map
|
|
4899
|
+
* (sourced from `walletProperties.settings.variant`, persisted at import).
|
|
4900
|
+
*
|
|
4901
|
+
* It is per-wallet protocol metadata — like `thresholdSignatureScheme` or
|
|
4902
|
+
* `addressType` — and the single source of truth for which signer to build when
|
|
4903
|
+
* operating on a wallet's key shares. Wallets imported from a raw ed25519 scalar
|
|
4904
|
+
* (e.g. Fireblocks Embedded Wallets) are `'ed25519Standard'` (non-exportable `Ed25519`);
|
|
4905
|
+
* everything else is `undefined` → the default exportable protocol. Using the
|
|
4906
|
+
* wrong one fails to deserialize the key share with "Invalid discriminant".
|
|
4907
|
+
*/ ed25519VariantForWallet(accountAddress) {
|
|
4908
|
+
var _this_getWalletFromMap;
|
|
4909
|
+
return (_this_getWalletFromMap = this.getWalletFromMap(accountAddress)) == null ? void 0 : _this_getWalletFromMap.variant;
|
|
4910
|
+
}
|
|
4911
|
+
async getExportId({ chainName, clientKeyShare, bitcoinConfig, accountAddress }) {
|
|
4852
4912
|
const mpcSigner = getMPCSigner({
|
|
4853
4913
|
chainName,
|
|
4854
4914
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
4855
|
-
bitcoinConfig
|
|
4915
|
+
bitcoinConfig,
|
|
4916
|
+
variant: accountAddress ? this.ed25519VariantForWallet(accountAddress) : undefined
|
|
4856
4917
|
});
|
|
4857
4918
|
try {
|
|
4858
4919
|
const exportId = await this.getKeygenIdForShare(mpcSigner, clientKeyShare);
|
|
@@ -4900,7 +4961,8 @@ class DynamicWalletClient {
|
|
|
4900
4961
|
return this.getExportId({
|
|
4901
4962
|
chainName,
|
|
4902
4963
|
clientKeyShare,
|
|
4903
|
-
bitcoinConfig
|
|
4964
|
+
bitcoinConfig,
|
|
4965
|
+
accountAddress
|
|
4904
4966
|
});
|
|
4905
4967
|
}
|
|
4906
4968
|
/**
|
|
@@ -4941,7 +5003,8 @@ class DynamicWalletClient {
|
|
|
4941
5003
|
const existingClientKeygenIds = await Promise.all(existingClientKeyShares.map(async (keyShare)=>await this.getExportId({
|
|
4942
5004
|
chainName,
|
|
4943
5005
|
clientKeyShare: keyShare,
|
|
4944
|
-
bitcoinConfig
|
|
5006
|
+
bitcoinConfig,
|
|
5007
|
+
accountAddress
|
|
4945
5008
|
})));
|
|
4946
5009
|
return {
|
|
4947
5010
|
newClientInitKeygenResults,
|
|
@@ -5010,7 +5073,8 @@ class DynamicWalletClient {
|
|
|
5010
5073
|
const existingClientKeygenId = await this.getExportId({
|
|
5011
5074
|
chainName,
|
|
5012
5075
|
clientKeyShare: existingClientShare,
|
|
5013
|
-
bitcoinConfig
|
|
5076
|
+
bitcoinConfig,
|
|
5077
|
+
accountAddress
|
|
5014
5078
|
});
|
|
5015
5079
|
// Gate matches internalRefresh/Reshare — SSE error rejects awaitTerminal.
|
|
5016
5080
|
let newShareSetId;
|
|
@@ -5619,7 +5683,11 @@ class DynamicWalletClient {
|
|
|
5619
5683
|
const mpcSigner = getMPCSigner({
|
|
5620
5684
|
chainName,
|
|
5621
5685
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
5622
|
-
bitcoinConfig
|
|
5686
|
+
bitcoinConfig,
|
|
5687
|
+
// Raw-scalar (non-exportable Ed25519) wallets must reshare on the same
|
|
5688
|
+
// protocol their shares were created with, or the ceremony fails to
|
|
5689
|
+
// deserialize them ("Invalid discriminant").
|
|
5690
|
+
variant: this.ed25519VariantForWallet(accountAddress)
|
|
5623
5691
|
});
|
|
5624
5692
|
let existingReshareResults;
|
|
5625
5693
|
let newReshareResults;
|
|
@@ -5983,6 +6051,9 @@ class DynamicWalletClient {
|
|
|
5983
6051
|
if (mpcSigner instanceof web.ExportableEd25519) {
|
|
5984
6052
|
return new web.ExportableEd25519KeygenResult(extractedPubkey, secretShare);
|
|
5985
6053
|
}
|
|
6054
|
+
if (mpcSigner instanceof web.Ed25519) {
|
|
6055
|
+
return new web.Ed25519KeygenResult(extractedPubkey, secretShare);
|
|
6056
|
+
}
|
|
5986
6057
|
return new web.BIP340KeygenResult(extractedPubkey, secretShare);
|
|
5987
6058
|
}
|
|
5988
6059
|
async exportKey({ accountAddress, chainName, bitcoinConfig, password = undefined, signedSessionId, mfaToken, elevatedAccessToken, traceContext }) {
|
|
@@ -6013,13 +6084,15 @@ class DynamicWalletClient {
|
|
|
6013
6084
|
const mpcSigner = getMPCSigner({
|
|
6014
6085
|
chainName,
|
|
6015
6086
|
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
6016
|
-
bitcoinConfig
|
|
6087
|
+
bitcoinConfig,
|
|
6088
|
+
variant: wallet.variant
|
|
6017
6089
|
});
|
|
6018
6090
|
const reconstructedKeyShare = await this.getReconstructedKeyShare(accountAddress, mpcSigner);
|
|
6019
6091
|
const exportId = await this.getExportId({
|
|
6020
6092
|
chainName,
|
|
6021
6093
|
clientKeyShare: reconstructedKeyShare,
|
|
6022
|
-
bitcoinConfig
|
|
6094
|
+
bitcoinConfig,
|
|
6095
|
+
accountAddress: wallet.accountAddress
|
|
6023
6096
|
});
|
|
6024
6097
|
const data = await this.apiClient.exportKey({
|
|
6025
6098
|
walletId: wallet.walletId,
|
|
@@ -6162,20 +6235,35 @@ class DynamicWalletClient {
|
|
|
6162
6235
|
if (mpcSigner instanceof web.Ecdsa) {
|
|
6163
6236
|
return mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
6164
6237
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
6238
|
+
// ExportableEd25519's exportFullPrivateKey already returns the RFC-8032
|
|
6239
|
+
// form (hex of seed||pubkey); pass it straight through.
|
|
6165
6240
|
return keyExportRaw;
|
|
6241
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
6242
|
+
// Non-exportable Ed25519's exportFullPrivateKey returns an `spriv`
|
|
6243
|
+
// (extended private key), NOT a raw hex key — it must be parsed to the
|
|
6244
|
+
// actual private scalar. Returning the spriv verbatim makes the chain
|
|
6245
|
+
// client hex-decode it into an empty buffer (blank export field).
|
|
6246
|
+
//
|
|
6247
|
+
// This protocol is only used for raw-scalar import, whose public key is
|
|
6248
|
+
// the ROOT scalar (`A = s·B`, derived at import via derivePubkey(_, undefined)).
|
|
6249
|
+
// The chain-config derivation path stored on the wallet (e.g. Solana's
|
|
6250
|
+
// [44,501,0,0,0]) must NOT be applied here — deriving a child scalar would
|
|
6251
|
+
// export a key that doesn't correspond to the wallet's address.
|
|
6252
|
+
return mpcSigner.derivePrivateKeyFromSpriv(keyExportRaw);
|
|
6166
6253
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
6167
6254
|
return mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, derivationPath);
|
|
6168
6255
|
}
|
|
6169
6256
|
return undefined;
|
|
6170
6257
|
}
|
|
6171
|
-
async offlineExportKey({ chainName, keyShares, derivationPath }) {
|
|
6258
|
+
async offlineExportKey({ chainName, keyShares, derivationPath, variant }) {
|
|
6172
6259
|
try {
|
|
6173
6260
|
if (!keyShares || keyShares.length < 2) {
|
|
6174
6261
|
throw new Error(`Must provide at least min threshold of key shares`);
|
|
6175
6262
|
}
|
|
6176
6263
|
const mpcSigner = getMPCSigner({
|
|
6177
6264
|
chainName,
|
|
6178
|
-
baseRelayUrl: this.baseMPCRelayApiUrl
|
|
6265
|
+
baseRelayUrl: this.baseMPCRelayApiUrl,
|
|
6266
|
+
variant
|
|
6179
6267
|
});
|
|
6180
6268
|
const walletKeyShares = keyShares.map((keyShare)=>{
|
|
6181
6269
|
if (!('pubkey' in keyShare)) {
|
|
@@ -6194,6 +6282,10 @@ class DynamicWalletClient {
|
|
|
6194
6282
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
6195
6283
|
} else if (mpcSigner instanceof web.ExportableEd25519) {
|
|
6196
6284
|
derivedPrivateKey = keyExportRaw;
|
|
6285
|
+
} else if (mpcSigner instanceof web.Ed25519) {
|
|
6286
|
+
// Non-exportable Ed25519 (raw-scalar) returns an `spriv`, not raw hex —
|
|
6287
|
+
// parse it to the root scalar (see derivePrivateKeyFromExport).
|
|
6288
|
+
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromSpriv(keyExportRaw);
|
|
6197
6289
|
} else if (mpcSigner instanceof web.BIP340) {
|
|
6198
6290
|
derivedPrivateKey = await mpcSigner.derivePrivateKeyFromXpriv(keyExportRaw, walletDerivationPath);
|
|
6199
6291
|
}
|
|
@@ -6399,7 +6491,7 @@ class DynamicWalletClient {
|
|
|
6399
6491
|
* @param thresholdSignatureScheme - The TSS scheme used
|
|
6400
6492
|
* @param derivationPath - Optional derivation path (will be computed from chainConfig if not provided)
|
|
6401
6493
|
* @param additionalProps - Any chain-specific additional properties to merge
|
|
6402
|
-
*/ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, shareSetId, shareSetType, otherShareSets, additionalProps = {} }) {
|
|
6494
|
+
*/ initializeWalletMapEntry({ accountAddress, walletId, chainName, thresholdSignatureScheme, derivationPath, shareSetId, shareSetType, otherShareSets, variant, additionalProps = {} }) {
|
|
6403
6495
|
this.updateWalletMap(accountAddress, _extends({
|
|
6404
6496
|
accountAddress,
|
|
6405
6497
|
walletId,
|
|
@@ -6413,6 +6505,8 @@ class DynamicWalletClient {
|
|
|
6413
6505
|
shareSetType
|
|
6414
6506
|
} : {}, otherShareSets ? {
|
|
6415
6507
|
otherShareSets
|
|
6508
|
+
} : {}, variant ? {
|
|
6509
|
+
variant
|
|
6416
6510
|
} : {}, {
|
|
6417
6511
|
clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
|
|
6418
6512
|
}, additionalProps));
|
|
@@ -6632,7 +6726,7 @@ class DynamicWalletClient {
|
|
|
6632
6726
|
tokenSource: googleDriveTokenSource
|
|
6633
6727
|
} : {});
|
|
6634
6728
|
}
|
|
6635
|
-
async publishDelegatedShare({ walletId, shareSetId, delegatedShare, signedSessionId, dynamicRequestId, chainName, bitcoinConfig, isPasswordEncrypted }) {
|
|
6729
|
+
async publishDelegatedShare({ walletId, shareSetId, delegatedShare, signedSessionId, dynamicRequestId, chainName, accountAddress, bitcoinConfig, isPasswordEncrypted }) {
|
|
6636
6730
|
var _publicKey_key, _publicKey_key1, _publicKey_key2;
|
|
6637
6731
|
const publicKey = await this.apiClient.getDelegatedEncryptionKey({
|
|
6638
6732
|
environmentId: this.environmentId
|
|
@@ -6657,7 +6751,8 @@ class DynamicWalletClient {
|
|
|
6657
6751
|
chainName,
|
|
6658
6752
|
clientKeyShare: delegatedShare,
|
|
6659
6753
|
bitcoinConfig,
|
|
6660
|
-
walletId
|
|
6754
|
+
walletId,
|
|
6755
|
+
accountAddress
|
|
6661
6756
|
});
|
|
6662
6757
|
return {
|
|
6663
6758
|
location: core.BackupLocation.DELEGATED,
|
|
@@ -6823,6 +6918,7 @@ class DynamicWalletClient {
|
|
|
6823
6918
|
signedSessionId: resolvedSignedSessionId,
|
|
6824
6919
|
dynamicRequestId,
|
|
6825
6920
|
chainName: walletData.chainName,
|
|
6921
|
+
accountAddress,
|
|
6826
6922
|
bitcoinConfig,
|
|
6827
6923
|
isPasswordEncrypted
|
|
6828
6924
|
}), {
|
|
@@ -6991,7 +7087,8 @@ class DynamicWalletClient {
|
|
|
6991
7087
|
accountAddress,
|
|
6992
7088
|
chainName,
|
|
6993
7089
|
localShares,
|
|
6994
|
-
recordedKeygenIds
|
|
7090
|
+
recordedKeygenIds,
|
|
7091
|
+
variant: walletData.variant
|
|
6995
7092
|
});
|
|
6996
7093
|
if (fresh) return;
|
|
6997
7094
|
this.logger.warn('[storeEncryptedBackupByWallet] Stale local shares detected; refreshing local storage', {
|
|
@@ -8416,9 +8513,17 @@ class DynamicWalletClient {
|
|
|
8416
8513
|
const user = await this.getUserWithEnvCheck(dynamicRequestId);
|
|
8417
8514
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
8418
8515
|
for (const vc of waasWallets != null ? waasWallets : []){
|
|
8516
|
+
var _props_settings, _this_getWalletFromMap;
|
|
8419
8517
|
const addr = vc.address;
|
|
8420
8518
|
const props = vc.walletProperties;
|
|
8421
|
-
|
|
8519
|
+
var _props_settings_variant, _ref;
|
|
8520
|
+
// Resolve the ed25519 protocol variant: server-surfaced (walletProperties
|
|
8521
|
+
// .settings or top-level) first, then any locally-seeded value (set at raw
|
|
8522
|
+
// import). Required on a fresh session (no local seed) so recover/sign
|
|
8523
|
+
// rebuild the non-exportable Ed25519 signer for raw-scalar wallets instead
|
|
8524
|
+
// of defaulting to exportable → "Invalid discriminant".
|
|
8525
|
+
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;
|
|
8526
|
+
this.updateWalletMap(addr, _extends({
|
|
8422
8527
|
walletId: vc.id,
|
|
8423
8528
|
chainName: core.verifiedCredentialNameToChainEnum[vc.chain],
|
|
8424
8529
|
accountAddress: addr,
|
|
@@ -8431,7 +8536,9 @@ class DynamicWalletClient {
|
|
|
8431
8536
|
shareSetId: props == null ? void 0 : props.shareSetId,
|
|
8432
8537
|
shareSetType: props == null ? void 0 : props.shareSetType,
|
|
8433
8538
|
otherShareSets: props == null ? void 0 : props.otherShareSets
|
|
8434
|
-
}
|
|
8539
|
+
}, resolvedVariant ? {
|
|
8540
|
+
variant: resolvedVariant
|
|
8541
|
+
} : {}));
|
|
8435
8542
|
}
|
|
8436
8543
|
if (walletOperation !== core.WalletOperation.NO_OPERATION && await this.requiresRestoreBackupSharesForOperation({
|
|
8437
8544
|
accountAddress,
|
|
@@ -8809,8 +8916,8 @@ class DynamicWalletClient {
|
|
|
8809
8916
|
this.userId = user.id;
|
|
8810
8917
|
const waasWallets = (_user_verifiedCredentials = user.verifiedCredentials) == null ? void 0 : _user_verifiedCredentials.filter((vc)=>vc.walletName === 'dynamicwaas');
|
|
8811
8918
|
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;
|
|
8919
|
+
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;
|
|
8920
|
+
var _this_getWalletFromMap_derivationPath, _vc_walletProperties_settings_variant, _ref;
|
|
8814
8921
|
return {
|
|
8815
8922
|
walletId: vc.id,
|
|
8816
8923
|
chainName: core.verifiedCredentialNameToChainEnum[vc.chain],
|
|
@@ -8828,14 +8935,18 @@ class DynamicWalletClient {
|
|
|
8828
8935
|
// On-sign-on settings (shouldRefreshOnNextSignOn / reshareOnNextSignOn /
|
|
8829
8936
|
// revokeOnNextSignOn). Surfaced so the SDK can self-drive the on-sign-on
|
|
8830
8937
|
// orchestration instead of the host reading these flags.
|
|
8831
|
-
settings: (_vc_walletProperties6 = vc.walletProperties) == null ? void 0 : _vc_walletProperties6.settings
|
|
8938
|
+
settings: (_vc_walletProperties6 = vc.walletProperties) == null ? void 0 : _vc_walletProperties6.settings,
|
|
8939
|
+
// Prefer the server-persisted variant (surfaced via walletProperties.settings),
|
|
8940
|
+
// then the locally-known value (set at raw import) so same-session
|
|
8941
|
+
// import → sign works before the server has surfaced it.
|
|
8942
|
+
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
8943
|
};
|
|
8833
8944
|
});
|
|
8834
8945
|
const existingWalletMap = this.walletMap;
|
|
8835
8946
|
this.walletMap = wallets.reduce((acc, wallet)=>{
|
|
8836
8947
|
const normalizedAddress = normalizeAddress(wallet.accountAddress);
|
|
8837
8948
|
const existingWallet = existingWalletMap[normalizedAddress];
|
|
8838
|
-
acc[normalizedAddress] = {
|
|
8949
|
+
acc[normalizedAddress] = _extends({
|
|
8839
8950
|
walletId: wallet.walletId,
|
|
8840
8951
|
chainName: wallet.chainName,
|
|
8841
8952
|
accountAddress: wallet.accountAddress,
|
|
@@ -8848,7 +8959,9 @@ class DynamicWalletClient {
|
|
|
8848
8959
|
shareSetType: wallet.shareSetType,
|
|
8849
8960
|
businessAccountId: wallet.businessAccountId,
|
|
8850
8961
|
otherShareSets: wallet.otherShareSets
|
|
8851
|
-
}
|
|
8962
|
+
}, wallet.variant ? {
|
|
8963
|
+
variant: wallet.variant
|
|
8964
|
+
} : {});
|
|
8852
8965
|
return acc;
|
|
8853
8966
|
}, {});
|
|
8854
8967
|
this.logger.info('[walletMap] getWallets: walletMap updated from API', {
|
|
@@ -9302,6 +9415,46 @@ DynamicWalletClient.roomsPersistDirty = false;
|
|
|
9302
9415
|
// rooms back into the new user's storage (TOCTOU on the create→merge gap).
|
|
9303
9416
|
DynamicWalletClient.roomsGeneration = 0;
|
|
9304
9417
|
|
|
9418
|
+
/**
|
|
9419
|
+
* Derive the 32-byte ed25519 public key from a **raw** 32-byte signing scalar
|
|
9420
|
+
* (`A = s·B`).
|
|
9421
|
+
*
|
|
9422
|
+
* This is NOT the same as deriving a public key from an RFC-8032 seed: the
|
|
9423
|
+
* standard ed25519 key-generation step takes a 32-byte seed and SHA-512-expands
|
|
9424
|
+
* it (then clamps) to obtain the scalar. A scalar exported from an external MPC
|
|
9425
|
+
* system (e.g. Fireblocks Embedded Wallets) has no preimage seed, so it must be
|
|
9426
|
+
* used directly as the scalar. Using `Keypair.fromSecretKey` / `keyPairFromSeed`
|
|
9427
|
+
* on such bytes would re-expand them and produce the wrong public key/address.
|
|
9428
|
+
*
|
|
9429
|
+
* @param scalar Raw 32-byte ed25519 scalar, little-endian, as hex (with or
|
|
9430
|
+
* without a `0x` prefix) or as raw bytes.
|
|
9431
|
+
* @returns The compressed 32-byte ed25519 public key.
|
|
9432
|
+
*/ const getEd25519PublicKeyFromRawScalar = (scalar)=>{
|
|
9433
|
+
let bytes;
|
|
9434
|
+
if (typeof scalar === 'string') {
|
|
9435
|
+
const hex = scalar.replace(/^0x/, '');
|
|
9436
|
+
// Validate before hexToBytes so malformed input fails with a clear, own
|
|
9437
|
+
// error rather than an opaque/implementation-specific one from the decoder.
|
|
9438
|
+
if (!/^[0-9a-fA-F]+$/.test(hex)) {
|
|
9439
|
+
throw new Error('Invalid raw ed25519 scalar: expected a hex string');
|
|
9440
|
+
}
|
|
9441
|
+
bytes = utils.hexToBytes(hex);
|
|
9442
|
+
} else {
|
|
9443
|
+
bytes = scalar;
|
|
9444
|
+
}
|
|
9445
|
+
if (bytes.length !== 32) {
|
|
9446
|
+
throw new Error(`Invalid raw ed25519 scalar length: ${bytes.length}, expected 32`);
|
|
9447
|
+
}
|
|
9448
|
+
// Interpret the scalar little-endian and reduce mod the curve order. We do NOT
|
|
9449
|
+
// clamp here: the caller provides the actual signing scalar `s`, and the public
|
|
9450
|
+
// key is exactly `s·B`.
|
|
9451
|
+
const reduced = utils.bytesToNumberLE(bytes) % ed25519.ed25519.CURVE.n;
|
|
9452
|
+
if (reduced === 0n) {
|
|
9453
|
+
throw new Error('Invalid raw ed25519 scalar: reduces to zero mod the curve order');
|
|
9454
|
+
}
|
|
9455
|
+
return ed25519.ed25519.ExtendedPoint.BASE.multiply(reduced).toRawBytes();
|
|
9456
|
+
};
|
|
9457
|
+
|
|
9305
9458
|
Object.defineProperty(exports, "Logger", {
|
|
9306
9459
|
enumerable: true,
|
|
9307
9460
|
get: function () { return core.Logger; }
|
|
@@ -9416,6 +9569,7 @@ exports.getBitcoinAddressTypeFromDerivationPath = getBitcoinAddressTypeFromDeriv
|
|
|
9416
9569
|
exports.getClientKeyShareBackupInfo = getClientKeyShareBackupInfo;
|
|
9417
9570
|
exports.getClientKeyShareExportFileName = getClientKeyShareExportFileName;
|
|
9418
9571
|
exports.getDelegatedShareSet = getDelegatedShareSet;
|
|
9572
|
+
exports.getEd25519PublicKeyFromRawScalar = getEd25519PublicKeyFromRawScalar;
|
|
9419
9573
|
exports.getGoogleOAuthAccountId = getGoogleOAuthAccountId;
|
|
9420
9574
|
exports.getHttpStatus = getHttpStatus;
|
|
9421
9575
|
exports.getICloudBackup = getICloudBackup;
|