@veridex/sdk 1.0.0-beta.1

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/LICENSE +21 -0
  3. package/README.md +212 -0
  4. package/dist/chains/aptos/index.d.mts +140 -0
  5. package/dist/chains/aptos/index.d.ts +140 -0
  6. package/dist/chains/aptos/index.js +563 -0
  7. package/dist/chains/aptos/index.js.map +1 -0
  8. package/dist/chains/aptos/index.mjs +536 -0
  9. package/dist/chains/aptos/index.mjs.map +1 -0
  10. package/dist/chains/evm/index.d.mts +5 -0
  11. package/dist/chains/evm/index.d.ts +5 -0
  12. package/dist/chains/evm/index.js +1233 -0
  13. package/dist/chains/evm/index.js.map +1 -0
  14. package/dist/chains/evm/index.mjs +1205 -0
  15. package/dist/chains/evm/index.mjs.map +1 -0
  16. package/dist/chains/solana/index.d.mts +116 -0
  17. package/dist/chains/solana/index.d.ts +116 -0
  18. package/dist/chains/solana/index.js +513 -0
  19. package/dist/chains/solana/index.js.map +1 -0
  20. package/dist/chains/solana/index.mjs +491 -0
  21. package/dist/chains/solana/index.mjs.map +1 -0
  22. package/dist/chains/starknet/index.d.mts +172 -0
  23. package/dist/chains/starknet/index.d.ts +172 -0
  24. package/dist/chains/starknet/index.js +534 -0
  25. package/dist/chains/starknet/index.js.map +1 -0
  26. package/dist/chains/starknet/index.mjs +507 -0
  27. package/dist/chains/starknet/index.mjs.map +1 -0
  28. package/dist/chains/sui/index.d.mts +182 -0
  29. package/dist/chains/sui/index.d.ts +182 -0
  30. package/dist/chains/sui/index.js +560 -0
  31. package/dist/chains/sui/index.js.map +1 -0
  32. package/dist/chains/sui/index.mjs +533 -0
  33. package/dist/chains/sui/index.mjs.map +1 -0
  34. package/dist/constants.d.mts +150 -0
  35. package/dist/constants.d.ts +150 -0
  36. package/dist/constants.js +430 -0
  37. package/dist/constants.js.map +1 -0
  38. package/dist/constants.mjs +392 -0
  39. package/dist/constants.mjs.map +1 -0
  40. package/dist/index-0NXfbk0z.d.ts +637 -0
  41. package/dist/index-D0dLVjTA.d.mts +637 -0
  42. package/dist/index.d.mts +3101 -0
  43. package/dist/index.d.ts +3101 -0
  44. package/dist/index.js +13186 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/index.mjs +13011 -0
  47. package/dist/index.mjs.map +1 -0
  48. package/dist/payload.d.mts +125 -0
  49. package/dist/payload.d.ts +125 -0
  50. package/dist/payload.js +315 -0
  51. package/dist/payload.js.map +1 -0
  52. package/dist/payload.mjs +269 -0
  53. package/dist/payload.mjs.map +1 -0
  54. package/dist/queries/index.d.mts +148 -0
  55. package/dist/queries/index.d.ts +148 -0
  56. package/dist/queries/index.js +1533 -0
  57. package/dist/queries/index.js.map +1 -0
  58. package/dist/queries/index.mjs +1508 -0
  59. package/dist/queries/index.mjs.map +1 -0
  60. package/dist/types-ChIsqCiw.d.mts +565 -0
  61. package/dist/types-ChIsqCiw.d.ts +565 -0
  62. package/dist/types-FJL7j6gQ.d.mts +172 -0
  63. package/dist/types-FJL7j6gQ.d.ts +172 -0
  64. package/dist/types.d.mts +407 -0
  65. package/dist/types.d.ts +407 -0
  66. package/dist/types.js +19 -0
  67. package/dist/types.js.map +1 -0
  68. package/dist/types.mjs +1 -0
  69. package/dist/types.mjs.map +1 -0
  70. package/dist/utils.d.mts +81 -0
  71. package/dist/utils.d.ts +81 -0
  72. package/dist/utils.js +430 -0
  73. package/dist/utils.js.map +1 -0
  74. package/dist/utils.mjs +390 -0
  75. package/dist/utils.mjs.map +1 -0
  76. package/dist/wormhole.d.mts +167 -0
  77. package/dist/wormhole.d.ts +167 -0
  78. package/dist/wormhole.js +468 -0
  79. package/dist/wormhole.js.map +1 -0
  80. package/dist/wormhole.mjs +422 -0
  81. package/dist/wormhole.mjs.map +1 -0
  82. package/package.json +151 -0
