@veridex/sdk 1.0.0-beta.18 → 1.0.0-beta.21

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.
@@ -0,0 +1,1553 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/auth/prepareAuth.ts
31
+ var prepareAuth_exports = {};
32
+ __export(prepareAuth_exports, {
33
+ authenticateAndPrepare: () => authenticateAndPrepare
34
+ });
35
+ module.exports = __toCommonJS(prepareAuth_exports);
36
+ var import_ethers5 = require("ethers");
37
+
38
+ // src/core/PasskeyManager.ts
39
+ var import_browser = require("@simplewebauthn/browser");
40
+ var import_ethers2 = require("ethers");
41
+
42
+ // src/utils.ts
43
+ var import_ethers = require("ethers");
44
+
45
+ // src/constants.ts
46
+ var ACTION_TRANSFER = 1;
47
+ var ACTION_EXECUTE = 2;
48
+ var ACTION_BRIDGE = 4;
49
+ var TESTNET_CHAINS = {
50
+ baseSepolia: {
51
+ name: "Base Sepolia",
52
+ chainId: 84532,
53
+ wormholeChainId: 10004,
54
+ rpcUrl: "https://sepolia.base.org",
55
+ // Public CORS-friendly RPC
56
+ explorerUrl: "https://sepolia.basescan.org",
57
+ isEvm: true,
58
+ contracts: {
59
+ hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
60
+ vaultFactory: "0xCFaEb5652aa2Ee60b2229dC8895B4159749C7e53",
61
+ vaultImplementation: "0x0d13367C16c6f0B24eD275CC67C7D9f42878285c",
62
+ wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
63
+ tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
64
+ }
65
+ },
66
+ ethereumSepolia: {
67
+ name: "Ethereum Sepolia",
68
+ chainId: 11155111,
69
+ wormholeChainId: 10002,
70
+ rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
71
+ explorerUrl: "https://sepolia.etherscan.io",
72
+ isEvm: true,
73
+ contracts: {
74
+ vaultFactory: "0x07F608AFf6d63b68029488b726d895c4Bb593038",
75
+ vaultImplementation: "0xD66153fccFB6731fB6c4944FbD607ba86A76a1f6",
76
+ wormholeCoreBridge: "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78",
77
+ tokenBridge: "0xDB5492265f6038831E89f495670FF909aDe94bd9"
78
+ }
79
+ },
80
+ optimismSepolia: {
81
+ name: "Optimism Sepolia",
82
+ chainId: 11155420,
83
+ wormholeChainId: 10005,
84
+ rpcUrl: "https://sepolia.optimism.io",
85
+ explorerUrl: "https://sepolia-optimism.etherscan.io",
86
+ isEvm: true,
87
+ contracts: {
88
+ vaultFactory: "0xA5653d54079ABeCe780F8d9597B2bc4B09fe464A",
89
+ vaultImplementation: "0x8099b1406485d2255ff89Ce5Ea18520802AFC150",
90
+ wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
91
+ tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
92
+ }
93
+ },
94
+ arbitrumSepolia: {
95
+ name: "Arbitrum Sepolia",
96
+ chainId: 421614,
97
+ wormholeChainId: 10003,
98
+ rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
99
+ explorerUrl: "https://sepolia.arbiscan.io",
100
+ isEvm: true,
101
+ contracts: {
102
+ vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
103
+ vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
104
+ wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
105
+ tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
106
+ }
107
+ },
108
+ seiTestnet: {
109
+ name: "Sei Atlantic-2",
110
+ chainId: 1328,
111
+ wormholeChainId: 40,
112
+ rpcUrl: "https://evm-rpc-testnet.sei-apis.com",
113
+ explorerUrl: "https://seitrace.com/?chain=atlantic-2",
114
+ isEvm: true,
115
+ contracts: {
116
+ vaultFactory: "0x07F608AFf6d63b68029488b726d895c4Bb593038",
117
+ vaultImplementation: "0xD66153fccFB6731fB6c4944FbD607ba86A76a1f6",
118
+ wormholeCoreBridge: "0x0000000000000000000000000000000000000000"
119
+ // Mock - not yet deployed
120
+ }
121
+ },
122
+ solanaDevnet: {
123
+ name: "Solana Devnet",
124
+ chainId: 0,
125
+ wormholeChainId: 1,
126
+ rpcUrl: "https://api.devnet.solana.com",
127
+ explorerUrl: "https://explorer.solana.com",
128
+ isEvm: false,
129
+ contracts: {
130
+ hub: "AnyXHsqq9c2BiW4WgBcj6Aye7Ua7a7L7iSuwpfJxECJM",
131
+ wormholeCoreBridge: "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5",
132
+ tokenBridge: "DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe"
133
+ }
134
+ },
135
+ aptosTestnet: {
136
+ name: "Aptos Testnet",
137
+ chainId: 0,
138
+ wormholeChainId: 22,
139
+ rpcUrl: "https://fullnode.testnet.aptoslabs.com/v1",
140
+ explorerUrl: "https://explorer.aptoslabs.com",
141
+ isEvm: false,
142
+ contracts: {
143
+ hub: "0x0237e04f74b991b5b6030a793779663033f4ff4a1682a9e66c1f41fc1ec3e2a4",
144
+ wormholeCoreBridge: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
145
+ tokenBridge: "0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f"
146
+ }
147
+ },
148
+ suiTestnet: {
149
+ name: "Sui Testnet",
150
+ chainId: 0,
151
+ wormholeChainId: 21,
152
+ rpcUrl: "https://fullnode.testnet.sui.io:443",
153
+ explorerUrl: "https://suiscan.xyz/testnet",
154
+ isEvm: false,
155
+ contracts: {
156
+ hub: "0x35e99fdbbc1cde7e093da6f9e758ba2c4a077904bd64caee2fa6db5e6c4e9e37",
157
+ wormholeCoreBridge: "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790"
158
+ }
159
+ },
160
+ starknetSepolia: {
161
+ name: "Starknet Sepolia",
162
+ chainId: 0,
163
+ // Native Starknet chain ID (SN_SEPOLIA = 0x534e5f5345504f4c4941)
164
+ wormholeChainId: 50001,
165
+ // Custom chain ID (50000+ reserved for non-Wormhole chains)
166
+ rpcUrl: "https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/tsOnfTBZDKMXcUA26OED-",
167
+ explorerUrl: "https://sepolia.starkscan.co",
168
+ isEvm: false,
169
+ contracts: {
170
+ // Starknet spoke contract
171
+ hub: "0x68adcc730ed6c355200d00f763825448497b9cdf7936ca121711e078c88e811",
172
+ // Custom bridge contract (NOT Wormhole)
173
+ wormholeCoreBridge: "0x2c458c1ae64556482b05cc2d3ee5b032ed114d68429dda2062c9849a5a725f8"
174
+ },
175
+ // Hub chain ID that Starknet bridge validates (Base Sepolia = 10004)
176
+ hubChainId: 10004
177
+ }
178
+ };
179
+ var MAINNET_CHAINS = {
180
+ ethereum: {
181
+ name: "Ethereum",
182
+ chainId: 1,
183
+ wormholeChainId: 2,
184
+ rpcUrl: "https://eth.llamarpc.com",
185
+ explorerUrl: "https://etherscan.io",
186
+ isEvm: true,
187
+ contracts: {
188
+ wormholeCoreBridge: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
189
+ tokenBridge: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
190
+ }
191
+ },
192
+ base: {
193
+ name: "Base",
194
+ chainId: 8453,
195
+ wormholeChainId: 30,
196
+ rpcUrl: "https://mainnet.base.org",
197
+ explorerUrl: "https://basescan.org",
198
+ isEvm: true,
199
+ contracts: {
200
+ wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
201
+ tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
202
+ }
203
+ },
204
+ optimism: {
205
+ name: "Optimism",
206
+ chainId: 10,
207
+ wormholeChainId: 24,
208
+ rpcUrl: "https://mainnet.optimism.io",
209
+ explorerUrl: "https://optimistic.etherscan.io",
210
+ isEvm: true,
211
+ contracts: {
212
+ wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
213
+ tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
214
+ }
215
+ },
216
+ arbitrum: {
217
+ name: "Arbitrum",
218
+ chainId: 42161,
219
+ wormholeChainId: 23,
220
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
221
+ explorerUrl: "https://arbiscan.io",
222
+ isEvm: true,
223
+ contracts: {
224
+ wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
225
+ tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
226
+ }
227
+ },
228
+ polygon: {
229
+ name: "Polygon",
230
+ chainId: 137,
231
+ wormholeChainId: 5,
232
+ rpcUrl: "https://polygon-rpc.com",
233
+ explorerUrl: "https://polygonscan.com",
234
+ isEvm: true,
235
+ contracts: {
236
+ wormholeCoreBridge: "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7",
237
+ tokenBridge: "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
238
+ }
239
+ },
240
+ solana: {
241
+ name: "Solana",
242
+ chainId: 0,
243
+ wormholeChainId: 1,
244
+ rpcUrl: "https://api.mainnet-beta.solana.com",
245
+ explorerUrl: "https://explorer.solana.com",
246
+ isEvm: false,
247
+ contracts: {
248
+ wormholeCoreBridge: "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth",
249
+ tokenBridge: "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
250
+ }
251
+ },
252
+ aptos: {
253
+ name: "Aptos",
254
+ chainId: 0,
255
+ wormholeChainId: 22,
256
+ rpcUrl: "https://fullnode.mainnet.aptoslabs.com/v1",
257
+ explorerUrl: "https://explorer.aptoslabs.com",
258
+ isEvm: false,
259
+ contracts: {
260
+ wormholeCoreBridge: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
261
+ tokenBridge: "0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f"
262
+ }
263
+ },
264
+ sui: {
265
+ name: "Sui",
266
+ chainId: 0,
267
+ wormholeChainId: 21,
268
+ rpcUrl: "https://fullnode.mainnet.sui.io:443",
269
+ explorerUrl: "https://suiscan.xyz/mainnet",
270
+ isEvm: false,
271
+ contracts: {
272
+ wormholeCoreBridge: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c"
273
+ }
274
+ }
275
+ };
276
+
277
+ // src/utils.ts
278
+ function base64URLEncode(buffer) {
279
+ const bytes = Array.from(buffer);
280
+ const binary = String.fromCharCode(...bytes);
281
+ const base64 = btoa(binary);
282
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
283
+ }
284
+ function base64URLDecode(str) {
285
+ const base64 = str.replace(/-/g, "+").replace(/_/g, "/");
286
+ const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
287
+ const binary = atob(padded);
288
+ const bytes = new Uint8Array(binary.length);
289
+ for (let i = 0; i < binary.length; i++) {
290
+ bytes[i] = binary.charCodeAt(i);
291
+ }
292
+ return bytes;
293
+ }
294
+ function parseDERSignature(signature) {
295
+ let offset = 0;
296
+ if (signature[offset++] !== 48) {
297
+ throw new Error("Invalid signature format");
298
+ }
299
+ offset++;
300
+ if (signature[offset++] !== 2) {
301
+ throw new Error("Invalid signature format");
302
+ }
303
+ const rLength = signature[offset++];
304
+ if (rLength === void 0) {
305
+ throw new Error("Invalid signature format: missing r length");
306
+ }
307
+ let r = signature.slice(offset, offset + rLength);
308
+ offset += rLength;
309
+ if (r[0] === 0 && r.length > 32) {
310
+ r = r.slice(1);
311
+ }
312
+ if (r.length < 32) {
313
+ const padded = new Uint8Array(32);
314
+ padded.set(r, 32 - r.length);
315
+ r = padded;
316
+ }
317
+ if (signature[offset++] !== 2) {
318
+ throw new Error("Invalid signature format");
319
+ }
320
+ const sLength = signature[offset++];
321
+ if (sLength === void 0) {
322
+ throw new Error("Invalid signature format: missing s length");
323
+ }
324
+ let s = signature.slice(offset, offset + sLength);
325
+ if (s[0] === 0 && s.length > 32) {
326
+ s = s.slice(1);
327
+ }
328
+ if (s.length < 32) {
329
+ const padded = new Uint8Array(32);
330
+ padded.set(s, 32 - s.length);
331
+ s = padded;
332
+ }
333
+ return { r, s };
334
+ }
335
+ function computeKeyHash(publicKeyX, publicKeyY) {
336
+ return import_ethers.ethers.keccak256(
337
+ import_ethers.ethers.solidityPacked(["uint256", "uint256"], [publicKeyX, publicKeyY])
338
+ );
339
+ }
340
+
341
+ // src/core/PasskeyManager.ts
342
+ var VERIDEX_RP_ID = "veridex.network";
343
+ function detectRpId(forceLocal) {
344
+ if (typeof window === "undefined") return "localhost";
345
+ const hostname = window.location.hostname;
346
+ if (hostname === "localhost" || hostname === "127.0.0.1" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) {
347
+ return hostname;
348
+ }
349
+ if (forceLocal) {
350
+ const parts = hostname.split(".");
351
+ if (parts.length <= 2) {
352
+ return hostname;
353
+ }
354
+ return parts.slice(-2).join(".");
355
+ }
356
+ return VERIDEX_RP_ID;
357
+ }
358
+ var PasskeyManager = class _PasskeyManager {
359
+ config;
360
+ credential = null;
361
+ constructor(config = {}) {
362
+ this.config = {
363
+ rpName: config.rpName ?? "Veridex Protocol",
364
+ rpId: config.rpId ?? detectRpId(),
365
+ timeout: config.timeout ?? 6e4,
366
+ userVerification: config.userVerification ?? "required",
367
+ authenticatorAttachment: config.authenticatorAttachment ?? "platform",
368
+ relayerUrl: config.relayerUrl ?? ""
369
+ };
370
+ }
371
+ static isSupported() {
372
+ return (0, import_browser.browserSupportsWebAuthn)();
373
+ }
374
+ static async isPlatformAuthenticatorAvailable() {
375
+ if (typeof window === "undefined" || !window.PublicKeyCredential) {
376
+ return false;
377
+ }
378
+ return await window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
379
+ }
380
+ async register(username, displayName) {
381
+ if (!_PasskeyManager.isSupported()) {
382
+ throw new Error("WebAuthn is not supported in this browser");
383
+ }
384
+ const challenge = import_ethers2.ethers.randomBytes(32);
385
+ const challengeBase64 = base64URLEncode(challenge);
386
+ const options = {
387
+ challenge: challengeBase64,
388
+ rp: {
389
+ name: this.config.rpName,
390
+ id: this.config.rpId
391
+ },
392
+ user: {
393
+ id: base64URLEncode(import_ethers2.ethers.toUtf8Bytes(username)),
394
+ name: username,
395
+ displayName
396
+ },
397
+ pubKeyCredParams: [
398
+ { alg: -7, type: "public-key" },
399
+ // ES256 (P-256) - WebAuthn default
400
+ { alg: -257, type: "public-key" },
401
+ // RS256 - Widely supported
402
+ { alg: -8, type: "public-key" },
403
+ // EdDSA - Modern, efficient
404
+ { alg: -35, type: "public-key" },
405
+ // ES384 (P-384)
406
+ { alg: -36, type: "public-key" },
407
+ // ES512 (P-521)
408
+ { alg: -37, type: "public-key" }
409
+ // PS256 - RSA PSS
410
+ ],
411
+ authenticatorSelection: {
412
+ authenticatorAttachment: this.config.authenticatorAttachment,
413
+ userVerification: this.config.userVerification,
414
+ residentKey: "required",
415
+ requireResidentKey: true
416
+ },
417
+ timeout: this.config.timeout,
418
+ attestation: "none"
419
+ };
420
+ const response = await (0, import_browser.startRegistration)(options);
421
+ const publicKey = this.extractPublicKeyFromAttestation(response);
422
+ const keyHash = computeKeyHash(publicKey.x, publicKey.y);
423
+ this.credential = {
424
+ credentialId: response.id,
425
+ publicKeyX: publicKey.x,
426
+ publicKeyY: publicKey.y,
427
+ keyHash
428
+ };
429
+ return this.credential;
430
+ }
431
+ async sign(challenge) {
432
+ if (!this.credential) {
433
+ throw new Error("No credential set. Call register() or setCredential() first.");
434
+ }
435
+ const challengeBase64 = base64URLEncode(challenge);
436
+ const options = {
437
+ challenge: challengeBase64,
438
+ rpId: this.config.rpId,
439
+ allowCredentials: [
440
+ {
441
+ id: this.credential.credentialId,
442
+ type: "public-key",
443
+ transports: ["internal"]
444
+ }
445
+ ],
446
+ userVerification: this.config.userVerification,
447
+ timeout: this.config.timeout
448
+ };
449
+ const response = await (0, import_browser.startAuthentication)(options);
450
+ return this.parseAuthenticationResponse(response);
451
+ }
452
+ /**
453
+ * Authenticate using a discoverable credential (passkey)
454
+ * This allows sign-in without knowing the credential ID ahead of time.
455
+ * The authenticator will show all available passkeys for this RP.
456
+ *
457
+ * @param challenge - Optional challenge bytes. If not provided, a random challenge is used.
458
+ * @returns The credential that was used to authenticate, along with the signature
459
+ */
460
+ async authenticate(challenge) {
461
+ if (!_PasskeyManager.isSupported()) {
462
+ throw new Error("WebAuthn is not supported in this browser");
463
+ }
464
+ const actualChallenge = challenge ?? import_ethers2.ethers.randomBytes(32);
465
+ const challengeBase64 = base64URLEncode(actualChallenge);
466
+ const options = {
467
+ challenge: challengeBase64,
468
+ rpId: this.config.rpId,
469
+ userVerification: this.config.userVerification,
470
+ timeout: this.config.timeout
471
+ };
472
+ const response = await (0, import_browser.startAuthentication)(options);
473
+ const credentialId = response.id;
474
+ const signature = this.parseAuthenticationResponse(response);
475
+ let storedCredential = this.findCredentialById(credentialId);
476
+ if (storedCredential) {
477
+ this.credential = storedCredential;
478
+ return { credential: storedCredential, signature };
479
+ }
480
+ if (this.config.relayerUrl) {
481
+ storedCredential = await this.loadCredentialFromRelayer(credentialId);
482
+ if (storedCredential) {
483
+ this.credential = storedCredential;
484
+ this.addCredentialToStorage(storedCredential);
485
+ return { credential: storedCredential, signature };
486
+ }
487
+ }
488
+ const hasRelayer = !!this.config.relayerUrl;
489
+ throw new Error(
490
+ "Credential not found. This passkey was registered on a different device or the data was cleared. " + (hasRelayer ? "The credential was not found. Please register a new passkey." : "Please register a new passkey or ensure the relayer URL is configured.")
491
+ );
492
+ }
493
+ /**
494
+ * Find a credential by ID in the list of stored credentials
495
+ */
496
+ findCredentialById(credentialId) {
497
+ if (typeof window === "undefined") return null;
498
+ const stored = this.getAllStoredCredentials();
499
+ return stored.find((c) => c.credentialId === credentialId) || null;
500
+ }
501
+ /**
502
+ * Get all credentials stored in localStorage
503
+ */
504
+ getAllStoredCredentials(key = "veridex_credentials") {
505
+ if (typeof window === "undefined") return [];
506
+ const stored = localStorage.getItem(key);
507
+ if (!stored) {
508
+ const legacy = localStorage.getItem("veridex_credential");
509
+ if (legacy) {
510
+ try {
511
+ const data = JSON.parse(legacy);
512
+ const cred = this.parseStoredCredential(data);
513
+ if (cred) {
514
+ this.saveCredentials([cred], key);
515
+ localStorage.removeItem("veridex_credential");
516
+ return [cred];
517
+ }
518
+ } catch (e) {
519
+ }
520
+ }
521
+ return [];
522
+ }
523
+ try {
524
+ const data = JSON.parse(stored);
525
+ if (Array.isArray(data)) {
526
+ return data.map((item) => this.parseStoredCredential(item)).filter((c) => c !== null);
527
+ }
528
+ return [];
529
+ } catch (error) {
530
+ console.error("Failed to load credentials:", error);
531
+ return [];
532
+ }
533
+ }
534
+ parseStoredCredential(data) {
535
+ try {
536
+ return {
537
+ credentialId: data.credentialId,
538
+ publicKeyX: BigInt(data.publicKeyX),
539
+ publicKeyY: BigInt(data.publicKeyY),
540
+ keyHash: data.keyHash
541
+ };
542
+ } catch {
543
+ return null;
544
+ }
545
+ }
546
+ /**
547
+ * Save a list of credentials to localStorage
548
+ */
549
+ saveCredentials(credentials, key = "veridex_credentials") {
550
+ if (typeof window === "undefined") return;
551
+ const data = credentials.map((c) => ({
552
+ credentialId: c.credentialId,
553
+ publicKeyX: c.publicKeyX.toString(),
554
+ publicKeyY: c.publicKeyY.toString(),
555
+ keyHash: c.keyHash
556
+ }));
557
+ localStorage.setItem(key, JSON.stringify(data));
558
+ }
559
+ /**
560
+ * Add a single credential to storage (append or update)
561
+ */
562
+ addCredentialToStorage(credential, key = "veridex_credentials") {
563
+ const stored = this.getAllStoredCredentials(key);
564
+ const existingIndex = stored.findIndex((c) => c.credentialId === credential.credentialId);
565
+ if (existingIndex >= 0) {
566
+ stored[existingIndex] = credential;
567
+ } else {
568
+ stored.push(credential);
569
+ }
570
+ this.saveCredentials(stored, key);
571
+ }
572
+ /**
573
+ * Check if there's ANY stored credential for this RP
574
+ */
575
+ hasStoredCredential() {
576
+ return this.getAllStoredCredentials().length > 0;
577
+ }
578
+ getCredential() {
579
+ return this.credential;
580
+ }
581
+ setCredential(credential) {
582
+ this.credential = credential;
583
+ }
584
+ createCredentialFromPublicKey(credentialId, publicKeyX, publicKeyY) {
585
+ const keyHash = computeKeyHash(publicKeyX, publicKeyY);
586
+ this.credential = {
587
+ credentialId,
588
+ publicKeyX,
589
+ publicKeyY,
590
+ keyHash
591
+ };
592
+ return this.credential;
593
+ }
594
+ clearCredential() {
595
+ this.credential = null;
596
+ }
597
+ /**
598
+ * Save the current credential to localStorage (appends to list)
599
+ */
600
+ saveToLocalStorage(key = "veridex_credentials") {
601
+ if (!this.credential) {
602
+ throw new Error("No credential to save");
603
+ }
604
+ this.addCredentialToStorage(this.credential, key);
605
+ }
606
+ loadFromLocalStorage(key = "veridex_credentials") {
607
+ if (typeof window === "undefined") {
608
+ return null;
609
+ }
610
+ const stored = this.getAllStoredCredentials(key);
611
+ if (stored.length > 0) {
612
+ this.credential = stored[stored.length - 1];
613
+ return this.credential;
614
+ }
615
+ return null;
616
+ }
617
+ removeFromLocalStorage(key = "veridex_credentials") {
618
+ if (typeof window !== "undefined") {
619
+ localStorage.removeItem(key);
620
+ localStorage.removeItem("veridex_credential");
621
+ }
622
+ }
623
+ // =========================================================================
624
+ // Relayer-based Credential Storage (Cross-Device Recovery)
625
+ // =========================================================================
626
+ /**
627
+ * Save the current credential to the relayer for cross-device recovery.
628
+ * This should be called after registration.
629
+ */
630
+ async saveCredentialToRelayer() {
631
+ if (!this.credential) {
632
+ throw new Error("No credential to save");
633
+ }
634
+ if (!this.config.relayerUrl) {
635
+ console.warn("Relayer URL not configured; skipping remote credential storage");
636
+ return false;
637
+ }
638
+ try {
639
+ const response = await fetch(`${this.config.relayerUrl}/api/v1/credential`, {
640
+ method: "POST",
641
+ headers: { "Content-Type": "application/json" },
642
+ body: JSON.stringify({
643
+ keyHash: this.credential.keyHash,
644
+ credentialId: this.credential.credentialId,
645
+ publicKeyX: this.credential.publicKeyX.toString(),
646
+ publicKeyY: this.credential.publicKeyY.toString()
647
+ })
648
+ });
649
+ if (!response.ok) {
650
+ const errorData = await response.json().catch(() => ({}));
651
+ console.error("Failed to save credential to relayer:", errorData);
652
+ return false;
653
+ }
654
+ console.log("Credential saved to relayer for cross-device recovery");
655
+ return true;
656
+ } catch (error) {
657
+ console.error("Failed to save credential to relayer:", error);
658
+ return false;
659
+ }
660
+ }
661
+ /**
662
+ * Load a credential from the relayer by credential ID.
663
+ * Used during discoverable credential authentication when localStorage is empty.
664
+ */
665
+ async loadCredentialFromRelayer(credentialId) {
666
+ if (!this.config.relayerUrl) {
667
+ return null;
668
+ }
669
+ try {
670
+ const response = await fetch(
671
+ `${this.config.relayerUrl}/api/v1/credential/by-id/${encodeURIComponent(credentialId)}`
672
+ );
673
+ if (!response.ok) {
674
+ return null;
675
+ }
676
+ const data = await response.json();
677
+ if (!data.exists) {
678
+ return null;
679
+ }
680
+ if (!data.credentialId || !data.publicKeyX || !data.publicKeyY || !data.keyHash) {
681
+ console.warn("Relayer returned incomplete credential data:", {
682
+ hasCredentialId: !!data.credentialId,
683
+ hasPublicKeyX: !!data.publicKeyX,
684
+ hasPublicKeyY: !!data.publicKeyY,
685
+ hasKeyHash: !!data.keyHash
686
+ });
687
+ return null;
688
+ }
689
+ return {
690
+ credentialId: data.credentialId,
691
+ publicKeyX: BigInt(data.publicKeyX),
692
+ publicKeyY: BigInt(data.publicKeyY),
693
+ keyHash: data.keyHash
694
+ };
695
+ } catch (error) {
696
+ console.error("Failed to load credential from relayer:", error);
697
+ return null;
698
+ }
699
+ }
700
+ /**
701
+ * Load a credential from the relayer by keyHash.
702
+ * Useful when you know the user's keyHash but not their credential ID.
703
+ */
704
+ async loadCredentialFromRelayerByKeyHash(keyHash) {
705
+ if (!this.config.relayerUrl) {
706
+ return null;
707
+ }
708
+ try {
709
+ const response = await fetch(
710
+ `${this.config.relayerUrl}/api/v1/credential/${encodeURIComponent(keyHash)}`
711
+ );
712
+ if (!response.ok) {
713
+ return null;
714
+ }
715
+ const data = await response.json();
716
+ if (!data.exists) {
717
+ return null;
718
+ }
719
+ if (!data.credentialId || !data.publicKeyX || !data.publicKeyY || !data.keyHash) {
720
+ console.warn("Relayer returned incomplete credential data for keyHash:", {
721
+ hasCredentialId: !!data.credentialId,
722
+ hasPublicKeyX: !!data.publicKeyX,
723
+ hasPublicKeyY: !!data.publicKeyY,
724
+ hasKeyHash: !!data.keyHash
725
+ });
726
+ return null;
727
+ }
728
+ return {
729
+ credentialId: data.credentialId,
730
+ publicKeyX: BigInt(data.publicKeyX),
731
+ publicKeyY: BigInt(data.publicKeyY),
732
+ keyHash: data.keyHash
733
+ };
734
+ } catch (error) {
735
+ console.error("Failed to load credential from relayer:", error);
736
+ return null;
737
+ }
738
+ }
739
+ extractPublicKeyFromAttestation(response) {
740
+ const attestationObject = base64URLDecode(response.response.attestationObject);
741
+ let offset = 0;
742
+ if (attestationObject[offset] >= 160 && attestationObject[offset] <= 191) {
743
+ offset++;
744
+ }
745
+ while (offset < attestationObject.length - 37) {
746
+ if (attestationObject[offset] === 104 && // text string, 8 bytes
747
+ attestationObject[offset + 1] === 97 && // 'a'
748
+ attestationObject[offset + 2] === 117 && // 'u'
749
+ attestationObject[offset + 3] === 116 && // 't'
750
+ attestationObject[offset + 4] === 104 && // 'h'
751
+ attestationObject[offset + 5] === 68 && // 'D'
752
+ attestationObject[offset + 6] === 97 && // 'a'
753
+ attestationObject[offset + 7] === 116 && // 't'
754
+ attestationObject[offset + 8] === 97) {
755
+ offset += 9;
756
+ break;
757
+ }
758
+ offset++;
759
+ }
760
+ if (attestationObject[offset] === 88 || attestationObject[offset] === 89) {
761
+ const lengthBytes = attestationObject[offset] === 88 ? 1 : 2;
762
+ offset += 1 + lengthBytes;
763
+ }
764
+ offset += 32;
765
+ offset += 1;
766
+ offset += 4;
767
+ offset += 16;
768
+ const credIdLen = attestationObject[offset] << 8 | attestationObject[offset + 1];
769
+ offset += 2;
770
+ offset += credIdLen;
771
+ const coseKey = attestationObject.slice(offset);
772
+ console.log("COSE key length:", coseKey.length);
773
+ console.log("COSE key hex:", this.bytesToHex(coseKey.slice(0, Math.min(100, coseKey.length))));
774
+ const { x, y } = this.parseCOSEKey(coseKey);
775
+ return { x, y };
776
+ }
777
+ parseCOSEKey(coseKey) {
778
+ console.log("COSE key length:", coseKey.length);
779
+ console.log("COSE key hex:", this.bytesToHex(coseKey));
780
+ const parsed = this.tryParseCOSEKeyStrategies(coseKey);
781
+ if (parsed) {
782
+ return parsed;
783
+ }
784
+ return this.parseCOSEKeyWithCBORStructure(coseKey);
785
+ }
786
+ tryParseCOSEKeyStrategies(coseKey) {
787
+ const keyBytes = new Uint8Array(coseKey);
788
+ for (let i = 0; i < keyBytes.length - 40; i++) {
789
+ if (keyBytes[i] === 88 && keyBytes[i + 1] === 32) {
790
+ const potentialX = keyBytes.slice(i + 2, i + 34);
791
+ for (let j = i + 34; j < keyBytes.length - 34; j++) {
792
+ if (keyBytes[j] === 88 && keyBytes[j + 1] === 32) {
793
+ const potentialY = keyBytes.slice(j + 2, j + 34);
794
+ if (this.isValidCoordinate(potentialX) && this.isValidCoordinate(potentialY)) {
795
+ console.log("Found coordinates via pattern matching");
796
+ return {
797
+ x: this.bytesToBigInt(potentialX),
798
+ y: this.bytesToBigInt(potentialY)
799
+ };
800
+ }
801
+ }
802
+ }
803
+ }
804
+ }
805
+ return this.tryParseASN1Structure(keyBytes);
806
+ }
807
+ parseCOSEKeyWithCBORStructure(coseKey) {
808
+ const bytes = new Uint8Array(coseKey);
809
+ let xBytes = null;
810
+ let yBytes = null;
811
+ let i = 0;
812
+ while (i < bytes.length) {
813
+ if (bytes[i] === 88) {
814
+ const length = bytes[i + 1];
815
+ if (length === 32) {
816
+ const start = i + 2;
817
+ const end = start + 32;
818
+ if (end <= bytes.length) {
819
+ const coordinate = bytes.slice(start, end);
820
+ if (!xBytes) {
821
+ xBytes = coordinate;
822
+ console.log("Found x at offset", i);
823
+ } else if (!yBytes) {
824
+ yBytes = coordinate;
825
+ console.log("Found y at offset", i);
826
+ break;
827
+ }
828
+ }
829
+ i = end;
830
+ } else {
831
+ i += length + 2;
832
+ }
833
+ } else if (bytes[i] === 66) {
834
+ if (i + 3 < bytes.length) {
835
+ const length = bytes[i + 1] << 8 | bytes[i + 2];
836
+ if (length === 32) {
837
+ const start = i + 3;
838
+ const end = start + 32;
839
+ if (end <= bytes.length) {
840
+ const coordinate = bytes.slice(start, end);
841
+ if (!xBytes) {
842
+ xBytes = coordinate;
843
+ console.log("Found x at offset", i);
844
+ } else if (!yBytes) {
845
+ yBytes = coordinate;
846
+ console.log("Found y at offset", i);
847
+ break;
848
+ }
849
+ }
850
+ i = end;
851
+ } else {
852
+ i += length + 3;
853
+ }
854
+ } else {
855
+ i++;
856
+ }
857
+ } else if (bytes[i] === 64) {
858
+ i++;
859
+ while (i < bytes.length && bytes[i] !== 255) {
860
+ if (bytes[i] === 4 && i + 65 <= bytes.length) {
861
+ const x = bytes.slice(i + 1, i + 33);
862
+ const y = bytes.slice(i + 33, i + 65);
863
+ if (this.isValidCoordinate(x) && this.isValidCoordinate(y)) {
864
+ xBytes = x;
865
+ yBytes = y;
866
+ console.log("Found coordinates in uncompressed point format");
867
+ break;
868
+ }
869
+ }
870
+ i++;
871
+ }
872
+ if (xBytes && yBytes) break;
873
+ } else {
874
+ i++;
875
+ }
876
+ }
877
+ if (!xBytes || !yBytes) {
878
+ const potentialCoords = this.find32ByteSequences(bytes);
879
+ if (potentialCoords.length >= 2) {
880
+ xBytes = potentialCoords[0];
881
+ yBytes = potentialCoords[1];
882
+ console.log("Fallback: Using first two 32-byte sequences as coordinates");
883
+ }
884
+ }
885
+ if (!xBytes || !yBytes) {
886
+ console.error("Failed to find coordinates in COSE key. Full dump:");
887
+ console.error("Hex:", this.bytesToHex(bytes));
888
+ console.error("Structure analysis:");
889
+ this.analyzeCOSEStructure(bytes);
890
+ throw new Error("Failed to extract public key coordinates from COSE key. Check console for details.");
891
+ }
892
+ return {
893
+ x: this.bytesToBigInt(xBytes),
894
+ y: this.bytesToBigInt(yBytes)
895
+ };
896
+ }
897
+ tryParseASN1Structure(bytes) {
898
+ if (bytes[0] === 48) {
899
+ let offset = 2;
900
+ if (bytes[offset] === 3) {
901
+ offset += 2;
902
+ if (bytes[offset] === 48) {
903
+ offset += 2;
904
+ offset += 12;
905
+ if (bytes[offset] === 3 && bytes[offset + 2] === 4) {
906
+ offset += 3;
907
+ const x = bytes.slice(offset, offset + 32);
908
+ const y = bytes.slice(offset + 32, offset + 64);
909
+ if (x.length === 32 && y.length === 32) {
910
+ console.log("Found coordinates via ASN.1 parsing");
911
+ return {
912
+ x: this.bytesToBigInt(x),
913
+ y: this.bytesToBigInt(y)
914
+ };
915
+ }
916
+ }
917
+ }
918
+ }
919
+ }
920
+ return null;
921
+ }
922
+ find32ByteSequences(bytes) {
923
+ const sequences = [];
924
+ for (let i = 0; i <= bytes.length - 32; i++) {
925
+ const sequence = bytes.slice(i, i + 32);
926
+ if (this.isValidCoordinate(sequence)) {
927
+ sequences.push(sequence);
928
+ }
929
+ }
930
+ return sequences;
931
+ }
932
+ isValidCoordinate(bytes) {
933
+ if (bytes.length !== 32) return false;
934
+ let allZeros = true;
935
+ let allOnes = true;
936
+ for (const byte of bytes) {
937
+ if (byte !== 0) allZeros = false;
938
+ if (byte !== 255) allOnes = false;
939
+ }
940
+ return !allZeros && !allOnes;
941
+ }
942
+ bytesToBigInt(bytes) {
943
+ return BigInt("0x" + this.bytesToHex(bytes));
944
+ }
945
+ bytesToHex(bytes) {
946
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
947
+ }
948
+ analyzeCOSEStructure(bytes) {
949
+ console.log("COSE Structure Analysis:");
950
+ console.log("First 20 bytes:", this.bytesToHex(bytes.slice(0, 20)));
951
+ const firstByte = bytes[0];
952
+ console.log("First byte (0x" + firstByte.toString(16) + "):");
953
+ if (firstByte >= 160 && firstByte <= 191) {
954
+ console.log("- Definite length map with", firstByte & 31, "pairs");
955
+ } else if (firstByte === 191) {
956
+ console.log("- Indefinite length map");
957
+ } else if (firstByte === 4) {
958
+ console.log("- Byte string");
959
+ } else if (firstByte === 2) {
960
+ console.log("- Negative integer");
961
+ }
962
+ let count32 = 0;
963
+ for (let i = 0; i <= bytes.length - 32; i++) {
964
+ const chunk = bytes.slice(i, i + 32);
965
+ if (this.isValidCoordinate(chunk)) {
966
+ console.log(
967
+ `Found valid 32-byte sequence at offset ${i}:`,
968
+ this.bytesToHex(chunk.slice(0, 8)) + "..."
969
+ );
970
+ count32++;
971
+ }
972
+ }
973
+ console.log(`Total valid 32-byte sequences: ${count32}`);
974
+ console.log("Looking for known markers:");
975
+ const markers = [
976
+ { byte: 4, name: "Uncompressed point marker" },
977
+ { byte: 3, name: "BIT STRING" },
978
+ { byte: 48, name: "SEQUENCE" },
979
+ { byte: 2, name: "INTEGER" },
980
+ { byte: 6, name: "OBJECT IDENTIFIER" },
981
+ { byte: 88, name: "Byte string with length byte" },
982
+ { byte: 66, name: "Byte string with 2-byte length" },
983
+ { byte: 64, name: "Byte string indefinite length" },
984
+ { byte: 160, name: "Map start" },
985
+ { byte: 191, name: "Indefinite map start" }
986
+ ];
987
+ for (const marker of markers) {
988
+ const indices = [];
989
+ for (let i = 0; i < bytes.length; i++) {
990
+ if (bytes[i] === marker.byte) {
991
+ indices.push(i);
992
+ }
993
+ }
994
+ if (indices.length > 0) {
995
+ console.log(` ${marker.name} (0x${marker.byte.toString(16)}) at positions:`, indices.slice(0, 5).join(", "));
996
+ }
997
+ }
998
+ }
999
+ parseAuthenticationResponse(response) {
1000
+ const authenticatorData = base64URLDecode(response.response.authenticatorData);
1001
+ const clientDataJSON = response.response.clientDataJSON;
1002
+ const signature = base64URLDecode(response.response.signature);
1003
+ const { r, s } = parseDERSignature(signature);
1004
+ const P256_N = BigInt("0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
1005
+ const P256_N_DIV_2 = BigInt(
1006
+ "0x7FFFFFFF800000007FFFFFFFFFFFFFFFDE737D56D38BCF4279DCE5617E3192A8"
1007
+ );
1008
+ const clientDataStr = new TextDecoder().decode(base64URLDecode(clientDataJSON));
1009
+ const challengeIndex = clientDataStr.indexOf('"challenge"');
1010
+ const typeIndex = clientDataStr.indexOf('"type"');
1011
+ if (challengeIndex === -1 || typeIndex === -1) {
1012
+ throw new Error("Invalid clientDataJSON format");
1013
+ }
1014
+ return {
1015
+ authenticatorData: import_ethers2.ethers.hexlify(authenticatorData),
1016
+ clientDataJSON: clientDataStr,
1017
+ challengeIndex,
1018
+ typeIndex,
1019
+ r: this.bytesToBigInt(r),
1020
+ s: (() => {
1021
+ const sBig = this.bytesToBigInt(s);
1022
+ return sBig > P256_N_DIV_2 ? P256_N - sBig : sBig;
1023
+ })()
1024
+ };
1025
+ }
1026
+ };
1027
+
1028
+ // src/payload.ts
1029
+ var import_ethers3 = require("ethers");
1030
+ function encodeTransferAction(token, recipient, amount) {
1031
+ const tokenPadded = padTo32Bytes(token);
1032
+ const recipientPadded = padTo32Bytes(recipient);
1033
+ const amountBytes = import_ethers3.ethers.zeroPadValue(import_ethers3.ethers.toBeHex(amount), 32);
1034
+ return import_ethers3.ethers.concat([
1035
+ import_ethers3.ethers.toBeHex(ACTION_TRANSFER, 1),
1036
+ tokenPadded,
1037
+ recipientPadded,
1038
+ amountBytes
1039
+ ]);
1040
+ }
1041
+ function encodeBridgeAction(token, amount, targetChain, recipient) {
1042
+ const tokenPadded = padTo32Bytes(token);
1043
+ const amountBytes = import_ethers3.ethers.zeroPadValue(import_ethers3.ethers.toBeHex(amount), 32);
1044
+ const targetChainBytes = import_ethers3.ethers.toBeHex(targetChain, 2);
1045
+ const recipientPadded = padTo32Bytes(recipient);
1046
+ return import_ethers3.ethers.concat([
1047
+ import_ethers3.ethers.toBeHex(ACTION_BRIDGE, 1),
1048
+ tokenPadded,
1049
+ amountBytes,
1050
+ targetChainBytes,
1051
+ recipientPadded
1052
+ ]);
1053
+ }
1054
+ function encodeExecuteAction(target, value, data) {
1055
+ const targetPadded = padTo32Bytes(target);
1056
+ const valueBytes = import_ethers3.ethers.zeroPadValue(import_ethers3.ethers.toBeHex(value), 32);
1057
+ const dataBytes = import_ethers3.ethers.getBytes(data);
1058
+ const dataLengthBytes = import_ethers3.ethers.toBeHex(dataBytes.length, 2);
1059
+ return import_ethers3.ethers.concat([
1060
+ import_ethers3.ethers.toBeHex(ACTION_EXECUTE, 1),
1061
+ targetPadded,
1062
+ valueBytes,
1063
+ dataLengthBytes,
1064
+ data
1065
+ ]);
1066
+ }
1067
+ function padTo32Bytes(address) {
1068
+ if (address.toLowerCase() === "native") {
1069
+ return "0x" + "0".repeat(64);
1070
+ }
1071
+ if (address.startsWith("0x")) {
1072
+ const hex2 = address.replace("0x", "");
1073
+ if (!/^[0-9a-fA-F]*$/.test(hex2)) {
1074
+ throw new Error(`Invalid address: ${address}. Expected hex string or 'native'.`);
1075
+ }
1076
+ return "0x" + hex2.padStart(64, "0");
1077
+ }
1078
+ const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1079
+ for (const char of address) {
1080
+ if (!base58Chars.includes(char)) {
1081
+ throw new Error(`Invalid address: ${address}. Contains invalid base58 character '${char}'.`);
1082
+ }
1083
+ }
1084
+ let value = BigInt(0);
1085
+ for (const char of address) {
1086
+ value = value * 58n + BigInt(base58Chars.indexOf(char));
1087
+ }
1088
+ let hex = value.toString(16);
1089
+ if (hex.length > 64) {
1090
+ throw new Error(`Invalid address: ${address}. Decoded value too large for 32 bytes.`);
1091
+ }
1092
+ return "0x" + hex.padStart(64, "0");
1093
+ }
1094
+ function createGaslessMessageHash(targetChain, actionPayload, nonce, hubChainId) {
1095
+ return import_ethers3.ethers.solidityPacked(
1096
+ ["uint16", "bytes", "uint256", "uint16"],
1097
+ [targetChain, actionPayload, nonce, hubChainId]
1098
+ );
1099
+ }
1100
+ function buildGaslessChallenge(targetChain, actionPayload, nonce, hubChainId) {
1101
+ const packed = createGaslessMessageHash(targetChain, actionPayload, nonce, hubChainId);
1102
+ return import_ethers3.ethers.getBytes(packed);
1103
+ }
1104
+
1105
+ // src/queries/hubState.ts
1106
+ var import_axios = __toESM(require("axios"));
1107
+ var import_buffer = require("buffer");
1108
+ var import_ethers4 = require("ethers");
1109
+ var import_wormhole_query_sdk = require("@wormhole-foundation/wormhole-query-sdk");
1110
+
1111
+ // src/queries/constants.ts
1112
+ var WORMHOLE_QUERY_PROXY_URLS = {
1113
+ mainnet: "https://query.wormhole.com/v1/query",
1114
+ testnet: "https://testnet.query.wormhole.com/v1/query"
1115
+ };
1116
+
1117
+ // src/queries/hubState.ts
1118
+ var QueryHubStateError = class extends Error {
1119
+ code;
1120
+ cause;
1121
+ constructor(code, message, cause) {
1122
+ super(message);
1123
+ this.name = "QueryHubStateError";
1124
+ this.code = code;
1125
+ this.cause = cause;
1126
+ }
1127
+ };
1128
+ function resolveNetwork(options) {
1129
+ if (options?.network) return options.network;
1130
+ const envCandidates = [
1131
+ globalThis?.process?.env?.NEXT_PUBLIC_VERIDEX_NETWORK,
1132
+ globalThis?.process?.env?.VERIDEX_NETWORK,
1133
+ globalThis?.process?.env?.NEXT_PUBLIC_WORMHOLE_NETWORK,
1134
+ globalThis?.process?.env?.WORMHOLE_NETWORK
1135
+ ].filter(Boolean);
1136
+ const env = envCandidates[0]?.toLowerCase();
1137
+ if (env === "mainnet" || env === "testnet") return env;
1138
+ return "testnet";
1139
+ }
1140
+ function concatBytes(parts) {
1141
+ const total = parts.reduce((sum, p) => sum + p.length, 0);
1142
+ const out = new Uint8Array(total);
1143
+ let offset = 0;
1144
+ for (const p of parts) {
1145
+ out.set(p, offset);
1146
+ offset += p.length;
1147
+ }
1148
+ return out;
1149
+ }
1150
+ function signaturesToProofBytes(signatures) {
1151
+ const chunks = [];
1152
+ for (const sig of signatures) {
1153
+ if (typeof sig !== "string" || sig.length !== 132 || !/^[0-9a-fA-F]+$/.test(sig)) {
1154
+ throw new QueryHubStateError(
1155
+ "PROXY_RESPONSE_INVALID",
1156
+ `Invalid guardian signature format (expected 132 hex chars): ${String(sig)}`
1157
+ );
1158
+ }
1159
+ chunks.push((0, import_wormhole_query_sdk.hexToUint8Array)(`0x${sig}`));
1160
+ }
1161
+ return concatBytes(chunks);
1162
+ }
1163
+ function decodeQueryBytes(bytes) {
1164
+ if (typeof bytes !== "string" || bytes.length === 0) {
1165
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Missing query response bytes");
1166
+ }
1167
+ if ((0, import_wormhole_query_sdk.isValidHexString)(bytes)) {
1168
+ return (0, import_wormhole_query_sdk.hexToUint8Array)(bytes);
1169
+ }
1170
+ try {
1171
+ if (typeof atob === "function") {
1172
+ const raw = atob(bytes);
1173
+ const arr = new Uint8Array(raw.length);
1174
+ for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
1175
+ return arr;
1176
+ }
1177
+ } catch {
1178
+ }
1179
+ try {
1180
+ return new Uint8Array(import_buffer.Buffer.from(bytes, "base64"));
1181
+ } catch (cause) {
1182
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Unrecognized query response bytes encoding", cause);
1183
+ }
1184
+ }
1185
+ function sleep(ms) {
1186
+ return new Promise((resolve) => setTimeout(resolve, ms));
1187
+ }
1188
+ async function withExponentialBackoff(fn, maxAttempts) {
1189
+ let attempt = 0;
1190
+ let lastError;
1191
+ while (attempt < maxAttempts) {
1192
+ try {
1193
+ return await fn();
1194
+ } catch (err) {
1195
+ lastError = err;
1196
+ attempt += 1;
1197
+ if (attempt >= maxAttempts) break;
1198
+ const baseMs = 250;
1199
+ const backoffMs = Math.min(5e3, baseMs * 2 ** (attempt - 1));
1200
+ const jitterMs = Math.floor(Math.random() * 100);
1201
+ await sleep(backoffMs + jitterMs);
1202
+ }
1203
+ }
1204
+ throw lastError;
1205
+ }
1206
+ function getHubConfig(network) {
1207
+ if (network === "testnet") {
1208
+ const baseSepolia = TESTNET_CHAINS.baseSepolia;
1209
+ if (!baseSepolia?.contracts?.hub) {
1210
+ throw new QueryHubStateError("MISSING_HUB_ADDRESS", "Missing Base Sepolia hub address in SDK constants");
1211
+ }
1212
+ return {
1213
+ wormholeChainId: baseSepolia.wormholeChainId,
1214
+ hubAddress: baseSepolia.contracts.hub,
1215
+ endpoint: WORMHOLE_QUERY_PROXY_URLS.testnet,
1216
+ rpcUrl: baseSepolia.rpcUrl
1217
+ };
1218
+ }
1219
+ if (network === "mainnet") {
1220
+ const base = MAINNET_CHAINS.base;
1221
+ const hubAddress = base?.contracts?.hub;
1222
+ if (!hubAddress) {
1223
+ throw new QueryHubStateError(
1224
+ "MISSING_HUB_ADDRESS",
1225
+ "Missing mainnet hub address in SDK constants (MAINNET_CHAINS.base.contracts.hub)"
1226
+ );
1227
+ }
1228
+ return {
1229
+ wormholeChainId: base.wormholeChainId,
1230
+ hubAddress,
1231
+ endpoint: WORMHOLE_QUERY_PROXY_URLS.mainnet,
1232
+ rpcUrl: base.rpcUrl
1233
+ };
1234
+ }
1235
+ throw new QueryHubStateError("UNSUPPORTED_NETWORK", `Unsupported network: ${network}`);
1236
+ }
1237
+ function encodeHubCalls(hubAddress, userKeyHash) {
1238
+ const nonceAbiCandidates = [
1239
+ "function getUserNonce(bytes32 userKeyHash) view returns (uint256)",
1240
+ "function userNonces(bytes32 userKeyHash) view returns (uint256)",
1241
+ "function getNonceByHash(bytes32 userKeyHash) view returns (uint256)"
1242
+ ];
1243
+ const registeredAbiCandidates = [
1244
+ "function registeredKeys(bytes32 userKeyHash) view returns (bool)",
1245
+ "function isKeyRegisteredByHash(bytes32 userKeyHash) view returns (bool)"
1246
+ ];
1247
+ const actionHashAbiCandidates = [
1248
+ "function getUserLastActionHash(bytes32 userKeyHash) view returns (bytes32)",
1249
+ "function userLastActionHash(bytes32 userKeyHash) view returns (bytes32)"
1250
+ ];
1251
+ const iface = new import_ethers4.ethers.Interface([
1252
+ ...nonceAbiCandidates,
1253
+ ...registeredAbiCandidates,
1254
+ ...actionHashAbiCandidates
1255
+ ]);
1256
+ const nonceFnNames = ["getUserNonce", "userNonces", "getNonceByHash"];
1257
+ const regFnNames = ["registeredKeys", "isKeyRegisteredByHash"];
1258
+ const actionHashFnNames = ["getUserLastActionHash", "userLastActionHash"];
1259
+ return [
1260
+ ...nonceFnNames.map((fn) => ({
1261
+ to: hubAddress,
1262
+ data: iface.encodeFunctionData(fn, [userKeyHash])
1263
+ })),
1264
+ ...regFnNames.map((fn) => ({
1265
+ to: hubAddress,
1266
+ data: iface.encodeFunctionData(fn, [userKeyHash])
1267
+ })),
1268
+ ...actionHashFnNames.map((fn) => ({
1269
+ to: hubAddress,
1270
+ data: iface.encodeFunctionData(fn, [userKeyHash])
1271
+ }))
1272
+ ];
1273
+ }
1274
+ function decodeFirstNonce(results) {
1275
+ const candidates = [
1276
+ "function getUserNonce(bytes32 userKeyHash) view returns (uint256)",
1277
+ "function userNonces(bytes32 userKeyHash) view returns (uint256)",
1278
+ "function getNonceByHash(bytes32 userKeyHash) view returns (uint256)"
1279
+ ];
1280
+ const iface = new import_ethers4.ethers.Interface(candidates);
1281
+ const fnNames = ["getUserNonce", "userNonces", "getNonceByHash"];
1282
+ for (let idx = 0; idx < fnNames.length; idx++) {
1283
+ const fnName = fnNames[idx];
1284
+ try {
1285
+ const data = results[idx];
1286
+ if (!data || data === "0x") continue;
1287
+ const decoded = iface.decodeFunctionResult(fnName, data);
1288
+ return decoded[0];
1289
+ } catch {
1290
+ }
1291
+ }
1292
+ throw new QueryHubStateError("QUERY_RESPONSE_INVALID", "Unable to decode user nonce from query response");
1293
+ }
1294
+ function decodeFirstIsRegistered(results) {
1295
+ const nonceCandidateCount = 3;
1296
+ const candidates = [
1297
+ "function registeredKeys(bytes32 userKeyHash) view returns (bool)",
1298
+ "function isKeyRegisteredByHash(bytes32 userKeyHash) view returns (bool)"
1299
+ ];
1300
+ const iface = new import_ethers4.ethers.Interface(candidates);
1301
+ const fnNames = ["registeredKeys", "isKeyRegisteredByHash"];
1302
+ for (let i = 0; i < fnNames.length; i++) {
1303
+ const fnName = fnNames[i];
1304
+ try {
1305
+ const data = results[nonceCandidateCount + i];
1306
+ if (!data || data === "0x") continue;
1307
+ const decoded = iface.decodeFunctionResult(fnName, data);
1308
+ return Boolean(decoded[0]);
1309
+ } catch {
1310
+ }
1311
+ }
1312
+ return false;
1313
+ }
1314
+ function decodeLastActionHash(results) {
1315
+ const nonceCandidateCount = 3;
1316
+ const registeredCandidateCount = 2;
1317
+ const actionHashOffset = nonceCandidateCount + registeredCandidateCount;
1318
+ const candidates = [
1319
+ "function getUserLastActionHash(bytes32 userKeyHash) view returns (bytes32)",
1320
+ "function userLastActionHash(bytes32 userKeyHash) view returns (bytes32)"
1321
+ ];
1322
+ const iface = new import_ethers4.ethers.Interface(candidates);
1323
+ const fnNames = ["getUserLastActionHash", "userLastActionHash"];
1324
+ for (let i = 0; i < fnNames.length; i++) {
1325
+ const fnName = fnNames[i];
1326
+ try {
1327
+ const data = results[actionHashOffset + i];
1328
+ if (!data || data === "0x") continue;
1329
+ const decoded = iface.decodeFunctionResult(fnName, data);
1330
+ return decoded[0];
1331
+ } catch {
1332
+ }
1333
+ }
1334
+ return import_ethers4.ethers.ZeroHash;
1335
+ }
1336
+ async function queryHubState(userKeyHash, apiKey, options) {
1337
+ if (typeof userKeyHash !== "string" || userKeyHash.length === 0) {
1338
+ throw new QueryHubStateError("INVALID_ARGUMENT", "userKeyHash is required");
1339
+ }
1340
+ if (!(0, import_wormhole_query_sdk.isValidHexString)(userKeyHash) || (0, import_wormhole_query_sdk.hexToUint8Array)(userKeyHash).length !== 32) {
1341
+ throw new QueryHubStateError("INVALID_ARGUMENT", "userKeyHash must be a 32-byte hex string");
1342
+ }
1343
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
1344
+ throw new QueryHubStateError("INVALID_ARGUMENT", "apiKey is required");
1345
+ }
1346
+ const network = resolveNetwork(options);
1347
+ const maxAgeSeconds = options?.maxAge ?? 60;
1348
+ const maxAttempts = options?.maxAttempts ?? 4;
1349
+ const { wormholeChainId, hubAddress, endpoint, rpcUrl } = getHubConfig(network);
1350
+ const provider = new import_ethers4.ethers.JsonRpcProvider(rpcUrl);
1351
+ const callData = encodeHubCalls(hubAddress, userKeyHash);
1352
+ const doFetch = async () => {
1353
+ try {
1354
+ const latestBlock = await provider.getBlockNumber();
1355
+ const blockTag = Math.max(0, latestBlock - 2);
1356
+ const request = new import_wormhole_query_sdk.QueryRequest(Date.now() & 4294967295, [
1357
+ new import_wormhole_query_sdk.PerChainQueryRequest(wormholeChainId, new import_wormhole_query_sdk.EthCallQueryRequest(blockTag, callData))
1358
+ ]);
1359
+ const requestHex = import_buffer.Buffer.from(request.serialize()).toString("hex");
1360
+ const response = await import_axios.default.post(
1361
+ endpoint,
1362
+ { bytes: requestHex },
1363
+ {
1364
+ headers: {
1365
+ "X-API-Key": apiKey,
1366
+ "Content-Type": "application/json"
1367
+ },
1368
+ timeout: 1e4
1369
+ }
1370
+ );
1371
+ const data = response.data;
1372
+ const signatures = data?.signatures;
1373
+ const bytes = data?.bytes;
1374
+ if (!Array.isArray(signatures) || typeof bytes !== "string") {
1375
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Query Proxy response missing signatures/bytes");
1376
+ }
1377
+ const proof = signaturesToProofBytes(signatures);
1378
+ const queryBytes = decodeQueryBytes(bytes);
1379
+ const parsed = import_wormhole_query_sdk.QueryResponse.from(queryBytes);
1380
+ const perChain = parsed.responses.find((r) => r.chainId === wormholeChainId);
1381
+ if (!perChain) {
1382
+ throw new QueryHubStateError("QUERY_RESPONSE_INVALID", "Missing per-chain response for hub chain");
1383
+ }
1384
+ const chainResp = import_wormhole_query_sdk.EthCallQueryResponse.from(perChain.response.serialize());
1385
+ const blockTime = Number(chainResp.blockTime);
1386
+ const nowSeconds = Math.floor(Date.now() / 1e3);
1387
+ if (nowSeconds - blockTime > maxAgeSeconds) {
1388
+ throw new QueryHubStateError(
1389
+ "ATTESTATION_STALE",
1390
+ `Guardian attestation is stale (blockTime=${blockTime}, now=${nowSeconds}, maxAge=${maxAgeSeconds}s)`
1391
+ );
1392
+ }
1393
+ const nonce = decodeFirstNonce(chainResp.results);
1394
+ const isRegistered = decodeFirstIsRegistered(chainResp.results);
1395
+ const lastActionHash = decodeLastActionHash(chainResp.results);
1396
+ return {
1397
+ nonce,
1398
+ isRegistered,
1399
+ blockTime,
1400
+ proof,
1401
+ lastActionHash
1402
+ };
1403
+ } catch (err) {
1404
+ if (err instanceof QueryHubStateError) throw err;
1405
+ if (import_axios.default.isAxiosError(err)) {
1406
+ const ax = err;
1407
+ const status = ax.response?.status;
1408
+ const statusText = ax.response?.statusText;
1409
+ const details = typeof ax.response?.data === "string" ? ax.response?.data : void 0;
1410
+ throw new QueryHubStateError(
1411
+ "PROXY_HTTP_ERROR",
1412
+ `Query Proxy request failed${status ? ` (${status} ${statusText ?? ""})` : ""}${details ? `: ${details}` : ""}`,
1413
+ err
1414
+ );
1415
+ }
1416
+ throw new QueryHubStateError("PROXY_HTTP_ERROR", "Query Proxy request failed", err);
1417
+ }
1418
+ };
1419
+ return await withExponentialBackoff(doFetch, maxAttempts);
1420
+ }
1421
+
1422
+ // src/auth/prepareAuth.ts
1423
+ function resolveNetwork2() {
1424
+ const envCandidates = [
1425
+ globalThis?.process?.env?.NEXT_PUBLIC_VERIDEX_NETWORK,
1426
+ globalThis?.process?.env?.VERIDEX_NETWORK,
1427
+ globalThis?.process?.env?.NEXT_PUBLIC_WORMHOLE_NETWORK,
1428
+ globalThis?.process?.env?.WORMHOLE_NETWORK
1429
+ ].filter(Boolean);
1430
+ const env = envCandidates[0]?.toLowerCase();
1431
+ if (env === "mainnet" || env === "testnet") return env;
1432
+ return "testnet";
1433
+ }
1434
+ function getHubChainId(network) {
1435
+ return network === "testnet" ? TESTNET_CHAINS.baseSepolia.wormholeChainId : MAINNET_CHAINS.base.wormholeChainId;
1436
+ }
1437
+ function getHubRpcUrl(network) {
1438
+ return network === "testnet" ? TESTNET_CHAINS.baseSepolia.rpcUrl : MAINNET_CHAINS.base.rpcUrl;
1439
+ }
1440
+ function getHubAddress(network) {
1441
+ const hub = network === "testnet" ? TESTNET_CHAINS.baseSepolia.contracts.hub : MAINNET_CHAINS.base.contracts?.hub;
1442
+ if (!hub) {
1443
+ throw new Error("Hub address missing in SDK constants");
1444
+ }
1445
+ return hub;
1446
+ }
1447
+ function encodeActionPayload(action, targetChain) {
1448
+ if (action.token !== void 0 && action.recipient !== void 0) {
1449
+ const a2 = action;
1450
+ return encodeTransferAction(a2.token, a2.recipient, a2.amount);
1451
+ }
1452
+ if (action.target !== void 0 && action.data !== void 0) {
1453
+ const a2 = action;
1454
+ return encodeExecuteAction(a2.target, a2.value, a2.data);
1455
+ }
1456
+ const a = action;
1457
+ return encodeBridgeAction(a.token, a.amount, targetChain, a.recipient);
1458
+ }
1459
+ async function fetchNonceViaRpc(userKeyHash, network) {
1460
+ const rpcUrl = getHubRpcUrl(network);
1461
+ const hubAddress = getHubAddress(network);
1462
+ const provider = new import_ethers5.ethers.JsonRpcProvider(rpcUrl);
1463
+ const hubIface = new import_ethers5.ethers.Interface([
1464
+ "function getNonce(bytes32 userKeyHash) external view returns (uint256)",
1465
+ "function userNonces(bytes32 userKeyHash) external view returns (uint256)"
1466
+ ]);
1467
+ const calls = [
1468
+ async () => {
1469
+ const data = hubIface.encodeFunctionData("getNonce", [userKeyHash]);
1470
+ const res = await provider.call({ to: hubAddress, data });
1471
+ const decoded = hubIface.decodeFunctionResult("getNonce", res);
1472
+ return decoded[0];
1473
+ },
1474
+ async () => {
1475
+ const data = hubIface.encodeFunctionData("userNonces", [userKeyHash]);
1476
+ const res = await provider.call({ to: hubAddress, data });
1477
+ const decoded = hubIface.decodeFunctionResult("userNonces", res);
1478
+ return decoded[0];
1479
+ }
1480
+ ];
1481
+ let lastErr;
1482
+ for (const fn of calls) {
1483
+ try {
1484
+ return await fn();
1485
+ } catch (e) {
1486
+ lastErr = e;
1487
+ }
1488
+ }
1489
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
1490
+ }
1491
+ function toHex32(value) {
1492
+ return "0x" + value.toString(16).padStart(64, "0");
1493
+ }
1494
+ async function authenticateAndPrepare(userParams, apiKey) {
1495
+ const network = resolveNetwork2();
1496
+ const hubChainId = getHubChainId(network);
1497
+ const actionPayload = userParams.actionPayload ?? (userParams.action ? encodeActionPayload(userParams.action, userParams.targetChain) : void 0);
1498
+ if (!actionPayload) {
1499
+ throw new Error("Missing action payload: provide either actionPayload or action");
1500
+ }
1501
+ let nonce;
1502
+ let queryProof = new Uint8Array();
1503
+ let fallbackAvailable = true;
1504
+ const queryStart = Date.now();
1505
+ try {
1506
+ if (!apiKey) {
1507
+ throw new Error("Missing Query Proxy apiKey");
1508
+ }
1509
+ const state = await queryHubState(userParams.credential.keyHash, apiKey, { network, maxAge: 60 });
1510
+ if (!state.isRegistered) {
1511
+ throw new Error("Passkey is not registered on Hub");
1512
+ }
1513
+ nonce = state.nonce;
1514
+ queryProof = state.proof;
1515
+ } catch {
1516
+ nonce = await fetchNonceViaRpc(userParams.credential.keyHash, network);
1517
+ queryProof = new Uint8Array();
1518
+ }
1519
+ const queryLatencyMs = Date.now() - queryStart;
1520
+ const challenge = buildGaslessChallenge(userParams.targetChain, actionPayload, nonce, hubChainId);
1521
+ const passkey = new PasskeyManager();
1522
+ passkey.setCredential(userParams.credential);
1523
+ const signature = await passkey.sign(challenge);
1524
+ const requestBody = {
1525
+ authenticatorData: signature.authenticatorData,
1526
+ clientDataJSON: signature.clientDataJSON,
1527
+ challengeIndex: signature.challengeIndex,
1528
+ typeIndex: signature.typeIndex,
1529
+ r: toHex32(signature.r),
1530
+ s: toHex32(signature.s),
1531
+ publicKeyX: toHex32(userParams.credential.publicKeyX),
1532
+ publicKeyY: toHex32(userParams.credential.publicKeyY),
1533
+ targetChain: userParams.targetChain,
1534
+ actionPayload,
1535
+ nonce: Number(nonce)
1536
+ };
1537
+ if (queryProof.length) {
1538
+ requestBody.queryProof = import_ethers5.ethers.hexlify(queryProof);
1539
+ }
1540
+ const serializedTx = new TextEncoder().encode(JSON.stringify(requestBody));
1541
+ const estimatedLatency = queryProof.length ? Math.max(250, queryLatencyMs) : Math.max(800, queryLatencyMs);
1542
+ return {
1543
+ serializedTx,
1544
+ queryProof,
1545
+ estimatedLatency,
1546
+ fallbackAvailable
1547
+ };
1548
+ }
1549
+ // Annotate the CommonJS export names for ESM import in node:
1550
+ 0 && (module.exports = {
1551
+ authenticateAndPrepare
1552
+ });
1553
+ //# sourceMappingURL=prepareAuth.js.map