@volr/sdk-core 0.1.0

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/dist/index.cjs ADDED
@@ -0,0 +1,1342 @@
1
+ 'use strict';
2
+
3
+ var hkdf = require('@noble/hashes/hkdf');
4
+ var sha256 = require('@noble/hashes/sha256');
5
+ var bip32 = require('@scure/bip32');
6
+ var secp256k1 = require('@noble/curves/secp256k1');
7
+ var sha3 = require('@noble/hashes/sha3');
8
+ var ethers = require('ethers');
9
+ var axios = require('axios');
10
+
11
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
+
13
+ var axios__default = /*#__PURE__*/_interopDefault(axios);
14
+
15
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
16
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
17
+ }) : x)(function(x) {
18
+ if (typeof require !== "undefined") return require.apply(this, arguments);
19
+ throw Error('Dynamic require of "' + x + '" is not supported');
20
+ });
21
+
22
+ // src/core/errors.ts
23
+ var VolrError = class _VolrError extends Error {
24
+ constructor(code, message, cause) {
25
+ super(message);
26
+ this.code = code;
27
+ this.cause = cause;
28
+ this.name = "VolrError";
29
+ Object.setPrototypeOf(this, _VolrError.prototype);
30
+ }
31
+ };
32
+ var ERR_INVALID_PARAM = "ERR_INVALID_PARAM";
33
+ var ERR_CRYPTO_FAIL = "ERR_CRYPTO_FAIL";
34
+ var ERR_AAD_MISMATCH = "ERR_AAD_MISMATCH";
35
+ var ERR_NONCE_SIZE = "ERR_NONCE_SIZE";
36
+ var ERR_LOW_ENTROPY = "ERR_LOW_ENTROPY";
37
+ var ERR_CHAIN_MISMATCH = "ERR_CHAIN_MISMATCH";
38
+
39
+ // src/core/crypto/hkdf.ts
40
+ function hkdfSha256(ikm, salt, info, len = 32) {
41
+ if (!info || info.length === 0) {
42
+ throw new Error(`${ERR_INVALID_PARAM}: info cannot be empty`);
43
+ }
44
+ if (len < 1 || len > 255 * 32) {
45
+ throw new Error(`${ERR_INVALID_PARAM}: len must be between 1 and ${255 * 32}`);
46
+ }
47
+ try {
48
+ const derived = hkdf.hkdf(sha256.sha256, ikm, salt, info, len);
49
+ return derived;
50
+ } catch (error) {
51
+ throw new Error(`HKDF derivation failed: ${error instanceof Error ? error.message : String(error)}`);
52
+ }
53
+ }
54
+
55
+ // src/core/crypto/rng.ts
56
+ function getRandomBytes(len) {
57
+ if (len < 0 || len > 65536) {
58
+ throw new Error(`Invalid length: ${len}`);
59
+ }
60
+ const arr = new Uint8Array(len);
61
+ if (typeof crypto !== "undefined" && crypto.getRandomValues) {
62
+ crypto.getRandomValues(arr);
63
+ return arr;
64
+ }
65
+ if (typeof __require !== "undefined") {
66
+ try {
67
+ const nodeCrypto = __require("crypto");
68
+ if (nodeCrypto.webcrypto && nodeCrypto.webcrypto.getRandomValues) {
69
+ nodeCrypto.webcrypto.getRandomValues(arr);
70
+ return arr;
71
+ }
72
+ const randomBytes = nodeCrypto.randomBytes(len);
73
+ arr.set(randomBytes);
74
+ return arr;
75
+ } catch {
76
+ }
77
+ }
78
+ throw new Error("No secure random source available");
79
+ }
80
+
81
+ // src/core/crypto/aes-gcm.ts
82
+ function getSubtleCrypto() {
83
+ if (typeof crypto !== "undefined" && crypto.subtle) {
84
+ return crypto.subtle;
85
+ }
86
+ if (typeof __require !== "undefined") {
87
+ try {
88
+ const nodeCrypto = __require("crypto");
89
+ if (nodeCrypto.webcrypto && nodeCrypto.webcrypto.subtle) {
90
+ return nodeCrypto.webcrypto.subtle;
91
+ }
92
+ } catch {
93
+ }
94
+ }
95
+ throw new Error(`${ERR_CRYPTO_FAIL}: WebCrypto API not available`);
96
+ }
97
+ async function aesGcmEncrypt(plain, params) {
98
+ const { key, nonce: providedNonce, aad } = params;
99
+ if (key.length !== 32) {
100
+ throw new Error(`${ERR_INVALID_PARAM}: key must be 32 bytes, got ${key.length}`);
101
+ }
102
+ const nonce = providedNonce || getRandomBytes(12);
103
+ if (nonce.length !== 12) {
104
+ throw new Error(`${ERR_NONCE_SIZE}: nonce must be 12 bytes, got ${nonce.length}`);
105
+ }
106
+ const subtle = getSubtleCrypto();
107
+ try {
108
+ const keyMaterial = await subtle.importKey(
109
+ "raw",
110
+ key,
111
+ { name: "AES-GCM" },
112
+ false,
113
+ ["encrypt"]
114
+ );
115
+ const encrypted = await subtle.encrypt(
116
+ {
117
+ name: "AES-GCM",
118
+ iv: nonce,
119
+ additionalData: aad,
120
+ tagLength: 128
121
+ // 16 bytes authentication tag
122
+ },
123
+ keyMaterial,
124
+ plain
125
+ );
126
+ return {
127
+ cipher: new Uint8Array(encrypted),
128
+ nonce
129
+ };
130
+ } catch (error) {
131
+ throw new Error(
132
+ `${ERR_CRYPTO_FAIL}: Encryption failed: ${error instanceof Error ? error.message : String(error)}`
133
+ );
134
+ }
135
+ }
136
+ async function aesGcmDecrypt(cipher, params) {
137
+ const { key, nonce, aad } = params;
138
+ if (key.length !== 32) {
139
+ throw new Error(`${ERR_INVALID_PARAM}: key must be 32 bytes, got ${key.length}`);
140
+ }
141
+ if (nonce.length !== 12) {
142
+ throw new Error(`${ERR_NONCE_SIZE}: nonce must be 12 bytes, got ${nonce.length}`);
143
+ }
144
+ const subtle = getSubtleCrypto();
145
+ try {
146
+ const keyMaterial = await subtle.importKey(
147
+ "raw",
148
+ key,
149
+ { name: "AES-GCM" },
150
+ false,
151
+ ["decrypt"]
152
+ );
153
+ const decrypted = await subtle.decrypt(
154
+ {
155
+ name: "AES-GCM",
156
+ iv: nonce,
157
+ additionalData: aad,
158
+ tagLength: 128
159
+ },
160
+ keyMaterial,
161
+ cipher
162
+ );
163
+ return new Uint8Array(decrypted);
164
+ } catch (error) {
165
+ const message = error instanceof Error ? error.message : String(error);
166
+ if (message.includes("decryption") || message.includes("authentication")) {
167
+ throw new Error(`${ERR_AAD_MISMATCH}: Decryption failed - wrong key or AAD`);
168
+ }
169
+ throw new Error(`${ERR_CRYPTO_FAIL}: Decryption failed: ${message}`);
170
+ }
171
+ }
172
+
173
+ // src/core/crypto/zeroize.ts
174
+ function zeroize(buf) {
175
+ let view;
176
+ if (buf instanceof ArrayBuffer) {
177
+ view = new Uint8Array(buf);
178
+ } else {
179
+ view = buf;
180
+ }
181
+ view.fill(0);
182
+ view.length;
183
+ }
184
+
185
+ // src/master-key/encryption.ts
186
+ async function sealMasterSeed(masterSeed, wrapKey, aad) {
187
+ return aesGcmEncrypt(masterSeed, { key: wrapKey, aad });
188
+ }
189
+ async function unsealMasterSeed(cipher, wrapKey, aad, nonce) {
190
+ const decrypted = await aesGcmDecrypt(cipher, { key: wrapKey, aad, nonce });
191
+ return decrypted;
192
+ }
193
+
194
+ // src/master-key/provider.ts
195
+ function createMasterKeyProvider() {
196
+ return {
197
+ async unsealFromBlob(blob, wrapKey) {
198
+ const masterSeed = await unsealMasterSeed(
199
+ blob.cipher,
200
+ wrapKey,
201
+ blob.aad,
202
+ blob.nonce
203
+ );
204
+ return {
205
+ bytes: masterSeed,
206
+ destroy() {
207
+ zeroize(masterSeed);
208
+ }
209
+ };
210
+ },
211
+ async generate() {
212
+ const masterSeed = getRandomBytes(32);
213
+ return {
214
+ bytes: masterSeed,
215
+ destroy() {
216
+ zeroize(masterSeed);
217
+ }
218
+ };
219
+ }
220
+ };
221
+ }
222
+ function deriveWrapKey(input) {
223
+ const { origin, projectId, salt } = input;
224
+ const rpId = typeof window !== "undefined" && origin ? new URL(origin).hostname : origin;
225
+ const prfInput = new TextEncoder().encode(`${rpId}|${projectId}`);
226
+ const prfOutput = sha256.sha256(prfInput);
227
+ const defaultSalt = salt || sha256.sha256(
228
+ new TextEncoder().encode(`volr/salt|${rpId}|${projectId}`)
229
+ );
230
+ const info = `volr/wrap-key/v1|${rpId}|${projectId}`;
231
+ const wrapKey = hkdfSha256(prfOutput, defaultSalt, info, 32);
232
+ return wrapKey;
233
+ }
234
+ var DEFAULT_EVM_PATH = "m/60'/0'/0'/0/0";
235
+ function deriveEvmKey(args) {
236
+ const { masterSeed, path = DEFAULT_EVM_PATH } = args;
237
+ const hdKey = bip32.HDKey.fromMasterSeed(masterSeed);
238
+ const derived = hdKey.derive(path);
239
+ if (!derived.privateKey) {
240
+ throw new Error("Failed to derive private key");
241
+ }
242
+ const privateKey = new Uint8Array(derived.privateKey);
243
+ const publicKey = derived.publicKey;
244
+ if (!publicKey || publicKey.length !== 33) {
245
+ throw new Error("Invalid public key length");
246
+ }
247
+ const pubKeyPoint = secp256k1.secp256k1.ProjectivePoint.fromHex(publicKey);
248
+ const uncompressedPubKey = pubKeyPoint.toRawBytes(false);
249
+ const pubKeyWithoutPrefix = uncompressedPubKey.slice(1);
250
+ const hash = sha3.keccak_256(pubKeyWithoutPrefix);
251
+ const addressBytes = hash.slice(12);
252
+ const address = "0x" + Array.from(addressBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
253
+ return {
254
+ privateKey,
255
+ publicKey: uncompressedPubKey,
256
+ address,
257
+ path
258
+ };
259
+ }
260
+ function toChecksumAddress(addr) {
261
+ if (!addr.startsWith("0x")) {
262
+ throw new Error(`${ERR_INVALID_PARAM}: Address must start with 0x`);
263
+ }
264
+ const address = addr.slice(2).toLowerCase();
265
+ if (address.length !== 40) {
266
+ throw new Error(`${ERR_INVALID_PARAM}: Address must be 40 hex characters (20 bytes)`);
267
+ }
268
+ if (!/^[0-9a-f]{40}$/.test(address)) {
269
+ throw new Error(`${ERR_INVALID_PARAM}: Address contains invalid hex characters`);
270
+ }
271
+ const addressBytes = new TextEncoder().encode(address);
272
+ const hash = sha3.keccak_256(addressBytes);
273
+ let checksummed = "0x";
274
+ for (let i = 0; i < address.length; i++) {
275
+ const char = address[i];
276
+ const hashByte = Math.floor(i / 2);
277
+ const hashNibble = i % 2 === 0 ? hash[hashByte] >> 4 : hash[hashByte] & 15;
278
+ checksummed += hashNibble >= 8 ? char.toUpperCase() : char;
279
+ }
280
+ return checksummed;
281
+ }
282
+ function evmSign(privKey, msgHash32) {
283
+ if (privKey.length !== 32) {
284
+ throw new Error(`${ERR_INVALID_PARAM}: Private key must be 32 bytes, got ${privKey.length}`);
285
+ }
286
+ if (msgHash32.length !== 32) {
287
+ throw new Error(`${ERR_INVALID_PARAM}: Message hash must be 32 bytes, got ${msgHash32.length}`);
288
+ }
289
+ const signature = secp256k1.secp256k1.sign(msgHash32, privKey, {
290
+ lowS: true
291
+ // Enforce low-S canonicalization
292
+ });
293
+ const r = signature.r;
294
+ const s = signature.s;
295
+ const rBytes = new Uint8Array(32);
296
+ const sBytes = new Uint8Array(32);
297
+ const rHex = r.toString(16).padStart(64, "0");
298
+ const sHex = s.toString(16).padStart(64, "0");
299
+ for (let i = 0; i < 32; i++) {
300
+ rBytes[i] = parseInt(rHex.slice(i * 2, i * 2 + 2), 16);
301
+ sBytes[i] = parseInt(sHex.slice(i * 2, i * 2 + 2), 16);
302
+ }
303
+ const yParity = signature.recovery % 2;
304
+ return {
305
+ r: rBytes,
306
+ s: sBytes,
307
+ yParity
308
+ };
309
+ }
310
+ var Secp256k1SoftwareSigner = class {
311
+ constructor(privateKey) {
312
+ this.privateKey = privateKey;
313
+ if (privateKey.length !== 32) {
314
+ throw new Error(`${ERR_INVALID_PARAM}: Private key must be 32 bytes, got ${privateKey.length}`);
315
+ }
316
+ if (privateKey.every((b) => b === 0)) {
317
+ throw new Error(`${ERR_INVALID_PARAM}: Private key cannot be zero`);
318
+ }
319
+ }
320
+ /**
321
+ * Get uncompressed public key (65 bytes)
322
+ *
323
+ * @returns Uncompressed public key with 0x04 prefix
324
+ */
325
+ async getPublicKey() {
326
+ const pubKeyPoint = secp256k1.secp256k1.getPublicKey(this.privateKey, false);
327
+ const pubKey = new Uint8Array(65);
328
+ pubKey[0] = 4;
329
+ pubKey.set(pubKeyPoint.slice(1), 1);
330
+ return pubKey;
331
+ }
332
+ /**
333
+ * Sign message hash with deterministic, low-S canonicalization
334
+ *
335
+ * @param msgHash32 - 32-byte message hash
336
+ * @returns Signature with r, s, and yParity
337
+ * @throws Error if message hash is invalid
338
+ */
339
+ async signMessage(msgHash32) {
340
+ if (msgHash32.length !== 32) {
341
+ throw new Error(`${ERR_INVALID_PARAM}: Message hash must be 32 bytes, got ${msgHash32.length}`);
342
+ }
343
+ const signature = secp256k1.secp256k1.sign(msgHash32, this.privateKey, {
344
+ lowS: true
345
+ // Enforce low-S canonicalization
346
+ });
347
+ const r = signature.r;
348
+ const s = signature.s;
349
+ const rBytes = new Uint8Array(32);
350
+ const sBytes = new Uint8Array(32);
351
+ const rHex = r.toString(16).padStart(64, "0");
352
+ const sHex = s.toString(16).padStart(64, "0");
353
+ for (let i = 0; i < 32; i++) {
354
+ rBytes[i] = parseInt(rHex.slice(i * 2, i * 2 + 2), 16);
355
+ sBytes[i] = parseInt(sHex.slice(i * 2, i * 2 + 2), 16);
356
+ }
357
+ const yParity = signature.recovery % 2;
358
+ return {
359
+ r: rBytes,
360
+ s: sBytes,
361
+ yParity
362
+ };
363
+ }
364
+ /**
365
+ * Sign raw hash without EIP-191 prefix (for EIP-7702)
366
+ *
367
+ * @param digest - 32-byte digest to sign
368
+ * @returns Signature with r, s, and yParity
369
+ * @throws Error if digest is invalid
370
+ */
371
+ async signRawHash(digest) {
372
+ return this.signMessage(digest);
373
+ }
374
+ /**
375
+ * Sign EIP-712 typed data
376
+ */
377
+ async signTyped(data) {
378
+ const hash = ethers.TypedDataEncoder.hash(data.domain, data.types, data.message);
379
+ const msgHash = new Uint8Array(Buffer.from(hash.slice(2), "hex"));
380
+ const sig = await this.signMessage(msgHash);
381
+ const rHex = Buffer.from(sig.r).toString("hex");
382
+ const sHex = Buffer.from(sig.s).toString("hex");
383
+ const v = (sig.yParity + 27).toString(16).padStart(2, "0");
384
+ return `0x${rHex}${sHex}${v}`;
385
+ }
386
+ };
387
+ function evmVerify(pubKeyUncompressed, msgHash32, sig) {
388
+ if (pubKeyUncompressed.length !== 65 || pubKeyUncompressed[0] !== 4) {
389
+ throw new Error(`${ERR_INVALID_PARAM}: Public key must be 65 bytes uncompressed (0x04 prefix)`);
390
+ }
391
+ if (msgHash32.length !== 32) {
392
+ throw new Error(`${ERR_INVALID_PARAM}: Message hash must be 32 bytes, got ${msgHash32.length}`);
393
+ }
394
+ if (sig.r.length !== 32 || sig.s.length !== 32) {
395
+ throw new Error(`${ERR_INVALID_PARAM}: Signature r and s must be 32 bytes each`);
396
+ }
397
+ const sBigInt = BigInt("0x" + Array.from(sig.s).map((b) => b.toString(16).padStart(2, "0")).join(""));
398
+ const n = secp256k1.secp256k1.CURVE.n;
399
+ const nHalf = n / 2n;
400
+ if (sBigInt > nHalf) {
401
+ return false;
402
+ }
403
+ const x = pubKeyUncompressed.slice(1, 33);
404
+ const y = pubKeyUncompressed.slice(33, 65);
405
+ const pubKeyPoint = secp256k1.secp256k1.ProjectivePoint.fromAffine({
406
+ x: BigInt("0x" + Array.from(x).map((b) => b.toString(16).padStart(2, "0")).join("")),
407
+ y: BigInt("0x" + Array.from(y).map((b) => b.toString(16).padStart(2, "0")).join(""))
408
+ });
409
+ const compressedPubKey = pubKeyPoint.toRawBytes(true);
410
+ try {
411
+ const compactSig = new Uint8Array([...sig.r, ...sig.s]);
412
+ return secp256k1.secp256k1.verify(compactSig, msgHash32, compressedPubKey);
413
+ } catch {
414
+ return false;
415
+ }
416
+ }
417
+ var PasskeyP256Signer = class {
418
+ constructor(provider) {
419
+ this.provider = provider;
420
+ }
421
+ /**
422
+ * Get uncompressed P-256 public key (65 bytes)
423
+ *
424
+ * @returns Uncompressed public key with 0x04 prefix
425
+ */
426
+ async getPublicKey() {
427
+ const { x, y } = await this.provider.getPublicKey();
428
+ if (x.length !== 32 || y.length !== 32) {
429
+ throw new Error(`${ERR_INVALID_PARAM}: Public key coordinates must be 32 bytes each`);
430
+ }
431
+ const pubKey = new Uint8Array(65);
432
+ pubKey[0] = 4;
433
+ pubKey.set(x, 1);
434
+ pubKey.set(y, 33);
435
+ return pubKey;
436
+ }
437
+ /**
438
+ * Sign message hash using P-256 passkey
439
+ *
440
+ * @param msgHash32 - 32-byte message hash
441
+ * @returns Signature with r, s, and yParity
442
+ * @throws Error if message hash is invalid
443
+ */
444
+ async signMessage(msgHash32) {
445
+ if (msgHash32.length !== 32) {
446
+ throw new Error(`${ERR_INVALID_PARAM}: Message hash must be 32 bytes, got ${msgHash32.length}`);
447
+ }
448
+ const { r, s } = await this.provider.signP256(msgHash32);
449
+ if (r.length !== 32 || s.length !== 32) {
450
+ throw new Error(`${ERR_INVALID_PARAM}: Signature r and s must be 32 bytes each`);
451
+ }
452
+ const { y } = await this.provider.getPublicKey();
453
+ const yParity = y[y.length - 1] & 1;
454
+ return {
455
+ r,
456
+ s,
457
+ yParity
458
+ };
459
+ }
460
+ /**
461
+ * Sign raw hash without EIP-191 prefix (for EIP-7702)
462
+ *
463
+ * @param _digest - 32-byte digest to sign
464
+ * @returns Signature with r, s, and yParity
465
+ * @throws Error - P-256 cannot be used for EIP-7702
466
+ */
467
+ async signRawHash(_digest) {
468
+ throw new Error("EIP-7702 authorization requires secp256k1, not P-256. Use Secp256k1SoftwareSigner for EIP-7702.");
469
+ }
470
+ /**
471
+ * Sign EIP-712 typed data
472
+ */
473
+ async signTyped(data) {
474
+ const hash = ethers.TypedDataEncoder.hash(data.domain, data.types, data.message);
475
+ const msgHash = new Uint8Array(Buffer.from(hash.slice(2), "hex"));
476
+ const sig = await this.signMessage(msgHash);
477
+ const rHex = Buffer.from(sig.r).toString("hex");
478
+ const sHex = Buffer.from(sig.s).toString("hex");
479
+ const v = sig.yParity.toString(16).padStart(2, "0");
480
+ return `0x${rHex}${sHex}${v}`;
481
+ }
482
+ };
483
+
484
+ // src/wallet/signers/external-wallet-errors.ts
485
+ var EIP1193ErrorCode = /* @__PURE__ */ ((EIP1193ErrorCode2) => {
486
+ EIP1193ErrorCode2[EIP1193ErrorCode2["USER_REJECTED"] = 4001] = "USER_REJECTED";
487
+ EIP1193ErrorCode2[EIP1193ErrorCode2["UNAUTHORIZED"] = 4100] = "UNAUTHORIZED";
488
+ EIP1193ErrorCode2[EIP1193ErrorCode2["UNSUPPORTED_METHOD"] = 4200] = "UNSUPPORTED_METHOD";
489
+ EIP1193ErrorCode2[EIP1193ErrorCode2["DISCONNECTED"] = 4900] = "DISCONNECTED";
490
+ EIP1193ErrorCode2[EIP1193ErrorCode2["CHAIN_DISCONNECTED"] = 4901] = "CHAIN_DISCONNECTED";
491
+ EIP1193ErrorCode2[EIP1193ErrorCode2["UNRECOGNIZED_CHAIN"] = 4902] = "UNRECOGNIZED_CHAIN";
492
+ EIP1193ErrorCode2[EIP1193ErrorCode2["RESOURCE_UNAVAILABLE"] = -32002] = "RESOURCE_UNAVAILABLE";
493
+ EIP1193ErrorCode2[EIP1193ErrorCode2["RESOURCE_NOT_FOUND"] = -32001] = "RESOURCE_NOT_FOUND";
494
+ EIP1193ErrorCode2[EIP1193ErrorCode2["INVALID_INPUT"] = -32e3] = "INVALID_INPUT";
495
+ EIP1193ErrorCode2[EIP1193ErrorCode2["INTERNAL_ERROR"] = -32603] = "INTERNAL_ERROR";
496
+ EIP1193ErrorCode2[EIP1193ErrorCode2["INVALID_REQUEST"] = -32600] = "INVALID_REQUEST";
497
+ EIP1193ErrorCode2[EIP1193ErrorCode2["METHOD_NOT_FOUND"] = -32601] = "METHOD_NOT_FOUND";
498
+ EIP1193ErrorCode2[EIP1193ErrorCode2["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
499
+ EIP1193ErrorCode2[EIP1193ErrorCode2["PARSE_ERROR"] = -32700] = "PARSE_ERROR";
500
+ return EIP1193ErrorCode2;
501
+ })(EIP1193ErrorCode || {});
502
+ var WalletError = class extends Error {
503
+ constructor(message, code, originalError) {
504
+ super(message);
505
+ this.code = code;
506
+ this.originalError = originalError;
507
+ this.name = "WalletError";
508
+ }
509
+ };
510
+ var UserRejectedError = class extends WalletError {
511
+ constructor(message = "User rejected the request", originalError) {
512
+ super(message, 4001 /* USER_REJECTED */, originalError);
513
+ this.name = "UserRejectedError";
514
+ }
515
+ };
516
+ var UnauthorizedError = class extends WalletError {
517
+ constructor(message = "Unauthorized to perform this action", originalError) {
518
+ super(message, 4100 /* UNAUTHORIZED */, originalError);
519
+ this.name = "UnauthorizedError";
520
+ }
521
+ };
522
+ var UnsupportedMethodError = class extends WalletError {
523
+ constructor(message = "Method not supported by wallet", originalError) {
524
+ super(message, 4200 /* UNSUPPORTED_METHOD */, originalError);
525
+ this.name = "UnsupportedMethodError";
526
+ }
527
+ };
528
+ var WrongNetworkError = class extends WalletError {
529
+ constructor(message = "Wrong network", expectedChainId, actualChainId, originalError) {
530
+ super(message, 4902 /* UNRECOGNIZED_CHAIN */, originalError);
531
+ this.expectedChainId = expectedChainId;
532
+ this.actualChainId = actualChainId;
533
+ this.name = "WrongNetworkError";
534
+ }
535
+ };
536
+ var ChainNotAddedError = class extends WalletError {
537
+ constructor(message = "Chain not added to wallet", originalError) {
538
+ super(message, 4902 /* UNRECOGNIZED_CHAIN */, originalError);
539
+ this.name = "ChainNotAddedError";
540
+ }
541
+ };
542
+ var RequestPendingError = class extends WalletError {
543
+ constructor(message = "Request already pending in wallet", originalError) {
544
+ super(message, -32002 /* RESOURCE_UNAVAILABLE */, originalError);
545
+ this.name = "RequestPendingError";
546
+ }
547
+ };
548
+ function normalizeWalletError(error) {
549
+ if (error instanceof WalletError) {
550
+ return error;
551
+ }
552
+ if (typeof error === "object" && error !== null) {
553
+ const err = error;
554
+ const code = err.code;
555
+ const message2 = err.message || "Unknown wallet error";
556
+ switch (code) {
557
+ case 4001 /* USER_REJECTED */:
558
+ return new UserRejectedError(message2, error);
559
+ case 4100 /* UNAUTHORIZED */:
560
+ return new UnauthorizedError(message2, error);
561
+ case 4200 /* UNSUPPORTED_METHOD */:
562
+ return new UnsupportedMethodError(message2, error);
563
+ case 4902 /* UNRECOGNIZED_CHAIN */:
564
+ return new ChainNotAddedError(message2, error);
565
+ case -32002 /* RESOURCE_UNAVAILABLE */:
566
+ return new RequestPendingError(message2, error);
567
+ default:
568
+ return new WalletError(message2, code || -1, error);
569
+ }
570
+ }
571
+ const message = error instanceof Error ? error.message : String(error);
572
+ return new WalletError(message, -1, error);
573
+ }
574
+
575
+ // src/wallet/signers/external-wallet.ts
576
+ var ExternalWalletSigner = class {
577
+ constructor(provider, expectedChainId, address) {
578
+ this.provider = provider;
579
+ this.expectedChainId = expectedChainId;
580
+ this.address = address;
581
+ }
582
+ /**
583
+ * Verify that the wallet is on the correct network
584
+ * @throws WrongNetworkError if network mismatch
585
+ */
586
+ async verifyNetwork() {
587
+ try {
588
+ const chainIdHex = await this.provider.request({
589
+ method: "eth_chainId"
590
+ });
591
+ const actualChainId = parseInt(chainIdHex, 16);
592
+ if (actualChainId !== this.expectedChainId) {
593
+ throw new WrongNetworkError(
594
+ `Wrong network. Expected: ${this.expectedChainId}, Got: ${actualChainId}`,
595
+ this.expectedChainId,
596
+ actualChainId
597
+ );
598
+ }
599
+ } catch (error) {
600
+ if (error instanceof WrongNetworkError) {
601
+ throw error;
602
+ }
603
+ throw normalizeWalletError(error);
604
+ }
605
+ }
606
+ /**
607
+ * Get the wallet address
608
+ */
609
+ async getAddress() {
610
+ return this.address;
611
+ }
612
+ /**
613
+ * Get public key - not directly supported by EIP-1193
614
+ * Returns a placeholder as external wallets don't expose raw public keys
615
+ *
616
+ * For external wallets, we rely on address-based verification instead
617
+ */
618
+ async getPublicKey() {
619
+ return new Uint8Array(65);
620
+ }
621
+ /**
622
+ * Sign a message hash using personal_sign
623
+ *
624
+ * Note: personal_sign automatically adds EIP-191 prefix
625
+ *
626
+ * @param msgHash32 - 32-byte message hash
627
+ * @returns Signature components (r, s, yParity)
628
+ */
629
+ async signMessage(msgHash32) {
630
+ try {
631
+ await this.verifyNetwork();
632
+ const msgHashHex = "0x" + Array.from(msgHash32).map((b) => b.toString(16).padStart(2, "0")).join("");
633
+ const signature = await this.provider.request({
634
+ method: "personal_sign",
635
+ params: [msgHashHex, this.address]
636
+ });
637
+ const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
638
+ if (sig.length !== 130) {
639
+ throw new Error(`Invalid signature length: ${sig.length}`);
640
+ }
641
+ const r = new Uint8Array(32);
642
+ const s = new Uint8Array(32);
643
+ for (let i = 0; i < 32; i++) {
644
+ r[i] = parseInt(sig.slice(i * 2, i * 2 + 2), 16);
645
+ s[i] = parseInt(sig.slice(64 + i * 2, 64 + i * 2 + 2), 16);
646
+ }
647
+ const v = parseInt(sig.slice(128, 130), 16);
648
+ const yParity = v === 27 ? 0 : 1;
649
+ return { r, s, yParity };
650
+ } catch (error) {
651
+ throw normalizeWalletError(error);
652
+ }
653
+ }
654
+ /**
655
+ * Sign raw hash for EIP-7702 authorization
656
+ *
657
+ * External wallets don't support raw hash signing directly (eth_sign is deprecated/disabled).
658
+ * Instead, we use personal_sign which adds EIP-191 prefix, then we need to account for this
659
+ * when verifying the signature on-chain.
660
+ *
661
+ * ⚠️ IMPORTANT: This creates a signature over the EIP-191 prefixed message, NOT the raw digest.
662
+ * The backend/contract must verify accordingly.
663
+ *
664
+ * Alternative approach: Use EIP-712 typed data for authorization (more wallet-friendly)
665
+ *
666
+ * @param digest - 32-byte digest to sign
667
+ * @returns Signature components (r, s, yParity)
668
+ * @throws WalletError if signing fails
669
+ */
670
+ async signRawHash(digest) {
671
+ try {
672
+ await this.verifyNetwork();
673
+ const digestHex = "0x" + Array.from(digest).map((b) => b.toString(16).padStart(2, "0")).join("");
674
+ let signature;
675
+ try {
676
+ signature = await this.provider.request({
677
+ method: "eth_sign",
678
+ params: [this.address, digestHex]
679
+ });
680
+ } catch (ethSignError) {
681
+ if (ethSignError?.code === -32004 || ethSignError?.code === 4200) {
682
+ throw new Error(
683
+ 'eth_sign is required for EIP-7702 authorization but is disabled in your wallet. \n\nTo enable eth_sign in MetaMask:\n1. Go to Settings \u2192 Advanced\n2. Enable "Eth_sign requests"\n\nNote: eth_sign can be dangerous if used carelessly. Only enable it for trusted dApps.\n\nOriginal error: ' + (ethSignError?.message || "eth_sign not supported")
684
+ );
685
+ } else {
686
+ throw ethSignError;
687
+ }
688
+ }
689
+ const sig = signature.startsWith("0x") ? signature.slice(2) : signature;
690
+ if (sig.length !== 130) {
691
+ throw new Error(`Invalid signature length: ${sig.length}`);
692
+ }
693
+ const r = new Uint8Array(32);
694
+ const s = new Uint8Array(32);
695
+ for (let i = 0; i < 32; i++) {
696
+ r[i] = parseInt(sig.slice(i * 2, i * 2 + 2), 16);
697
+ s[i] = parseInt(sig.slice(64 + i * 2, 64 + i * 2 + 2), 16);
698
+ }
699
+ const v = parseInt(sig.slice(128, 130), 16);
700
+ const yParity = v === 27 || v === 0 ? 0 : 1;
701
+ return { r, s, yParity };
702
+ } catch (error) {
703
+ throw normalizeWalletError(error);
704
+ }
705
+ }
706
+ /**
707
+ * Sign typed data (EIP-712)
708
+ *
709
+ * @param typed - Typed data structure with domain, types, and message
710
+ * @returns Signature hex string
711
+ */
712
+ async signTyped(typed) {
713
+ try {
714
+ await this.verifyNetwork();
715
+ if (typeof typed !== "object" || typed === null) {
716
+ throw new Error("Invalid typed data: must be an object");
717
+ }
718
+ const input = typed;
719
+ if (!input.domain || !input.types || !input.message) {
720
+ throw new Error("Invalid typed data: missing domain, types, or message");
721
+ }
722
+ const signature = await this.provider.request({
723
+ method: "eth_signTypedData_v4",
724
+ params: [this.address, JSON.stringify(input)]
725
+ });
726
+ return signature;
727
+ } catch (error) {
728
+ throw normalizeWalletError(error);
729
+ }
730
+ }
731
+ /**
732
+ * Switch to the expected network
733
+ *
734
+ * @returns true if switch successful, false if user rejected
735
+ * @throws Error if switch fails for other reasons
736
+ */
737
+ async switchNetwork() {
738
+ try {
739
+ const chainIdHex = "0x" + this.expectedChainId.toString(16);
740
+ await this.provider.request({
741
+ method: "wallet_switchEthereumChain",
742
+ params: [{ chainId: chainIdHex }]
743
+ });
744
+ return true;
745
+ } catch (error) {
746
+ const normalized = normalizeWalletError(error);
747
+ if (normalized.code === 4902) {
748
+ return false;
749
+ }
750
+ if (normalized.code === 4001) {
751
+ return false;
752
+ }
753
+ throw normalized;
754
+ }
755
+ }
756
+ /**
757
+ * Add a new network to the wallet
758
+ *
759
+ * @param chainConfig - Network configuration
760
+ * @returns true if add successful, false if user rejected
761
+ */
762
+ async addNetwork(chainConfig) {
763
+ try {
764
+ await this.provider.request({
765
+ method: "wallet_addEthereumChain",
766
+ params: [chainConfig]
767
+ });
768
+ return true;
769
+ } catch (error) {
770
+ const normalized = normalizeWalletError(error);
771
+ if (normalized.code === 4001) {
772
+ return false;
773
+ }
774
+ throw normalized;
775
+ }
776
+ }
777
+ };
778
+ function blobToBase64(blob) {
779
+ return new Promise((resolve, reject) => {
780
+ if (blob instanceof Uint8Array || blob instanceof ArrayBuffer) {
781
+ const bytes = blob instanceof Uint8Array ? blob : new Uint8Array(blob);
782
+ const binary = String.fromCharCode(...bytes);
783
+ resolve(btoa(binary));
784
+ } else if (blob instanceof Blob) {
785
+ const reader = new FileReader();
786
+ reader.onloadend = () => {
787
+ const base64 = reader.result.split(",")[1] || reader.result;
788
+ resolve(base64);
789
+ };
790
+ reader.onerror = reject;
791
+ reader.readAsDataURL(blob);
792
+ } else {
793
+ reject(new Error("Unsupported blob type"));
794
+ }
795
+ });
796
+ }
797
+ async function uploadBlob(options) {
798
+ const {
799
+ baseUrl,
800
+ apiKey,
801
+ accessToken,
802
+ blob,
803
+ axiosInstance
804
+ } = options;
805
+ if (!apiKey) {
806
+ throw new Error("apiKey is required");
807
+ }
808
+ if (!accessToken) {
809
+ throw new Error("accessToken is required");
810
+ }
811
+ const blobB64 = await blobToBase64(blob);
812
+ let axiosClient;
813
+ if (axiosInstance) {
814
+ axiosClient = axiosInstance;
815
+ } else {
816
+ axiosClient = axios__default.default.create({
817
+ baseURL: baseUrl.replace(/\/+$/, ""),
818
+ withCredentials: true,
819
+ headers: {
820
+ "Content-Type": "application/json",
821
+ "X-API-Key": String(apiKey),
822
+ Authorization: `Bearer ${accessToken}`
823
+ }
824
+ });
825
+ }
826
+ const response = await axiosClient.post("/blob/upload", {
827
+ blobB64
828
+ });
829
+ if (!response.data?.ok || !response.data?.data?.key) {
830
+ const errorMessage = response.data?.error?.message || "Failed to upload blob";
831
+ throw new Error(errorMessage);
832
+ }
833
+ return { key: response.data.data.key };
834
+ }
835
+ function stripTrailingSlash(u) {
836
+ return u.endsWith("/") ? u.slice(0, -1) : u;
837
+ }
838
+ async function uploadBlobViaPresign(options) {
839
+ const {
840
+ baseUrl,
841
+ apiKey,
842
+ accessToken,
843
+ blob,
844
+ contentType = "application/octet-stream",
845
+ fetchFn
846
+ } = options;
847
+ const f = fetchFn ?? globalThis.fetch;
848
+ if (typeof f !== "function") {
849
+ throw new Error("Global fetch is not available; provide options.fetchFn");
850
+ }
851
+ const base = stripTrailingSlash(baseUrl);
852
+ const presignRes = await f(`${base}/blob/presign`, {
853
+ method: "POST",
854
+ headers: {
855
+ "Content-Type": "application/json",
856
+ ...apiKey ? { "X-API-Key": String(apiKey) } : {},
857
+ ...accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
858
+ },
859
+ // backend expects { op: 'put', contentType }
860
+ body: JSON.stringify({ op: "put", contentType }),
861
+ // include cookies if backend also uses cookie-based sessions
862
+ credentials: "include"
863
+ });
864
+ const presignJson = await presignRes.json();
865
+ if (!presignRes.ok || presignJson && presignJson.ok === false) {
866
+ const msg = presignJson && presignJson.error && presignJson.error.message || `presign failed with status ${presignRes.status}`;
867
+ throw new Error(typeof msg === "string" ? msg : "Presign failed");
868
+ }
869
+ const url = presignJson?.data?.url ?? presignJson?.url;
870
+ const s3Key = presignJson?.data?.proposedKey ?? presignJson?.s3Key;
871
+ if (!url || !s3Key) {
872
+ throw new Error("Invalid presign response: missing url or s3Key");
873
+ }
874
+ const putHeaders = {
875
+ "Content-Type": contentType,
876
+ "x-amz-server-side-encryption": "AES256"
877
+ };
878
+ const body = typeof Blob !== "undefined" && blob instanceof Blob ? blob : blob;
879
+ const putRes = await f(url, {
880
+ method: "PUT",
881
+ headers: putHeaders,
882
+ body,
883
+ // allow browser CORS preflight to succeed
884
+ // (ignored in Node >= 18 where fetch is not CORS-restricted)
885
+ mode: "cors"
886
+ });
887
+ if (!putRes.ok) {
888
+ const text = await putRes.text().catch(() => "");
889
+ throw new Error(`S3 upload failed (${putRes.status}): ${text || putRes.statusText}`);
890
+ }
891
+ return { s3Key, url };
892
+ }
893
+
894
+ // src/chains/evm/constants.ts
895
+ var ZERO_HASH = "0x0000000000000000000000000000000000000000000000000000000000000000";
896
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
897
+ function computeCallsHash(calls) {
898
+ const coder = ethers.AbiCoder.defaultAbiCoder();
899
+ const tupleType = "tuple(address,uint256,bytes,uint256)[]";
900
+ const values = calls.map((c) => [c.target, c.value, c.data, c.gasLimit]);
901
+ const encoded = coder.encode([tupleType], [values]);
902
+ return ethers.keccak256(encoded);
903
+ }
904
+
905
+ // src/chains/evm/eip712/session.ts
906
+ var DOMAIN = (chainId, verifyingContract) => ({
907
+ name: "volr",
908
+ version: "1",
909
+ chainId,
910
+ verifyingContract
911
+ });
912
+ var EIP712_DOMAIN = DOMAIN;
913
+ var TYPES = {
914
+ Call: [
915
+ { name: "target", type: "address" },
916
+ { name: "data", type: "bytes" },
917
+ { name: "value", type: "uint256" },
918
+ { name: "gasLimit", type: "uint256" }
919
+ ],
920
+ SessionAuth: [
921
+ { name: "chainId", type: "uint256" },
922
+ { name: "sessionKey", type: "address" },
923
+ { name: "sessionId", type: "uint64" },
924
+ { name: "nonce", type: "uint64" },
925
+ { name: "expiresAt", type: "uint64" },
926
+ { name: "policyId", type: "bytes32" },
927
+ { name: "policySnapshotHash", type: "bytes32" },
928
+ { name: "gasLimitMax", type: "uint256" },
929
+ { name: "maxFeePerGas", type: "uint256" },
930
+ { name: "maxPriorityFeePerGas", type: "uint256" },
931
+ { name: "totalGasCap", type: "uint256" }
932
+ ],
933
+ SignedBatch: [
934
+ { name: "auth", type: "SessionAuth" },
935
+ { name: "calls", type: "Call[]" },
936
+ { name: "revertOnFail", type: "bool" },
937
+ { name: "callsHash", type: "bytes32" }
938
+ ]
939
+ };
940
+ function buildSignedBatchMessage(input) {
941
+ const callsHash = computeCallsHash(input.calls);
942
+ const domain = DOMAIN(Number(input.auth.chainId), input.invokerAddress);
943
+ const ensureHex = (val) => val.startsWith("0x") ? val : `0x${val}`;
944
+ const eip712Auth = {
945
+ chainId: BigInt(input.auth.chainId),
946
+ sessionKey: ensureHex(input.auth.sessionKey),
947
+ sessionId: input.auth.sessionId,
948
+ nonce: input.auth.nonce,
949
+ expiresAt: BigInt(input.auth.expiresAt),
950
+ policyId: ensureHex(input.auth.policyId),
951
+ policySnapshotHash: ensureHex(input.auth.policySnapshotHash),
952
+ gasLimitMax: input.auth.gasLimitMax,
953
+ maxFeePerGas: input.auth.maxFeePerGas,
954
+ maxPriorityFeePerGas: input.auth.maxPriorityFeePerGas,
955
+ totalGasCap: input.auth.totalGasCap
956
+ };
957
+ const eip712Calls = input.calls.map((call) => ({
958
+ target: ensureHex(call.target),
959
+ data: ensureHex(call.data),
960
+ value: call.value,
961
+ gasLimit: call.gasLimit
962
+ }));
963
+ return {
964
+ domain,
965
+ primaryType: "SignedBatch",
966
+ message: {
967
+ auth: eip712Auth,
968
+ calls: eip712Calls,
969
+ revertOnFail: input.revertOnFail ?? true,
970
+ callsHash
971
+ },
972
+ callsHash
973
+ };
974
+ }
975
+ async function signSession(input) {
976
+ if (input.from.toLowerCase() !== input.auth.sessionKey.toLowerCase()) {
977
+ throw new Error(
978
+ `from address (${input.from}) does not match sessionKey (${input.auth.sessionKey})`
979
+ );
980
+ }
981
+ const message = buildSignedBatchMessage({
982
+ auth: input.auth,
983
+ calls: input.calls,
984
+ invokerAddress: input.invokerAddress
985
+ });
986
+ if (!input.signer.signTyped) {
987
+ throw new Error("Signer must support signTyped for EIP-712 signing");
988
+ }
989
+ const typedData = {
990
+ domain: message.domain,
991
+ types: TYPES,
992
+ primaryType: message.primaryType,
993
+ message: message.message
994
+ };
995
+ const sessionSig = await input.signer.signTyped(typedData);
996
+ return {
997
+ sessionSig,
998
+ callsHash: message.callsHash
999
+ };
1000
+ }
1001
+ async function signAuthorization(input) {
1002
+ const { signer, chainId, address, nonce } = input;
1003
+ if (chainId === 0) {
1004
+ throw new Error(`${ERR_INVALID_PARAM}: chainId cannot be 0 (use specific chain ID)`);
1005
+ }
1006
+ if (nonce < 0n) {
1007
+ throw new Error(`${ERR_INVALID_PARAM}: nonce must be >= 0`);
1008
+ }
1009
+ if (!address.startsWith("0x") || address.length !== 42) {
1010
+ throw new Error(`${ERR_INVALID_PARAM}: address must be valid Ethereum address`);
1011
+ }
1012
+ const digest = ethers.hashAuthorization({
1013
+ chainId: BigInt(chainId),
1014
+ address: address.toLowerCase(),
1015
+ nonce
1016
+ });
1017
+ const digestBytes = ethers.getBytes(digest);
1018
+ let sig = await signer.signRawHash(digestBytes);
1019
+ const sBigInt = BigInt("0x" + Array.from(sig.s).map((b) => b.toString(16).padStart(2, "0")).join(""));
1020
+ const SECP256K1_N = secp256k1.secp256k1.CURVE.n;
1021
+ const nHalf = SECP256K1_N / 2n;
1022
+ let finalS = sig.s;
1023
+ let finalYParity = sig.yParity;
1024
+ if (sBigInt > nHalf) {
1025
+ const normalizedS = SECP256K1_N - sBigInt;
1026
+ const normalizedSHex = normalizedS.toString(16).padStart(64, "0");
1027
+ finalS = new Uint8Array(32);
1028
+ for (let i = 0; i < 32; i++) {
1029
+ finalS[i] = parseInt(normalizedSHex.slice(i * 2, i * 2 + 2), 16);
1030
+ }
1031
+ finalYParity = finalYParity === 0 ? 1 : 0;
1032
+ }
1033
+ const rHex = Array.from(sig.r).map((b) => b.toString(16).padStart(2, "0")).join("");
1034
+ const sHex = Array.from(finalS).map((b) => b.toString(16).padStart(2, "0")).join("");
1035
+ return {
1036
+ chainId,
1037
+ address: address.toLowerCase(),
1038
+ nonce,
1039
+ yParity: finalYParity,
1040
+ r: `0x${rHex}`,
1041
+ s: `0x${sHex}`
1042
+ };
1043
+ }
1044
+
1045
+ // src/chains/evm/nonce.ts
1046
+ async function getAuthNonce(client, from, mode) {
1047
+ if (!client.getTransactionCount) {
1048
+ throw new Error("RPC client must implement getTransactionCount method. Use ExtendedRPCClient.");
1049
+ }
1050
+ const nonce = await client.getTransactionCount(from, "latest");
1051
+ if (mode === "self") {
1052
+ return nonce + 1n;
1053
+ } else {
1054
+ return nonce;
1055
+ }
1056
+ }
1057
+ function createPasskeyProvider(passkey, options) {
1058
+ let unwrappedKeypair = null;
1059
+ const ensureSession = async (opts) => {
1060
+ if (opts?.force && unwrappedKeypair) {
1061
+ console.log("[PasskeyProvider] force=true: zeroizing existing session");
1062
+ zeroize(unwrappedKeypair.privateKey);
1063
+ zeroize(unwrappedKeypair.publicKey);
1064
+ unwrappedKeypair = null;
1065
+ }
1066
+ if (unwrappedKeypair) {
1067
+ return;
1068
+ }
1069
+ if (opts?.interactive || opts?.force) {
1070
+ console.log("[PasskeyProvider] interactive mode: triggering WebAuthn prompt");
1071
+ const prfSalt = deriveWrapKey(options.prfInput);
1072
+ const { prfOutput } = await passkey.authenticate({
1073
+ salt: prfSalt,
1074
+ credentialId: options.prfInput.credentialId
1075
+ });
1076
+ const wrapKey = prfOutput;
1077
+ const aad = options.aad || new TextEncoder().encode("volr/master-seed/v1");
1078
+ const masterSeed = await unsealMasterSeed(
1079
+ options.encryptedBlob.cipher,
1080
+ wrapKey,
1081
+ aad,
1082
+ options.encryptedBlob.nonce
1083
+ );
1084
+ const keypair = deriveEvmKey({ masterSeed });
1085
+ unwrappedKeypair = {
1086
+ privateKey: keypair.privateKey,
1087
+ publicKey: keypair.publicKey,
1088
+ address: keypair.address
1089
+ };
1090
+ zeroize(masterSeed);
1091
+ zeroize(wrapKey);
1092
+ zeroize(prfSalt);
1093
+ } else {
1094
+ console.warn("[PasskeyProvider] Non-interactive mode: using deterministic wrap key (insecure for TTL=0)");
1095
+ const wrapKey = deriveWrapKey(options.prfInput);
1096
+ const aad = options.aad || new TextEncoder().encode("volr/master-seed/v1");
1097
+ const masterSeed = await unsealMasterSeed(
1098
+ options.encryptedBlob.cipher,
1099
+ wrapKey,
1100
+ aad,
1101
+ options.encryptedBlob.nonce
1102
+ );
1103
+ const keypair = deriveEvmKey({ masterSeed });
1104
+ unwrappedKeypair = {
1105
+ privateKey: keypair.privateKey,
1106
+ publicKey: keypair.publicKey,
1107
+ address: keypair.address
1108
+ };
1109
+ zeroize(masterSeed);
1110
+ zeroize(wrapKey);
1111
+ }
1112
+ };
1113
+ const getAddress = async () => {
1114
+ await ensureSession();
1115
+ if (!unwrappedKeypair) {
1116
+ throw new Error(`${ERR_INVALID_PARAM}: Session not established`);
1117
+ }
1118
+ return toChecksumAddress(unwrappedKeypair.address);
1119
+ };
1120
+ const signMessage = async (hash32) => {
1121
+ if (hash32.length !== 32) {
1122
+ throw new Error(`${ERR_INVALID_PARAM}: Message hash must be 32 bytes, got ${hash32.length}`);
1123
+ }
1124
+ await ensureSession();
1125
+ if (!unwrappedKeypair) {
1126
+ throw new Error(`${ERR_INVALID_PARAM}: Session not established`);
1127
+ }
1128
+ const sig = evmSign(unwrappedKeypair.privateKey, hash32);
1129
+ return {
1130
+ r: sig.r,
1131
+ s: sig.s,
1132
+ yParity: sig.yParity
1133
+ };
1134
+ };
1135
+ const signTypedData = async (input) => {
1136
+ await ensureSession();
1137
+ if (!unwrappedKeypair) {
1138
+ throw new Error(`${ERR_INVALID_PARAM}: Session not established`);
1139
+ }
1140
+ console.log("[PasskeyProvider] signTypedData input:", JSON.stringify(
1141
+ input,
1142
+ (_key, value) => typeof value === "bigint" ? value.toString() : value
1143
+ ));
1144
+ const hash = ethers.TypedDataEncoder.hash(input.domain, input.types, input.message);
1145
+ const msgHash = hash;
1146
+ console.log("[PasskeyProvider] msgHash:", msgHash);
1147
+ const msgHashBytes = new Uint8Array(32);
1148
+ const hex = msgHash.startsWith("0x") ? msgHash.slice(2) : msgHash;
1149
+ for (let i = 0; i < 32; i++) {
1150
+ msgHashBytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
1151
+ }
1152
+ const sig = await signMessage(msgHashBytes);
1153
+ const v = sig.yParity + 27;
1154
+ const rHex = Array.from(sig.r).map((b) => b.toString(16).padStart(2, "0")).join("");
1155
+ const sHex = Array.from(sig.s).map((b) => b.toString(16).padStart(2, "0")).join("");
1156
+ const vHex = v.toString(16).padStart(2, "0");
1157
+ return `0x${rHex}${sHex}${vHex}`;
1158
+ };
1159
+ const getPublicKey = async () => {
1160
+ await ensureSession();
1161
+ if (!unwrappedKeypair) {
1162
+ throw new Error(`${ERR_INVALID_PARAM}: Session not established`);
1163
+ }
1164
+ return new Uint8Array(unwrappedKeypair.publicKey);
1165
+ };
1166
+ const lock = async () => {
1167
+ if (unwrappedKeypair) {
1168
+ console.log("[PasskeyProvider] lock(): zeroizing sensitive data from memory");
1169
+ zeroize(unwrappedKeypair.privateKey);
1170
+ zeroize(unwrappedKeypair.publicKey);
1171
+ unwrappedKeypair = null;
1172
+ }
1173
+ };
1174
+ return {
1175
+ keyStorageType: "passkey",
1176
+ ensureSession,
1177
+ getAddress,
1178
+ signMessage,
1179
+ signTypedData,
1180
+ getPublicKey,
1181
+ lock,
1182
+ capabilities: {
1183
+ secp256k1: true,
1184
+ p256: false
1185
+ // Passkey provider uses secp256k1 after unwrapping
1186
+ }
1187
+ };
1188
+ }
1189
+
1190
+ // src/chains/evm/providers/mpcProvider.ts
1191
+ function createMpcProvider(transport) {
1192
+ return {
1193
+ keyStorageType: "mpc",
1194
+ ensureSession: () => transport.ensureSession(),
1195
+ getAddress: () => transport.getAddress(),
1196
+ signMessage: (hash32) => transport.signMessage(hash32),
1197
+ signTypedData: (input) => transport.signTypedData(input),
1198
+ getPublicKey: transport.getPublicKey ? () => transport.getPublicKey() : void 0,
1199
+ capabilities: {
1200
+ secp256k1: true,
1201
+ p256: false
1202
+ // MPC uses secp256k1 via backend
1203
+ }
1204
+ };
1205
+ }
1206
+
1207
+ // src/chains/evm/signers/selectSigner.ts
1208
+ async function selectSigner(ctx) {
1209
+ const { provider } = ctx;
1210
+ if (provider.getPublicKey) {
1211
+ return new ProviderBackedSigner(provider);
1212
+ }
1213
+ throw new Error(
1214
+ `${ERR_INVALID_PARAM}: Provider must expose getPublicKey() for signer creation. Use provider.signMessage() directly or implement getPublicKey() in provider.`
1215
+ );
1216
+ }
1217
+ var ProviderBackedSigner = class {
1218
+ constructor(provider) {
1219
+ this.provider = provider;
1220
+ if (!provider.getPublicKey) {
1221
+ throw new Error(`${ERR_INVALID_PARAM}: Provider must implement getPublicKey()`);
1222
+ }
1223
+ }
1224
+ async getPublicKey() {
1225
+ return this.provider.getPublicKey();
1226
+ }
1227
+ async signMessage(msgHash32) {
1228
+ return this.provider.signMessage(msgHash32);
1229
+ }
1230
+ async signRawHash(digest) {
1231
+ return this.provider.signMessage(digest);
1232
+ }
1233
+ async signTyped(typed) {
1234
+ if (typeof typed === "object" && typed !== null) {
1235
+ const input = typed;
1236
+ return this.provider.signTypedData(input);
1237
+ }
1238
+ throw new Error(`${ERR_INVALID_PARAM}: Invalid typed data input`);
1239
+ }
1240
+ };
1241
+
1242
+ // src/wallet/signer.ts
1243
+ var ProviderBackedSigner2 = class {
1244
+ constructor(provider) {
1245
+ this.provider = provider;
1246
+ if (!provider.getPublicKey) {
1247
+ throw new Error(`${ERR_INVALID_PARAM}: Provider must implement getPublicKey()`);
1248
+ }
1249
+ }
1250
+ async getPublicKey() {
1251
+ return this.provider.getPublicKey();
1252
+ }
1253
+ async signMessage(msgHash32) {
1254
+ return this.provider.signMessage(msgHash32);
1255
+ }
1256
+ async signRawHash(_digest) {
1257
+ throw new Error("EIP-7702 raw hash signing not supported by wallet provider. Use Secp256k1SoftwareSigner for EIP-7702.");
1258
+ }
1259
+ async signTyped(typed) {
1260
+ if (typeof typed === "object" && typed !== null) {
1261
+ const input = typed;
1262
+ return this.provider.signTypedData(input);
1263
+ }
1264
+ throw new Error(`${ERR_INVALID_PARAM}: Invalid typed data input`);
1265
+ }
1266
+ };
1267
+ async function selectSigner2(ctx) {
1268
+ const { provider, secpKey } = ctx;
1269
+ if (provider) {
1270
+ if (!provider.getPublicKey) {
1271
+ throw new Error(
1272
+ `${ERR_INVALID_PARAM}: Provider must implement getPublicKey() for signer creation. Use provider.signMessage() directly or implement getPublicKey() in provider.`
1273
+ );
1274
+ }
1275
+ return {
1276
+ kind: "secp256k1",
1277
+ // Provider signs with secp256k1
1278
+ signer: new ProviderBackedSigner2(provider)
1279
+ };
1280
+ }
1281
+ if (secpKey) {
1282
+ return {
1283
+ kind: "secp256k1",
1284
+ signer: new Secp256k1SoftwareSigner(secpKey)
1285
+ };
1286
+ }
1287
+ throw new Error(
1288
+ `${ERR_INVALID_PARAM}: No signer input available. Provide either provider, passkey (for P-256), or secpKey (for secp256k1).`
1289
+ );
1290
+ }
1291
+
1292
+ exports.ChainNotAddedError = ChainNotAddedError;
1293
+ exports.DEFAULT_EVM_PATH = DEFAULT_EVM_PATH;
1294
+ exports.DOMAIN = DOMAIN;
1295
+ exports.EIP1193ErrorCode = EIP1193ErrorCode;
1296
+ exports.EIP712_DOMAIN = EIP712_DOMAIN;
1297
+ exports.ERR_AAD_MISMATCH = ERR_AAD_MISMATCH;
1298
+ exports.ERR_CHAIN_MISMATCH = ERR_CHAIN_MISMATCH;
1299
+ exports.ERR_CRYPTO_FAIL = ERR_CRYPTO_FAIL;
1300
+ exports.ERR_INVALID_PARAM = ERR_INVALID_PARAM;
1301
+ exports.ERR_LOW_ENTROPY = ERR_LOW_ENTROPY;
1302
+ exports.ERR_NONCE_SIZE = ERR_NONCE_SIZE;
1303
+ exports.ExternalWalletSigner = ExternalWalletSigner;
1304
+ exports.PasskeyP256Signer = PasskeyP256Signer;
1305
+ exports.RequestPendingError = RequestPendingError;
1306
+ exports.Secp256k1SoftwareSigner = Secp256k1SoftwareSigner;
1307
+ exports.TYPES = TYPES;
1308
+ exports.UnauthorizedError = UnauthorizedError;
1309
+ exports.UnsupportedMethodError = UnsupportedMethodError;
1310
+ exports.UserRejectedError = UserRejectedError;
1311
+ exports.VolrError = VolrError;
1312
+ exports.WalletError = WalletError;
1313
+ exports.WrongNetworkError = WrongNetworkError;
1314
+ exports.ZERO_ADDRESS = ZERO_ADDRESS;
1315
+ exports.ZERO_HASH = ZERO_HASH;
1316
+ exports.aesGcmDecrypt = aesGcmDecrypt;
1317
+ exports.aesGcmEncrypt = aesGcmEncrypt;
1318
+ exports.buildSignedBatchMessage = buildSignedBatchMessage;
1319
+ exports.computeCallsHash = computeCallsHash;
1320
+ exports.createMasterKeyProvider = createMasterKeyProvider;
1321
+ exports.createMpcProvider = createMpcProvider;
1322
+ exports.createPasskeyProvider = createPasskeyProvider;
1323
+ exports.deriveEvmKey = deriveEvmKey;
1324
+ exports.deriveWrapKey = deriveWrapKey;
1325
+ exports.evmSign = evmSign;
1326
+ exports.evmVerify = evmVerify;
1327
+ exports.getAuthNonce = getAuthNonce;
1328
+ exports.getRandomBytes = getRandomBytes;
1329
+ exports.hkdfSha256 = hkdfSha256;
1330
+ exports.normalizeWalletError = normalizeWalletError;
1331
+ exports.sealMasterSeed = sealMasterSeed;
1332
+ exports.selectSigner = selectSigner;
1333
+ exports.selectSignerLegacy = selectSigner2;
1334
+ exports.signAuthorization = signAuthorization;
1335
+ exports.signSession = signSession;
1336
+ exports.toChecksumAddress = toChecksumAddress;
1337
+ exports.unsealMasterSeed = unsealMasterSeed;
1338
+ exports.uploadBlob = uploadBlob;
1339
+ exports.uploadBlobViaPresign = uploadBlobViaPresign;
1340
+ exports.zeroize = zeroize;
1341
+ //# sourceMappingURL=index.cjs.map
1342
+ //# sourceMappingURL=index.cjs.map