@compass-labs/widgets 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,3299 @@
1
+ import { createContext, useMemo, useContext, useState, useEffect, useCallback } from 'react';
2
+ import { QueryClient, QueryClientProvider, useQueryClient, useQuery } from '@tanstack/react-query';
3
+ import { CompassApiSDK } from '@compass-labs/api-sdk';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+ import { arbitrum, base, mainnet } from 'viem/chains';
6
+ import { Wallet, LogOut, X, ChevronDown, AlertCircle, Loader2, ArrowDownLeft, ArrowUpRight, ExternalLink, Search, TrendingUp, Calendar, ArrowDownUp } from 'lucide-react';
7
+
8
+ // src/provider/CompassProvider.tsx
9
+ var ApiContext = createContext(null);
10
+ function ApiProvider({ children, apiKey, serverURL }) {
11
+ const client = useMemo(() => {
12
+ return new CompassApiSDK({
13
+ apiKeyAuth: apiKey,
14
+ serverURL: serverURL || "https://api.compasslabs.ai"
15
+ });
16
+ }, [apiKey, serverURL]);
17
+ const value = useMemo(
18
+ () => ({
19
+ apiKey,
20
+ client
21
+ }),
22
+ [apiKey, client]
23
+ );
24
+ return /* @__PURE__ */ jsx(ApiContext.Provider, { value, children });
25
+ }
26
+ function useCompassApi() {
27
+ const context = useContext(ApiContext);
28
+ if (!context) {
29
+ throw new Error("useCompassApi must be used within a CompassProvider");
30
+ }
31
+ return context;
32
+ }
33
+ var useEmbeddableApi = useCompassApi;
34
+ var ChainContext = createContext(null);
35
+ var CHAIN_CONFIG = {
36
+ ethereum: {
37
+ viemChain: mainnet,
38
+ name: "Ethereum",
39
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png"
40
+ },
41
+ base: {
42
+ viemChain: base,
43
+ name: "Base",
44
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png"
45
+ },
46
+ arbitrum: {
47
+ viemChain: arbitrum,
48
+ name: "Arbitrum",
49
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrum/info/logo.png"
50
+ }
51
+ };
52
+ function ChainProvider({ children, defaultChain }) {
53
+ const [chainId, setChainIdState] = useState(defaultChain);
54
+ useEffect(() => {
55
+ if (typeof window === "undefined") return;
56
+ const stored = localStorage.getItem("compass-chain");
57
+ if (stored && stored in CHAIN_CONFIG) {
58
+ setChainIdState(stored);
59
+ }
60
+ }, []);
61
+ const setChainId = (newChainId) => {
62
+ setChainIdState(newChainId);
63
+ if (typeof window !== "undefined") {
64
+ localStorage.setItem("compass-chain", newChainId);
65
+ }
66
+ };
67
+ const chain = useMemo(() => CHAIN_CONFIG[chainId], [chainId]);
68
+ return /* @__PURE__ */ jsx(ChainContext.Provider, { value: { chainId, setChainId, setChain: setChainId, chain }, children });
69
+ }
70
+ function useCompassChain() {
71
+ const context = useContext(ChainContext);
72
+ if (!context) {
73
+ throw new Error("useCompassChain must be used within a CompassProvider");
74
+ }
75
+ return context;
76
+ }
77
+ var useChain = useCompassChain;
78
+ var WalletContext = createContext(null);
79
+ var disconnectedWallet = {
80
+ address: null,
81
+ isConnected: false,
82
+ signTypedData: async () => {
83
+ throw new Error("No wallet connected. Please connect a wallet first.");
84
+ },
85
+ switchChain: null,
86
+ login: null,
87
+ logout: null
88
+ };
89
+ function WalletProvider({ children, wallet }) {
90
+ const value = wallet ? {
91
+ address: wallet.address,
92
+ isConnected: wallet.address !== null,
93
+ signTypedData: wallet.signTypedData,
94
+ switchChain: wallet.switchChain ?? null,
95
+ login: wallet.login ?? null,
96
+ logout: wallet.logout ?? null
97
+ } : disconnectedWallet;
98
+ return /* @__PURE__ */ jsx(WalletContext.Provider, { value, children });
99
+ }
100
+ function useCompassWallet() {
101
+ const context = useContext(WalletContext);
102
+ if (!context) {
103
+ return disconnectedWallet;
104
+ }
105
+ return context;
106
+ }
107
+ var useEmbeddableWallet = useCompassWallet;
108
+ var EarnAccountContext = createContext(null);
109
+ function EarnAccountProvider({ children }) {
110
+ const { address } = useEmbeddableWallet();
111
+ const { chainId } = useChain();
112
+ const queryClient = useQueryClient();
113
+ const [isCreating, setIsCreating] = useState(false);
114
+ const [createError, setCreateError] = useState(null);
115
+ const {
116
+ data: accountData,
117
+ isLoading: isChecking,
118
+ error: checkError,
119
+ refetch
120
+ } = useQuery({
121
+ queryKey: ["earnAccount", address, chainId],
122
+ queryFn: async () => {
123
+ if (!address) return null;
124
+ const response = await fetch(
125
+ `/api/compass/earn-account/check?owner=${address}&chain=${chainId}`
126
+ );
127
+ if (!response.ok) {
128
+ const error = await response.json();
129
+ throw new Error(error.error || "Failed to check earn account");
130
+ }
131
+ return response.json();
132
+ },
133
+ enabled: !!address,
134
+ staleTime: 60 * 1e3,
135
+ // 1 minute
136
+ refetchOnMount: true
137
+ });
138
+ const createAccount = useCallback(async () => {
139
+ if (!address) {
140
+ throw new Error("No wallet connected");
141
+ }
142
+ if (accountData?.isDeployed) {
143
+ return accountData.earnAccountAddress;
144
+ }
145
+ setIsCreating(true);
146
+ setCreateError(null);
147
+ try {
148
+ const response = await fetch("/api/compass/create-account", {
149
+ method: "POST",
150
+ headers: { "Content-Type": "application/json" },
151
+ body: JSON.stringify({
152
+ owner: address,
153
+ chain: chainId
154
+ })
155
+ });
156
+ if (!response.ok) {
157
+ const error = await response.json();
158
+ throw new Error(error.error || "Failed to create earn account");
159
+ }
160
+ const result = await response.json();
161
+ await queryClient.invalidateQueries({
162
+ queryKey: ["earnAccount", address, chainId]
163
+ });
164
+ return result.earnAccountAddress;
165
+ } catch (error) {
166
+ const err = error instanceof Error ? error : new Error("Unknown error");
167
+ setCreateError(err);
168
+ throw err;
169
+ } finally {
170
+ setIsCreating(false);
171
+ }
172
+ }, [address, chainId, accountData, queryClient]);
173
+ const value = {
174
+ earnAccountAddress: accountData?.earnAccountAddress || null,
175
+ isDeployed: accountData?.isDeployed ?? false,
176
+ isChecking,
177
+ isCreating,
178
+ error: checkError instanceof Error ? checkError : createError,
179
+ createAccount,
180
+ refetch
181
+ };
182
+ return /* @__PURE__ */ jsx(EarnAccountContext.Provider, { value, children });
183
+ }
184
+ function useEarnAccount() {
185
+ const context = useContext(EarnAccountContext);
186
+ if (!context) {
187
+ throw new Error("useEarnAccount must be used within a CompassProvider");
188
+ }
189
+ return context;
190
+ }
191
+
192
+ // src/theme/presets.ts
193
+ var baseTypography = {
194
+ fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
195
+ fontFamilyMono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
196
+ heading: {
197
+ fontSize: "1.5rem",
198
+ lineHeight: 1.2,
199
+ fontWeight: 600,
200
+ letterSpacing: "-0.02em"
201
+ },
202
+ subheading: {
203
+ fontSize: "1.125rem",
204
+ lineHeight: 1.3,
205
+ fontWeight: 500,
206
+ letterSpacing: "-0.01em"
207
+ },
208
+ body: {
209
+ fontSize: "0.875rem",
210
+ lineHeight: 1.5,
211
+ fontWeight: 400
212
+ },
213
+ caption: {
214
+ fontSize: "0.75rem",
215
+ lineHeight: 1.4,
216
+ fontWeight: 400
217
+ },
218
+ label: {
219
+ fontSize: "0.75rem",
220
+ lineHeight: 1.2,
221
+ fontWeight: 500,
222
+ letterSpacing: "0.02em"
223
+ }
224
+ };
225
+ var highContrastTypography = {
226
+ ...baseTypography,
227
+ heading: {
228
+ fontSize: "1.75rem",
229
+ lineHeight: 1.2,
230
+ fontWeight: 700,
231
+ letterSpacing: "-0.02em"
232
+ },
233
+ subheading: {
234
+ fontSize: "1.25rem",
235
+ lineHeight: 1.3,
236
+ fontWeight: 600,
237
+ letterSpacing: "-0.01em"
238
+ },
239
+ body: {
240
+ fontSize: "1rem",
241
+ lineHeight: 1.6,
242
+ fontWeight: 400
243
+ },
244
+ caption: {
245
+ fontSize: "0.875rem",
246
+ lineHeight: 1.5,
247
+ fontWeight: 400
248
+ },
249
+ label: {
250
+ fontSize: "0.875rem",
251
+ lineHeight: 1.2,
252
+ fontWeight: 600,
253
+ letterSpacing: "0.02em"
254
+ }
255
+ };
256
+ var baseSpacing = {
257
+ unit: "4px",
258
+ containerPadding: "1.5rem",
259
+ cardPadding: "1.25rem",
260
+ inputPadding: "0.75rem 1rem"
261
+ };
262
+ var baseTransitions = {
263
+ fast: "150ms ease-out",
264
+ normal: "200ms ease-out",
265
+ slow: "300ms ease-out"
266
+ };
267
+ var compassDark = {
268
+ name: "compass-dark",
269
+ mode: "dark",
270
+ colors: {
271
+ backgrounds: {
272
+ // wallet-earn: --background: #050507
273
+ background: "#050507",
274
+ // wallet-earn: --surface-1: #0c0c10, --surface-2: #12121a
275
+ surface: "#0c0c10",
276
+ surfaceHover: "#12121a",
277
+ overlay: "rgba(0, 0, 0, 0.7)"
278
+ },
279
+ brand: {
280
+ // wallet-earn: --accent-primary: #6366f1, --accent-secondary: #8b5cf6
281
+ primary: "#6366f1",
282
+ primaryHover: "#818cf8",
283
+ primaryText: "#ffffff",
284
+ secondary: "#8b5cf6",
285
+ secondaryHover: "#a78bfa"
286
+ },
287
+ text: {
288
+ // wallet-earn: --foreground: #f4f4f5, text-zinc-400: #a1a1aa, text-zinc-500: #71717a
289
+ text: "#f4f4f5",
290
+ textSecondary: "#a1a1aa",
291
+ textTertiary: "#71717a"
292
+ },
293
+ borders: {
294
+ // wallet-earn: --border-subtle: rgba(255,255,255,0.06), --border-default: rgba(255,255,255,0.1)
295
+ border: "rgba(255, 255, 255, 0.08)",
296
+ borderHover: "rgba(255, 255, 255, 0.12)",
297
+ borderFocus: "#6366f1"
298
+ },
299
+ semantic: {
300
+ success: {
301
+ // wallet-earn: --success: #10b981
302
+ DEFAULT: "#10b981",
303
+ muted: "rgba(16, 185, 129, 0.15)"
304
+ },
305
+ warning: {
306
+ DEFAULT: "#f59e0b",
307
+ muted: "rgba(245, 158, 11, 0.15)"
308
+ },
309
+ error: {
310
+ // wallet-earn: --error: #ef4444
311
+ DEFAULT: "#ef4444",
312
+ muted: "rgba(239, 68, 68, 0.15)"
313
+ },
314
+ info: {
315
+ DEFAULT: "#6366f1",
316
+ muted: "rgba(99, 102, 241, 0.15)"
317
+ }
318
+ }
319
+ },
320
+ typography: baseTypography,
321
+ spacing: baseSpacing,
322
+ shape: {
323
+ borderRadius: {
324
+ none: "0",
325
+ sm: "0.375rem",
326
+ md: "0.5rem",
327
+ lg: "0.75rem",
328
+ xl: "1rem",
329
+ full: "9999px"
330
+ },
331
+ borderWidth: "1px"
332
+ },
333
+ effects: {
334
+ shadow: {
335
+ sm: "0 1px 2px rgba(0, 0, 0, 0.3)",
336
+ md: "0 4px 12px rgba(0, 0, 0, 0.4)",
337
+ lg: "0 8px 24px rgba(0, 0, 0, 0.5)"
338
+ },
339
+ blur: {
340
+ sm: "4px",
341
+ md: "8px",
342
+ lg: "16px"
343
+ },
344
+ transition: baseTransitions
345
+ }
346
+ };
347
+ var compassLight = {
348
+ name: "compass-light",
349
+ mode: "light",
350
+ colors: {
351
+ backgrounds: {
352
+ background: "#ffffff",
353
+ surface: "#f8fafc",
354
+ surfaceHover: "#f1f5f9",
355
+ overlay: "rgba(0, 0, 0, 0.5)"
356
+ },
357
+ brand: {
358
+ primary: "#6366f1",
359
+ primaryHover: "#4f46e5",
360
+ primaryText: "#ffffff",
361
+ secondary: "#4f46e5",
362
+ secondaryHover: "#4338ca"
363
+ },
364
+ text: {
365
+ text: "#0f172a",
366
+ textSecondary: "#475569",
367
+ textTertiary: "#94a3b8"
368
+ },
369
+ borders: {
370
+ border: "#e2e8f0",
371
+ borderHover: "#cbd5e1",
372
+ borderFocus: "#6366f1"
373
+ },
374
+ semantic: {
375
+ success: {
376
+ DEFAULT: "#16a34a",
377
+ muted: "#dcfce7"
378
+ },
379
+ warning: {
380
+ DEFAULT: "#d97706",
381
+ muted: "#fef3c7"
382
+ },
383
+ error: {
384
+ DEFAULT: "#dc2626",
385
+ muted: "#fee2e2"
386
+ },
387
+ info: {
388
+ DEFAULT: "#2563eb",
389
+ muted: "#dbeafe"
390
+ }
391
+ }
392
+ },
393
+ typography: baseTypography,
394
+ spacing: baseSpacing,
395
+ shape: {
396
+ borderRadius: {
397
+ none: "0",
398
+ sm: "0.375rem",
399
+ md: "0.5rem",
400
+ lg: "0.75rem",
401
+ xl: "1rem",
402
+ full: "9999px"
403
+ },
404
+ borderWidth: "1px"
405
+ },
406
+ effects: {
407
+ shadow: {
408
+ sm: "0 1px 2px rgba(0, 0, 0, 0.05)",
409
+ md: "0 4px 12px rgba(0, 0, 0, 0.08)",
410
+ lg: "0 8px 24px rgba(0, 0, 0, 0.12)"
411
+ },
412
+ blur: {
413
+ sm: "4px",
414
+ md: "8px",
415
+ lg: "16px"
416
+ },
417
+ transition: baseTransitions
418
+ }
419
+ };
420
+ var minimalDark = {
421
+ name: "minimal-dark",
422
+ mode: "dark",
423
+ colors: {
424
+ backgrounds: {
425
+ background: "#0a0a0a",
426
+ surface: "#171717",
427
+ surfaceHover: "#262626",
428
+ overlay: "rgba(0, 0, 0, 0.8)"
429
+ },
430
+ brand: {
431
+ primary: "#ffffff",
432
+ primaryHover: "#e5e5e5",
433
+ primaryText: "#0a0a0a",
434
+ secondary: "#a3a3a3",
435
+ secondaryHover: "#d4d4d4"
436
+ },
437
+ text: {
438
+ text: "#fafafa",
439
+ textSecondary: "#a3a3a3",
440
+ textTertiary: "#525252"
441
+ },
442
+ borders: {
443
+ border: "#262626",
444
+ borderHover: "#404040",
445
+ borderFocus: "#ffffff"
446
+ },
447
+ semantic: {
448
+ success: {
449
+ DEFAULT: "#4ade80",
450
+ muted: "rgba(74, 222, 128, 0.1)"
451
+ },
452
+ warning: {
453
+ DEFAULT: "#fbbf24",
454
+ muted: "rgba(251, 191, 36, 0.1)"
455
+ },
456
+ error: {
457
+ DEFAULT: "#f87171",
458
+ muted: "rgba(248, 113, 113, 0.1)"
459
+ },
460
+ info: {
461
+ DEFAULT: "#60a5fa",
462
+ muted: "rgba(96, 165, 250, 0.1)"
463
+ }
464
+ }
465
+ },
466
+ typography: baseTypography,
467
+ spacing: baseSpacing,
468
+ shape: {
469
+ borderRadius: {
470
+ none: "0",
471
+ sm: "0.125rem",
472
+ md: "0.25rem",
473
+ lg: "0.375rem",
474
+ xl: "0.5rem",
475
+ full: "9999px"
476
+ },
477
+ borderWidth: "1px"
478
+ },
479
+ effects: {
480
+ shadow: {
481
+ sm: "none",
482
+ md: "none",
483
+ lg: "none"
484
+ },
485
+ blur: {
486
+ sm: "0",
487
+ md: "0",
488
+ lg: "0"
489
+ },
490
+ transition: baseTransitions
491
+ }
492
+ };
493
+ var minimalLight = {
494
+ name: "minimal-light",
495
+ mode: "light",
496
+ colors: {
497
+ backgrounds: {
498
+ background: "#ffffff",
499
+ surface: "#fafafa",
500
+ surfaceHover: "#f5f5f5",
501
+ overlay: "rgba(0, 0, 0, 0.6)"
502
+ },
503
+ brand: {
504
+ primary: "#0a0a0a",
505
+ primaryHover: "#262626",
506
+ primaryText: "#ffffff",
507
+ secondary: "#525252",
508
+ secondaryHover: "#404040"
509
+ },
510
+ text: {
511
+ text: "#0a0a0a",
512
+ textSecondary: "#525252",
513
+ textTertiary: "#a3a3a3"
514
+ },
515
+ borders: {
516
+ border: "#e5e5e5",
517
+ borderHover: "#d4d4d4",
518
+ borderFocus: "#0a0a0a"
519
+ },
520
+ semantic: {
521
+ success: {
522
+ DEFAULT: "#16a34a",
523
+ muted: "#f0fdf4"
524
+ },
525
+ warning: {
526
+ DEFAULT: "#ca8a04",
527
+ muted: "#fefce8"
528
+ },
529
+ error: {
530
+ DEFAULT: "#dc2626",
531
+ muted: "#fef2f2"
532
+ },
533
+ info: {
534
+ DEFAULT: "#2563eb",
535
+ muted: "#eff6ff"
536
+ }
537
+ }
538
+ },
539
+ typography: baseTypography,
540
+ spacing: baseSpacing,
541
+ shape: {
542
+ borderRadius: {
543
+ none: "0",
544
+ sm: "0.125rem",
545
+ md: "0.25rem",
546
+ lg: "0.375rem",
547
+ xl: "0.5rem",
548
+ full: "9999px"
549
+ },
550
+ borderWidth: "1px"
551
+ },
552
+ effects: {
553
+ shadow: {
554
+ sm: "none",
555
+ md: "none",
556
+ lg: "none"
557
+ },
558
+ blur: {
559
+ sm: "0",
560
+ md: "0",
561
+ lg: "0"
562
+ },
563
+ transition: baseTransitions
564
+ }
565
+ };
566
+ var highContrastDark = {
567
+ name: "high-contrast-dark",
568
+ mode: "dark",
569
+ colors: {
570
+ backgrounds: {
571
+ background: "#000000",
572
+ surface: "#0a0a0a",
573
+ surfaceHover: "#1a1a1a",
574
+ overlay: "rgba(0, 0, 0, 0.9)"
575
+ },
576
+ brand: {
577
+ primary: "#3b82f6",
578
+ primaryHover: "#60a5fa",
579
+ primaryText: "#ffffff",
580
+ secondary: "#60a5fa",
581
+ secondaryHover: "#93c5fd"
582
+ },
583
+ text: {
584
+ text: "#ffffff",
585
+ textSecondary: "#e5e5e5",
586
+ textTertiary: "#a3a3a3"
587
+ },
588
+ borders: {
589
+ border: "#404040",
590
+ borderHover: "#525252",
591
+ borderFocus: "#3b82f6"
592
+ },
593
+ semantic: {
594
+ success: {
595
+ DEFAULT: "#22c55e",
596
+ muted: "rgba(34, 197, 94, 0.2)"
597
+ },
598
+ warning: {
599
+ DEFAULT: "#fbbf24",
600
+ muted: "rgba(251, 191, 36, 0.2)"
601
+ },
602
+ error: {
603
+ DEFAULT: "#ef4444",
604
+ muted: "rgba(239, 68, 68, 0.2)"
605
+ },
606
+ info: {
607
+ DEFAULT: "#3b82f6",
608
+ muted: "rgba(59, 130, 246, 0.2)"
609
+ }
610
+ }
611
+ },
612
+ typography: highContrastTypography,
613
+ spacing: baseSpacing,
614
+ shape: {
615
+ borderRadius: {
616
+ none: "0",
617
+ sm: "0.25rem",
618
+ md: "0.375rem",
619
+ lg: "0.5rem",
620
+ xl: "0.75rem",
621
+ full: "9999px"
622
+ },
623
+ borderWidth: "2px"
624
+ },
625
+ effects: {
626
+ shadow: {
627
+ sm: "0 1px 3px rgba(0, 0, 0, 0.5)",
628
+ md: "0 4px 12px rgba(0, 0, 0, 0.6)",
629
+ lg: "0 8px 24px rgba(0, 0, 0, 0.7)"
630
+ },
631
+ blur: {
632
+ sm: "4px",
633
+ md: "8px",
634
+ lg: "16px"
635
+ },
636
+ transition: baseTransitions
637
+ }
638
+ };
639
+ var highContrastLight = {
640
+ name: "high-contrast-light",
641
+ mode: "light",
642
+ colors: {
643
+ backgrounds: {
644
+ background: "#ffffff",
645
+ surface: "#f5f5f5",
646
+ surfaceHover: "#e5e5e5",
647
+ overlay: "rgba(0, 0, 0, 0.7)"
648
+ },
649
+ brand: {
650
+ primary: "#1d4ed8",
651
+ primaryHover: "#1e40af",
652
+ primaryText: "#ffffff",
653
+ secondary: "#1e40af",
654
+ secondaryHover: "#1e3a8a"
655
+ },
656
+ text: {
657
+ text: "#000000",
658
+ textSecondary: "#1a1a1a",
659
+ textTertiary: "#525252"
660
+ },
661
+ borders: {
662
+ border: "#525252",
663
+ borderHover: "#404040",
664
+ borderFocus: "#1d4ed8"
665
+ },
666
+ semantic: {
667
+ success: {
668
+ DEFAULT: "#15803d",
669
+ muted: "#dcfce7"
670
+ },
671
+ warning: {
672
+ DEFAULT: "#a16207",
673
+ muted: "#fef9c3"
674
+ },
675
+ error: {
676
+ DEFAULT: "#b91c1c",
677
+ muted: "#fee2e2"
678
+ },
679
+ info: {
680
+ DEFAULT: "#1d4ed8",
681
+ muted: "#dbeafe"
682
+ }
683
+ }
684
+ },
685
+ typography: highContrastTypography,
686
+ spacing: baseSpacing,
687
+ shape: {
688
+ borderRadius: {
689
+ none: "0",
690
+ sm: "0.25rem",
691
+ md: "0.375rem",
692
+ lg: "0.5rem",
693
+ xl: "0.75rem",
694
+ full: "9999px"
695
+ },
696
+ borderWidth: "2px"
697
+ },
698
+ effects: {
699
+ shadow: {
700
+ sm: "0 1px 2px rgba(0, 0, 0, 0.1)",
701
+ md: "0 4px 8px rgba(0, 0, 0, 0.15)",
702
+ lg: "0 8px 16px rgba(0, 0, 0, 0.2)"
703
+ },
704
+ blur: {
705
+ sm: "4px",
706
+ md: "8px",
707
+ lg: "16px"
708
+ },
709
+ transition: baseTransitions
710
+ }
711
+ };
712
+ var themePresets = {
713
+ "compass-dark": compassDark,
714
+ "compass-light": compassLight,
715
+ "minimal-dark": minimalDark,
716
+ "minimal-light": minimalLight,
717
+ "high-contrast-dark": highContrastDark,
718
+ "high-contrast-light": highContrastLight
719
+ };
720
+ var defaultTheme = compassDark;
721
+
722
+ // src/theme/css-variables.ts
723
+ function camelToKebab(str) {
724
+ return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
725
+ }
726
+ function cssVarPath(category, ...parts) {
727
+ const kebabParts = parts.map(camelToKebab).join("-");
728
+ return `--compass-${category}-${kebabParts}`;
729
+ }
730
+ function colorsToCssVariables(colors) {
731
+ const vars = {};
732
+ for (const [key, value] of Object.entries(colors.backgrounds)) {
733
+ vars[cssVarPath("color", key)] = value;
734
+ }
735
+ for (const [key, value] of Object.entries(colors.brand)) {
736
+ vars[cssVarPath("color", key)] = value;
737
+ }
738
+ for (const [key, value] of Object.entries(colors.text)) {
739
+ vars[cssVarPath("color", key)] = value;
740
+ }
741
+ for (const [key, value] of Object.entries(colors.borders)) {
742
+ vars[cssVarPath("color", key)] = value;
743
+ }
744
+ for (const [semantic, colorObj] of Object.entries(colors.semantic)) {
745
+ const semanticColor = colorObj;
746
+ vars[cssVarPath("color", semantic)] = semanticColor.DEFAULT;
747
+ vars[cssVarPath("color", semantic, "muted")] = semanticColor.muted;
748
+ }
749
+ return vars;
750
+ }
751
+ function typographyScaleToCssVariables(name, scale) {
752
+ const vars = {};
753
+ vars[cssVarPath("font", name, "size")] = scale.fontSize;
754
+ vars[cssVarPath("font", name, "line-height")] = String(scale.lineHeight);
755
+ vars[cssVarPath("font", name, "weight")] = String(scale.fontWeight);
756
+ if (scale.letterSpacing) {
757
+ vars[cssVarPath("font", name, "letter-spacing")] = scale.letterSpacing;
758
+ }
759
+ return vars;
760
+ }
761
+ function typographyToCssVariables(typography) {
762
+ const vars = {};
763
+ vars["--compass-font-family"] = typography.fontFamily;
764
+ vars["--compass-font-family-mono"] = typography.fontFamilyMono;
765
+ const scales = [
766
+ "heading",
767
+ "subheading",
768
+ "body",
769
+ "caption",
770
+ "label"
771
+ ];
772
+ for (const scaleName of scales) {
773
+ const scale = typography[scaleName];
774
+ if (typeof scale === "object" && "fontSize" in scale) {
775
+ Object.assign(
776
+ vars,
777
+ typographyScaleToCssVariables(scaleName, scale)
778
+ );
779
+ }
780
+ }
781
+ return vars;
782
+ }
783
+ function spacingToCssVariables(spacing) {
784
+ const vars = {};
785
+ vars["--compass-spacing-unit"] = spacing.unit;
786
+ vars["--compass-spacing-container"] = spacing.containerPadding;
787
+ vars["--compass-spacing-card"] = spacing.cardPadding;
788
+ vars["--compass-spacing-input"] = spacing.inputPadding;
789
+ return vars;
790
+ }
791
+ function shapeToCssVariables(shape) {
792
+ const vars = {};
793
+ for (const [key, value] of Object.entries(shape.borderRadius)) {
794
+ vars[cssVarPath("radius", key)] = value;
795
+ }
796
+ vars["--compass-border-width"] = shape.borderWidth;
797
+ return vars;
798
+ }
799
+ function effectsToCssVariables(effects) {
800
+ const vars = {};
801
+ for (const [key, value] of Object.entries(effects.shadow)) {
802
+ vars[cssVarPath("shadow", key)] = value;
803
+ }
804
+ for (const [key, value] of Object.entries(effects.blur)) {
805
+ vars[cssVarPath("blur", key)] = value;
806
+ }
807
+ for (const [key, value] of Object.entries(effects.transition)) {
808
+ vars[cssVarPath("transition", key)] = value;
809
+ }
810
+ return vars;
811
+ }
812
+ function themeToCssVariables(theme) {
813
+ return {
814
+ // Theme metadata
815
+ "--compass-theme-name": theme.name,
816
+ "--compass-theme-mode": theme.mode,
817
+ // Color variables
818
+ ...colorsToCssVariables(theme.colors),
819
+ // Typography variables
820
+ ...typographyToCssVariables(theme.typography),
821
+ // Spacing variables
822
+ ...spacingToCssVariables(theme.spacing),
823
+ // Shape variables (border radius)
824
+ ...shapeToCssVariables(theme.shape),
825
+ // Effects variables (shadows, blur, transitions)
826
+ ...effectsToCssVariables(theme.effects)
827
+ };
828
+ }
829
+ function injectCssVariables(theme, selector = ".compass-widget") {
830
+ const variables = themeToCssVariables(theme);
831
+ const variableLines = Object.entries(variables).map(([name, value]) => ` ${name}: ${value};`).join("\n");
832
+ return `${selector} {
833
+ ${variableLines}
834
+ }`;
835
+ }
836
+ var ThemeContext = createContext(void 0);
837
+ function deepMerge(target, source) {
838
+ const result = { ...target };
839
+ for (const key in source) {
840
+ const sourceValue = source[key];
841
+ const targetValue = target[key];
842
+ if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null) {
843
+ result[key] = deepMerge(
844
+ targetValue,
845
+ sourceValue
846
+ );
847
+ } else if (sourceValue !== void 0) {
848
+ result[key] = sourceValue;
849
+ }
850
+ }
851
+ return result;
852
+ }
853
+ function resolveTheme(input) {
854
+ if (!input) return defaultTheme;
855
+ if (typeof input === "string") {
856
+ return themePresets[input] ?? defaultTheme;
857
+ }
858
+ if ("preset" in input) {
859
+ const base3 = themePresets[input.preset] ?? defaultTheme;
860
+ return input.overrides ? deepMerge(base3, input.overrides) : base3;
861
+ }
862
+ return input;
863
+ }
864
+ function ThemeProvider({ children, theme: initialTheme }) {
865
+ const [themeInput, setThemeInput] = useState(initialTheme);
866
+ const theme = useMemo(() => resolveTheme(themeInput), [themeInput]);
867
+ const cssVariables = useMemo(() => injectCssVariables(theme, ".compass-widget"), [theme]);
868
+ useEffect(() => {
869
+ const styleId = "compass-theme-variables";
870
+ let styleEl = document.getElementById(styleId);
871
+ if (!styleEl) {
872
+ styleEl = document.createElement("style");
873
+ styleEl.id = styleId;
874
+ document.head.appendChild(styleEl);
875
+ }
876
+ styleEl.textContent = cssVariables;
877
+ return () => {
878
+ };
879
+ }, [cssVariables]);
880
+ const value = useMemo(
881
+ () => ({
882
+ theme,
883
+ setTheme: setThemeInput,
884
+ cssVariables
885
+ }),
886
+ [theme, cssVariables]
887
+ );
888
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children });
889
+ }
890
+ function useTheme() {
891
+ const context = useContext(ThemeContext);
892
+ if (!context) {
893
+ throw new Error("useTheme must be used within a ThemeProvider");
894
+ }
895
+ return context;
896
+ }
897
+ function CompassProvider({
898
+ children,
899
+ apiKey,
900
+ defaultChain = "base",
901
+ theme,
902
+ wallet
903
+ }) {
904
+ const queryClient = useMemo(
905
+ () => new QueryClient({
906
+ defaultOptions: {
907
+ queries: {
908
+ staleTime: 30 * 1e3,
909
+ // 30 seconds
910
+ gcTime: 2 * 60 * 1e3,
911
+ // 2 minutes
912
+ refetchOnWindowFocus: false
913
+ }
914
+ }
915
+ }),
916
+ []
917
+ );
918
+ return /* @__PURE__ */ jsx(QueryClientProvider, { client: queryClient, children: /* @__PURE__ */ jsx(ThemeProvider, { theme, children: /* @__PURE__ */ jsx(ApiProvider, { apiKey, children: /* @__PURE__ */ jsx(ChainProvider, { defaultChain, children: /* @__PURE__ */ jsx(WalletProvider, { wallet, children: /* @__PURE__ */ jsx(EarnAccountProvider, { children: /* @__PURE__ */ jsx("div", { className: "compass-widget", children }) }) }) }) }) }) });
919
+ }
920
+ var SUPPORTED_CHAINS = ["ethereum", "base", "arbitrum"];
921
+ function ChainSwitcher() {
922
+ const { chainId, setChain, chain } = useChain();
923
+ const chainConfigs = {
924
+ ethereum: {
925
+ viemChain: chain.viemChain,
926
+ name: "Ethereum",
927
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png"
928
+ },
929
+ base: {
930
+ viemChain: chain.viemChain,
931
+ name: "Base",
932
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png"
933
+ },
934
+ arbitrum: {
935
+ viemChain: chain.viemChain,
936
+ name: "Arbitrum",
937
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrum/info/logo.png"
938
+ }
939
+ };
940
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-1 p-1 rounded-lg", style: { backgroundColor: "var(--compass-color-surface)" }, children: SUPPORTED_CHAINS.map((id) => {
941
+ const config = chainConfigs[id];
942
+ const isActive = chainId === id;
943
+ return /* @__PURE__ */ jsxs(
944
+ "button",
945
+ {
946
+ onClick: () => setChain(id),
947
+ className: "flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all",
948
+ style: {
949
+ backgroundColor: isActive ? "var(--compass-color-primary)" : "transparent",
950
+ color: isActive ? "var(--compass-color-primary-text)" : "var(--compass-color-text-secondary)"
951
+ },
952
+ children: [
953
+ /* @__PURE__ */ jsx("img", { src: config.icon, alt: config.name, className: "w-4 h-4" }),
954
+ /* @__PURE__ */ jsx("span", { children: config.name })
955
+ ]
956
+ },
957
+ id
958
+ );
959
+ }) });
960
+ }
961
+ function truncateAddress(address) {
962
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
963
+ }
964
+ function WalletStatus({
965
+ showFullAddress = false,
966
+ showLogout = true,
967
+ compact = false,
968
+ onConnect,
969
+ onDisconnect
970
+ }) {
971
+ const { address, isConnected, login, logout } = useCompassWallet();
972
+ const connectFn = onConnect ?? login;
973
+ const disconnectFn = onDisconnect ?? logout;
974
+ if (!isConnected || !address) {
975
+ if (connectFn !== null) {
976
+ return /* @__PURE__ */ jsxs(
977
+ "button",
978
+ {
979
+ onClick: () => connectFn(),
980
+ className: `flex items-center gap-2 rounded-lg font-medium transition-colors hover:opacity-90 ${compact ? "px-2 py-1.5" : "px-4 py-2"}`,
981
+ style: {
982
+ backgroundColor: "var(--compass-color-primary)",
983
+ color: "var(--compass-color-primary-text)"
984
+ },
985
+ children: [
986
+ /* @__PURE__ */ jsx(Wallet, { size: compact ? 14 : 16 }),
987
+ !compact && /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Connect Wallet" })
988
+ ]
989
+ }
990
+ );
991
+ }
992
+ return /* @__PURE__ */ jsxs(
993
+ "div",
994
+ {
995
+ className: `flex items-center gap-2 rounded-lg ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
996
+ style: {
997
+ backgroundColor: "var(--compass-color-surface)",
998
+ color: "var(--compass-color-text-secondary)"
999
+ },
1000
+ children: [
1001
+ /* @__PURE__ */ jsx(Wallet, { size: compact ? 14 : 16 }),
1002
+ !compact && /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Not connected" })
1003
+ ]
1004
+ }
1005
+ );
1006
+ }
1007
+ return /* @__PURE__ */ jsxs(
1008
+ "div",
1009
+ {
1010
+ className: `flex items-center gap-2 rounded-lg ${compact ? "px-2 py-1.5" : "px-3 py-2"}`,
1011
+ style: {
1012
+ backgroundColor: "var(--compass-color-surface)",
1013
+ border: "1px solid var(--compass-color-border)"
1014
+ },
1015
+ children: [
1016
+ /* @__PURE__ */ jsx(
1017
+ "div",
1018
+ {
1019
+ className: "w-2 h-2 rounded-full flex-shrink-0",
1020
+ style: { backgroundColor: "var(--compass-color-success)" }
1021
+ }
1022
+ ),
1023
+ /* @__PURE__ */ jsx(
1024
+ "span",
1025
+ {
1026
+ className: `font-mono whitespace-nowrap ${compact ? "text-xs" : "text-sm"}`,
1027
+ style: { color: "var(--compass-color-text)" },
1028
+ children: showFullAddress ? address : truncateAddress(address)
1029
+ }
1030
+ ),
1031
+ showLogout && disconnectFn !== null && /* @__PURE__ */ jsx(
1032
+ "button",
1033
+ {
1034
+ onClick: () => disconnectFn(),
1035
+ className: `rounded hover:bg-white/10 transition-all flex-shrink-0 ${compact ? "p-1" : "p-1.5"}`,
1036
+ title: "Disconnect wallet",
1037
+ children: /* @__PURE__ */ jsx(LogOut, { size: 14, style: { color: "var(--compass-color-text-secondary)" } })
1038
+ }
1039
+ )
1040
+ ]
1041
+ }
1042
+ );
1043
+ }
1044
+ function ActionModal({ isOpen, onClose, title, children }) {
1045
+ useEffect(() => {
1046
+ const handleEscape = (e) => {
1047
+ if (e.key === "Escape") onClose();
1048
+ };
1049
+ if (isOpen) {
1050
+ document.addEventListener("keydown", handleEscape);
1051
+ document.body.style.overflow = "hidden";
1052
+ }
1053
+ return () => {
1054
+ document.removeEventListener("keydown", handleEscape);
1055
+ document.body.style.overflow = "";
1056
+ };
1057
+ }, [isOpen, onClose]);
1058
+ if (!isOpen) return null;
1059
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
1060
+ /* @__PURE__ */ jsx(
1061
+ "div",
1062
+ {
1063
+ className: "absolute inset-0",
1064
+ style: { backgroundColor: "var(--compass-color-overlay)" },
1065
+ onClick: onClose
1066
+ }
1067
+ ),
1068
+ /* @__PURE__ */ jsxs(
1069
+ "div",
1070
+ {
1071
+ className: "relative w-full max-w-md mx-4 rounded-xl overflow-hidden",
1072
+ style: {
1073
+ backgroundColor: "var(--compass-color-surface)",
1074
+ boxShadow: "var(--compass-shadow-lg)"
1075
+ },
1076
+ children: [
1077
+ /* @__PURE__ */ jsxs(
1078
+ "div",
1079
+ {
1080
+ className: "flex items-center justify-between px-4 py-3 border-b",
1081
+ style: { borderColor: "var(--compass-color-border)" },
1082
+ children: [
1083
+ /* @__PURE__ */ jsx(
1084
+ "h2",
1085
+ {
1086
+ className: "font-semibold",
1087
+ style: {
1088
+ fontSize: "var(--compass-font-size-subheading)",
1089
+ color: "var(--compass-color-text)"
1090
+ },
1091
+ children: title
1092
+ }
1093
+ ),
1094
+ /* @__PURE__ */ jsx(
1095
+ "button",
1096
+ {
1097
+ onClick: onClose,
1098
+ className: "p-1 rounded-md transition-colors hover:opacity-70",
1099
+ style: { color: "var(--compass-color-text-secondary)" },
1100
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
1101
+ }
1102
+ )
1103
+ ]
1104
+ }
1105
+ ),
1106
+ /* @__PURE__ */ jsx("div", { className: "p-4", children })
1107
+ ]
1108
+ }
1109
+ )
1110
+ ] });
1111
+ }
1112
+ function PnLSummary({ pnl, tokenSymbol, tokenPrice }) {
1113
+ const [isExpanded, setIsExpanded] = useState(false);
1114
+ if (!pnl) return null;
1115
+ const unrealizedPnl = parseFloat(pnl.unrealizedPnl || "0");
1116
+ const realizedPnl = parseFloat(pnl.realizedPnl || "0");
1117
+ const totalPnl = parseFloat(pnl.totalPnl || "0");
1118
+ const totalDeposited = parseFloat(pnl.totalDeposited || "0");
1119
+ if (totalDeposited === 0) return null;
1120
+ const formatPnL = (value) => {
1121
+ const prefix = value >= 0 ? "+" : "";
1122
+ return `${prefix}${value.toFixed(4)}`;
1123
+ };
1124
+ const formatUSD = (value) => {
1125
+ const usdValue = value * tokenPrice;
1126
+ const prefix = usdValue >= 0 ? "+$" : "-$";
1127
+ return `${prefix}${Math.abs(usdValue).toFixed(2)}`;
1128
+ };
1129
+ const pnlColor = (value) => value >= 0 ? "var(--compass-color-success)" : "var(--compass-color-error)";
1130
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col w-full mt-2", children: [
1131
+ /* @__PURE__ */ jsxs(
1132
+ "button",
1133
+ {
1134
+ onClick: () => setIsExpanded(!isExpanded),
1135
+ className: "flex items-center justify-between w-full px-3 py-2 rounded-lg border transition-colors",
1136
+ style: {
1137
+ backgroundColor: "var(--compass-color-surface)",
1138
+ borderColor: "var(--compass-color-border)"
1139
+ },
1140
+ children: [
1141
+ /* @__PURE__ */ jsx(
1142
+ "span",
1143
+ {
1144
+ className: "text-sm font-medium",
1145
+ style: { color: "var(--compass-color-text-secondary)" },
1146
+ children: "Performance"
1147
+ }
1148
+ ),
1149
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1150
+ /* @__PURE__ */ jsxs(
1151
+ "span",
1152
+ {
1153
+ className: "text-xs font-mono font-medium",
1154
+ style: { color: pnlColor(totalPnl) },
1155
+ children: [
1156
+ formatPnL(totalPnl),
1157
+ " ",
1158
+ tokenSymbol
1159
+ ]
1160
+ }
1161
+ ),
1162
+ /* @__PURE__ */ jsx(
1163
+ ChevronDown,
1164
+ {
1165
+ size: 16,
1166
+ className: `transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}`,
1167
+ style: { color: "var(--compass-color-text-tertiary)" }
1168
+ }
1169
+ )
1170
+ ] })
1171
+ ]
1172
+ }
1173
+ ),
1174
+ isExpanded && /* @__PURE__ */ jsxs(
1175
+ "div",
1176
+ {
1177
+ className: "mt-2 p-3 rounded-lg border",
1178
+ style: {
1179
+ backgroundColor: "var(--compass-color-surface)",
1180
+ borderColor: "var(--compass-color-border)"
1181
+ },
1182
+ children: [
1183
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
1184
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1185
+ /* @__PURE__ */ jsx(
1186
+ "span",
1187
+ {
1188
+ className: "text-[10px] font-medium uppercase tracking-wide",
1189
+ style: { color: "var(--compass-color-text-tertiary)" },
1190
+ children: "Unrealized P&L"
1191
+ }
1192
+ ),
1193
+ /* @__PURE__ */ jsx(
1194
+ "span",
1195
+ {
1196
+ className: "text-sm font-bold font-mono",
1197
+ style: { color: pnlColor(unrealizedPnl) },
1198
+ children: formatPnL(unrealizedPnl)
1199
+ }
1200
+ ),
1201
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(unrealizedPnl) }, children: formatUSD(unrealizedPnl) })
1202
+ ] }),
1203
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1204
+ /* @__PURE__ */ jsx(
1205
+ "span",
1206
+ {
1207
+ className: "text-[10px] font-medium uppercase tracking-wide",
1208
+ style: { color: "var(--compass-color-text-tertiary)" },
1209
+ children: "Realized P&L"
1210
+ }
1211
+ ),
1212
+ /* @__PURE__ */ jsx(
1213
+ "span",
1214
+ {
1215
+ className: "text-sm font-bold font-mono",
1216
+ style: { color: pnlColor(realizedPnl) },
1217
+ children: formatPnL(realizedPnl)
1218
+ }
1219
+ ),
1220
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono", style: { color: pnlColor(realizedPnl) }, children: formatUSD(realizedPnl) })
1221
+ ] })
1222
+ ] }),
1223
+ /* @__PURE__ */ jsxs(
1224
+ "div",
1225
+ {
1226
+ className: "flex items-center justify-between mt-3 pt-3 border-t",
1227
+ style: { borderColor: "var(--compass-color-border)" },
1228
+ children: [
1229
+ /* @__PURE__ */ jsx(
1230
+ "span",
1231
+ {
1232
+ className: "text-xs font-medium",
1233
+ style: { color: "var(--compass-color-text-tertiary)" },
1234
+ children: "Total P&L"
1235
+ }
1236
+ ),
1237
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1238
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-bold font-mono", style: { color: pnlColor(totalPnl) }, children: [
1239
+ formatPnL(totalPnl),
1240
+ " ",
1241
+ tokenSymbol
1242
+ ] }),
1243
+ /* @__PURE__ */ jsxs("span", { className: "text-xs font-mono", style: { color: pnlColor(totalPnl) }, children: [
1244
+ "(",
1245
+ formatUSD(totalPnl),
1246
+ ")"
1247
+ ] })
1248
+ ] })
1249
+ ]
1250
+ }
1251
+ )
1252
+ ]
1253
+ }
1254
+ )
1255
+ ] });
1256
+ }
1257
+ var CHAINS = {
1258
+ ethereum: {
1259
+ id: "ethereum",
1260
+ name: "Ethereum",
1261
+ viemChain: mainnet,
1262
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png"
1263
+ },
1264
+ base: {
1265
+ id: "base",
1266
+ name: "Base",
1267
+ viemChain: base,
1268
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/base/info/logo.png"
1269
+ },
1270
+ arbitrum: {
1271
+ id: "arbitrum",
1272
+ name: "Arbitrum",
1273
+ viemChain: arbitrum,
1274
+ icon: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/arbitrum/info/logo.png"
1275
+ }
1276
+ };
1277
+ var TOKEN_DECIMALS = {
1278
+ USDC: 6,
1279
+ USDT: 6,
1280
+ DAI: 18,
1281
+ ETH: 18,
1282
+ WETH: 18,
1283
+ WBTC: 8,
1284
+ cbBTC: 8
1285
+ };
1286
+ function DepositWithdrawForm({
1287
+ venueType,
1288
+ venueAddress,
1289
+ tokenSymbol,
1290
+ tokenDecimals,
1291
+ positionBalance = "0",
1292
+ onSuccess,
1293
+ onError
1294
+ }) {
1295
+ const [activeTab, setActiveTab] = useState("deposit");
1296
+ const [amount, setAmount] = useState("");
1297
+ const [isSubmitting, setIsSubmitting] = useState(false);
1298
+ const [statusMessage, setStatusMessage] = useState("");
1299
+ const [error, setError] = useState(null);
1300
+ const { address, isConnected, signTypedData, switchChain } = useCompassWallet();
1301
+ const { client } = useEmbeddableApi();
1302
+ const { chainId } = useChain();
1303
+ const { earnAccountAddress } = useEarnAccount();
1304
+ const queryClient = useQueryClient();
1305
+ const chainConfig = CHAINS[chainId];
1306
+ const targetChainId = chainConfig?.viemChain?.id;
1307
+ const { data: tokenBalance } = useQuery({
1308
+ queryKey: ["earnAccountTokenBalance", chainId, earnAccountAddress, tokenSymbol],
1309
+ queryFn: async () => {
1310
+ if (!earnAccountAddress) return "0";
1311
+ try {
1312
+ const response = await client.token.tokenBalance({
1313
+ chain: chainId,
1314
+ user: earnAccountAddress,
1315
+ token: tokenSymbol
1316
+ });
1317
+ return response.amount || "0";
1318
+ } catch (error2) {
1319
+ console.error("Error fetching earn account token balance:", error2);
1320
+ return "0";
1321
+ }
1322
+ },
1323
+ enabled: !!earnAccountAddress,
1324
+ staleTime: 10 * 1e3
1325
+ });
1326
+ const availableBalance = tokenBalance || "0";
1327
+ const maxBalance = activeTab === "deposit" ? availableBalance : positionBalance;
1328
+ const handleQuickAmount = useCallback((percentage) => {
1329
+ const max = parseFloat(maxBalance);
1330
+ if (isNaN(max)) return;
1331
+ setAmount((max * percentage).toFixed(tokenDecimals > 6 ? 6 : tokenDecimals));
1332
+ }, [maxBalance, tokenDecimals]);
1333
+ const handleSubmit = useCallback(async () => {
1334
+ if (!address || !amount) return;
1335
+ setIsSubmitting(true);
1336
+ setStatusMessage("Preparing transaction...");
1337
+ setError(null);
1338
+ try {
1339
+ if (switchChain && targetChainId) {
1340
+ setStatusMessage("Checking network...");
1341
+ try {
1342
+ await switchChain(targetChainId);
1343
+ } catch {
1344
+ }
1345
+ }
1346
+ const isDeposit = activeTab === "deposit";
1347
+ const prepareEndpoint = isDeposit ? "/api/compass/deposit/prepare" : "/api/compass/withdraw/prepare";
1348
+ const executeEndpoint = isDeposit ? "/api/compass/deposit/execute" : "/api/compass/withdraw/execute";
1349
+ const prepareBody = {
1350
+ amount,
1351
+ token: tokenSymbol,
1352
+ owner: address,
1353
+ chain: chainId,
1354
+ venueType
1355
+ };
1356
+ if (venueType === "VAULT") {
1357
+ prepareBody.vaultAddress = venueAddress;
1358
+ } else if (venueType === "PENDLE_PT") {
1359
+ prepareBody.marketAddress = venueAddress;
1360
+ prepareBody.maxSlippagePercent = 1;
1361
+ }
1362
+ setStatusMessage("Getting transaction data...");
1363
+ const prepareResponse = await fetch(prepareEndpoint, {
1364
+ method: "POST",
1365
+ headers: { "Content-Type": "application/json" },
1366
+ body: JSON.stringify(prepareBody)
1367
+ });
1368
+ if (!prepareResponse.ok) {
1369
+ const errorData = await prepareResponse.json();
1370
+ throw new Error(errorData.error || "Failed to prepare transaction");
1371
+ }
1372
+ const { eip712, normalizedTypes, domain, message } = await prepareResponse.json();
1373
+ setStatusMessage("Please sign the transaction...");
1374
+ const signature = await signTypedData({
1375
+ domain,
1376
+ types: normalizedTypes,
1377
+ primaryType: "SafeTx",
1378
+ message
1379
+ });
1380
+ setStatusMessage("Executing transaction...");
1381
+ const executeResponse = await fetch(executeEndpoint, {
1382
+ method: "POST",
1383
+ headers: { "Content-Type": "application/json" },
1384
+ body: JSON.stringify({
1385
+ owner: address,
1386
+ eip712,
1387
+ signature,
1388
+ chain: chainId
1389
+ })
1390
+ });
1391
+ if (!executeResponse.ok) {
1392
+ const errorData = await executeResponse.json();
1393
+ throw new Error(errorData.error || "Failed to execute transaction");
1394
+ }
1395
+ const { txHash } = await executeResponse.json();
1396
+ setStatusMessage("Transaction successful!");
1397
+ onSuccess?.(activeTab, amount, txHash);
1398
+ setAmount("");
1399
+ queryClient.invalidateQueries({ queryKey: ["earnAccountTokenBalance"] });
1400
+ queryClient.invalidateQueries({ queryKey: ["vaults"] });
1401
+ queryClient.invalidateQueries({ queryKey: ["vaultPositions"] });
1402
+ queryClient.invalidateQueries({ queryKey: ["aaveMarkets"] });
1403
+ queryClient.invalidateQueries({ queryKey: ["aavePositions"] });
1404
+ queryClient.invalidateQueries({ queryKey: ["pendleMarkets"] });
1405
+ queryClient.invalidateQueries({ queryKey: ["pendlePositions"] });
1406
+ setTimeout(() => setStatusMessage(""), 3e3);
1407
+ setTimeout(() => {
1408
+ queryClient.invalidateQueries({ queryKey: ["earnAccountTokenBalance"] });
1409
+ queryClient.invalidateQueries({ queryKey: ["vaults"] });
1410
+ queryClient.invalidateQueries({ queryKey: ["vaultPositions"] });
1411
+ }, 5e3);
1412
+ } catch (err) {
1413
+ console.error("Transaction failed:", err);
1414
+ const errorMessage = err instanceof Error ? err.message : "Transaction failed";
1415
+ setError(errorMessage);
1416
+ onError?.(err);
1417
+ } finally {
1418
+ setIsSubmitting(false);
1419
+ }
1420
+ }, [
1421
+ address,
1422
+ amount,
1423
+ chainId,
1424
+ targetChainId,
1425
+ activeTab,
1426
+ venueType,
1427
+ venueAddress,
1428
+ tokenSymbol,
1429
+ signTypedData,
1430
+ switchChain,
1431
+ queryClient,
1432
+ onSuccess,
1433
+ onError
1434
+ ]);
1435
+ if (!isConnected) {
1436
+ return /* @__PURE__ */ jsxs(
1437
+ "div",
1438
+ {
1439
+ className: "flex flex-col items-center gap-3 py-6 px-4 rounded-lg",
1440
+ style: {
1441
+ backgroundColor: "var(--compass-color-surface)",
1442
+ border: "1px solid var(--compass-color-border)"
1443
+ },
1444
+ children: [
1445
+ /* @__PURE__ */ jsx(AlertCircle, { size: 24, style: { color: "var(--compass-color-text-tertiary)" } }),
1446
+ /* @__PURE__ */ jsx(
1447
+ "p",
1448
+ {
1449
+ className: "text-sm text-center",
1450
+ style: { color: "var(--compass-color-text-secondary)" },
1451
+ children: "Connect your wallet to deposit or withdraw"
1452
+ }
1453
+ )
1454
+ ]
1455
+ }
1456
+ );
1457
+ }
1458
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
1459
+ /* @__PURE__ */ jsx(
1460
+ "div",
1461
+ {
1462
+ className: "flex gap-1 p-1 rounded-lg",
1463
+ style: { backgroundColor: "var(--compass-color-background)" },
1464
+ children: ["deposit", "withdraw"].map((tab) => /* @__PURE__ */ jsx(
1465
+ "button",
1466
+ {
1467
+ onClick: () => {
1468
+ setActiveTab(tab);
1469
+ setError(null);
1470
+ setStatusMessage("");
1471
+ },
1472
+ className: "flex-1 py-2 rounded-md text-sm font-medium capitalize transition-all",
1473
+ style: {
1474
+ backgroundColor: activeTab === tab ? "var(--compass-color-surface)" : "transparent",
1475
+ color: activeTab === tab ? "var(--compass-color-text)" : "var(--compass-color-text-secondary)"
1476
+ },
1477
+ children: tab
1478
+ },
1479
+ tab
1480
+ ))
1481
+ }
1482
+ ),
1483
+ /* @__PURE__ */ jsxs("div", { children: [
1484
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
1485
+ /* @__PURE__ */ jsx(
1486
+ "label",
1487
+ {
1488
+ className: "text-sm font-medium",
1489
+ style: { color: "var(--compass-color-text-secondary)" },
1490
+ children: "Amount"
1491
+ }
1492
+ ),
1493
+ /* @__PURE__ */ jsxs("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: [
1494
+ "Available: ",
1495
+ parseFloat(maxBalance).toFixed(4),
1496
+ " ",
1497
+ tokenSymbol
1498
+ ] })
1499
+ ] }),
1500
+ /* @__PURE__ */ jsxs(
1501
+ "div",
1502
+ {
1503
+ className: "flex items-center gap-2 p-3 rounded-lg border",
1504
+ style: {
1505
+ backgroundColor: "var(--compass-color-background)",
1506
+ borderColor: "var(--compass-color-border)"
1507
+ },
1508
+ children: [
1509
+ /* @__PURE__ */ jsx(
1510
+ "input",
1511
+ {
1512
+ type: "number",
1513
+ value: amount,
1514
+ onChange: (e) => {
1515
+ setAmount(e.target.value);
1516
+ setError(null);
1517
+ },
1518
+ placeholder: "0.00",
1519
+ className: "flex-1 bg-transparent outline-none text-lg font-mono",
1520
+ style: { color: "var(--compass-color-text)" }
1521
+ }
1522
+ ),
1523
+ /* @__PURE__ */ jsx(
1524
+ "span",
1525
+ {
1526
+ className: "text-sm font-medium",
1527
+ style: { color: "var(--compass-color-text-secondary)" },
1528
+ children: tokenSymbol
1529
+ }
1530
+ )
1531
+ ]
1532
+ }
1533
+ )
1534
+ ] }),
1535
+ /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [0.25, 0.5, 1].map((pct) => /* @__PURE__ */ jsx(
1536
+ "button",
1537
+ {
1538
+ onClick: () => handleQuickAmount(pct),
1539
+ className: "flex-1 py-1.5 rounded-md text-xs font-medium transition-colors",
1540
+ style: {
1541
+ backgroundColor: "var(--compass-color-secondary)",
1542
+ color: "var(--compass-color-text-secondary)"
1543
+ },
1544
+ children: pct === 1 ? "Max" : `${pct * 100}%`
1545
+ },
1546
+ pct
1547
+ )) }),
1548
+ error && /* @__PURE__ */ jsx(
1549
+ "div",
1550
+ {
1551
+ className: "p-3 rounded-lg text-sm",
1552
+ style: {
1553
+ backgroundColor: "var(--compass-color-error-muted, rgba(239, 68, 68, 0.1))",
1554
+ color: "var(--compass-color-error, #ef4444)"
1555
+ },
1556
+ children: error
1557
+ }
1558
+ ),
1559
+ statusMessage && !error && /* @__PURE__ */ jsx(
1560
+ "div",
1561
+ {
1562
+ className: "p-3 rounded-lg text-sm text-center",
1563
+ style: {
1564
+ backgroundColor: "var(--compass-color-success-muted, rgba(34, 197, 94, 0.1))",
1565
+ color: "var(--compass-color-success, #22c55e)"
1566
+ },
1567
+ children: statusMessage
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsxs(
1571
+ "button",
1572
+ {
1573
+ onClick: handleSubmit,
1574
+ disabled: isSubmitting || !amount || parseFloat(amount) <= 0,
1575
+ className: "w-full py-3 rounded-lg font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
1576
+ style: {
1577
+ backgroundColor: "var(--compass-color-primary)",
1578
+ color: "var(--compass-color-primary-text)"
1579
+ },
1580
+ children: [
1581
+ isSubmitting && /* @__PURE__ */ jsx(Loader2, { size: 18, className: "animate-spin" }),
1582
+ isSubmitting ? "Processing..." : activeTab === "deposit" ? "Deposit" : "Withdraw"
1583
+ ]
1584
+ }
1585
+ )
1586
+ ] });
1587
+ }
1588
+ function getExplorerUrl(txHash, chainId) {
1589
+ const explorers = {
1590
+ ethereum: "https://etherscan.io/tx/",
1591
+ arbitrum: "https://arbiscan.io/tx/",
1592
+ base: "https://basescan.org/tx/"
1593
+ };
1594
+ return `${explorers[chainId] || explorers.ethereum}${txHash}`;
1595
+ }
1596
+ function TransactionHistory({
1597
+ deposits = [],
1598
+ withdrawals = [],
1599
+ tokenSymbol
1600
+ }) {
1601
+ const [isExpanded, setIsExpanded] = useState(false);
1602
+ const { chainId } = useChain();
1603
+ const allEvents = [
1604
+ ...deposits.map((d) => ({ type: "deposit", event: d })),
1605
+ ...withdrawals.map((w) => ({ type: "withdrawal", event: w }))
1606
+ ].sort((a, b) => b.event.blockNumber - a.event.blockNumber);
1607
+ if (allEvents.length === 0) return null;
1608
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col w-full", children: [
1609
+ /* @__PURE__ */ jsxs(
1610
+ "button",
1611
+ {
1612
+ onClick: () => setIsExpanded(!isExpanded),
1613
+ className: "flex items-center justify-between w-full px-3 py-2 rounded-lg border transition-colors",
1614
+ style: {
1615
+ backgroundColor: "var(--compass-color-surface)",
1616
+ borderColor: "var(--compass-color-border)"
1617
+ },
1618
+ children: [
1619
+ /* @__PURE__ */ jsxs(
1620
+ "span",
1621
+ {
1622
+ className: "text-sm font-medium",
1623
+ style: { color: "var(--compass-color-text-secondary)" },
1624
+ children: [
1625
+ "Transaction History (",
1626
+ allEvents.length,
1627
+ ")"
1628
+ ]
1629
+ }
1630
+ ),
1631
+ /* @__PURE__ */ jsx(
1632
+ ChevronDown,
1633
+ {
1634
+ size: 16,
1635
+ className: `transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}`,
1636
+ style: { color: "var(--compass-color-text-tertiary)" }
1637
+ }
1638
+ )
1639
+ ]
1640
+ }
1641
+ ),
1642
+ isExpanded && /* @__PURE__ */ jsx(
1643
+ "div",
1644
+ {
1645
+ className: "mt-2 max-h-48 overflow-y-auto rounded-lg border",
1646
+ style: { borderColor: "var(--compass-color-border)" },
1647
+ children: /* @__PURE__ */ jsx("div", { children: allEvents.map((item, index) => /* @__PURE__ */ jsxs(
1648
+ "div",
1649
+ {
1650
+ className: "flex items-center justify-between px-3 py-2",
1651
+ style: {
1652
+ backgroundColor: "var(--compass-color-surface)",
1653
+ borderTop: index > 0 ? "1px solid var(--compass-color-border)" : "none"
1654
+ },
1655
+ children: [
1656
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1657
+ item.type === "deposit" ? /* @__PURE__ */ jsx(
1658
+ "div",
1659
+ {
1660
+ className: "p-1 rounded-full",
1661
+ style: { backgroundColor: "var(--compass-color-success-muted)" },
1662
+ children: /* @__PURE__ */ jsx(ArrowDownLeft, { size: 12, style: { color: "var(--compass-color-success)" } })
1663
+ }
1664
+ ) : /* @__PURE__ */ jsx(
1665
+ "div",
1666
+ {
1667
+ className: "p-1 rounded-full",
1668
+ style: { backgroundColor: "var(--compass-color-warning-muted)" },
1669
+ children: /* @__PURE__ */ jsx(ArrowUpRight, { size: 12, style: { color: "var(--compass-color-warning)" } })
1670
+ }
1671
+ ),
1672
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1673
+ /* @__PURE__ */ jsx(
1674
+ "span",
1675
+ {
1676
+ className: "text-xs font-medium",
1677
+ style: { color: "var(--compass-color-text)" },
1678
+ children: item.type === "deposit" ? "Deposit" : "Withdrawal"
1679
+ }
1680
+ ),
1681
+ /* @__PURE__ */ jsxs(
1682
+ "span",
1683
+ {
1684
+ className: "text-[10px]",
1685
+ style: { color: "var(--compass-color-text-tertiary)" },
1686
+ children: [
1687
+ "Block #",
1688
+ item.event.blockNumber.toLocaleString()
1689
+ ]
1690
+ }
1691
+ )
1692
+ ] })
1693
+ ] }),
1694
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1695
+ /* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxs(
1696
+ "span",
1697
+ {
1698
+ className: "text-xs font-mono font-medium",
1699
+ style: {
1700
+ color: item.type === "deposit" ? "var(--compass-color-success)" : "var(--compass-color-warning)"
1701
+ },
1702
+ children: [
1703
+ item.type === "deposit" ? "+" : "-",
1704
+ parseFloat(item.event.amount).toFixed(4),
1705
+ " ",
1706
+ tokenSymbol
1707
+ ]
1708
+ }
1709
+ ) }),
1710
+ /* @__PURE__ */ jsx(
1711
+ "a",
1712
+ {
1713
+ href: getExplorerUrl(item.event.txHash, chainId),
1714
+ target: "_blank",
1715
+ rel: "noopener noreferrer",
1716
+ className: "p-1 rounded hover:opacity-70 transition-opacity",
1717
+ title: "View on explorer",
1718
+ children: /* @__PURE__ */ jsx(
1719
+ ExternalLink,
1720
+ {
1721
+ size: 12,
1722
+ style: { color: "var(--compass-color-text-tertiary)" }
1723
+ }
1724
+ )
1725
+ }
1726
+ )
1727
+ ] })
1728
+ ]
1729
+ },
1730
+ `${item.type}-${item.event.txHash}-${index}`
1731
+ )) })
1732
+ }
1733
+ )
1734
+ ] });
1735
+ }
1736
+ function EarnAccountGuard({
1737
+ children,
1738
+ loadingComponent,
1739
+ createAccountComponent
1740
+ }) {
1741
+ const { isConnected, login } = useEmbeddableWallet();
1742
+ const { isDeployed, isChecking, isCreating, createAccount, error } = useEarnAccount();
1743
+ const [createError, setCreateError] = useState(null);
1744
+ if (!isConnected) {
1745
+ return /* @__PURE__ */ jsxs(
1746
+ "div",
1747
+ {
1748
+ className: "compass-earn-guard p-6 text-center rounded-xl",
1749
+ style: {
1750
+ backgroundColor: "var(--compass-color-surface)"
1751
+ },
1752
+ children: [
1753
+ /* @__PURE__ */ jsx("p", { className: "text-sm mb-4", style: { color: "var(--compass-color-text-secondary)" }, children: "Connect your wallet to continue" }),
1754
+ login && /* @__PURE__ */ jsx(
1755
+ "button",
1756
+ {
1757
+ onClick: login,
1758
+ className: "px-4 py-2 rounded-lg font-medium transition-colors",
1759
+ style: {
1760
+ backgroundColor: "var(--compass-color-primary)",
1761
+ color: "var(--compass-color-primary-text)"
1762
+ },
1763
+ children: "Connect Wallet"
1764
+ }
1765
+ )
1766
+ ]
1767
+ }
1768
+ );
1769
+ }
1770
+ if (isChecking) {
1771
+ if (loadingComponent) {
1772
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent });
1773
+ }
1774
+ return /* @__PURE__ */ jsxs(
1775
+ "div",
1776
+ {
1777
+ className: "compass-earn-guard p-6 text-center rounded-xl",
1778
+ style: { backgroundColor: "var(--compass-color-surface)" },
1779
+ children: [
1780
+ /* @__PURE__ */ jsx(
1781
+ Loader2,
1782
+ {
1783
+ size: 24,
1784
+ className: "animate-spin mx-auto mb-3",
1785
+ style: { color: "var(--compass-color-primary)" }
1786
+ }
1787
+ ),
1788
+ /* @__PURE__ */ jsx("p", { className: "text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: "Checking account status..." })
1789
+ ]
1790
+ }
1791
+ );
1792
+ }
1793
+ if (isDeployed) {
1794
+ return /* @__PURE__ */ jsx(Fragment, { children });
1795
+ }
1796
+ if (createAccountComponent) {
1797
+ return /* @__PURE__ */ jsx(Fragment, { children: createAccountComponent });
1798
+ }
1799
+ const handleCreate = async () => {
1800
+ setCreateError(null);
1801
+ try {
1802
+ await createAccount();
1803
+ } catch (err) {
1804
+ setCreateError(err instanceof Error ? err.message : "Failed to create account");
1805
+ }
1806
+ };
1807
+ return /* @__PURE__ */ jsxs(
1808
+ "div",
1809
+ {
1810
+ className: "compass-earn-guard p-6 text-center rounded-xl border",
1811
+ style: {
1812
+ backgroundColor: "var(--compass-color-surface)",
1813
+ borderColor: "var(--compass-color-border)"
1814
+ },
1815
+ children: [
1816
+ /* @__PURE__ */ jsx(
1817
+ "div",
1818
+ {
1819
+ className: "w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-4",
1820
+ style: { backgroundColor: "var(--compass-color-primary)", opacity: 0.15 },
1821
+ children: /* @__PURE__ */ jsx(
1822
+ "svg",
1823
+ {
1824
+ width: "24",
1825
+ height: "24",
1826
+ viewBox: "0 0 24 24",
1827
+ fill: "none",
1828
+ stroke: "var(--compass-color-primary)",
1829
+ strokeWidth: "2",
1830
+ strokeLinecap: "round",
1831
+ strokeLinejoin: "round",
1832
+ children: /* @__PURE__ */ jsx("path", { d: "M12 2v4m0 12v4M4.93 4.93l2.83 2.83m8.48 8.48l2.83 2.83M2 12h4m12 0h4M4.93 19.07l2.83-2.83m8.48-8.48l2.83-2.83" })
1833
+ }
1834
+ )
1835
+ }
1836
+ ),
1837
+ /* @__PURE__ */ jsx(
1838
+ "h3",
1839
+ {
1840
+ className: "text-lg font-semibold mb-2",
1841
+ style: { color: "var(--compass-color-text)" },
1842
+ children: "Create Your Earn Account"
1843
+ }
1844
+ ),
1845
+ /* @__PURE__ */ jsx(
1846
+ "p",
1847
+ {
1848
+ className: "text-sm mb-6 max-w-xs mx-auto",
1849
+ style: { color: "var(--compass-color-text-secondary)" },
1850
+ children: "To start earning, we need to create a secure smart wallet for you. This is a one-time setup with no gas fees."
1851
+ }
1852
+ ),
1853
+ (error || createError) && /* @__PURE__ */ jsx(
1854
+ "p",
1855
+ {
1856
+ className: "text-sm mb-4",
1857
+ style: { color: "var(--compass-color-error)" },
1858
+ children: createError || (error instanceof Error ? error.message : "An error occurred")
1859
+ }
1860
+ ),
1861
+ /* @__PURE__ */ jsxs(
1862
+ "button",
1863
+ {
1864
+ onClick: handleCreate,
1865
+ disabled: isCreating,
1866
+ className: "px-6 py-3 rounded-lg font-medium transition-all inline-flex items-center gap-2",
1867
+ style: {
1868
+ backgroundColor: isCreating ? "var(--compass-color-surface-hover)" : "var(--compass-color-primary)",
1869
+ color: isCreating ? "var(--compass-color-text-secondary)" : "var(--compass-color-primary-text)",
1870
+ cursor: isCreating ? "not-allowed" : "pointer"
1871
+ },
1872
+ children: [
1873
+ isCreating && /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
1874
+ isCreating ? "Creating..." : "Create Account"
1875
+ ]
1876
+ }
1877
+ ),
1878
+ /* @__PURE__ */ jsx(
1879
+ "p",
1880
+ {
1881
+ className: "text-xs mt-4",
1882
+ style: { color: "var(--compass-color-text-tertiary)" },
1883
+ children: "Gas fees are sponsored by Compass"
1884
+ }
1885
+ )
1886
+ ]
1887
+ }
1888
+ );
1889
+ }
1890
+ function formatTVL(tvl) {
1891
+ if (!tvl) return "$0";
1892
+ const num = parseFloat(tvl);
1893
+ if (isNaN(num)) return "$0";
1894
+ if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`;
1895
+ if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`;
1896
+ if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`;
1897
+ return `$${num.toFixed(2)}`;
1898
+ }
1899
+ function formatAPY(apy) {
1900
+ if (!apy) return "0.00%";
1901
+ const num = parseFloat(apy);
1902
+ if (isNaN(num)) return "0.00%";
1903
+ return `${num.toFixed(2)}%`;
1904
+ }
1905
+ function VaultCard({
1906
+ vault,
1907
+ showApy,
1908
+ apyPeriods,
1909
+ showTvl,
1910
+ showUserPosition,
1911
+ onClick
1912
+ }) {
1913
+ const getApyValue = (period) => {
1914
+ switch (period) {
1915
+ case "7d":
1916
+ return vault.apy7d;
1917
+ case "30d":
1918
+ return vault.apy30d;
1919
+ case "90d":
1920
+ return vault.apy90d;
1921
+ }
1922
+ };
1923
+ const primaryApy = getApyValue(apyPeriods[0] || "7d");
1924
+ const hasPosition = vault.userPosition && parseFloat(vault.userPosition.balance) > 0;
1925
+ return /* @__PURE__ */ jsxs(
1926
+ "button",
1927
+ {
1928
+ onClick,
1929
+ className: "w-full p-4 rounded-xl border transition-all text-left hover:scale-[1.01]",
1930
+ style: {
1931
+ backgroundColor: "var(--compass-color-surface)",
1932
+ borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)"
1933
+ },
1934
+ children: [
1935
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
1936
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
1937
+ /* @__PURE__ */ jsx(
1938
+ "h3",
1939
+ {
1940
+ className: "font-semibold",
1941
+ style: {
1942
+ fontSize: "var(--compass-font-size-body)",
1943
+ color: "var(--compass-color-text)"
1944
+ },
1945
+ children: vault.name
1946
+ }
1947
+ ),
1948
+ /* @__PURE__ */ jsx(
1949
+ "span",
1950
+ {
1951
+ className: "text-sm",
1952
+ style: { color: "var(--compass-color-text-secondary)" },
1953
+ children: vault.assetSymbol
1954
+ }
1955
+ )
1956
+ ] }),
1957
+ showApy && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1958
+ /* @__PURE__ */ jsx(TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }),
1959
+ /* @__PURE__ */ jsx(
1960
+ "span",
1961
+ {
1962
+ className: "font-mono font-semibold",
1963
+ style: { color: "var(--compass-color-success)" },
1964
+ children: formatAPY(primaryApy)
1965
+ }
1966
+ )
1967
+ ] })
1968
+ ] }),
1969
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-3 pt-3 border-t", style: { borderColor: "var(--compass-color-border)" }, children: [
1970
+ showTvl && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1971
+ /* @__PURE__ */ jsx(
1972
+ "span",
1973
+ {
1974
+ className: "text-xs",
1975
+ style: { color: "var(--compass-color-text-tertiary)" },
1976
+ children: "TVL"
1977
+ }
1978
+ ),
1979
+ /* @__PURE__ */ jsx(
1980
+ "span",
1981
+ {
1982
+ className: "font-mono text-sm",
1983
+ style: { color: "var(--compass-color-text-secondary)" },
1984
+ children: formatTVL(vault.tvlUsd)
1985
+ }
1986
+ )
1987
+ ] }),
1988
+ showUserPosition && hasPosition && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
1989
+ /* @__PURE__ */ jsx(
1990
+ "span",
1991
+ {
1992
+ className: "text-xs",
1993
+ style: { color: "var(--compass-color-text-tertiary)" },
1994
+ children: "Your Position"
1995
+ }
1996
+ ),
1997
+ /* @__PURE__ */ jsxs(
1998
+ "span",
1999
+ {
2000
+ className: "font-mono text-sm font-medium",
2001
+ style: { color: "var(--compass-color-primary)" },
2002
+ children: [
2003
+ parseFloat(vault.userPosition.balance).toFixed(4),
2004
+ " ",
2005
+ vault.assetSymbol
2006
+ ]
2007
+ }
2008
+ )
2009
+ ] })
2010
+ ] })
2011
+ ]
2012
+ }
2013
+ );
2014
+ }
2015
+ function useVaultsData(options = {}) {
2016
+ const { client } = useEmbeddableApi();
2017
+ const { address } = useEmbeddableWallet();
2018
+ const { chainId } = useChain();
2019
+ const { sortBy = "apy_7d", assetFilter, minApy } = options;
2020
+ const vaultsQuery = useQuery({
2021
+ queryKey: ["vaults", chainId, sortBy, assetFilter, minApy],
2022
+ queryFn: async () => {
2023
+ const assetSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
2024
+ const response = await client.earn.earnVaults({
2025
+ chain: chainId,
2026
+ orderBy: sortBy === "tvl" ? "tvl_usd" : sortBy,
2027
+ direction: "desc",
2028
+ limit: 100,
2029
+ ...assetSymbol && { assetSymbol }
2030
+ });
2031
+ let vaults = (response.vaults || []).map((v) => ({
2032
+ vaultAddress: v.vaultAddress,
2033
+ name: v.name || "Unknown Vault",
2034
+ assetSymbol: v.assetSymbol || "UNKNOWN",
2035
+ apy7d: v.apy7d ?? null,
2036
+ apy30d: v.apy30d ?? null,
2037
+ apy90d: v.apy90d ?? null,
2038
+ tvlUsd: v.tvlUsd ?? null
2039
+ }));
2040
+ if (assetFilter && assetFilter.length > 1) {
2041
+ vaults = vaults.filter(
2042
+ (v) => assetFilter.includes(v.assetSymbol.toUpperCase())
2043
+ );
2044
+ }
2045
+ if (minApy !== void 0 && minApy > 0) {
2046
+ vaults = vaults.filter((v) => {
2047
+ const apy = parseFloat(v.apy7d || "0");
2048
+ return apy >= minApy;
2049
+ });
2050
+ }
2051
+ return vaults;
2052
+ },
2053
+ staleTime: 30 * 1e3
2054
+ });
2055
+ const positionsQuery = useQuery({
2056
+ queryKey: ["vaultPositions", chainId, address],
2057
+ queryFn: async () => {
2058
+ if (!address) return [];
2059
+ const response = await client.earn.earnPositions({
2060
+ chain: chainId,
2061
+ owner: address
2062
+ });
2063
+ return (response.vaults || []).map((p) => ({
2064
+ vaultAddress: p.vaultAddress,
2065
+ balance: p.balance || "0",
2066
+ pnl: p.pnl ? {
2067
+ unrealizedPnl: p.pnl.unrealizedPnl || "0",
2068
+ realizedPnl: p.pnl.realizedPnl || "0",
2069
+ totalPnl: p.pnl.totalPnl || "0",
2070
+ totalDeposited: p.pnl.totalDeposited || "0"
2071
+ } : void 0,
2072
+ deposits: (p.deposits || []).map((d) => ({
2073
+ amount: d.inputAmount || d.amount || "0",
2074
+ blockNumber: d.blockNumber || 0,
2075
+ txHash: d.transactionHash || d.txHash || ""
2076
+ })),
2077
+ withdrawals: (p.withdrawals || []).map((w) => ({
2078
+ amount: w.outputAmount || w.amount || "0",
2079
+ blockNumber: w.blockNumber || 0,
2080
+ txHash: w.transactionHash || w.txHash || ""
2081
+ }))
2082
+ }));
2083
+ },
2084
+ enabled: !!address,
2085
+ staleTime: 30 * 1e3
2086
+ });
2087
+ const vaultsWithPositions = (vaultsQuery.data || []).map((vault) => {
2088
+ const position = positionsQuery.data?.find(
2089
+ (p) => p.vaultAddress.toLowerCase() === vault.vaultAddress.toLowerCase()
2090
+ );
2091
+ return { ...vault, userPosition: position };
2092
+ });
2093
+ return {
2094
+ vaults: vaultsWithPositions,
2095
+ isLoading: vaultsQuery.isLoading,
2096
+ isError: vaultsQuery.isError,
2097
+ error: vaultsQuery.error,
2098
+ refetch: () => {
2099
+ vaultsQuery.refetch();
2100
+ positionsQuery.refetch();
2101
+ }
2102
+ };
2103
+ }
2104
+ var TOKEN_DECIMALS2 = {
2105
+ USDC: 6,
2106
+ USDT: 6,
2107
+ DAI: 18,
2108
+ ETH: 18,
2109
+ WETH: 18,
2110
+ WBTC: 8,
2111
+ cbBTC: 8
2112
+ };
2113
+ function VaultsList({
2114
+ showApy = true,
2115
+ apyPeriods = ["7d", "30d", "90d"],
2116
+ showTvl = true,
2117
+ showUserPosition = true,
2118
+ showPnL = true,
2119
+ showHistory = true,
2120
+ showSearch = true,
2121
+ showSort = true,
2122
+ actionMode = "modal",
2123
+ defaultSort = "apy_7d",
2124
+ assetFilter,
2125
+ minApy,
2126
+ onVaultSelect,
2127
+ onDeposit,
2128
+ onWithdraw
2129
+ }) {
2130
+ const [searchQuery, setSearchQuery] = useState("");
2131
+ const [sortBy, setSortBy] = useState(defaultSort);
2132
+ const [selectedVault, setSelectedVault] = useState(null);
2133
+ const { vaults, isLoading, isError, refetch } = useVaultsData({
2134
+ sortBy,
2135
+ assetFilter,
2136
+ minApy
2137
+ });
2138
+ const filteredVaults = useMemo(() => {
2139
+ if (!searchQuery) return vaults;
2140
+ const query = searchQuery.toLowerCase();
2141
+ return vaults.filter(
2142
+ (v) => v.name.toLowerCase().includes(query) || v.assetSymbol.toLowerCase().includes(query)
2143
+ );
2144
+ }, [vaults, searchQuery]);
2145
+ const handleVaultClick = (vault) => {
2146
+ setSelectedVault(vault);
2147
+ onVaultSelect?.(vault);
2148
+ };
2149
+ const handleActionSuccess = (action, amount, txHash) => {
2150
+ if (action === "deposit") {
2151
+ onDeposit?.(selectedVault, amount, txHash);
2152
+ } else {
2153
+ onWithdraw?.(selectedVault, amount, txHash);
2154
+ }
2155
+ refetch();
2156
+ };
2157
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2158
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2159
+ /* @__PURE__ */ jsx(ChainSwitcher, {}),
2160
+ /* @__PURE__ */ jsx(WalletStatus, { compact: true })
2161
+ ] }),
2162
+ (showSearch || showSort) && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2163
+ showSearch && /* @__PURE__ */ jsxs(
2164
+ "div",
2165
+ {
2166
+ className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
2167
+ style: {
2168
+ backgroundColor: "var(--compass-color-background)",
2169
+ borderColor: "var(--compass-color-border)"
2170
+ },
2171
+ children: [
2172
+ /* @__PURE__ */ jsx(Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
2173
+ /* @__PURE__ */ jsx(
2174
+ "input",
2175
+ {
2176
+ type: "text",
2177
+ placeholder: "Search vaults...",
2178
+ value: searchQuery,
2179
+ onChange: (e) => setSearchQuery(e.target.value),
2180
+ className: "flex-1 bg-transparent outline-none text-sm",
2181
+ style: { color: "var(--compass-color-text)" }
2182
+ }
2183
+ )
2184
+ ]
2185
+ }
2186
+ ),
2187
+ showSort && /* @__PURE__ */ jsxs(
2188
+ "select",
2189
+ {
2190
+ value: sortBy,
2191
+ onChange: (e) => setSortBy(e.target.value),
2192
+ className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
2193
+ style: {
2194
+ backgroundColor: "var(--compass-color-background)",
2195
+ borderColor: "var(--compass-color-border)",
2196
+ color: "var(--compass-color-text)"
2197
+ },
2198
+ children: [
2199
+ /* @__PURE__ */ jsx("option", { value: "apy_7d", children: "APY (7D)" }),
2200
+ /* @__PURE__ */ jsx("option", { value: "apy_30d", children: "APY (30D)" }),
2201
+ /* @__PURE__ */ jsx("option", { value: "apy_90d", children: "APY (90D)" }),
2202
+ /* @__PURE__ */ jsx("option", { value: "tvl", children: "TVL" })
2203
+ ]
2204
+ }
2205
+ )
2206
+ ] }),
2207
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsx(
2208
+ "div",
2209
+ {
2210
+ className: "p-4 rounded-lg text-center",
2211
+ style: {
2212
+ backgroundColor: "var(--compass-color-error-muted)",
2213
+ color: "var(--compass-color-error)"
2214
+ },
2215
+ children: "Failed to load vaults. Please try again."
2216
+ }
2217
+ ) : filteredVaults.length === 0 ? /* @__PURE__ */ jsx(
2218
+ "div",
2219
+ {
2220
+ className: "p-8 text-center",
2221
+ style: { color: "var(--compass-color-text-secondary)" },
2222
+ children: "No vaults found"
2223
+ }
2224
+ ) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: filteredVaults.map((vault) => /* @__PURE__ */ jsx(
2225
+ VaultCard,
2226
+ {
2227
+ vault,
2228
+ showApy,
2229
+ apyPeriods,
2230
+ showTvl,
2231
+ showUserPosition,
2232
+ onClick: () => handleVaultClick(vault)
2233
+ },
2234
+ vault.vaultAddress
2235
+ )) }),
2236
+ actionMode === "modal" && selectedVault && /* @__PURE__ */ jsx(
2237
+ ActionModal,
2238
+ {
2239
+ isOpen: !!selectedVault,
2240
+ onClose: () => setSelectedVault(null),
2241
+ title: selectedVault.name,
2242
+ children: /* @__PURE__ */ jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2243
+ showPnL && selectedVault.userPosition?.pnl && /* @__PURE__ */ jsx(
2244
+ PnLSummary,
2245
+ {
2246
+ pnl: selectedVault.userPosition.pnl,
2247
+ tokenSymbol: selectedVault.assetSymbol,
2248
+ tokenPrice: 1
2249
+ }
2250
+ ),
2251
+ showHistory && (selectedVault.userPosition?.deposits?.length || selectedVault.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsx(
2252
+ TransactionHistory,
2253
+ {
2254
+ deposits: selectedVault.userPosition?.deposits,
2255
+ withdrawals: selectedVault.userPosition?.withdrawals,
2256
+ tokenSymbol: selectedVault.assetSymbol
2257
+ }
2258
+ ),
2259
+ /* @__PURE__ */ jsx(
2260
+ DepositWithdrawForm,
2261
+ {
2262
+ venueType: "VAULT",
2263
+ venueAddress: selectedVault.vaultAddress,
2264
+ tokenSymbol: selectedVault.assetSymbol,
2265
+ tokenDecimals: TOKEN_DECIMALS2[selectedVault.assetSymbol] || 18,
2266
+ positionBalance: selectedVault.userPosition?.balance,
2267
+ onSuccess: handleActionSuccess
2268
+ }
2269
+ )
2270
+ ] }) })
2271
+ }
2272
+ )
2273
+ ] });
2274
+ }
2275
+ function useAaveData(options = {}) {
2276
+ const { client } = useEmbeddableApi();
2277
+ const { address } = useEmbeddableWallet();
2278
+ const { chainId } = useChain();
2279
+ const { sortBy = "supply_apy", assetFilter } = options;
2280
+ const marketsQuery = useQuery({
2281
+ queryKey: ["aaveMarkets", chainId, sortBy, assetFilter],
2282
+ queryFn: async () => {
2283
+ const response = await client.earn.earnAaveMarkets({
2284
+ chain: chainId
2285
+ });
2286
+ const marketsDict = response.markets || {};
2287
+ let markets = [];
2288
+ for (const [symbol, tokenData] of Object.entries(marketsDict)) {
2289
+ if (assetFilter && assetFilter.length > 0) {
2290
+ if (!assetFilter.includes(symbol.toUpperCase())) {
2291
+ continue;
2292
+ }
2293
+ }
2294
+ const token = tokenData;
2295
+ const chainData = token.chains?.[chainId];
2296
+ if (chainData) {
2297
+ markets.push({
2298
+ marketAddress: chainData.address || "",
2299
+ reserveSymbol: symbol,
2300
+ underlyingSymbol: symbol,
2301
+ supplyApy: chainData.supplyApy?.toString() ?? null,
2302
+ borrowApy: chainData.borrowApy?.toString() ?? null,
2303
+ // Aave API doesn't provide TVL
2304
+ tvlUsd: null
2305
+ });
2306
+ }
2307
+ }
2308
+ markets.sort((a, b) => {
2309
+ return parseFloat(b.supplyApy || "0") - parseFloat(a.supplyApy || "0");
2310
+ });
2311
+ return markets;
2312
+ },
2313
+ staleTime: 30 * 1e3
2314
+ });
2315
+ const positionsQuery = useQuery({
2316
+ queryKey: ["aavePositions", chainId, address],
2317
+ queryFn: async () => {
2318
+ if (!address) return [];
2319
+ const response = await client.earn.earnPositions({
2320
+ chain: chainId,
2321
+ owner: address
2322
+ });
2323
+ return (response.aave || []).map((p) => ({
2324
+ marketAddress: p.marketAddress || "",
2325
+ reserveSymbol: p.reserveSymbol || "",
2326
+ balance: p.balance || "0",
2327
+ pnl: p.pnl ? {
2328
+ unrealizedPnl: p.pnl.unrealizedPnl || "0",
2329
+ realizedPnl: p.pnl.realizedPnl || "0",
2330
+ totalPnl: p.pnl.totalPnl || "0",
2331
+ totalDeposited: p.pnl.totalDeposited || "0"
2332
+ } : void 0,
2333
+ deposits: (p.deposits || []).map((d) => ({
2334
+ amount: d.inputAmount || d.amount || "0",
2335
+ blockNumber: d.blockNumber || 0,
2336
+ txHash: d.transactionHash || d.txHash || ""
2337
+ })),
2338
+ withdrawals: (p.withdrawals || []).map((w) => ({
2339
+ amount: w.outputAmount || w.amount || "0",
2340
+ blockNumber: w.blockNumber || 0,
2341
+ txHash: w.transactionHash || w.txHash || ""
2342
+ }))
2343
+ }));
2344
+ },
2345
+ enabled: !!address,
2346
+ staleTime: 30 * 1e3
2347
+ });
2348
+ const marketsWithPositions = (marketsQuery.data || []).map((market) => {
2349
+ const position = positionsQuery.data?.find(
2350
+ (p) => p.reserveSymbol.toLowerCase() === market.reserveSymbol.toLowerCase()
2351
+ );
2352
+ return { ...market, userPosition: position };
2353
+ });
2354
+ return {
2355
+ markets: marketsWithPositions,
2356
+ isLoading: marketsQuery.isLoading,
2357
+ isError: marketsQuery.isError,
2358
+ error: marketsQuery.error,
2359
+ refetch: () => {
2360
+ marketsQuery.refetch();
2361
+ positionsQuery.refetch();
2362
+ }
2363
+ };
2364
+ }
2365
+ var TOKEN_DECIMALS3 = {
2366
+ USDC: 6,
2367
+ USDT: 6,
2368
+ DAI: 18,
2369
+ ETH: 18,
2370
+ WETH: 18,
2371
+ WBTC: 8,
2372
+ cbBTC: 8
2373
+ };
2374
+ function formatAPY2(apy) {
2375
+ if (!apy) return "0.00%";
2376
+ const num = parseFloat(apy);
2377
+ return `${num.toFixed(2)}%`;
2378
+ }
2379
+ function formatTVL2(tvl) {
2380
+ if (!tvl) return "$0";
2381
+ const num = parseFloat(tvl);
2382
+ if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`;
2383
+ if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`;
2384
+ if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`;
2385
+ return `$${num.toFixed(2)}`;
2386
+ }
2387
+ function AaveMarketsList({
2388
+ showApy = true,
2389
+ showTvl = false,
2390
+ // Aave API doesn't provide TVL data
2391
+ showUserPosition = true,
2392
+ showPnL = true,
2393
+ showHistory = true,
2394
+ showSearch = true,
2395
+ showSort = false,
2396
+ // Only one sort option (APY), so hide by default
2397
+ actionMode = "modal",
2398
+ defaultSort = "supply_apy",
2399
+ assetFilter,
2400
+ onMarketSelect,
2401
+ onSupply,
2402
+ onWithdraw
2403
+ }) {
2404
+ const [searchQuery, setSearchQuery] = useState("");
2405
+ const [sortBy, setSortBy] = useState(defaultSort);
2406
+ const [selectedMarket, setSelectedMarket] = useState(null);
2407
+ const { markets, isLoading, isError, refetch } = useAaveData({ sortBy, assetFilter });
2408
+ const filteredMarkets = useMemo(() => {
2409
+ if (!searchQuery) return markets;
2410
+ const query = searchQuery.toLowerCase();
2411
+ return markets.filter(
2412
+ (m) => m.reserveSymbol.toLowerCase().includes(query) || m.underlyingSymbol.toLowerCase().includes(query)
2413
+ );
2414
+ }, [markets, searchQuery]);
2415
+ const handleMarketClick = (market) => {
2416
+ setSelectedMarket(market);
2417
+ onMarketSelect?.(market);
2418
+ };
2419
+ const handleActionSuccess = (action, amount, txHash) => {
2420
+ if (action === "deposit") {
2421
+ onSupply?.(selectedMarket, amount, txHash);
2422
+ } else {
2423
+ onWithdraw?.(selectedMarket, amount, txHash);
2424
+ }
2425
+ refetch();
2426
+ };
2427
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2429
+ /* @__PURE__ */ jsx(ChainSwitcher, {}),
2430
+ /* @__PURE__ */ jsx(WalletStatus, { compact: true })
2431
+ ] }),
2432
+ (showSearch || showSort) && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2433
+ showSearch && /* @__PURE__ */ jsxs(
2434
+ "div",
2435
+ {
2436
+ className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
2437
+ style: {
2438
+ backgroundColor: "var(--compass-color-background)",
2439
+ borderColor: "var(--compass-color-border)"
2440
+ },
2441
+ children: [
2442
+ /* @__PURE__ */ jsx(Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
2443
+ /* @__PURE__ */ jsx(
2444
+ "input",
2445
+ {
2446
+ type: "text",
2447
+ placeholder: "Search markets...",
2448
+ value: searchQuery,
2449
+ onChange: (e) => setSearchQuery(e.target.value),
2450
+ className: "flex-1 bg-transparent outline-none text-sm",
2451
+ style: { color: "var(--compass-color-text)" }
2452
+ }
2453
+ )
2454
+ ]
2455
+ }
2456
+ ),
2457
+ showSort && /* @__PURE__ */ jsx(
2458
+ "select",
2459
+ {
2460
+ value: sortBy,
2461
+ onChange: (e) => setSortBy(e.target.value),
2462
+ className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
2463
+ style: {
2464
+ backgroundColor: "var(--compass-color-background)",
2465
+ borderColor: "var(--compass-color-border)",
2466
+ color: "var(--compass-color-text)"
2467
+ },
2468
+ children: /* @__PURE__ */ jsx("option", { value: "supply_apy", children: "Supply APY" })
2469
+ }
2470
+ )
2471
+ ] }),
2472
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsx(
2473
+ "div",
2474
+ {
2475
+ className: "p-4 rounded-lg text-center",
2476
+ style: {
2477
+ backgroundColor: "var(--compass-color-error-muted)",
2478
+ color: "var(--compass-color-error)"
2479
+ },
2480
+ children: "Failed to load Aave markets. Please try again."
2481
+ }
2482
+ ) : filteredMarkets.length === 0 ? /* @__PURE__ */ jsx(
2483
+ "div",
2484
+ {
2485
+ className: "p-8 text-center",
2486
+ style: { color: "var(--compass-color-text-secondary)" },
2487
+ children: "No markets found"
2488
+ }
2489
+ ) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: filteredMarkets.map((market) => {
2490
+ const hasPosition = market.userPosition && parseFloat(market.userPosition.balance) > 0;
2491
+ return /* @__PURE__ */ jsxs(
2492
+ "button",
2493
+ {
2494
+ onClick: () => handleMarketClick(market),
2495
+ className: "w-full p-4 rounded-xl border transition-all text-left hover:scale-[1.01]",
2496
+ style: {
2497
+ backgroundColor: "var(--compass-color-surface)",
2498
+ borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)"
2499
+ },
2500
+ children: [
2501
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
2502
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2503
+ /* @__PURE__ */ jsx(
2504
+ "h3",
2505
+ {
2506
+ className: "font-semibold",
2507
+ style: {
2508
+ fontSize: "var(--compass-font-size-body)",
2509
+ color: "var(--compass-color-text)"
2510
+ },
2511
+ children: market.underlyingSymbol
2512
+ }
2513
+ ),
2514
+ /* @__PURE__ */ jsx(
2515
+ "span",
2516
+ {
2517
+ className: "text-sm",
2518
+ style: { color: "var(--compass-color-text-secondary)" },
2519
+ children: "Aave V3"
2520
+ }
2521
+ )
2522
+ ] }),
2523
+ showApy && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2524
+ /* @__PURE__ */ jsx(TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }),
2525
+ /* @__PURE__ */ jsx(
2526
+ "span",
2527
+ {
2528
+ className: "font-mono font-semibold",
2529
+ style: { color: "var(--compass-color-success)" },
2530
+ children: formatAPY2(market.supplyApy)
2531
+ }
2532
+ )
2533
+ ] })
2534
+ ] }),
2535
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-3 pt-3 border-t", style: { borderColor: "var(--compass-color-border)" }, children: [
2536
+ showTvl && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2537
+ /* @__PURE__ */ jsx(
2538
+ "span",
2539
+ {
2540
+ className: "text-xs",
2541
+ style: { color: "var(--compass-color-text-tertiary)" },
2542
+ children: "TVL"
2543
+ }
2544
+ ),
2545
+ /* @__PURE__ */ jsx(
2546
+ "span",
2547
+ {
2548
+ className: "font-mono text-sm",
2549
+ style: { color: "var(--compass-color-text-secondary)" },
2550
+ children: formatTVL2(market.tvlUsd)
2551
+ }
2552
+ )
2553
+ ] }),
2554
+ showUserPosition && hasPosition && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
2555
+ /* @__PURE__ */ jsx(
2556
+ "span",
2557
+ {
2558
+ className: "text-xs",
2559
+ style: { color: "var(--compass-color-text-tertiary)" },
2560
+ children: "Your Position"
2561
+ }
2562
+ ),
2563
+ /* @__PURE__ */ jsxs(
2564
+ "span",
2565
+ {
2566
+ className: "font-mono text-sm font-medium",
2567
+ style: { color: "var(--compass-color-primary)" },
2568
+ children: [
2569
+ parseFloat(market.userPosition.balance).toFixed(4),
2570
+ " ",
2571
+ market.underlyingSymbol
2572
+ ]
2573
+ }
2574
+ )
2575
+ ] })
2576
+ ] })
2577
+ ]
2578
+ },
2579
+ market.marketAddress
2580
+ );
2581
+ }) }),
2582
+ actionMode === "modal" && selectedMarket && /* @__PURE__ */ jsx(
2583
+ ActionModal,
2584
+ {
2585
+ isOpen: !!selectedMarket,
2586
+ onClose: () => setSelectedMarket(null),
2587
+ title: `${selectedMarket.underlyingSymbol} - Aave`,
2588
+ children: /* @__PURE__ */ jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2589
+ showPnL && selectedMarket.userPosition?.pnl && /* @__PURE__ */ jsx(
2590
+ PnLSummary,
2591
+ {
2592
+ pnl: selectedMarket.userPosition.pnl,
2593
+ tokenSymbol: selectedMarket.underlyingSymbol,
2594
+ tokenPrice: 1
2595
+ }
2596
+ ),
2597
+ showHistory && (selectedMarket.userPosition?.deposits?.length || selectedMarket.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsx(
2598
+ TransactionHistory,
2599
+ {
2600
+ deposits: selectedMarket.userPosition?.deposits,
2601
+ withdrawals: selectedMarket.userPosition?.withdrawals,
2602
+ tokenSymbol: selectedMarket.underlyingSymbol
2603
+ }
2604
+ ),
2605
+ /* @__PURE__ */ jsx(
2606
+ DepositWithdrawForm,
2607
+ {
2608
+ venueType: "AAVE",
2609
+ venueAddress: selectedMarket.marketAddress,
2610
+ tokenSymbol: selectedMarket.underlyingSymbol,
2611
+ tokenDecimals: TOKEN_DECIMALS3[selectedMarket.underlyingSymbol] || 18,
2612
+ positionBalance: selectedMarket.userPosition?.balance,
2613
+ onSuccess: handleActionSuccess
2614
+ }
2615
+ )
2616
+ ] }) })
2617
+ }
2618
+ )
2619
+ ] });
2620
+ }
2621
+ function usePendleData(options = {}) {
2622
+ const { client } = useEmbeddableApi();
2623
+ const { address } = useEmbeddableWallet();
2624
+ const { chainId } = useChain();
2625
+ const { sortBy = "fixed_apy", assetFilter } = options;
2626
+ const marketsQuery = useQuery({
2627
+ queryKey: ["pendleMarkets", chainId, sortBy, assetFilter],
2628
+ queryFn: async () => {
2629
+ const underlyingSymbol = assetFilter && assetFilter.length === 1 ? assetFilter[0] : void 0;
2630
+ const orderBy = sortBy === "tvl" ? "tvl_usd" : "implied_apy";
2631
+ const response = await client.earn.earnPendleMarkets({
2632
+ chain: chainId,
2633
+ orderBy,
2634
+ direction: "desc",
2635
+ limit: 100,
2636
+ ...underlyingSymbol && { underlyingSymbol }
2637
+ });
2638
+ const now = Date.now() / 1e3;
2639
+ let markets = (response.markets || []).filter((m) => m.expiry && m.expiry > now).map((m) => ({
2640
+ marketAddress: m.marketAddress || "",
2641
+ ptAddress: m.ptAddress || "",
2642
+ name: m.ptName || `PT-${m.underlyingSymbol || "UNKNOWN"}`,
2643
+ underlyingSymbol: m.underlyingSymbol || "UNKNOWN",
2644
+ // Use impliedApy as the main APY (this is what wallet-earn displays)
2645
+ fixedApy: m.impliedApy?.toString() ?? null,
2646
+ impliedApy: m.impliedApy?.toString() ?? null,
2647
+ tvlUsd: m.tvlUsd?.toString() ?? null,
2648
+ // Convert Unix timestamp to ISO string for display
2649
+ expiry: new Date(m.expiry * 1e3).toISOString()
2650
+ }));
2651
+ if (assetFilter && assetFilter.length > 1) {
2652
+ markets = markets.filter(
2653
+ (m) => assetFilter.includes(m.underlyingSymbol.toUpperCase())
2654
+ );
2655
+ }
2656
+ if (sortBy === "expiry") {
2657
+ markets.sort((a, b) => {
2658
+ return new Date(a.expiry).getTime() - new Date(b.expiry).getTime();
2659
+ });
2660
+ }
2661
+ return markets;
2662
+ },
2663
+ staleTime: 30 * 1e3
2664
+ });
2665
+ const positionsQuery = useQuery({
2666
+ queryKey: ["pendlePositions", chainId, address],
2667
+ queryFn: async () => {
2668
+ if (!address) return [];
2669
+ const response = await client.earn.earnPositions({
2670
+ chain: chainId,
2671
+ owner: address
2672
+ });
2673
+ return (response.pendlePt || []).map((p) => ({
2674
+ marketAddress: p.marketAddress || "",
2675
+ balance: p.balance || "0",
2676
+ pnl: p.pnl ? {
2677
+ unrealizedPnl: p.pnl.unrealizedPnl || "0",
2678
+ realizedPnl: p.pnl.realizedPnl || "0",
2679
+ totalPnl: p.pnl.totalPnl || "0",
2680
+ totalDeposited: p.pnl.totalDeposited || "0"
2681
+ } : void 0,
2682
+ deposits: (p.deposits || []).map((d) => ({
2683
+ amount: d.inputAmount || d.amount || "0",
2684
+ blockNumber: d.blockNumber || 0,
2685
+ txHash: d.transactionHash || d.txHash || ""
2686
+ })),
2687
+ withdrawals: (p.withdrawals || []).map((w) => ({
2688
+ amount: w.outputAmount || w.amount || "0",
2689
+ blockNumber: w.blockNumber || 0,
2690
+ txHash: w.transactionHash || w.txHash || ""
2691
+ }))
2692
+ }));
2693
+ },
2694
+ enabled: !!address,
2695
+ staleTime: 30 * 1e3
2696
+ });
2697
+ const marketsWithPositions = (marketsQuery.data || []).map((market) => {
2698
+ const position = positionsQuery.data?.find(
2699
+ (p) => p.marketAddress.toLowerCase() === market.marketAddress.toLowerCase()
2700
+ );
2701
+ return { ...market, userPosition: position };
2702
+ });
2703
+ return {
2704
+ markets: marketsWithPositions,
2705
+ isLoading: marketsQuery.isLoading,
2706
+ isError: marketsQuery.isError,
2707
+ error: marketsQuery.error,
2708
+ refetch: () => {
2709
+ marketsQuery.refetch();
2710
+ positionsQuery.refetch();
2711
+ }
2712
+ };
2713
+ }
2714
+ var TOKEN_DECIMALS4 = {
2715
+ USDC: 6,
2716
+ USDT: 6,
2717
+ DAI: 18,
2718
+ ETH: 18,
2719
+ WETH: 18,
2720
+ WBTC: 8,
2721
+ cbBTC: 8
2722
+ };
2723
+ function formatAPY3(apy) {
2724
+ if (!apy) return "0.00%";
2725
+ return `${parseFloat(apy).toFixed(2)}%`;
2726
+ }
2727
+ function formatExpiry(expiry) {
2728
+ const date = new Date(expiry);
2729
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
2730
+ }
2731
+ function formatTVL3(tvl) {
2732
+ if (!tvl) return "$0";
2733
+ const num = parseFloat(tvl);
2734
+ if (num >= 1e9) return `$${(num / 1e9).toFixed(2)}B`;
2735
+ if (num >= 1e6) return `$${(num / 1e6).toFixed(2)}M`;
2736
+ if (num >= 1e3) return `$${(num / 1e3).toFixed(2)}K`;
2737
+ return `$${num.toFixed(2)}`;
2738
+ }
2739
+ function PendleMarketsList({
2740
+ showApy = true,
2741
+ showTvl = true,
2742
+ showExpiry = true,
2743
+ showUserPosition = true,
2744
+ showPnL = true,
2745
+ showHistory = true,
2746
+ showSearch = true,
2747
+ showSort = true,
2748
+ actionMode = "modal",
2749
+ defaultSort = "fixed_apy",
2750
+ assetFilter,
2751
+ onMarketSelect,
2752
+ onDeposit,
2753
+ onWithdraw
2754
+ }) {
2755
+ const [searchQuery, setSearchQuery] = useState("");
2756
+ const [sortBy, setSortBy] = useState(defaultSort);
2757
+ const [selectedMarket, setSelectedMarket] = useState(null);
2758
+ const { markets, isLoading, isError, refetch } = usePendleData({ sortBy, assetFilter });
2759
+ const filteredMarkets = useMemo(() => {
2760
+ if (!searchQuery) return markets;
2761
+ const query = searchQuery.toLowerCase();
2762
+ return markets.filter(
2763
+ (m) => m.name.toLowerCase().includes(query) || m.underlyingSymbol.toLowerCase().includes(query)
2764
+ );
2765
+ }, [markets, searchQuery]);
2766
+ const handleMarketClick = (market) => {
2767
+ setSelectedMarket(market);
2768
+ onMarketSelect?.(market);
2769
+ };
2770
+ const handleActionSuccess = (action, amount, txHash) => {
2771
+ if (action === "deposit") {
2772
+ onDeposit?.(selectedMarket, amount, txHash);
2773
+ } else {
2774
+ onWithdraw?.(selectedMarket, amount, txHash);
2775
+ }
2776
+ refetch();
2777
+ };
2778
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2779
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
2780
+ /* @__PURE__ */ jsx(ChainSwitcher, {}),
2781
+ /* @__PURE__ */ jsx(WalletStatus, { compact: true })
2782
+ ] }),
2783
+ (showSearch || showSort) && /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2784
+ showSearch && /* @__PURE__ */ jsxs(
2785
+ "div",
2786
+ {
2787
+ className: "flex-1 flex items-center gap-2 px-3 py-2 rounded-lg border",
2788
+ style: {
2789
+ backgroundColor: "var(--compass-color-background)",
2790
+ borderColor: "var(--compass-color-border)"
2791
+ },
2792
+ children: [
2793
+ /* @__PURE__ */ jsx(Search, { size: 16, style: { color: "var(--compass-color-text-tertiary)" } }),
2794
+ /* @__PURE__ */ jsx(
2795
+ "input",
2796
+ {
2797
+ type: "text",
2798
+ placeholder: "Search markets...",
2799
+ value: searchQuery,
2800
+ onChange: (e) => setSearchQuery(e.target.value),
2801
+ className: "flex-1 bg-transparent outline-none text-sm",
2802
+ style: { color: "var(--compass-color-text)" }
2803
+ }
2804
+ )
2805
+ ]
2806
+ }
2807
+ ),
2808
+ showSort && /* @__PURE__ */ jsxs(
2809
+ "select",
2810
+ {
2811
+ value: sortBy,
2812
+ onChange: (e) => setSortBy(e.target.value),
2813
+ className: "px-3 py-2 rounded-lg border text-sm cursor-pointer",
2814
+ style: {
2815
+ backgroundColor: "var(--compass-color-background)",
2816
+ borderColor: "var(--compass-color-border)",
2817
+ color: "var(--compass-color-text)"
2818
+ },
2819
+ children: [
2820
+ /* @__PURE__ */ jsx("option", { value: "fixed_apy", children: "Fixed APY" }),
2821
+ /* @__PURE__ */ jsx("option", { value: "tvl", children: "TVL" }),
2822
+ /* @__PURE__ */ jsx("option", { value: "expiry", children: "Expiry" })
2823
+ ]
2824
+ }
2825
+ )
2826
+ ] }),
2827
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Loader2, { size: 24, className: "animate-spin", style: { color: "var(--compass-color-primary)" } }) }) : isError ? /* @__PURE__ */ jsx(
2828
+ "div",
2829
+ {
2830
+ className: "p-4 rounded-lg text-center",
2831
+ style: {
2832
+ backgroundColor: "var(--compass-color-error-muted)",
2833
+ color: "var(--compass-color-error)"
2834
+ },
2835
+ children: "Failed to load Pendle markets. Please try again."
2836
+ }
2837
+ ) : filteredMarkets.length === 0 ? /* @__PURE__ */ jsx(
2838
+ "div",
2839
+ {
2840
+ className: "p-8 text-center",
2841
+ style: { color: "var(--compass-color-text-secondary)" },
2842
+ children: "No active markets found"
2843
+ }
2844
+ ) : /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: filteredMarkets.map((market) => {
2845
+ const hasPosition = market.userPosition && parseFloat(market.userPosition.balance) > 0;
2846
+ return /* @__PURE__ */ jsxs(
2847
+ "button",
2848
+ {
2849
+ onClick: () => handleMarketClick(market),
2850
+ className: "w-full p-4 rounded-xl border transition-all text-left hover:scale-[1.01]",
2851
+ style: {
2852
+ backgroundColor: "var(--compass-color-surface)",
2853
+ borderColor: hasPosition ? "var(--compass-color-primary)" : "var(--compass-color-border)"
2854
+ },
2855
+ children: [
2856
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
2857
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2858
+ /* @__PURE__ */ jsx(
2859
+ "h3",
2860
+ {
2861
+ className: "font-semibold",
2862
+ style: {
2863
+ fontSize: "var(--compass-font-size-body)",
2864
+ color: "var(--compass-color-text)"
2865
+ },
2866
+ children: market.name
2867
+ }
2868
+ ),
2869
+ /* @__PURE__ */ jsx(
2870
+ "span",
2871
+ {
2872
+ className: "text-sm",
2873
+ style: { color: "var(--compass-color-text-secondary)" },
2874
+ children: market.underlyingSymbol
2875
+ }
2876
+ )
2877
+ ] }),
2878
+ showApy && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2879
+ /* @__PURE__ */ jsx(TrendingUp, { size: 14, style: { color: "var(--compass-color-success)" } }),
2880
+ /* @__PURE__ */ jsx(
2881
+ "span",
2882
+ {
2883
+ className: "font-mono font-semibold",
2884
+ style: { color: "var(--compass-color-success)" },
2885
+ children: formatAPY3(market.fixedApy)
2886
+ }
2887
+ )
2888
+ ] })
2889
+ ] }),
2890
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-3 pt-3 border-t", style: { borderColor: "var(--compass-color-border)" }, children: [
2891
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
2892
+ showTvl && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2893
+ /* @__PURE__ */ jsx(
2894
+ "span",
2895
+ {
2896
+ className: "text-xs",
2897
+ style: { color: "var(--compass-color-text-tertiary)" },
2898
+ children: "TVL"
2899
+ }
2900
+ ),
2901
+ /* @__PURE__ */ jsx(
2902
+ "span",
2903
+ {
2904
+ className: "font-mono text-sm",
2905
+ style: { color: "var(--compass-color-text-secondary)" },
2906
+ children: formatTVL3(market.tvlUsd)
2907
+ }
2908
+ )
2909
+ ] }),
2910
+ showExpiry && /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
2911
+ /* @__PURE__ */ jsx(
2912
+ "span",
2913
+ {
2914
+ className: "text-xs",
2915
+ style: { color: "var(--compass-color-text-tertiary)" },
2916
+ children: "Expiry"
2917
+ }
2918
+ ),
2919
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
2920
+ /* @__PURE__ */ jsx(Calendar, { size: 12, style: { color: "var(--compass-color-text-secondary)" } }),
2921
+ /* @__PURE__ */ jsx(
2922
+ "span",
2923
+ {
2924
+ className: "font-mono text-sm",
2925
+ style: { color: "var(--compass-color-text-secondary)" },
2926
+ children: formatExpiry(market.expiry)
2927
+ }
2928
+ )
2929
+ ] })
2930
+ ] })
2931
+ ] }),
2932
+ showUserPosition && hasPosition && /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end", children: [
2933
+ /* @__PURE__ */ jsx(
2934
+ "span",
2935
+ {
2936
+ className: "text-xs",
2937
+ style: { color: "var(--compass-color-text-tertiary)" },
2938
+ children: "Your Position"
2939
+ }
2940
+ ),
2941
+ /* @__PURE__ */ jsxs(
2942
+ "span",
2943
+ {
2944
+ className: "font-mono text-sm font-medium",
2945
+ style: { color: "var(--compass-color-primary)" },
2946
+ children: [
2947
+ parseFloat(market.userPosition.balance).toFixed(4),
2948
+ " PT"
2949
+ ]
2950
+ }
2951
+ )
2952
+ ] })
2953
+ ] })
2954
+ ]
2955
+ },
2956
+ market.marketAddress
2957
+ );
2958
+ }) }),
2959
+ actionMode === "modal" && selectedMarket && /* @__PURE__ */ jsx(
2960
+ ActionModal,
2961
+ {
2962
+ isOpen: !!selectedMarket,
2963
+ onClose: () => setSelectedMarket(null),
2964
+ title: selectedMarket.name,
2965
+ children: /* @__PURE__ */ jsx(EarnAccountGuard, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
2966
+ showPnL && selectedMarket.userPosition?.pnl && /* @__PURE__ */ jsx(
2967
+ PnLSummary,
2968
+ {
2969
+ pnl: selectedMarket.userPosition.pnl,
2970
+ tokenSymbol: selectedMarket.underlyingSymbol,
2971
+ tokenPrice: 1
2972
+ }
2973
+ ),
2974
+ showHistory && (selectedMarket.userPosition?.deposits?.length || selectedMarket.userPosition?.withdrawals?.length) && /* @__PURE__ */ jsx(
2975
+ TransactionHistory,
2976
+ {
2977
+ deposits: selectedMarket.userPosition?.deposits,
2978
+ withdrawals: selectedMarket.userPosition?.withdrawals,
2979
+ tokenSymbol: selectedMarket.underlyingSymbol
2980
+ }
2981
+ ),
2982
+ /* @__PURE__ */ jsx(
2983
+ DepositWithdrawForm,
2984
+ {
2985
+ venueType: "PENDLE_PT",
2986
+ venueAddress: selectedMarket.marketAddress,
2987
+ tokenSymbol: selectedMarket.underlyingSymbol,
2988
+ tokenDecimals: TOKEN_DECIMALS4[selectedMarket.underlyingSymbol] || 18,
2989
+ positionBalance: selectedMarket.userPosition?.balance,
2990
+ onSuccess: handleActionSuccess
2991
+ }
2992
+ )
2993
+ ] }) })
2994
+ }
2995
+ )
2996
+ ] });
2997
+ }
2998
+ function useSwapQuote({ fromToken, toToken, amount, enabled = true }) {
2999
+ const { client } = useEmbeddableApi();
3000
+ const { chainId } = useChain();
3001
+ const query = useQuery({
3002
+ queryKey: ["swapQuote", chainId, fromToken, toToken, amount],
3003
+ queryFn: async () => {
3004
+ if (!fromToken || !toToken || !amount || parseFloat(amount) <= 0) {
3005
+ return null;
3006
+ }
3007
+ try {
3008
+ const estimatedRate = 1;
3009
+ const outputAmount = (parseFloat(amount) * estimatedRate).toString();
3010
+ return {
3011
+ inputAmount: amount,
3012
+ outputAmount,
3013
+ rate: estimatedRate.toString(),
3014
+ priceImpact: "0",
3015
+ fee: "0"
3016
+ };
3017
+ } catch (error) {
3018
+ console.error("Failed to get swap quote:", error);
3019
+ return null;
3020
+ }
3021
+ },
3022
+ enabled: enabled && !!fromToken && !!toToken && !!amount && parseFloat(amount) > 0,
3023
+ staleTime: 10 * 1e3,
3024
+ // 10 seconds - quotes change frequently
3025
+ refetchInterval: 15 * 1e3
3026
+ // Refresh every 15 seconds
3027
+ });
3028
+ return {
3029
+ quote: query.data,
3030
+ isLoading: query.isLoading,
3031
+ isError: query.isError,
3032
+ refetch: query.refetch
3033
+ };
3034
+ }
3035
+ var DEFAULT_TOKENS = ["USDC", "ETH", "WETH", "WBTC", "DAI", "USDT"];
3036
+ function SwapWidget({
3037
+ layout = "full",
3038
+ defaultFromToken = "ETH",
3039
+ defaultToToken = "USDC",
3040
+ allowedTokens = DEFAULT_TOKENS,
3041
+ showReverseButton = true,
3042
+ showSettings = false,
3043
+ showPriceImpact = true,
3044
+ onSwapSuccess,
3045
+ onSwapError
3046
+ }) {
3047
+ const [fromToken, setFromToken] = useState(defaultFromToken);
3048
+ const [toToken, setToToken] = useState(defaultToToken);
3049
+ const [fromAmount, setFromAmount] = useState("");
3050
+ const [isSwapping, setIsSwapping] = useState(false);
3051
+ const { address, isConnected } = useCompassWallet();
3052
+ const { client } = useEmbeddableApi();
3053
+ const { chainId } = useChain();
3054
+ const { quote, isLoading: isQuoteLoading } = useSwapQuote({
3055
+ fromToken,
3056
+ toToken,
3057
+ amount: fromAmount,
3058
+ enabled: !!fromAmount && parseFloat(fromAmount) > 0
3059
+ });
3060
+ const handleReverse = useCallback(() => {
3061
+ setFromToken(toToken);
3062
+ setToToken(fromToken);
3063
+ setFromAmount("");
3064
+ }, [fromToken, toToken]);
3065
+ const handleSwap = useCallback(async () => {
3066
+ if (!address || !fromAmount || !quote) return;
3067
+ setIsSwapping(true);
3068
+ try {
3069
+ const response = await client.earn.earnSwap({
3070
+ chain: chainId,
3071
+ sender: address,
3072
+ tokenIn: fromToken,
3073
+ tokenOut: toToken,
3074
+ humanReadableAmountIn: fromAmount
3075
+ });
3076
+ console.log("Swap prepared:", response);
3077
+ onSwapSuccess?.(fromToken, toToken, fromAmount, quote.outputAmount, "pending");
3078
+ setFromAmount("");
3079
+ } catch (error) {
3080
+ console.error("Swap failed:", error);
3081
+ onSwapError?.(error);
3082
+ } finally {
3083
+ setIsSwapping(false);
3084
+ }
3085
+ }, [address, fromAmount, quote, client, chainId, fromToken, toToken, onSwapSuccess, onSwapError]);
3086
+ const isCompact = layout === "compact";
3087
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3088
+ !isCompact && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 flex-wrap", children: [
3089
+ /* @__PURE__ */ jsx(ChainSwitcher, {}),
3090
+ /* @__PURE__ */ jsx(WalletStatus, { compact: true })
3091
+ ] }),
3092
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
3093
+ /* @__PURE__ */ jsxs("div", { className: "p-4 rounded-xl border", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)" }, children: [
3094
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "From" }) }),
3095
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3096
+ /* @__PURE__ */ jsx(
3097
+ "input",
3098
+ {
3099
+ type: "number",
3100
+ value: fromAmount,
3101
+ onChange: (e) => setFromAmount(e.target.value),
3102
+ placeholder: "0.00",
3103
+ className: "flex-1 bg-transparent outline-none text-2xl font-mono",
3104
+ style: { color: "var(--compass-color-text)" }
3105
+ }
3106
+ ),
3107
+ /* @__PURE__ */ jsx(
3108
+ "select",
3109
+ {
3110
+ value: fromToken,
3111
+ onChange: (e) => setFromToken(e.target.value),
3112
+ className: "px-3 py-2 rounded-lg border text-sm font-medium cursor-pointer",
3113
+ style: { backgroundColor: "var(--compass-color-background)", borderColor: "var(--compass-color-border)", color: "var(--compass-color-text)" },
3114
+ children: allowedTokens.map((token) => /* @__PURE__ */ jsx("option", { value: token, children: token }, token))
3115
+ }
3116
+ )
3117
+ ] })
3118
+ ] }),
3119
+ showReverseButton && /* @__PURE__ */ jsx("div", { className: "flex justify-center -my-1 relative z-10", children: /* @__PURE__ */ jsx(
3120
+ "button",
3121
+ {
3122
+ onClick: handleReverse,
3123
+ className: "p-2 rounded-full border transition-colors hover:opacity-80",
3124
+ style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)" },
3125
+ children: /* @__PURE__ */ jsx(ArrowDownUp, { size: 16, style: { color: "var(--compass-color-text-secondary)" } })
3126
+ }
3127
+ ) }),
3128
+ /* @__PURE__ */ jsxs("div", { className: "p-4 rounded-xl border", style: { backgroundColor: "var(--compass-color-surface)", borderColor: "var(--compass-color-border)" }, children: [
3129
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ jsx("span", { className: "text-xs", style: { color: "var(--compass-color-text-tertiary)" }, children: "To" }) }),
3130
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3131
+ /* @__PURE__ */ jsx("div", { className: "flex-1 text-2xl font-mono", style: { color: isQuoteLoading ? "var(--compass-color-text-tertiary)" : "var(--compass-color-text)" }, children: isQuoteLoading ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
3132
+ /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
3133
+ "Loading..."
3134
+ ] }) : quote?.outputAmount ? parseFloat(quote.outputAmount).toFixed(6) : "0.00" }),
3135
+ /* @__PURE__ */ jsx(
3136
+ "select",
3137
+ {
3138
+ value: toToken,
3139
+ onChange: (e) => setToToken(e.target.value),
3140
+ className: "px-3 py-2 rounded-lg border text-sm font-medium cursor-pointer",
3141
+ style: { backgroundColor: "var(--compass-color-background)", borderColor: "var(--compass-color-border)", color: "var(--compass-color-text)" },
3142
+ children: allowedTokens.filter((t) => t !== fromToken).map((token) => /* @__PURE__ */ jsx("option", { value: token, children: token }, token))
3143
+ }
3144
+ )
3145
+ ] })
3146
+ ] })
3147
+ ] }),
3148
+ showPriceImpact && quote && parseFloat(quote.priceImpact) > 0.01 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-3 rounded-lg", style: { backgroundColor: "var(--compass-color-warning-muted)" }, children: [
3149
+ /* @__PURE__ */ jsx(AlertCircle, { size: 16, style: { color: "var(--compass-color-warning)" } }),
3150
+ /* @__PURE__ */ jsxs("span", { className: "text-sm", style: { color: "var(--compass-color-warning)" }, children: [
3151
+ "Price impact: ",
3152
+ (parseFloat(quote.priceImpact) * 100).toFixed(2),
3153
+ "%"
3154
+ ] })
3155
+ ] }),
3156
+ quote && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm", style: { color: "var(--compass-color-text-secondary)" }, children: [
3157
+ /* @__PURE__ */ jsx("span", { children: "Rate" }),
3158
+ /* @__PURE__ */ jsxs("span", { className: "font-mono", children: [
3159
+ "1 ",
3160
+ fromToken,
3161
+ " = ",
3162
+ parseFloat(quote.rate).toFixed(6),
3163
+ " ",
3164
+ toToken
3165
+ ] })
3166
+ ] }),
3167
+ !isConnected ? /* @__PURE__ */ jsxs(
3168
+ "div",
3169
+ {
3170
+ className: "flex flex-col items-center gap-3 py-6 px-4 rounded-lg",
3171
+ style: {
3172
+ backgroundColor: "var(--compass-color-surface)",
3173
+ border: "1px solid var(--compass-color-border)"
3174
+ },
3175
+ children: [
3176
+ /* @__PURE__ */ jsx(AlertCircle, { size: 24, style: { color: "var(--compass-color-text-tertiary)" } }),
3177
+ /* @__PURE__ */ jsx(
3178
+ "p",
3179
+ {
3180
+ className: "text-sm text-center",
3181
+ style: { color: "var(--compass-color-text-secondary)" },
3182
+ children: "Connect your wallet to swap"
3183
+ }
3184
+ )
3185
+ ]
3186
+ }
3187
+ ) : /* @__PURE__ */ jsxs(
3188
+ "button",
3189
+ {
3190
+ onClick: handleSwap,
3191
+ disabled: isSwapping || !quote || !fromAmount || parseFloat(fromAmount) <= 0,
3192
+ className: "w-full py-3 rounded-lg font-medium transition-colors flex items-center justify-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed",
3193
+ style: { backgroundColor: "var(--compass-color-primary)", color: "var(--compass-color-primary-text)" },
3194
+ children: [
3195
+ isSwapping && /* @__PURE__ */ jsx(Loader2, { size: 18, className: "animate-spin" }),
3196
+ isSwapping ? "Swapping..." : "Swap"
3197
+ ]
3198
+ }
3199
+ )
3200
+ ] });
3201
+ }
3202
+
3203
+ // src/components/CompassEarnWidget/presets.ts
3204
+ var allTabs = [
3205
+ { id: "vaults", label: "Vaults", enabled: true },
3206
+ { id: "aave", label: "Aave", enabled: true },
3207
+ { id: "pendle", label: "Pendle", enabled: true },
3208
+ { id: "swap", label: "Swap", enabled: true }
3209
+ ];
3210
+ function getTabsForPreset(preset) {
3211
+ switch (preset) {
3212
+ case "full":
3213
+ return allTabs;
3214
+ case "earn-only":
3215
+ return allTabs.map((tab) => ({
3216
+ ...tab,
3217
+ enabled: tab.id !== "swap"
3218
+ }));
3219
+ case "swap-only":
3220
+ return allTabs.map((tab) => ({
3221
+ ...tab,
3222
+ enabled: tab.id === "swap"
3223
+ }));
3224
+ case "vaults-only":
3225
+ return allTabs.map((tab) => ({
3226
+ ...tab,
3227
+ enabled: tab.id === "vaults"
3228
+ }));
3229
+ default:
3230
+ return allTabs;
3231
+ }
3232
+ }
3233
+ function getDefaultTab(tabs) {
3234
+ const enabledTab = tabs.find((t) => t.enabled);
3235
+ return enabledTab?.id || "vaults";
3236
+ }
3237
+ function CompassEarnWidget({
3238
+ preset = "full",
3239
+ enableVaults,
3240
+ enableAave,
3241
+ enablePendle,
3242
+ enableSwap,
3243
+ defaultTab,
3244
+ showHeader = true,
3245
+ onTabChange
3246
+ }) {
3247
+ const tabs = useMemo(() => {
3248
+ const baseTabs = getTabsForPreset(preset);
3249
+ return baseTabs.map((tab) => {
3250
+ let enabled = tab.enabled;
3251
+ if (tab.id === "vaults" && enableVaults !== void 0) enabled = enableVaults;
3252
+ if (tab.id === "aave" && enableAave !== void 0) enabled = enableAave;
3253
+ if (tab.id === "pendle" && enablePendle !== void 0) enabled = enablePendle;
3254
+ if (tab.id === "swap" && enableSwap !== void 0) enabled = enableSwap;
3255
+ return { ...tab, enabled };
3256
+ });
3257
+ }, [preset, enableVaults, enableAave, enablePendle, enableSwap]);
3258
+ const enabledTabs = tabs.filter((t) => t.enabled);
3259
+ const initialTab = defaultTab && tabs.find((t) => t.id === defaultTab)?.enabled ? defaultTab : getDefaultTab(tabs);
3260
+ const [activeTab, setActiveTab] = useState(initialTab);
3261
+ const handleTabChange = (tab) => {
3262
+ setActiveTab(tab);
3263
+ onTabChange?.(tab);
3264
+ };
3265
+ const showTabs = enabledTabs.length > 1;
3266
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
3267
+ showHeader && /* @__PURE__ */ jsx("h1", { className: "font-bold text-xl", style: { color: "var(--compass-color-text)" }, children: "Compass Earn" }),
3268
+ showTabs && /* @__PURE__ */ jsx(
3269
+ "div",
3270
+ {
3271
+ className: "flex gap-1 p-1 rounded-lg",
3272
+ style: { backgroundColor: "var(--compass-color-surface)" },
3273
+ children: enabledTabs.map((tab) => /* @__PURE__ */ jsx(
3274
+ "button",
3275
+ {
3276
+ onClick: () => handleTabChange(tab.id),
3277
+ className: "flex-1 py-2 px-3 rounded-md text-sm font-medium transition-all",
3278
+ style: {
3279
+ backgroundColor: activeTab === tab.id ? "var(--compass-color-primary)" : "transparent",
3280
+ color: activeTab === tab.id ? "var(--compass-color-primary-text)" : "var(--compass-color-text-secondary)"
3281
+ },
3282
+ children: tab.label
3283
+ },
3284
+ tab.id
3285
+ ))
3286
+ }
3287
+ ),
3288
+ /* @__PURE__ */ jsxs("div", { children: [
3289
+ activeTab === "vaults" && tabs.find((t) => t.id === "vaults")?.enabled && /* @__PURE__ */ jsx(VaultsList, { showSearch: true, showSort: true }),
3290
+ activeTab === "aave" && tabs.find((t) => t.id === "aave")?.enabled && /* @__PURE__ */ jsx(AaveMarketsList, { showSearch: true, showSort: true }),
3291
+ activeTab === "pendle" && tabs.find((t) => t.id === "pendle")?.enabled && /* @__PURE__ */ jsx(PendleMarketsList, { showSearch: true, showSort: true }),
3292
+ activeTab === "swap" && tabs.find((t) => t.id === "swap")?.enabled && /* @__PURE__ */ jsx(SwapWidget, { layout: "full" })
3293
+ ] })
3294
+ ] });
3295
+ }
3296
+
3297
+ export { AaveMarketsList, ActionModal, ApiProvider, CHAINS, ChainSwitcher, CompassEarnWidget, CompassProvider, DepositWithdrawForm, EarnAccountGuard, PendleMarketsList, PnLSummary, SwapWidget, TOKEN_DECIMALS, ThemeProvider, TransactionHistory, VaultsList, WalletStatus, themePresets, useAaveData, useChain, useCompassApi, useCompassChain, useCompassWallet, useEarnAccount, useEmbeddableApi, useEmbeddableWallet, usePendleData, useSwapQuote, useTheme, useVaultsData };
3298
+ //# sourceMappingURL=index.mjs.map
3299
+ //# sourceMappingURL=index.mjs.map