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