@@ -0,0 +1,1533 @@
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/queries/index.ts
31
+ var queries_exports = {};
32
+ __export(queries_exports, {
33
+ QueryHubStateError: () => QueryHubStateError,
34
+ QueryPortfolioError: () => QueryPortfolioError,
35
+ WORMHOLE_QUERY_CHAIN_IDS: () => WORMHOLE_QUERY_CHAIN_IDS,
36
+ WORMHOLE_QUERY_PROXY_URLS: () => WORMHOLE_QUERY_PROXY_URLS,
37
+ WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND: () => WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND,
38
+ queryHubState: () => queryHubState,
39
+ queryPortfolio: () => queryPortfolio
40
+ });
41
+ module.exports = __toCommonJS(queries_exports);
42
+
43
+ // src/queries/constants.ts
44
+ var WORMHOLE_QUERY_PROXY_URLS = {
45
+ mainnet: "https://query.wormhole.com/v1/query",
46
+ testnet: "https://testnet.query.wormhole.com/v1/query"
47
+ };
48
+ var WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND = 6;
49
+ var WORMHOLE_QUERY_CHAIN_IDS = {
50
+ ETHEREUM: 2,
51
+ POLYGON: 5,
52
+ ARBITRUM: 23,
53
+ OPTIMISM: 24,
54
+ BASE: 30
55
+ };
56
+
57
+ // src/queries/hubState.ts
58
+ var import_axios = __toESM(require("axios"));
59
+ var import_buffer = require("buffer");
60
+ var import_ethers = require("ethers");
61
+ var import_wormhole_query_sdk = require("@wormhole-foundation/wormhole-query-sdk");
62
+
63
+ // src/constants.ts
64
+ var TESTNET_CHAINS = {
65
+ baseSepolia: {
66
+ name: "Base Sepolia",
67
+ chainId: 84532,
68
+ wormholeChainId: 10004,
69
+ rpcUrl: "https://sepolia.base.org",
70
+ // Public CORS-friendly RPC
71
+ explorerUrl: "https://sepolia.basescan.org",
72
+ isEvm: true,
73
+ contracts: {
74
+ hub: "0x66D87dE68327f48A099c5B9bE97020Feab9a7c82",
75
+ vaultFactory: "0x40D9B16094808Fa48e73598E31AB964Cf15b475f",
76
+ vaultImplementation: "0xcBEb49b0109E61c1C69C51D5D9483A3aD6D18258",
77
+ wormholeCoreBridge: "0x79A1027a6A159502049F10906D333EC57E95F083",
78
+ tokenBridge: "0x86F55A04690fd7815A3D802bD587e83eA888B239"
79
+ }
80
+ },
81
+ optimismSepolia: {
82
+ name: "Optimism Sepolia",
83
+ chainId: 11155420,
84
+ wormholeChainId: 10005,
85
+ rpcUrl: "https://sepolia.optimism.io",
86
+ explorerUrl: "https://sepolia-optimism.etherscan.io",
87
+ isEvm: true,
88
+ contracts: {
89
+ vaultFactory: "0xAbB421166E648953CDBE93c0078a0A794c56Fb84",
90
+ vaultImplementation: "0xDCD7daEf1AC06f4a8392957cca4834F7a16c058D",
91
+ wormholeCoreBridge: "0x31377888146f3253211EFEf5c676D41ECe7D58Fe",
92
+ tokenBridge: "0x99737Ec4B815d816c49A385943baf0380e75c0Ac"
93
+ }
94
+ },
95
+ arbitrumSepolia: {
96
+ name: "Arbitrum Sepolia",
97
+ chainId: 421614,
98
+ wormholeChainId: 10003,
99
+ rpcUrl: "https://sepolia-rollup.arbitrum.io/rpc",
100
+ explorerUrl: "https://sepolia.arbiscan.io",
101
+ isEvm: true,
102
+ contracts: {
103
+ vaultFactory: "0xd36D3D5DB59d78f1E33813490F72DABC15C9B07c",
104
+ vaultImplementation: "0xB10ACf39eBF17fc33F722cBD955b7aeCB0611bc4",
105
+ wormholeCoreBridge: "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35",
106
+ tokenBridge: "0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"
107
+ }
108
+ },
109
+ seiTestnet: {
110
+ name: "Sei Atlantic-2",
111
+ chainId: 1328,
112
+ wormholeChainId: 40,
113
+ rpcUrl: "https://evm-rpc-testnet.sei-apis.com",
114
+ explorerUrl: "https://seitrace.com/?chain=atlantic-2",
115
+ isEvm: true,
116
+ contracts: {
117
+ vaultFactory: "0x07F608AFf6d63b68029488b726d895c4Bb593038",
118
+ vaultImplementation: "0xD66153fccFB6731fB6c4944FbD607ba86A76a1f6",
119
+ wormholeCoreBridge: "0x0000000000000000000000000000000000000000"
120
+ // Mock - not yet deployed
121
+ }
122
+ },
123
+ solanaDevnet: {
124
+ name: "Solana Devnet",
125
+ chainId: 0,
126
+ wormholeChainId: 1,
127
+ rpcUrl: "https://api.devnet.solana.com",
128
+ explorerUrl: "https://explorer.solana.com",
129
+ isEvm: false,
130
+ contracts: {
131
+ hub: "AnyXHsqq9c2BiW4WgBcj6Aye7Ua7a7L7iSuwpfJxECJM",
132
+ wormholeCoreBridge: "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5",
133
+ tokenBridge: "DZnkkTmCiFWfYTfT41X3Rd1kDgozqzxWaHqsw6W4x2oe"
134
+ }
135
+ },
136
+ aptosTestnet: {
137
+ name: "Aptos Testnet",
138
+ chainId: 0,
139
+ wormholeChainId: 22,
140
+ rpcUrl: "https://fullnode.testnet.aptoslabs.com/v1",
141
+ explorerUrl: "https://explorer.aptoslabs.com",
142
+ isEvm: false,
143
+ contracts: {
144
+ hub: "0x1a89da9e9f8f0bc90d8d492890bd55fb261c6277d2a95dfcac70c268d0c23dcc",
145
+ wormholeCoreBridge: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
146
+ tokenBridge: "0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f"
147
+ }
148
+ },
149
+ suiTestnet: {
150
+ name: "Sui Testnet",
151
+ chainId: 0,
152
+ wormholeChainId: 21,
153
+ rpcUrl: "https://fullnode.testnet.sui.io:443",
154
+ explorerUrl: "https://suiscan.xyz/testnet",
155
+ isEvm: false,
156
+ contracts: {
157
+ hub: "0x35e99fdbbc1cde7e093da6f9e758ba2c4a077904bd64caee2fa6db5e6c4e9e37",
158
+ wormholeCoreBridge: "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790"
159
+ }
160
+ },
161
+ starknetSepolia: {
162
+ name: "Starknet Sepolia",
163
+ chainId: 0,
164
+ // Native Starknet chain ID (SN_SEPOLIA = 0x534e5f5345504f4c4941)
165
+ wormholeChainId: 50001,
166
+ // Custom chain ID (50000+ reserved for non-Wormhole chains)
167
+ rpcUrl: "https://starknet-sepolia.g.alchemy.com/starknet/version/rpc/v0_7/tsOnfTBZDKMXcUA26OED-",
168
+ explorerUrl: "https://sepolia.starkscan.co",
169
+ isEvm: false,
170
+ contracts: {
171
+ // Starknet spoke contract
172
+ hub: "0x68adcc730ed6c355200d00f763825448497b9cdf7936ca121711e078c88e811",
173
+ // Custom bridge contract (NOT Wormhole)
174
+ wormholeCoreBridge: "0x2c458c1ae64556482b05cc2d3ee5b032ed114d68429dda2062c9849a5a725f8"
175
+ },
176
+ // Hub chain ID that Starknet bridge validates (Base Sepolia = 10004)
177
+ hubChainId: 10004
178
+ }
179
+ };
180
+ var MAINNET_CHAINS = {
181
+ ethereum: {
182
+ name: "Ethereum",
183
+ chainId: 1,
184
+ wormholeChainId: 2,
185
+ rpcUrl: "https://eth.llamarpc.com",
186
+ explorerUrl: "https://etherscan.io",
187
+ isEvm: true,
188
+ contracts: {
189
+ wormholeCoreBridge: "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
190
+ tokenBridge: "0x3ee18B2214AFF97000D974cf647E7C347E8fa585"
191
+ }
192
+ },
193
+ base: {
194
+ name: "Base",
195
+ chainId: 8453,
196
+ wormholeChainId: 30,
197
+ rpcUrl: "https://mainnet.base.org",
198
+ explorerUrl: "https://basescan.org",
199
+ isEvm: true,
200
+ contracts: {
201
+ wormholeCoreBridge: "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6",
202
+ tokenBridge: "0x8d2de8d2f73F1F4cAB472AC9A881C9b123C79627"
203
+ }
204
+ },
205
+ optimism: {
206
+ name: "Optimism",
207
+ chainId: 10,
208
+ wormholeChainId: 24,
209
+ rpcUrl: "https://mainnet.optimism.io",
210
+ explorerUrl: "https://optimistic.etherscan.io",
211
+ isEvm: true,
212
+ contracts: {
213
+ wormholeCoreBridge: "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722",
214
+ tokenBridge: "0x1D68124e65faFC907325e3EDbF8c4d84499DAa8b"
215
+ }
216
+ },
217
+ arbitrum: {
218
+ name: "Arbitrum",
219
+ chainId: 42161,
220
+ wormholeChainId: 23,
221
+ rpcUrl: "https://arb1.arbitrum.io/rpc",
222
+ explorerUrl: "https://arbiscan.io",
223
+ isEvm: true,
224
+ contracts: {
225
+ wormholeCoreBridge: "0xa5f208e072434bC67592E4C49C1B991BA79BCA46",
226
+ tokenBridge: "0x0b2402144Bb366A632D14B83F244D2e0e21bD39c"
227
+ }
228
+ },
229
+ polygon: {
230
+ name: "Polygon",
231
+ chainId: 137,
232
+ wormholeChainId: 5,
233
+ rpcUrl: "https://polygon-rpc.com",
234
+ explorerUrl: "https://polygonscan.com",
235
+ isEvm: true,
236
+ contracts: {
237
+ wormholeCoreBridge: "0x7A4B5a56256163F07b2C80A7cA55aBE66c4ec4d7",
238
+ tokenBridge: "0x5a58505a96D1dbf8dF91cB21B54419FC36e93fdE"
239
+ }
240
+ },
241
+ solana: {
242
+ name: "Solana",
243
+ chainId: 0,
244
+ wormholeChainId: 1,
245
+ rpcUrl: "https://api.mainnet-beta.solana.com",
246
+ explorerUrl: "https://explorer.solana.com",
247
+ isEvm: false,
248
+ contracts: {
249
+ wormholeCoreBridge: "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth",
250
+ tokenBridge: "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"
251
+ }
252
+ },
253
+ aptos: {
254
+ name: "Aptos",
255
+ chainId: 0,
256
+ wormholeChainId: 22,
257
+ rpcUrl: "https://fullnode.mainnet.aptoslabs.com/v1",
258
+ explorerUrl: "https://explorer.aptoslabs.com",
259
+ isEvm: false,
260
+ contracts: {
261
+ wormholeCoreBridge: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
262
+ tokenBridge: "0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f"
263
+ }
264
+ },
265
+ sui: {
266
+ name: "Sui",
267
+ chainId: 0,
268
+ wormholeChainId: 21,
269
+ rpcUrl: "https://fullnode.mainnet.sui.io:443",
270
+ explorerUrl: "https://suiscan.xyz/mainnet",
271
+ isEvm: false,
272
+ contracts: {
273
+ wormholeCoreBridge: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c"
274
+ }
275
+ }
276
+ };
277
+
278
+ // src/queries/hubState.ts
279
+ var QueryHubStateError = class extends Error {
280
+ code;
281
+ cause;
282
+ constructor(code, message, cause) {
283
+ super(message);
284
+ this.name = "QueryHubStateError";
285
+ this.code = code;
286
+ this.cause = cause;
287
+ }
288
+ };
289
+ function resolveNetwork(options) {
290
+ if (options?.network) return options.network;
291
+ const envCandidates = [
292
+ globalThis?.process?.env?.NEXT_PUBLIC_VERIDEX_NETWORK,
293
+ globalThis?.process?.env?.VERIDEX_NETWORK,
294
+ globalThis?.process?.env?.NEXT_PUBLIC_WORMHOLE_NETWORK,
295
+ globalThis?.process?.env?.WORMHOLE_NETWORK
296
+ ].filter(Boolean);
297
+ const env = envCandidates[0]?.toLowerCase();
298
+ if (env === "mainnet" || env === "testnet") return env;
299
+ return "testnet";
300
+ }
301
+ function concatBytes(parts) {
302
+ const total = parts.reduce((sum, p) => sum + p.length, 0);
303
+ const out = new Uint8Array(total);
304
+ let offset = 0;
305
+ for (const p of parts) {
306
+ out.set(p, offset);
307
+ offset += p.length;
308
+ }
309
+ return out;
310
+ }
311
+ function signaturesToProofBytes(signatures) {
312
+ const chunks = [];
313
+ for (const sig of signatures) {
314
+ if (typeof sig !== "string" || sig.length !== 132 || !/^[0-9a-fA-F]+$/.test(sig)) {
315
+ throw new QueryHubStateError(
316
+ "PROXY_RESPONSE_INVALID",
317
+ `Invalid guardian signature format (expected 132 hex chars): ${String(sig)}`
318
+ );
319
+ }
320
+ chunks.push((0, import_wormhole_query_sdk.hexToUint8Array)(`0x${sig}`));
321
+ }
322
+ return concatBytes(chunks);
323
+ }
324
+ function decodeQueryBytes(bytes) {
325
+ if (typeof bytes !== "string" || bytes.length === 0) {
326
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Missing query response bytes");
327
+ }
328
+ if ((0, import_wormhole_query_sdk.isValidHexString)(bytes)) {
329
+ return (0, import_wormhole_query_sdk.hexToUint8Array)(bytes);
330
+ }
331
+ try {
332
+ if (typeof atob === "function") {
333
+ const raw = atob(bytes);
334
+ const arr = new Uint8Array(raw.length);
335
+ for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
336
+ return arr;
337
+ }
338
+ } catch {
339
+ }
340
+ try {
341
+ return new Uint8Array(import_buffer.Buffer.from(bytes, "base64"));
342
+ } catch (cause) {
343
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Unrecognized query response bytes encoding", cause);
344
+ }
345
+ }
346
+ function sleep(ms) {
347
+ return new Promise((resolve) => setTimeout(resolve, ms));
348
+ }
349
+ async function withExponentialBackoff(fn, maxAttempts) {
350
+ let attempt = 0;
351
+ let lastError;
352
+ while (attempt < maxAttempts) {
353
+ try {
354
+ return await fn();
355
+ } catch (err) {
356
+ lastError = err;
357
+ attempt += 1;
358
+ if (attempt >= maxAttempts) break;
359
+ const baseMs = 250;
360
+ const backoffMs = Math.min(5e3, baseMs * 2 ** (attempt - 1));
361
+ const jitterMs = Math.floor(Math.random() * 100);
362
+ await sleep(backoffMs + jitterMs);
363
+ }
364
+ }
365
+ throw lastError;
366
+ }
367
+ function getHubConfig(network) {
368
+ if (network === "testnet") {
369
+ const baseSepolia = TESTNET_CHAINS.baseSepolia;
370
+ if (!baseSepolia?.contracts?.hub) {
371
+ throw new QueryHubStateError("MISSING_HUB_ADDRESS", "Missing Base Sepolia hub address in SDK constants");
372
+ }
373
+ return {
374
+ wormholeChainId: baseSepolia.wormholeChainId,
375
+ hubAddress: baseSepolia.contracts.hub,
376
+ endpoint: WORMHOLE_QUERY_PROXY_URLS.testnet,
377
+ rpcUrl: baseSepolia.rpcUrl
378
+ };
379
+ }
380
+ if (network === "mainnet") {
381
+ const base = MAINNET_CHAINS.base;
382
+ const hubAddress = base?.contracts?.hub;
383
+ if (!hubAddress) {
384
+ throw new QueryHubStateError(
385
+ "MISSING_HUB_ADDRESS",
386
+ "Missing mainnet hub address in SDK constants (MAINNET_CHAINS.base.contracts.hub)"
387
+ );
388
+ }
389
+ return {
390
+ wormholeChainId: base.wormholeChainId,
391
+ hubAddress,
392
+ endpoint: WORMHOLE_QUERY_PROXY_URLS.mainnet,
393
+ rpcUrl: base.rpcUrl
394
+ };
395
+ }
396
+ throw new QueryHubStateError("UNSUPPORTED_NETWORK", `Unsupported network: ${network}`);
397
+ }
398
+ function encodeHubCalls(hubAddress, userKeyHash) {
399
+ const nonceAbiCandidates = [
400
+ "function getUserNonce(bytes32 userKeyHash) view returns (uint256)",
401
+ "function userNonces(bytes32 userKeyHash) view returns (uint256)",
402
+ "function getNonceByHash(bytes32 userKeyHash) view returns (uint256)"
403
+ ];
404
+ const registeredAbiCandidates = [
405
+ "function registeredKeys(bytes32 userKeyHash) view returns (bool)",
406
+ "function isKeyRegisteredByHash(bytes32 userKeyHash) view returns (bool)"
407
+ ];
408
+ const actionHashAbiCandidates = [
409
+ "function getUserLastActionHash(bytes32 userKeyHash) view returns (bytes32)",
410
+ "function userLastActionHash(bytes32 userKeyHash) view returns (bytes32)"
411
+ ];
412
+ const iface = new import_ethers.ethers.Interface([
413
+ ...nonceAbiCandidates,
414
+ ...registeredAbiCandidates,
415
+ ...actionHashAbiCandidates
416
+ ]);
417
+ const nonceFnNames = ["getUserNonce", "userNonces", "getNonceByHash"];
418
+ const regFnNames = ["registeredKeys", "isKeyRegisteredByHash"];
419
+ const actionHashFnNames = ["getUserLastActionHash", "userLastActionHash"];
420
+ return [
421
+ ...nonceFnNames.map((fn) => ({
422
+ to: hubAddress,
423
+ data: iface.encodeFunctionData(fn, [userKeyHash])
424
+ })),
425
+ ...regFnNames.map((fn) => ({
426
+ to: hubAddress,
427
+ data: iface.encodeFunctionData(fn, [userKeyHash])
428
+ })),
429
+ ...actionHashFnNames.map((fn) => ({
430
+ to: hubAddress,
431
+ data: iface.encodeFunctionData(fn, [userKeyHash])
432
+ }))
433
+ ];
434
+ }
435
+ function decodeFirstNonce(results) {
436
+ const candidates = [
437
+ "function getUserNonce(bytes32 userKeyHash) view returns (uint256)",
438
+ "function userNonces(bytes32 userKeyHash) view returns (uint256)",
439
+ "function getNonceByHash(bytes32 userKeyHash) view returns (uint256)"
440
+ ];
441
+ const iface = new import_ethers.ethers.Interface(candidates);
442
+ const fnNames = ["getUserNonce", "userNonces", "getNonceByHash"];
443
+ for (let idx = 0; idx < fnNames.length; idx++) {
444
+ const fnName = fnNames[idx];
445
+ try {
446
+ const data = results[idx];
447
+ if (!data || data === "0x") continue;
448
+ const decoded = iface.decodeFunctionResult(fnName, data);
449
+ return decoded[0];
450
+ } catch {
451
+ }
452
+ }
453
+ throw new QueryHubStateError("QUERY_RESPONSE_INVALID", "Unable to decode user nonce from query response");
454
+ }
455
+ function decodeFirstIsRegistered(results) {
456
+ const nonceCandidateCount = 3;
457
+ const candidates = [
458
+ "function registeredKeys(bytes32 userKeyHash) view returns (bool)",
459
+ "function isKeyRegisteredByHash(bytes32 userKeyHash) view returns (bool)"
460
+ ];
461
+ const iface = new import_ethers.ethers.Interface(candidates);
462
+ const fnNames = ["registeredKeys", "isKeyRegisteredByHash"];
463
+ for (let i = 0; i < fnNames.length; i++) {
464
+ const fnName = fnNames[i];
465
+ try {
466
+ const data = results[nonceCandidateCount + i];
467
+ if (!data || data === "0x") continue;
468
+ const decoded = iface.decodeFunctionResult(fnName, data);
469
+ return Boolean(decoded[0]);
470
+ } catch {
471
+ }
472
+ }
473
+ return false;
474
+ }
475
+ function decodeLastActionHash(results) {
476
+ const nonceCandidateCount = 3;
477
+ const registeredCandidateCount = 2;
478
+ const actionHashOffset = nonceCandidateCount + registeredCandidateCount;
479
+ const candidates = [
480
+ "function getUserLastActionHash(bytes32 userKeyHash) view returns (bytes32)",
481
+ "function userLastActionHash(bytes32 userKeyHash) view returns (bytes32)"
482
+ ];
483
+ const iface = new import_ethers.ethers.Interface(candidates);
484
+ const fnNames = ["getUserLastActionHash", "userLastActionHash"];
485
+ for (let i = 0; i < fnNames.length; i++) {
486
+ const fnName = fnNames[i];
487
+ try {
488
+ const data = results[actionHashOffset + i];
489
+ if (!data || data === "0x") continue;
490
+ const decoded = iface.decodeFunctionResult(fnName, data);
491
+ return decoded[0];
492
+ } catch {
493
+ }
494
+ }
495
+ return import_ethers.ethers.ZeroHash;
496
+ }
497
+ async function queryHubState(userKeyHash, apiKey, options) {
498
+ if (typeof userKeyHash !== "string" || userKeyHash.length === 0) {
499
+ throw new QueryHubStateError("INVALID_ARGUMENT", "userKeyHash is required");
500
+ }
501
+ if (!(0, import_wormhole_query_sdk.isValidHexString)(userKeyHash) || (0, import_wormhole_query_sdk.hexToUint8Array)(userKeyHash).length !== 32) {
502
+ throw new QueryHubStateError("INVALID_ARGUMENT", "userKeyHash must be a 32-byte hex string");
503
+ }
504
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
505
+ throw new QueryHubStateError("INVALID_ARGUMENT", "apiKey is required");
506
+ }
507
+ const network = resolveNetwork(options);
508
+ const maxAgeSeconds = options?.maxAge ?? 60;
509
+ const maxAttempts = options?.maxAttempts ?? 4;
510
+ const { wormholeChainId, hubAddress, endpoint, rpcUrl } = getHubConfig(network);
511
+ const provider = new import_ethers.ethers.JsonRpcProvider(rpcUrl);
512
+ const callData = encodeHubCalls(hubAddress, userKeyHash);
513
+ const doFetch = async () => {
514
+ try {
515
+ const latestBlock = await provider.getBlockNumber();
516
+ const blockTag = Math.max(0, latestBlock - 2);
517
+ const request = new import_wormhole_query_sdk.QueryRequest(Date.now() & 4294967295, [
518
+ new import_wormhole_query_sdk.PerChainQueryRequest(wormholeChainId, new import_wormhole_query_sdk.EthCallQueryRequest(blockTag, callData))
519
+ ]);
520
+ const requestHex = import_buffer.Buffer.from(request.serialize()).toString("hex");
521
+ const response = await import_axios.default.post(
522
+ endpoint,
523
+ { bytes: requestHex },
524
+ {
525
+ headers: {
526
+ "X-API-Key": apiKey,
527
+ "Content-Type": "application/json"
528
+ },
529
+ timeout: 1e4
530
+ }
531
+ );
532
+ const data = response.data;
533
+ const signatures = data?.signatures;
534
+ const bytes = data?.bytes;
535
+ if (!Array.isArray(signatures) || typeof bytes !== "string") {
536
+ throw new QueryHubStateError("PROXY_RESPONSE_INVALID", "Query Proxy response missing signatures/bytes");
537
+ }
538
+ const proof = signaturesToProofBytes(signatures);
539
+ const queryBytes = decodeQueryBytes(bytes);
540
+ const parsed = import_wormhole_query_sdk.QueryResponse.from(queryBytes);
541
+ const perChain = parsed.responses.find((r) => r.chainId === wormholeChainId);
542
+ if (!perChain) {
543
+ throw new QueryHubStateError("QUERY_RESPONSE_INVALID", "Missing per-chain response for hub chain");
544
+ }
545
+ const chainResp = import_wormhole_query_sdk.EthCallQueryResponse.from(perChain.response.serialize());
546
+ const blockTime = Number(chainResp.blockTime);
547
+ const nowSeconds = Math.floor(Date.now() / 1e3);
548
+ if (nowSeconds - blockTime > maxAgeSeconds) {
549
+ throw new QueryHubStateError(
550
+ "ATTESTATION_STALE",
551
+ `Guardian attestation is stale (blockTime=${blockTime}, now=${nowSeconds}, maxAge=${maxAgeSeconds}s)`
552
+ );
553
+ }
554
+ const nonce = decodeFirstNonce(chainResp.results);
555
+ const isRegistered = decodeFirstIsRegistered(chainResp.results);
556
+ const lastActionHash = decodeLastActionHash(chainResp.results);
557
+ return {
558
+ nonce,
559
+ isRegistered,
560
+ blockTime,
561
+ proof,
562
+ lastActionHash
563
+ };
564
+ } catch (err) {
565
+ if (err instanceof QueryHubStateError) throw err;
566
+ if (import_axios.default.isAxiosError(err)) {
567
+ const ax = err;
568
+ const status = ax.response?.status;
569
+ const statusText = ax.response?.statusText;
570
+ const details = typeof ax.response?.data === "string" ? ax.response?.data : void 0;
571
+ throw new QueryHubStateError(
572
+ "PROXY_HTTP_ERROR",
573
+ `Query Proxy request failed${status ? ` (${status} ${statusText ?? ""})` : ""}${details ? `: ${details}` : ""}`,
574
+ err
575
+ );
576
+ }
577
+ throw new QueryHubStateError("PROXY_HTTP_ERROR", "Query Proxy request failed", err);
578
+ }
579
+ };
580
+ return await withExponentialBackoff(doFetch, maxAttempts);
581
+ }
582
+
583
+ // src/queries/portfolio.ts
584
+ var import_axios2 = __toESM(require("axios"));
585
+ var import_buffer2 = require("buffer");
586
+ var import_ethers4 = require("ethers");
587
+ var import_web3 = require("@solana/web3.js");
588
+ var import_wormhole_query_sdk2 = require("@wormhole-foundation/wormhole-query-sdk");
589
+
590
+ // src/core/WalletManager.ts
591
+ var import_ethers3 = require("ethers");
592
+
593
+ // src/utils.ts
594
+ var import_ethers2 = require("ethers");
595
+ function computeKeyHash(publicKeyX, publicKeyY) {
596
+ return import_ethers2.ethers.keccak256(
597
+ import_ethers2.ethers.solidityPacked(["uint256", "uint256"], [publicKeyX, publicKeyY])
598
+ );
599
+ }
600
+
601
+ // src/core/WalletManager.ts
602
+ var PROXY_BYTECODE_PREFIX = "0x3d602d80600a3d3981f3363d3d373d3d3d363d73";
603
+ var PROXY_BYTECODE_SUFFIX = "5af43d82803e903d91602b57fd5bf3";
604
+ var WalletManager = class {
605
+ config;
606
+ addressCache = /* @__PURE__ */ new Map();
607
+ constructor(config = {}) {
608
+ this.config = {
609
+ cacheAddresses: config.cacheAddresses ?? true,
610
+ persistToStorage: config.persistToStorage ?? false,
611
+ storageKey: config.storageKey ?? "veridex_wallet_addresses"
612
+ };
613
+ if (this.config.persistToStorage && typeof window !== "undefined") {
614
+ this.loadFromStorage();
615
+ }
616
+ }
617
+ // ========================================================================
618
+ // Address Computation
619
+ // ========================================================================
620
+ /**
621
+ * Compute the deterministic vault address for an EVM chain
622
+ *
623
+ * Uses CREATE2 with EIP-1167 minimal proxy pattern:
624
+ * - Salt = keccak256(factoryAddress, ownerKeyHash)
625
+ * - InitCode = EIP-1167 proxy bytecode with implementation address
626
+ *
627
+ * @param keyHash - The owner's key hash (keccak256 of public key coordinates)
628
+ * @param factoryAddress - The vault factory contract address
629
+ * @param implementationAddress - The vault implementation contract address
630
+ * @returns The deterministic vault address
631
+ */
632
+ computeVaultAddress(keyHash, factoryAddress, implementationAddress) {
633
+ const salt = import_ethers3.ethers.keccak256(
634
+ import_ethers3.ethers.solidityPacked(
635
+ ["address", "bytes32"],
636
+ [factoryAddress, keyHash]
637
+ )
638
+ );
639
+ const initCode = this.buildProxyInitCode(implementationAddress);
640
+ const initCodeHash = import_ethers3.ethers.keccak256(initCode);
641
+ const create2Data = import_ethers3.ethers.solidityPacked(
642
+ ["bytes1", "address", "bytes32", "bytes32"],
643
+ ["0xff", factoryAddress, salt, initCodeHash]
644
+ );
645
+ const hash = import_ethers3.ethers.keccak256(create2Data);
646
+ return import_ethers3.ethers.getAddress("0x" + hash.slice(26));
647
+ }
648
+ /**
649
+ * Compute vault address from public key coordinates
650
+ *
651
+ * @param publicKeyX - P-256 public key X coordinate
652
+ * @param publicKeyY - P-256 public key Y coordinate
653
+ * @param factoryAddress - The vault factory contract address
654
+ * @param implementationAddress - The vault implementation contract address
655
+ * @returns The deterministic vault address
656
+ */
657
+ computeVaultAddressFromPublicKey(publicKeyX, publicKeyY, factoryAddress, implementationAddress) {
658
+ const keyHash = computeKeyHash(publicKeyX, publicKeyY);
659
+ return this.computeVaultAddress(keyHash, factoryAddress, implementationAddress);
660
+ }
661
+ /**
662
+ * Build EIP-1167 minimal proxy initcode
663
+ */
664
+ buildProxyInitCode(implementationAddress) {
665
+ const impl = implementationAddress.toLowerCase().replace("0x", "");
666
+ return PROXY_BYTECODE_PREFIX + impl + PROXY_BYTECODE_SUFFIX;
667
+ }
668
+ // ========================================================================
669
+ // Unified Identity
670
+ // ========================================================================
671
+ /**
672
+ * Get unified identity with addresses across all configured chains
673
+ *
674
+ * @param credential - The passkey credential
675
+ * @param chainConfigs - Map of chain configurations with factory/implementation addresses
676
+ * @returns Unified identity with addresses on each chain
677
+ */
678
+ async getUnifiedIdentity(credential, chainConfigs) {
679
+ const addresses = [];
680
+ for (const [wormholeChainId, config] of chainConfigs) {
681
+ const address = await this.deriveAddressForChain(
682
+ credential,
683
+ wormholeChainId,
684
+ config
685
+ );
686
+ if (address) {
687
+ addresses.push(address);
688
+ }
689
+ }
690
+ const identity = {
691
+ keyHash: credential.keyHash,
692
+ publicKeyX: credential.publicKeyX,
693
+ publicKeyY: credential.publicKeyY,
694
+ credentialId: credential.credentialId,
695
+ addresses,
696
+ createdAt: Date.now(),
697
+ updatedAt: Date.now()
698
+ };
699
+ if (this.config.cacheAddresses) {
700
+ this.addressCache.set(credential.keyHash, addresses);
701
+ }
702
+ if (this.config.persistToStorage) {
703
+ this.saveToStorage(identity);
704
+ }
705
+ return identity;
706
+ }
707
+ /**
708
+ * Derive address for a specific chain
709
+ */
710
+ async deriveAddressForChain(credential, wormholeChainId, config) {
711
+ if (config.isEvm) {
712
+ if (!config.factoryAddress || !config.implementationAddress) {
713
+ return null;
714
+ }
715
+ const address = this.computeVaultAddress(
716
+ credential.keyHash,
717
+ config.factoryAddress,
718
+ config.implementationAddress
719
+ );
720
+ return {
721
+ wormholeChainId,
722
+ chainName: config.chainName,
723
+ address,
724
+ isEvm: true,
725
+ deployed: false
726
+ // Will be checked separately
727
+ };
728
+ } else {
729
+ return this.deriveNonEvmAddress(credential, wormholeChainId, config);
730
+ }
731
+ }
732
+ /**
733
+ * Derive address for non-EVM chains
734
+ *
735
+ * Each chain has its own address format:
736
+ * - Solana: Base58 encoded public key hash
737
+ * - Aptos: 32-byte hex address
738
+ * - Sui: 32-byte hex address with 0x prefix
739
+ */
740
+ deriveNonEvmAddress(credential, wormholeChainId, _config) {
741
+ switch (wormholeChainId) {
742
+ case 1:
743
+ return {
744
+ wormholeChainId: 1,
745
+ chainName: "Solana",
746
+ address: credential.keyHash,
747
+ // PDA will be derived from this
748
+ isEvm: false,
749
+ derivationType: "pda",
750
+ deployed: false
751
+ };
752
+ case 22:
753
+ return {
754
+ wormholeChainId: 22,
755
+ chainName: "Aptos",
756
+ address: credential.keyHash,
757
+ isEvm: false,
758
+ derivationType: "resource_account",
759
+ deployed: false
760
+ };
761
+ case 21:
762
+ return {
763
+ wormholeChainId: 21,
764
+ chainName: "Sui",
765
+ address: credential.keyHash,
766
+ isEvm: false,
767
+ derivationType: "object",
768
+ deployed: false
769
+ };
770
+ default:
771
+ return null;
772
+ }
773
+ }
774
+ // ========================================================================
775
+ // Address Lookup
776
+ // ========================================================================
777
+ /**
778
+ * Get cached address for a chain
779
+ */
780
+ getAddressForChain(keyHash, wormholeChainId) {
781
+ const addresses = this.addressCache.get(keyHash);
782
+ return addresses?.find((a) => a.wormholeChainId === wormholeChainId);
783
+ }
784
+ /**
785
+ * Get all cached addresses for a key hash
786
+ */
787
+ getAddresses(keyHash) {
788
+ return this.addressCache.get(keyHash) ?? [];
789
+ }
790
+ /**
791
+ * Update deployment status for an address
792
+ */
793
+ updateDeploymentStatus(keyHash, wormholeChainId, deployed, deploymentTxHash) {
794
+ const addresses = this.addressCache.get(keyHash);
795
+ if (!addresses) return;
796
+ const address = addresses.find((a) => a.wormholeChainId === wormholeChainId);
797
+ if (address) {
798
+ address.deployed = deployed;
799
+ address.deploymentTxHash = deploymentTxHash;
800
+ }
801
+ if (this.config.persistToStorage) {
802
+ this.saveAddressesToStorage(keyHash, addresses);
803
+ }
804
+ }
805
+ // ========================================================================
806
+ // Storage
807
+ // ========================================================================
808
+ /**
809
+ * Load addresses from localStorage
810
+ */
811
+ loadFromStorage() {
812
+ if (typeof window === "undefined") return;
813
+ try {
814
+ const stored = localStorage.getItem(this.config.storageKey);
815
+ if (!stored) return;
816
+ const data = JSON.parse(stored);
817
+ for (const [keyHash, addresses] of Object.entries(data.addresses)) {
818
+ this.addressCache.set(keyHash, addresses);
819
+ }
820
+ } catch (error) {
821
+ console.warn("Failed to load wallet addresses from storage:", error);
822
+ }
823
+ }
824
+ /**
825
+ * Save identity to localStorage
826
+ */
827
+ saveToStorage(identity) {
828
+ if (typeof window === "undefined") return;
829
+ try {
830
+ const stored = localStorage.getItem(this.config.storageKey) ?? "{}";
831
+ const data = JSON.parse(stored);
832
+ if (!data.addresses) {
833
+ data.addresses = {};
834
+ }
835
+ data.addresses[identity.keyHash] = identity.addresses;
836
+ data.identities = data.identities ?? {};
837
+ data.identities[identity.keyHash] = {
838
+ keyHash: identity.keyHash,
839
+ publicKeyX: identity.publicKeyX.toString(),
840
+ publicKeyY: identity.publicKeyY.toString(),
841
+ credentialId: identity.credentialId,
842
+ createdAt: identity.createdAt,
843
+ updatedAt: identity.updatedAt
844
+ };
845
+ localStorage.setItem(this.config.storageKey, JSON.stringify(data));
846
+ } catch (error) {
847
+ console.warn("Failed to save wallet addresses to storage:", error);
848
+ }
849
+ }
850
+ /**
851
+ * Save addresses to localStorage
852
+ */
853
+ saveAddressesToStorage(keyHash, addresses) {
854
+ if (typeof window === "undefined") return;
855
+ try {
856
+ const stored = localStorage.getItem(this.config.storageKey) ?? "{}";
857
+ const data = JSON.parse(stored);
858
+ if (!data.addresses) {
859
+ data.addresses = {};
860
+ }
861
+ data.addresses[keyHash] = addresses;
862
+ localStorage.setItem(this.config.storageKey, JSON.stringify(data));
863
+ } catch (error) {
864
+ console.warn("Failed to save wallet addresses to storage:", error);
865
+ }
866
+ }
867
+ /**
868
+ * Clear all cached data
869
+ */
870
+ clearCache() {
871
+ this.addressCache.clear();
872
+ if (this.config.persistToStorage && typeof window !== "undefined") {
873
+ localStorage.removeItem(this.config.storageKey);
874
+ }
875
+ }
876
+ /**
877
+ * Load identity from storage
878
+ */
879
+ loadIdentityFromStorage(keyHash) {
880
+ if (typeof window === "undefined") return null;
881
+ try {
882
+ const stored = localStorage.getItem(this.config.storageKey);
883
+ if (!stored) return null;
884
+ const data = JSON.parse(stored);
885
+ const storedIdentity = data.identities?.[keyHash];
886
+ const addresses = data.addresses?.[keyHash];
887
+ if (!storedIdentity || !addresses) return null;
888
+ return {
889
+ keyHash: storedIdentity.keyHash,
890
+ publicKeyX: BigInt(storedIdentity.publicKeyX),
891
+ publicKeyY: BigInt(storedIdentity.publicKeyY),
892
+ credentialId: storedIdentity.credentialId,
893
+ addresses,
894
+ createdAt: storedIdentity.createdAt,
895
+ updatedAt: storedIdentity.updatedAt
896
+ };
897
+ } catch (error) {
898
+ console.warn("Failed to load identity from storage:", error);
899
+ return null;
900
+ }
901
+ }
902
+ };
903
+
904
+ // src/constants/tokens.ts
905
+ var NATIVE_TOKEN_ADDRESS = "native";
906
+ var EVM_ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
907
+ var BASE_SEPOLIA_TOKENS = {
908
+ wormholeChainId: 10004,
909
+ chainName: "Base Sepolia",
910
+ nativeToken: {
911
+ symbol: "ETH",
912
+ name: "Ether",
913
+ address: NATIVE_TOKEN_ADDRESS,
914
+ decimals: 18,
915
+ isNative: true
916
+ },
917
+ tokens: [
918
+ {
919
+ symbol: "USDC",
920
+ name: "USD Coin (Test)",
921
+ address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
922
+ // Circle test USDC
923
+ decimals: 6,
924
+ isNative: false
925
+ },
926
+ {
927
+ symbol: "WETH",
928
+ name: "Wrapped Ether",
929
+ address: "0x4200000000000000000000000000000000000006",
930
+ decimals: 18,
931
+ isNative: false
932
+ }
933
+ ]
934
+ };
935
+ var OPTIMISM_SEPOLIA_TOKENS = {
936
+ wormholeChainId: 10005,
937
+ chainName: "Optimism Sepolia",
938
+ nativeToken: {
939
+ symbol: "ETH",
940
+ name: "Ether",
941
+ address: NATIVE_TOKEN_ADDRESS,
942
+ decimals: 18,
943
+ isNative: true
944
+ },
945
+ tokens: [
946
+ {
947
+ symbol: "USDC",
948
+ name: "USD Coin (Test)",
949
+ address: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
950
+ // Test USDC
951
+ decimals: 6,
952
+ isNative: false
953
+ },
954
+ {
955
+ symbol: "WETH",
956
+ name: "Wrapped Ether",
957
+ address: "0x4200000000000000000000000000000000000006",
958
+ decimals: 18,
959
+ isNative: false
960
+ }
961
+ ]
962
+ };
963
+ var ARBITRUM_SEPOLIA_TOKENS = {
964
+ wormholeChainId: 10003,
965
+ chainName: "Arbitrum Sepolia",
966
+ nativeToken: {
967
+ symbol: "ETH",
968
+ name: "Ether",
969
+ address: NATIVE_TOKEN_ADDRESS,
970
+ decimals: 18,
971
+ isNative: true
972
+ },
973
+ tokens: [
974
+ {
975
+ symbol: "USDC",
976
+ name: "USD Coin (Test)",
977
+ address: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
978
+ // Circle USDC Arbitrum Sepolia
979
+ decimals: 6,
980
+ isNative: false
981
+ },
982
+ {
983
+ symbol: "WETH",
984
+ name: "Wrapped Ether",
985
+ address: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73",
986
+ decimals: 18,
987
+ isNative: false
988
+ }
989
+ ]
990
+ };
991
+ var TOKEN_REGISTRY = {
992
+ 10004: BASE_SEPOLIA_TOKENS,
993
+ 10005: OPTIMISM_SEPOLIA_TOKENS,
994
+ 10003: ARBITRUM_SEPOLIA_TOKENS
995
+ };
996
+ function getTokenList(wormholeChainId) {
997
+ return TOKEN_REGISTRY[wormholeChainId] ?? null;
998
+ }
999
+ function getAllTokens(wormholeChainId) {
1000
+ const list = getTokenList(wormholeChainId);
1001
+ if (!list) return [];
1002
+ return [list.nativeToken, ...list.tokens];
1003
+ }
1004
+ function isNativeToken(address) {
1005
+ return address.toLowerCase() === NATIVE_TOKEN_ADDRESS || address === EVM_ZERO_ADDRESS;
1006
+ }
1007
+
1008
+ // src/queries/portfolio.ts
1009
+ var QueryPortfolioError = class extends Error {
1010
+ code;
1011
+ cause;
1012
+ constructor(code, message, cause) {
1013
+ super(message);
1014
+ this.name = "QueryPortfolioError";
1015
+ this.code = code;
1016
+ this.cause = cause;
1017
+ }
1018
+ };
1019
+ var PORTFOLIO_CACHE = /* @__PURE__ */ new Map();
1020
+ function resolveNetwork2(options) {
1021
+ if (options?.network) return options.network;
1022
+ const envCandidates = [
1023
+ globalThis?.process?.env?.NEXT_PUBLIC_VERIDEX_NETWORK,
1024
+ globalThis?.process?.env?.VERIDEX_NETWORK,
1025
+ globalThis?.process?.env?.NEXT_PUBLIC_WORMHOLE_NETWORK,
1026
+ globalThis?.process?.env?.WORMHOLE_NETWORK
1027
+ ].filter(Boolean);
1028
+ const env = envCandidates[0]?.toLowerCase();
1029
+ if (env === "mainnet" || env === "testnet") return env;
1030
+ return "testnet";
1031
+ }
1032
+ function concatBytes2(parts) {
1033
+ const total = parts.reduce((sum, p) => sum + p.length, 0);
1034
+ const out = new Uint8Array(total);
1035
+ let offset = 0;
1036
+ for (const p of parts) {
1037
+ out.set(p, offset);
1038
+ offset += p.length;
1039
+ }
1040
+ return out;
1041
+ }
1042
+ function signaturesToProofBytes2(signatures) {
1043
+ const chunks = [];
1044
+ for (const sig of signatures) {
1045
+ if (typeof sig !== "string" || sig.length !== 132 || !/^[0-9a-fA-F]+$/.test(sig)) {
1046
+ throw new QueryPortfolioError(
1047
+ "PROXY_RESPONSE_INVALID",
1048
+ `Invalid guardian signature format (expected 132 hex chars): ${String(sig)}`
1049
+ );
1050
+ }
1051
+ chunks.push((0, import_wormhole_query_sdk2.hexToUint8Array)(`0x${sig}`));
1052
+ }
1053
+ return concatBytes2(chunks);
1054
+ }
1055
+ function decodeQueryBytes2(bytes) {
1056
+ if (typeof bytes !== "string" || bytes.length === 0) {
1057
+ throw new QueryPortfolioError("PROXY_RESPONSE_INVALID", "Missing query response bytes");
1058
+ }
1059
+ if ((0, import_wormhole_query_sdk2.isValidHexString)(bytes)) {
1060
+ return (0, import_wormhole_query_sdk2.hexToUint8Array)(bytes);
1061
+ }
1062
+ try {
1063
+ if (typeof atob === "function") {
1064
+ const raw = atob(bytes);
1065
+ const arr = new Uint8Array(raw.length);
1066
+ for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
1067
+ return arr;
1068
+ }
1069
+ } catch {
1070
+ }
1071
+ try {
1072
+ return new Uint8Array(import_buffer2.Buffer.from(bytes, "base64"));
1073
+ } catch (cause) {
1074
+ throw new QueryPortfolioError("PROXY_RESPONSE_INVALID", "Unrecognized query response bytes encoding", cause);
1075
+ }
1076
+ }
1077
+ function sleep2(ms) {
1078
+ return new Promise((resolve) => setTimeout(resolve, ms));
1079
+ }
1080
+ var rateLimiter = {
1081
+ queue: [],
1082
+ lastRequest: 0,
1083
+ minInterval: 170,
1084
+ // ~6 req/s with buffer
1085
+ async acquire() {
1086
+ return new Promise((resolve) => {
1087
+ const tryAcquire = () => {
1088
+ const now = Date.now();
1089
+ const elapsed = now - rateLimiter.lastRequest;
1090
+ if (elapsed >= rateLimiter.minInterval) {
1091
+ rateLimiter.lastRequest = now;
1092
+ resolve();
1093
+ } else {
1094
+ setTimeout(tryAcquire, rateLimiter.minInterval - elapsed);
1095
+ }
1096
+ };
1097
+ tryAcquire();
1098
+ });
1099
+ }
1100
+ };
1101
+ async function withExponentialBackoff2(fn, maxAttempts) {
1102
+ let attempt = 0;
1103
+ let lastError;
1104
+ while (attempt < maxAttempts) {
1105
+ try {
1106
+ return await fn();
1107
+ } catch (err) {
1108
+ lastError = err;
1109
+ attempt += 1;
1110
+ if (attempt >= maxAttempts) break;
1111
+ const baseMs = 250;
1112
+ const backoffMs = Math.min(5e3, baseMs * 2 ** (attempt - 1));
1113
+ const jitterMs = Math.floor(Math.random() * 100);
1114
+ await sleep2(backoffMs + jitterMs);
1115
+ }
1116
+ }
1117
+ throw lastError;
1118
+ }
1119
+ function getChainConfigs(network) {
1120
+ return network === "testnet" ? TESTNET_CHAINS : MAINNET_CHAINS;
1121
+ }
1122
+ function getProxyEndpoint(network, options) {
1123
+ if (options?.endpoint) return options.endpoint;
1124
+ return network === "testnet" ? WORMHOLE_QUERY_PROXY_URLS.testnet : WORMHOLE_QUERY_PROXY_URLS.mainnet;
1125
+ }
1126
+ function getDefaultPortfolioChains(network) {
1127
+ return network === "testnet" ? [10004, 10005, 10003, 1] : [30, 24, 23, 1];
1128
+ }
1129
+ function normalizeHex32(hex) {
1130
+ if (typeof hex !== "string" || !/^0x[0-9a-fA-F]{64}$/.test(hex)) {
1131
+ throw new QueryPortfolioError("INVALID_ARGUMENT", `Invalid userKeyHash (expected 0x + 32 bytes hex): ${hex}`);
1132
+ }
1133
+ return new Uint8Array(import_buffer2.Buffer.from(hex.slice(2), "hex"));
1134
+ }
1135
+ function deriveSolanaVaultAddress(programIdBase58, userKeyHash) {
1136
+ const programId = new import_web3.PublicKey(programIdBase58);
1137
+ const keyHashBytes = normalizeHex32(userKeyHash);
1138
+ const [vaultPda] = import_web3.PublicKey.findProgramAddressSync([import_buffer2.Buffer.from("vault"), import_buffer2.Buffer.from(keyHashBytes)], programId);
1139
+ return vaultPda.toBase58();
1140
+ }
1141
+ function makeCacheKey(userKeyHash, apiKey, options, chainIds) {
1142
+ const payload = {
1143
+ userKeyHash,
1144
+ network: resolveNetwork2(options),
1145
+ chainIds: [...chainIds].sort((a, b) => a - b),
1146
+ vaultAddresses: options?.vaultAddresses ?? null,
1147
+ evmTokenAddresses: options?.evmTokenAddresses ?? null,
1148
+ solanaAccounts: options?.solanaAccounts ?? null,
1149
+ endpoint: options?.endpoint ?? null
1150
+ };
1151
+ void apiKey;
1152
+ return JSON.stringify(payload);
1153
+ }
1154
+ function safeNumberFromBigint(b) {
1155
+ const n = Number(b);
1156
+ return Number.isFinite(n) ? n : 0;
1157
+ }
1158
+ function priceLookup(pricesUsd, symbol, assetId) {
1159
+ if (!pricesUsd) return void 0;
1160
+ if (symbol && pricesUsd[symbol] != null) return pricesUsd[symbol];
1161
+ if (assetId && pricesUsd[assetId] != null) return pricesUsd[assetId];
1162
+ return void 0;
1163
+ }
1164
+ function sumUsd(balances, pricesUsd) {
1165
+ let total = 0;
1166
+ let any = false;
1167
+ for (const b of balances) {
1168
+ const p = priceLookup(pricesUsd, b.symbol, b.assetId);
1169
+ if (p == null) continue;
1170
+ if (b.decimals == null) continue;
1171
+ const denom = 10 ** b.decimals;
1172
+ const amount = Number(b.amount) / denom;
1173
+ if (!Number.isFinite(amount)) continue;
1174
+ total += amount * p;
1175
+ any = true;
1176
+ }
1177
+ return any ? total : void 0;
1178
+ }
1179
+ async function getRecentBlockTag(rpcUrl) {
1180
+ const provider = new import_ethers4.ethers.JsonRpcProvider(rpcUrl);
1181
+ const latest = await provider.getBlockNumber();
1182
+ return Math.max(0, latest - 2);
1183
+ }
1184
+ function encodeErc20BalanceCalls(vaultAddress, tokenAddresses) {
1185
+ const iface = new import_ethers4.ethers.Interface(["function balanceOf(address owner) view returns (uint256)"]);
1186
+ return tokenAddresses.map((token) => ({
1187
+ to: token,
1188
+ data: iface.encodeFunctionData("balanceOf", [vaultAddress])
1189
+ }));
1190
+ }
1191
+ function decodeErc20Balances(results, tokenAddresses) {
1192
+ const iface = new import_ethers4.ethers.Interface(["function balanceOf(address owner) view returns (uint256)"]);
1193
+ const out = [];
1194
+ for (let i = 0; i < tokenAddresses.length; i++) {
1195
+ const data = results[i];
1196
+ if (!data || data === "0x") {
1197
+ out.push(0n);
1198
+ continue;
1199
+ }
1200
+ const decoded = iface.decodeFunctionResult("balanceOf", data);
1201
+ out.push(decoded[0]);
1202
+ }
1203
+ return out;
1204
+ }
1205
+ async function queryPortfolio(userKeyHash, apiKey, options) {
1206
+ const network = resolveNetwork2(options);
1207
+ const maxAgeSeconds = options?.maxAge ?? 60;
1208
+ const maxAttempts = options?.maxAttempts ?? 4;
1209
+ const cacheTtlMs = options?.cacheTtlMs ?? 3e4;
1210
+ const endpoint = getProxyEndpoint(network, options);
1211
+ const chainIds = options?.rpcUrls ? Object.keys(options.rpcUrls).map(Number) : void 0;
1212
+ void chainIds;
1213
+ const requestedChains = options?.vaultAddresses ? Object.keys(options.vaultAddresses).map(Number) : void 0;
1214
+ const defaultChains = getDefaultPortfolioChains(network);
1215
+ const wormholeChainIds = options?.evmTokenAddresses ? Array.from(/* @__PURE__ */ new Set([...defaultChains, ...Object.keys(options.evmTokenAddresses).map(Number)])) : defaultChains;
1216
+ const finalChainIds = requestedChains ? Array.from(/* @__PURE__ */ new Set([...wormholeChainIds, ...requestedChains])) : wormholeChainIds;
1217
+ if (!apiKey || typeof apiKey !== "string") {
1218
+ throw new QueryPortfolioError("INVALID_ARGUMENT", "Missing Query Proxy apiKey");
1219
+ }
1220
+ const cacheKey = makeCacheKey(userKeyHash, apiKey, options, finalChainIds);
1221
+ const cached = PORTFOLIO_CACHE.get(cacheKey);
1222
+ if (cached && cached.expiresAt > Date.now()) {
1223
+ return cached.value;
1224
+ }
1225
+ const chains = getChainConfigs(network);
1226
+ const walletManager = new WalletManager({ cacheAddresses: true, persistToStorage: false });
1227
+ const pricesUsd = {
1228
+ USDC: 1,
1229
+ ...options?.pricesUsd ?? {}
1230
+ };
1231
+ const doFetch = async () => {
1232
+ try {
1233
+ const perChainRequests = [];
1234
+ const chainMeta = {};
1235
+ const evmChainIds = finalChainIds.filter((id) => {
1236
+ const cfg = Object.values(chains).find((c) => c.wormholeChainId === id);
1237
+ return cfg?.isEvm;
1238
+ });
1239
+ const blockTagsByChainId = /* @__PURE__ */ new Map();
1240
+ await Promise.all(
1241
+ evmChainIds.map(async (wormholeChainId) => {
1242
+ const cfg = Object.values(chains).find((c) => c.wormholeChainId === wormholeChainId);
1243
+ if (!cfg) return;
1244
+ const rpcUrl = options?.rpcUrls?.[wormholeChainId] ?? cfg.rpcUrl;
1245
+ const blockTag = await getRecentBlockTag(rpcUrl);
1246
+ blockTagsByChainId.set(wormholeChainId, blockTag);
1247
+ })
1248
+ );
1249
+ for (const wormholeChainId of finalChainIds) {
1250
+ const cfg = Object.values(chains).find((c) => c.wormholeChainId === wormholeChainId);
1251
+ if (wormholeChainId === 22) {
1252
+ chainMeta[wormholeChainId] = { kind: "unsupported", chainName: cfg?.name };
1253
+ continue;
1254
+ }
1255
+ if (!cfg) {
1256
+ chainMeta[wormholeChainId] = { kind: "unsupported" };
1257
+ continue;
1258
+ }
1259
+ if (cfg.isEvm) {
1260
+ const vaultAddress = options?.vaultAddresses?.[wormholeChainId] ?? (cfg.contracts.vaultFactory && cfg.contracts.vaultImplementation ? walletManager.computeVaultAddress(userKeyHash, cfg.contracts.vaultFactory, cfg.contracts.vaultImplementation) : void 0);
1261
+ chainMeta[wormholeChainId] = { kind: "evm", vaultAddress, chainName: cfg.name };
1262
+ if (!vaultAddress) {
1263
+ continue;
1264
+ }
1265
+ const tokenAddresses = options?.evmTokenAddresses?.[wormholeChainId] ?? getAllTokens(wormholeChainId).filter((t) => !isNativeToken(t.address)).map((t) => t.address);
1266
+ if (!tokenAddresses.length) {
1267
+ continue;
1268
+ }
1269
+ const blockTag = blockTagsByChainId.get(wormholeChainId);
1270
+ if (blockTag == null) {
1271
+ continue;
1272
+ }
1273
+ const calls = encodeErc20BalanceCalls(vaultAddress, tokenAddresses);
1274
+ perChainRequests.push(new import_wormhole_query_sdk2.PerChainQueryRequest(wormholeChainId, new import_wormhole_query_sdk2.EthCallQueryRequest(blockTag, calls)));
1275
+ continue;
1276
+ }
1277
+ if (wormholeChainId === 1) {
1278
+ const programId = options?.vaultAddresses?.[wormholeChainId] ?? cfg.contracts.hub;
1279
+ chainMeta[wormholeChainId] = { kind: "solana", chainName: cfg.name };
1280
+ if (!programId) {
1281
+ continue;
1282
+ }
1283
+ const vaultAddress = deriveSolanaVaultAddress(programId, userKeyHash);
1284
+ chainMeta[wormholeChainId] = { kind: "solana", chainName: cfg.name, vaultAddress };
1285
+ const accounts = [vaultAddress, ...options?.solanaAccounts ?? []];
1286
+ perChainRequests.push(
1287
+ new import_wormhole_query_sdk2.PerChainQueryRequest(
1288
+ wormholeChainId,
1289
+ new import_wormhole_query_sdk2.SolanaAccountQueryRequest("finalized", accounts, void 0, 0n, 0n)
1290
+ )
1291
+ );
1292
+ continue;
1293
+ }
1294
+ chainMeta[wormholeChainId] = { kind: "unsupported", chainName: cfg.name };
1295
+ }
1296
+ if (perChainRequests.length > 255) {
1297
+ throw new QueryPortfolioError(
1298
+ "INVALID_ARGUMENT",
1299
+ `Too many per-chain requests (${perChainRequests.length}); max is 255 per Query Proxy request`
1300
+ );
1301
+ }
1302
+ if (perChainRequests.length === 0) {
1303
+ const chainsOut2 = finalChainIds.map((wormholeChainId) => {
1304
+ const meta = chainMeta[wormholeChainId] ?? { kind: "unsupported" };
1305
+ return {
1306
+ wormholeChainId,
1307
+ chainName: meta.chainName,
1308
+ vaultAddress: meta.vaultAddress,
1309
+ balances: [],
1310
+ error: {
1311
+ code: "MISSING_VAULT",
1312
+ message: "Unable to build query: missing vault address or no tokens configured"
1313
+ }
1314
+ };
1315
+ });
1316
+ return { proof: new Uint8Array(), totalUsd: 0, chains: chainsOut2 };
1317
+ }
1318
+ const request = new import_wormhole_query_sdk2.QueryRequest(Date.now() & 4294967295, perChainRequests);
1319
+ const requestHex = import_buffer2.Buffer.from(request.serialize()).toString("hex");
1320
+ await rateLimiter.acquire();
1321
+ let response;
1322
+ try {
1323
+ console.log("[queryPortfolio] Sending request to:", endpoint);
1324
+ console.log("[queryPortfolio] Request bytes length:", requestHex.length);
1325
+ const defaultTimeout = network === "testnet" ? 15e3 : 1e4;
1326
+ const requestTimeout = options?.timeout ?? defaultTimeout;
1327
+ response = await import_axios2.default.post(
1328
+ endpoint,
1329
+ { bytes: requestHex },
1330
+ {
1331
+ headers: {
1332
+ "X-API-Key": apiKey,
1333
+ "Content-Type": "application/json"
1334
+ },
1335
+ timeout: requestTimeout
1336
+ }
1337
+ );
1338
+ } catch (axiosErr) {
1339
+ const err = axiosErr;
1340
+ if (!err.response) {
1341
+ console.error("[queryPortfolio] Network error:", {
1342
+ message: err.message,
1343
+ code: err.code,
1344
+ name: err.name
1345
+ });
1346
+ throw new QueryPortfolioError(
1347
+ "NETWORK_ERROR",
1348
+ `Network error: ${err.message} (code: ${err.code})`
1349
+ );
1350
+ }
1351
+ const status = err.response.status;
1352
+ const errData = err.response.data;
1353
+ console.error("[queryPortfolio] Wormhole Query Proxy error:", { status, data: errData });
1354
+ throw new QueryPortfolioError(
1355
+ "PROXY_RESPONSE_INVALID",
1356
+ `Query Proxy returned ${status}: ${JSON.stringify(errData)}`
1357
+ );
1358
+ }
1359
+ const data = response.data;
1360
+ const signatures = data?.signatures;
1361
+ const bytes = data?.bytes;
1362
+ if (!Array.isArray(signatures) || typeof bytes !== "string") {
1363
+ throw new QueryPortfolioError("PROXY_RESPONSE_INVALID", "Query Proxy response missing signatures/bytes");
1364
+ }
1365
+ const proof = signaturesToProofBytes2(signatures);
1366
+ const queryBytes = decodeQueryBytes2(bytes);
1367
+ const parsed = import_wormhole_query_sdk2.QueryResponse.from(queryBytes);
1368
+ const nowSeconds = Math.floor(Date.now() / 1e3);
1369
+ const chainsOut = [];
1370
+ for (const wormholeChainId of finalChainIds) {
1371
+ const meta = chainMeta[wormholeChainId] ?? { kind: "unsupported" };
1372
+ if (meta.kind === "unsupported") {
1373
+ const msg = wormholeChainId === 22 ? "Aptos Queries are not supported by the Wormhole Query SDK yet" : "Chain is not supported by this portfolio query implementation";
1374
+ chainsOut.push({
1375
+ wormholeChainId,
1376
+ chainName: meta.chainName,
1377
+ balances: [],
1378
+ error: { code: "UNSUPPORTED_CHAIN", message: msg }
1379
+ });
1380
+ continue;
1381
+ }
1382
+ if (!meta.vaultAddress) {
1383
+ chainsOut.push({
1384
+ wormholeChainId,
1385
+ chainName: meta.chainName,
1386
+ balances: [],
1387
+ error: { code: "MISSING_VAULT", message: "Unable to derive vault address for chain" }
1388
+ });
1389
+ continue;
1390
+ }
1391
+ const perChain = parsed.responses.find((r) => r.chainId === wormholeChainId);
1392
+ if (!perChain) {
1393
+ chainsOut.push({
1394
+ wormholeChainId,
1395
+ chainName: meta.chainName,
1396
+ vaultAddress: meta.vaultAddress,
1397
+ balances: [],
1398
+ error: { code: "DECODE_ERROR", message: "Missing per-chain response from Query Proxy" }
1399
+ });
1400
+ continue;
1401
+ }
1402
+ try {
1403
+ if (meta.kind === "evm") {
1404
+ const cfg = Object.values(chains).find((c) => c.wormholeChainId === wormholeChainId);
1405
+ const tokenAddresses = options?.evmTokenAddresses?.[wormholeChainId] ?? getAllTokens(wormholeChainId).filter((t) => !isNativeToken(t.address)).map((t) => t.address);
1406
+ if (!tokenAddresses.length) {
1407
+ chainsOut.push({
1408
+ wormholeChainId,
1409
+ chainName: meta.chainName,
1410
+ vaultAddress: meta.vaultAddress,
1411
+ balances: [],
1412
+ error: { code: "MISSING_TOKENS", message: "No ERC20 tokens configured for EVM portfolio query" }
1413
+ });
1414
+ continue;
1415
+ }
1416
+ const chainResp2 = import_wormhole_query_sdk2.EthCallQueryResponse.from(perChain.response.serialize());
1417
+ const blockTime2 = Number(chainResp2.blockTime);
1418
+ const age2 = nowSeconds - blockTime2;
1419
+ if (age2 > maxAgeSeconds) {
1420
+ chainsOut.push({
1421
+ wormholeChainId,
1422
+ chainName: meta.chainName,
1423
+ vaultAddress: meta.vaultAddress,
1424
+ blockTime: blockTime2,
1425
+ balances: [],
1426
+ error: { code: "ATTESTATION_STALE", message: `Attestation stale by ${age2}s (maxAge=${maxAgeSeconds}s)` }
1427
+ });
1428
+ continue;
1429
+ }
1430
+ const amounts = decodeErc20Balances(chainResp2.results, tokenAddresses);
1431
+ const tokenInfos = getAllTokens(wormholeChainId).filter((t) => !isNativeToken(t.address));
1432
+ const balances2 = tokenAddresses.map((addr, i) => {
1433
+ const info = tokenInfos.find((t) => t.address.toLowerCase() === addr.toLowerCase());
1434
+ const symbol = info?.symbol;
1435
+ const decimals = info?.decimals;
1436
+ const usdPrice = priceLookup(pricesUsd, symbol, addr);
1437
+ const usdValue = usdPrice != null && decimals != null ? Number(amounts[i]) / 10 ** decimals * usdPrice : void 0;
1438
+ return {
1439
+ assetId: addr,
1440
+ amount: amounts[i],
1441
+ symbol,
1442
+ decimals,
1443
+ usdValue: Number.isFinite(usdValue ?? NaN) ? usdValue : void 0
1444
+ };
1445
+ });
1446
+ void cfg;
1447
+ chainsOut.push({
1448
+ wormholeChainId,
1449
+ chainName: meta.chainName,
1450
+ vaultAddress: meta.vaultAddress,
1451
+ blockTime: blockTime2,
1452
+ balances: balances2
1453
+ });
1454
+ continue;
1455
+ }
1456
+ const chainResp = import_wormhole_query_sdk2.SolanaAccountQueryResponse.from(perChain.response.serialize());
1457
+ const blockTime = safeNumberFromBigint(chainResp.blockTime);
1458
+ const age = nowSeconds - blockTime;
1459
+ if (age > maxAgeSeconds) {
1460
+ chainsOut.push({
1461
+ wormholeChainId,
1462
+ chainName: meta.chainName,
1463
+ vaultAddress: meta.vaultAddress,
1464
+ blockTime,
1465
+ balances: [],
1466
+ error: { code: "ATTESTATION_STALE", message: `Attestation stale by ${age}s (maxAge=${maxAgeSeconds}s)` }
1467
+ });
1468
+ continue;
1469
+ }
1470
+ const vaultResult = chainResp.results[0];
1471
+ const lamports = vaultResult?.lamports ?? 0n;
1472
+ const balances = [
1473
+ {
1474
+ assetId: "SOL",
1475
+ amount: lamports,
1476
+ decimals: 9,
1477
+ symbol: "SOL",
1478
+ usdValue: priceLookup(pricesUsd, "SOL", "SOL") != null ? Number(lamports) / 1e9 * priceLookup(pricesUsd, "SOL", "SOL") : void 0
1479
+ }
1480
+ ];
1481
+ chainsOut.push({
1482
+ wormholeChainId,
1483
+ chainName: meta.chainName,
1484
+ vaultAddress: meta.vaultAddress,
1485
+ blockTime,
1486
+ balances
1487
+ });
1488
+ } catch (cause) {
1489
+ chainsOut.push({
1490
+ wormholeChainId,
1491
+ chainName: meta.chainName,
1492
+ vaultAddress: meta.vaultAddress,
1493
+ balances: [],
1494
+ error: { code: "DECODE_ERROR", message: `Failed to decode chain response: ${String(cause?.message ?? cause)}` }
1495
+ });
1496
+ }
1497
+ }
1498
+ const allBalances = chainsOut.flatMap((c) => c.balances);
1499
+ const totalUsd = sumUsd(allBalances, pricesUsd);
1500
+ const result2 = { proof, totalUsd, chains: chainsOut };
1501
+ PORTFOLIO_CACHE.set(cacheKey, { expiresAt: Date.now() + cacheTtlMs, value: result2 });
1502
+ return result2;
1503
+ } catch (err) {
1504
+ if (err instanceof QueryPortfolioError) throw err;
1505
+ if (import_axios2.default.isAxiosError(err)) {
1506
+ const ax = err;
1507
+ const status = ax.response?.status;
1508
+ const statusText = ax.response?.statusText;
1509
+ const details = typeof ax.response?.data === "string" ? ax.response?.data : void 0;
1510
+ throw new QueryPortfolioError(
1511
+ "PROXY_HTTP_ERROR",
1512
+ `Query Proxy request failed${status ? ` (${status} ${statusText ?? ""})` : ""}${details ? `: ${details}` : ""}`,
1513
+ err
1514
+ );
1515
+ }
1516
+ throw new QueryPortfolioError("PROXY_HTTP_ERROR", "Query Proxy request failed", err);
1517
+ }
1518
+ };
1519
+ const result = await withExponentialBackoff2(doFetch, maxAttempts);
1520
+ PORTFOLIO_CACHE.set(cacheKey, { expiresAt: Date.now() + cacheTtlMs, value: result });
1521
+ return result;
1522
+ }
1523
+ // Annotate the CommonJS export names for ESM import in node:
1524
+ 0 && (module.exports = {
1525
+ QueryHubStateError,
1526
+ QueryPortfolioError,
1527
+ WORMHOLE_QUERY_CHAIN_IDS,
1528
+ WORMHOLE_QUERY_PROXY_URLS,
1529
+ WORMHOLE_QUERY_RATE_LIMIT_PER_SECOND,
1530
+ queryHubState,
1531
+ queryPortfolio
1532
+ });
1533
+ //# sourceMappingURL=index.js.map