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