@iamgame/wallet-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1973 @@
1
+ import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext } from 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+ import { createPortal } from 'react-dom';
4
+
5
+ // ../../solven/sdk-client/src/errors.ts
6
+ var SolvenSdkError = class _SolvenSdkError extends Error {
7
+ constructor(args) {
8
+ super(args.message);
9
+ this.name = "SolvenSdkError";
10
+ this.code = args.code;
11
+ this.httpStatus = args.httpStatus;
12
+ this.details = args.details;
13
+ }
14
+ static fromEnvelope(envelope, httpStatus) {
15
+ return new _SolvenSdkError({
16
+ code: envelope.code,
17
+ message: envelope.message,
18
+ httpStatus,
19
+ details: envelope.details
20
+ });
21
+ }
22
+ };
23
+
24
+ // ../../solven/sdk-client/src/session.ts
25
+ var STORAGE_KEY = "solven.session.v1";
26
+ var localStorageSession = {
27
+ get() {
28
+ try {
29
+ const raw = globalThis.localStorage?.getItem(STORAGE_KEY);
30
+ if (!raw) return null;
31
+ return JSON.parse(raw);
32
+ } catch {
33
+ return null;
34
+ }
35
+ },
36
+ set(session) {
37
+ try {
38
+ if (session) {
39
+ globalThis.localStorage?.setItem(STORAGE_KEY, JSON.stringify(session));
40
+ } else {
41
+ globalThis.localStorage?.removeItem(STORAGE_KEY);
42
+ }
43
+ } catch {
44
+ }
45
+ }
46
+ };
47
+ var inMemorySession = () => {
48
+ let s = null;
49
+ return {
50
+ get: () => s,
51
+ set: (next) => {
52
+ s = next;
53
+ }
54
+ };
55
+ };
56
+
57
+ // ../../solven/sdk-client/src/client.ts
58
+ var SolvenClient = class {
59
+ constructor(options) {
60
+ this.opts = {
61
+ publishableKey: options.publishableKey,
62
+ baseUrl: options.baseUrl.replace(/\/$/, ""),
63
+ storage: options.storage ?? localStorageSession,
64
+ fetchImpl: options.fetchImpl ?? globalThis.fetch.bind(globalThis)
65
+ };
66
+ }
67
+ // ─── Auth ──────────────────────────────────────────────────────────────────
68
+ async requestSiwsChallenge(publicKey) {
69
+ return this.publicCall("POST", "/auth/siws/challenge", { publicKey });
70
+ }
71
+ async verifySiws(req) {
72
+ const session = await this.publicCall("POST", "/auth/siws/verify", req);
73
+ this.opts.storage.set(session);
74
+ return session;
75
+ }
76
+ async verifyTelegram(req) {
77
+ const session = await this.publicCall("POST", "/auth/telegram/verify", req);
78
+ this.opts.storage.set(session);
79
+ return session;
80
+ }
81
+ async refreshSession() {
82
+ const current = this.opts.storage.get();
83
+ if (!current) return null;
84
+ try {
85
+ const next = await this.publicCall("POST", "/auth/refresh", {
86
+ refreshToken: current.refreshToken
87
+ });
88
+ this.opts.storage.set(next);
89
+ return next;
90
+ } catch (e) {
91
+ this.opts.storage.set(null);
92
+ throw e;
93
+ }
94
+ }
95
+ async logout() {
96
+ const current = this.opts.storage.get();
97
+ if (current) {
98
+ try {
99
+ await this.publicCall("POST", "/auth/logout", { refreshToken: current.refreshToken });
100
+ } catch {
101
+ }
102
+ }
103
+ this.opts.storage.set(null);
104
+ }
105
+ getSession() {
106
+ return this.opts.storage.get();
107
+ }
108
+ // ─── User / wallet ─────────────────────────────────────────────────────────
109
+ async getMe() {
110
+ return this.userCall("GET", "/me");
111
+ }
112
+ async getMyWallet() {
113
+ return this.userCall("GET", "/wallets/me");
114
+ }
115
+ async getBalance(walletId) {
116
+ return this.userCall("GET", `/wallets/${encodeURIComponent(walletId)}/balance`);
117
+ }
118
+ async signAction(req, idempotencyKey) {
119
+ return this.userCall(
120
+ "POST",
121
+ `/wallets/${encodeURIComponent(req.walletId)}/sign`,
122
+ { txBase64: req.txBase64, label: req.label },
123
+ idempotencyKey
124
+ );
125
+ }
126
+ async withdraw(req) {
127
+ return this.userCall(
128
+ "POST",
129
+ `/wallets/${encodeURIComponent(req.walletId)}/withdraw`,
130
+ {
131
+ toAddress: req.toAddress,
132
+ mint: req.mint,
133
+ amount: req.amount,
134
+ idempotencyKey: req.idempotencyKey
135
+ },
136
+ req.idempotencyKey
137
+ );
138
+ }
139
+ async exportPreflight(walletId) {
140
+ return this.userCall(
141
+ "GET",
142
+ `/wallets/${encodeURIComponent(walletId)}/export/preflight`
143
+ );
144
+ }
145
+ async exportWallet(walletId, idempotencyKey) {
146
+ return this.userCall(
147
+ "POST",
148
+ `/wallets/${encodeURIComponent(walletId)}/export`,
149
+ { idempotencyKey },
150
+ idempotencyKey
151
+ );
152
+ }
153
+ // ─── HTTP plumbing ─────────────────────────────────────────────────────────
154
+ async publicCall(method, path, body) {
155
+ return this.doFetch(method, path, body, {
156
+ Authorization: `Bearer ${this.opts.publishableKey}`
157
+ });
158
+ }
159
+ async userCall(method, path, body, idempotencyKey) {
160
+ const headers = {};
161
+ if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
162
+ const session = this.opts.storage.get();
163
+ if (!session) {
164
+ throw new SolvenSdkError({ code: "auth/missing_token", message: "not signed in" });
165
+ }
166
+ headers.Authorization = `Bearer ${session.accessToken}`;
167
+ try {
168
+ return await this.doFetch(method, path, body, headers);
169
+ } catch (e) {
170
+ if (e instanceof SolvenSdkError && e.code === "auth/expired_token") {
171
+ const refreshed = await this.refreshSession();
172
+ if (!refreshed) throw e;
173
+ headers.Authorization = `Bearer ${refreshed.accessToken}`;
174
+ return this.doFetch(method, path, body, headers);
175
+ }
176
+ throw e;
177
+ }
178
+ }
179
+ async doFetch(method, path, body, headers) {
180
+ const url = this.opts.baseUrl + path;
181
+ let resp;
182
+ try {
183
+ resp = await this.opts.fetchImpl(url, {
184
+ method,
185
+ headers: { "Content-Type": "application/json", ...headers },
186
+ body: body === void 0 ? void 0 : JSON.stringify(body)
187
+ });
188
+ } catch (e) {
189
+ throw new SolvenSdkError({
190
+ code: "client/network",
191
+ message: e instanceof Error ? e.message : "network error"
192
+ });
193
+ }
194
+ if (resp.status === 204) {
195
+ return void 0;
196
+ }
197
+ const text = await resp.text();
198
+ const parsed = text ? JSON.parse(text) : null;
199
+ if (!resp.ok) {
200
+ const envelope = parsed?.error;
201
+ if (envelope) {
202
+ throw SolvenSdkError.fromEnvelope(envelope, resp.status);
203
+ }
204
+ throw new SolvenSdkError({
205
+ code: "server/internal",
206
+ message: `unexpected status ${resp.status}`,
207
+ httpStatus: resp.status
208
+ });
209
+ }
210
+ return parsed;
211
+ }
212
+ };
213
+
214
+ // ../../solven/sdk-client/src/siws.ts
215
+ function buildChallengeMessageOnClient(c) {
216
+ return [
217
+ `${c.domain} wants you to sign in with your Solana account.`,
218
+ "",
219
+ c.statement,
220
+ "",
221
+ `URI: ${c.uri}`,
222
+ `Nonce: ${c.nonce}`,
223
+ `Issued At: ${c.issuedAt}`,
224
+ `Expires At: ${c.expiresAt}`
225
+ ].join("\n");
226
+ }
227
+
228
+ // ../../solven/sdk-client/src/telegram.ts
229
+ function getTelegram() {
230
+ if (typeof window === "undefined") return void 0;
231
+ return window.Telegram;
232
+ }
233
+ function getTelegramInitData() {
234
+ const data = getTelegram()?.WebApp?.initData;
235
+ if (!data || typeof data !== "string" || data.length === 0) return null;
236
+ return data;
237
+ }
238
+ function isTelegramMiniApp() {
239
+ return getTelegramInitData() !== null;
240
+ }
241
+ function notifyTelegramReady() {
242
+ try {
243
+ const wa = getTelegram()?.WebApp;
244
+ wa?.ready?.();
245
+ wa?.expand?.();
246
+ } catch {
247
+ }
248
+ }
249
+ var SolvenContext = createContext(null);
250
+ function useSolvenContext() {
251
+ const ctx = useContext(SolvenContext);
252
+ if (!ctx) throw new Error("useSolvenContext must be inside a <SolvenProvider />");
253
+ return ctx;
254
+ }
255
+ function SolvenProvider({ children, ...opts }) {
256
+ const client = useMemo(() => new SolvenClient(opts), [
257
+ opts.publishableKey,
258
+ opts.baseUrl,
259
+ opts.storage,
260
+ opts.fetchImpl
261
+ ]);
262
+ const [session, setSession] = useState(client.getSession());
263
+ const [status, setStatus] = useState("loading");
264
+ useEffect(() => {
265
+ let cancelled = false;
266
+ (async () => {
267
+ const stored = client.getSession();
268
+ if (!stored) {
269
+ if (!cancelled) {
270
+ setSession(null);
271
+ setStatus("anonymous");
272
+ }
273
+ return;
274
+ }
275
+ const expired = new Date(stored.expiresAt).getTime() <= Date.now();
276
+ if (expired) {
277
+ try {
278
+ const next = await client.refreshSession();
279
+ if (cancelled) return;
280
+ setSession(next);
281
+ setStatus(next ? "authenticated" : "anonymous");
282
+ } catch {
283
+ if (cancelled) return;
284
+ setSession(null);
285
+ setStatus("anonymous");
286
+ }
287
+ } else if (!cancelled) {
288
+ setSession(stored);
289
+ setStatus("authenticated");
290
+ }
291
+ })();
292
+ return () => {
293
+ cancelled = true;
294
+ };
295
+ }, [client]);
296
+ const value = {
297
+ client,
298
+ session,
299
+ status,
300
+ setSession: (s) => {
301
+ setSession(s);
302
+ setStatus(s ? "authenticated" : "anonymous");
303
+ }
304
+ };
305
+ return /* @__PURE__ */ jsx(SolvenContext.Provider, { value, children });
306
+ }
307
+
308
+ // ../../solven/sdk-client/src/hooks.ts
309
+ function useSolvenAuth() {
310
+ const { client, session, status, setSession } = useSolvenContext();
311
+ const connectExternal = useCallback(
312
+ async (adapter) => {
313
+ const { publicKey } = await adapter.connect();
314
+ const challenge = await client.requestSiwsChallenge(publicKey);
315
+ const message = buildChallengeMessageOnClient(challenge);
316
+ const { signature } = await adapter.signMessage(message);
317
+ const session2 = await client.verifySiws({ challenge, publicKey, signature });
318
+ setSession(session2);
319
+ },
320
+ [client, setSession]
321
+ );
322
+ const connectTelegram = useCallback(async () => {
323
+ const initData = getTelegramInitData();
324
+ if (!initData) throw new Error("Not running inside a Telegram Mini App.");
325
+ const session2 = await client.verifyTelegram({ initData });
326
+ setSession(session2);
327
+ }, [client, setSession]);
328
+ const logout = useCallback(async () => {
329
+ await client.logout();
330
+ setSession(null);
331
+ }, [client, setSession]);
332
+ return {
333
+ user: session?.user ?? null,
334
+ status,
335
+ connectExternal,
336
+ connectTelegram,
337
+ logout
338
+ };
339
+ }
340
+ function useSolvenWallet() {
341
+ const { client, status } = useSolvenContext();
342
+ const [wallet, setWallet] = useState(null);
343
+ useEffect(() => {
344
+ if (status !== "authenticated") {
345
+ setWallet(null);
346
+ return;
347
+ }
348
+ let cancelled = false;
349
+ client.getMyWallet().then((w) => !cancelled && setWallet(w)).catch(() => !cancelled && setWallet(null));
350
+ return () => {
351
+ cancelled = true;
352
+ };
353
+ }, [client, status]);
354
+ return wallet;
355
+ }
356
+ function useSolvenBalance(walletId, pollMs = 15e3) {
357
+ const { client, status } = useSolvenContext();
358
+ const [balance, setBalance] = useState(null);
359
+ useEffect(() => {
360
+ if (status !== "authenticated" || !walletId) {
361
+ setBalance(null);
362
+ return;
363
+ }
364
+ let cancelled = false;
365
+ const tick = async () => {
366
+ try {
367
+ const b = await client.getBalance(walletId);
368
+ if (!cancelled) setBalance(b);
369
+ } catch {
370
+ }
371
+ };
372
+ void tick();
373
+ const t = setInterval(tick, pollMs);
374
+ return () => {
375
+ cancelled = true;
376
+ clearInterval(t);
377
+ };
378
+ }, [client, status, walletId, pollMs]);
379
+ return balance;
380
+ }
381
+ function useSolvenSign() {
382
+ const { client } = useSolvenContext();
383
+ const signAction = useCallback(
384
+ ({ walletId, txBase64, label, idempotencyKey }) => client.signAction({ walletId, txBase64, label }, idempotencyKey),
385
+ [client]
386
+ );
387
+ return { signAction };
388
+ }
389
+ function useSolvenExport(walletId) {
390
+ const { client } = useSolvenContext();
391
+ const [preflight, setPreflight] = useState(null);
392
+ const refreshPreflight = useCallback(async () => {
393
+ if (!walletId) return;
394
+ const p = await client.exportPreflight(walletId);
395
+ setPreflight(p);
396
+ }, [client, walletId]);
397
+ useEffect(() => {
398
+ if (walletId) void refreshPreflight();
399
+ }, [walletId, refreshPreflight]);
400
+ const exportKey = useCallback(
401
+ async (idempotencyKey) => {
402
+ if (!walletId) throw new Error("No wallet selected for export");
403
+ return client.exportWallet(walletId, idempotencyKey);
404
+ },
405
+ [client, walletId]
406
+ );
407
+ return { preflight, refreshPreflight, exportKey };
408
+ }
409
+ function useSolvenTransferIn() {
410
+ const wallet = useSolvenWallet();
411
+ return { destinationAddress: wallet?.address ?? null };
412
+ }
413
+
414
+ // ../../solven/sdk-client/src/wallet-adapter.ts
415
+ function phantomAdapter() {
416
+ const w = typeof window !== "undefined" ? window : null;
417
+ const provider = w?.phantom?.solana ?? w?.solana;
418
+ if (!provider) {
419
+ throw new Error("No Solana provider detected on window (Phantom/Solflare not installed?).");
420
+ }
421
+ return {
422
+ async connect() {
423
+ const out = await provider.connect();
424
+ return { publicKey: out.publicKey.toString() };
425
+ },
426
+ async disconnect() {
427
+ if (typeof provider.disconnect === "function") {
428
+ await provider.disconnect();
429
+ }
430
+ },
431
+ async signMessage(message) {
432
+ const enc = new TextEncoder().encode(message);
433
+ const out = await provider.signMessage(enc, "utf8");
434
+ const sig = out.signature ?? out;
435
+ return {
436
+ signature: btoa(String.fromCharCode(...sig)),
437
+ publicKey: provider.publicKey.toString()
438
+ };
439
+ }
440
+ };
441
+ }
442
+
443
+ // ../../solven/sdk-client/src/wallet-adapters.ts
444
+ function getWindow() {
445
+ return typeof window !== "undefined" ? window : null;
446
+ }
447
+ function phantomDetected() {
448
+ const w = getWindow();
449
+ return Boolean(w?.phantom?.solana?.isPhantom || w?.solana?.isPhantom);
450
+ }
451
+ function solflareDetected() {
452
+ const w = getWindow();
453
+ return Boolean(w?.solflare?.isSolflare);
454
+ }
455
+ function backpackDetected() {
456
+ const w = getWindow();
457
+ return Boolean(w?.backpack?.isBackpack);
458
+ }
459
+ function genericAdapter(provider) {
460
+ if (!provider) {
461
+ throw new Error("Wallet provider not detected on window");
462
+ }
463
+ return {
464
+ async connect() {
465
+ const out = await provider.connect();
466
+ return { publicKey: out.publicKey.toString() };
467
+ },
468
+ async disconnect() {
469
+ if (typeof provider.disconnect === "function") {
470
+ await provider.disconnect();
471
+ }
472
+ },
473
+ async signMessage(message) {
474
+ const enc = new TextEncoder().encode(message);
475
+ const out = await provider.signMessage(enc, "utf8");
476
+ const sig = out.signature ?? out;
477
+ return {
478
+ signature: btoa(String.fromCharCode(...sig)),
479
+ publicKey: provider.publicKey.toString()
480
+ };
481
+ }
482
+ };
483
+ }
484
+ var solflareAdapter = () => {
485
+ const w = getWindow();
486
+ return genericAdapter(w?.solflare);
487
+ };
488
+ var backpackAdapter = () => {
489
+ const w = getWindow();
490
+ return genericAdapter(w?.backpack);
491
+ };
492
+ function listSupportedWallets() {
493
+ return [
494
+ {
495
+ id: "phantom",
496
+ name: "Phantom",
497
+ detected: phantomDetected(),
498
+ downloadUrl: "https://phantom.app/download",
499
+ buildAdapter: phantomAdapter
500
+ },
501
+ {
502
+ id: "solflare",
503
+ name: "Solflare",
504
+ detected: solflareDetected(),
505
+ downloadUrl: "https://solflare.com/download",
506
+ buildAdapter: solflareAdapter
507
+ },
508
+ {
509
+ id: "backpack",
510
+ name: "Backpack",
511
+ detected: backpackDetected(),
512
+ downloadUrl: "https://backpack.app",
513
+ buildAdapter: backpackAdapter
514
+ }
515
+ ];
516
+ }
517
+ var defaultTheme = {
518
+ primary: "#0f766e",
519
+ background: "#ffffff",
520
+ foreground: "#1a1714",
521
+ muted: "#6b6359",
522
+ surface: "#f3ede2",
523
+ border: "#ece7dd",
524
+ radius: "1rem",
525
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
526
+ };
527
+ function SolvenLoginContent(props) {
528
+ const { connectExternal, connectTelegram, status, user } = useSolvenAuth();
529
+ const [busy, setBusy] = useState(null);
530
+ const [error, setError] = useState(null);
531
+ const theme = useMemo(
532
+ () => ({ ...defaultTheme, ...props.theme ?? {} }),
533
+ [props.theme]
534
+ );
535
+ const inTma = useMemo(() => isTelegramMiniApp(), []);
536
+ const autoTelegram = props.autoTelegram ?? true;
537
+ const wallets = useMemo(listSupportedWallets, []);
538
+ useEffect(() => {
539
+ notifyTelegramReady();
540
+ if (!inTma || !autoTelegram) return;
541
+ if (status === "authenticated") return;
542
+ let cancelled = false;
543
+ (async () => {
544
+ setBusy("tg");
545
+ setError(null);
546
+ try {
547
+ await connectTelegram();
548
+ if (cancelled) return;
549
+ props.onSignIn?.();
550
+ props.onCloseAfterSignIn?.();
551
+ } catch (e) {
552
+ if (!cancelled) {
553
+ setError(e instanceof Error ? e.message : "Telegram sign-in failed");
554
+ }
555
+ } finally {
556
+ if (!cancelled) setBusy(null);
557
+ }
558
+ })();
559
+ return () => {
560
+ cancelled = true;
561
+ };
562
+ }, [inTma, autoTelegram, status, connectTelegram]);
563
+ if (status === "authenticated" && user) {
564
+ return null;
565
+ }
566
+ const handleWallet = async (descriptor) => {
567
+ setBusy(descriptor.id);
568
+ setError(null);
569
+ try {
570
+ if (!descriptor.detected) {
571
+ window.open(descriptor.downloadUrl, "_blank", "noopener,noreferrer");
572
+ return;
573
+ }
574
+ const adapter = props.adapterFactory ? props.adapterFactory() : descriptor.buildAdapter();
575
+ await connectExternal(adapter);
576
+ props.onSignIn?.();
577
+ props.onCloseAfterSignIn?.();
578
+ } catch (e) {
579
+ setError(e instanceof Error ? e.message : "Wallet sign-in failed");
580
+ } finally {
581
+ setBusy(null);
582
+ }
583
+ };
584
+ const handleTelegramButton = async () => {
585
+ setBusy("tg");
586
+ setError(null);
587
+ try {
588
+ await connectTelegram();
589
+ props.onSignIn?.();
590
+ props.onCloseAfterSignIn?.();
591
+ } catch (e) {
592
+ setError(e instanceof Error ? e.message : "Telegram sign-in failed");
593
+ } finally {
594
+ setBusy(null);
595
+ }
596
+ };
597
+ const buttonBase = {
598
+ width: "100%",
599
+ padding: "0.75rem 0.875rem",
600
+ borderRadius: theme.radius,
601
+ border: `1px solid ${theme.border}`,
602
+ fontWeight: 600,
603
+ fontSize: "0.9375rem",
604
+ cursor: "pointer",
605
+ transition: "transform 100ms ease, opacity 100ms ease",
606
+ display: "flex",
607
+ alignItems: "center",
608
+ gap: "0.625rem",
609
+ background: theme.surface,
610
+ color: theme.foreground
611
+ };
612
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
613
+ /* @__PURE__ */ jsx(
614
+ "div",
615
+ {
616
+ style: {
617
+ marginBottom: "0.25rem",
618
+ fontWeight: 700,
619
+ fontSize: "1.25rem"
620
+ },
621
+ children: props.title ?? "Sign in"
622
+ }
623
+ ),
624
+ /* @__PURE__ */ jsx(
625
+ "div",
626
+ {
627
+ style: {
628
+ marginBottom: "1.25rem",
629
+ color: theme.muted,
630
+ fontSize: "0.9375rem"
631
+ },
632
+ children: props.subtitle ?? (inTma ? "Continuing with your Telegram account\u2026" : "Choose a wallet to continue.")
633
+ }
634
+ ),
635
+ inTma ? /* @__PURE__ */ jsx(
636
+ "button",
637
+ {
638
+ type: "button",
639
+ onClick: handleTelegramButton,
640
+ disabled: busy !== null,
641
+ style: {
642
+ ...buttonBase,
643
+ justifyContent: "center",
644
+ background: theme.primary,
645
+ color: "#ffffff",
646
+ border: "none",
647
+ opacity: busy === "tg" ? 0.7 : 1
648
+ },
649
+ children: busy === "tg" ? "Signing in with Telegram\u2026" : "Continue with Telegram"
650
+ }
651
+ ) : /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: wallets.map((w) => {
652
+ const isBusy = busy === w.id;
653
+ return /* @__PURE__ */ jsxs(
654
+ "button",
655
+ {
656
+ type: "button",
657
+ onClick: () => handleWallet(w),
658
+ disabled: busy !== null,
659
+ style: {
660
+ ...buttonBase,
661
+ opacity: isBusy ? 0.7 : 1
662
+ },
663
+ "aria-label": w.detected ? `Connect with ${w.name}` : `Install ${w.name}`,
664
+ children: [
665
+ /* @__PURE__ */ jsx("span", { style: { flex: 1, textAlign: "left" }, children: w.name }),
666
+ /* @__PURE__ */ jsx(
667
+ "span",
668
+ {
669
+ style: {
670
+ fontSize: "0.75rem",
671
+ color: w.detected ? theme.primary : theme.muted,
672
+ fontWeight: 500
673
+ },
674
+ children: isBusy ? "Connecting\u2026" : w.detected ? "Detected" : "Install"
675
+ }
676
+ )
677
+ ]
678
+ },
679
+ w.id
680
+ );
681
+ }) }),
682
+ error && /* @__PURE__ */ jsx(
683
+ "div",
684
+ {
685
+ role: "alert",
686
+ style: {
687
+ marginTop: "0.875rem",
688
+ padding: "0.625rem 0.75rem",
689
+ borderRadius: "0.5rem",
690
+ background: "#fee2e2",
691
+ color: "#991b1b",
692
+ fontSize: "0.875rem"
693
+ },
694
+ children: error
695
+ }
696
+ )
697
+ ] });
698
+ }
699
+ function SolvenLogin(props) {
700
+ const theme = useMemo(
701
+ () => ({ ...defaultTheme, ...props.theme ?? {} }),
702
+ [props.theme]
703
+ );
704
+ const { status } = useSolvenAuth();
705
+ if (status === "authenticated") return null;
706
+ return /* @__PURE__ */ jsx(
707
+ "div",
708
+ {
709
+ role: "dialog",
710
+ "aria-label": "Wallet sign-in",
711
+ style: {
712
+ background: theme.background,
713
+ color: theme.foreground,
714
+ fontFamily: theme.fontFamily,
715
+ padding: "1.5rem",
716
+ borderRadius: theme.radius,
717
+ boxShadow: "0 10px 40px rgba(0,0,0,0.08)",
718
+ maxWidth: 380,
719
+ margin: "0 auto"
720
+ },
721
+ children: /* @__PURE__ */ jsx(SolvenLoginContent, { ...props })
722
+ }
723
+ );
724
+ }
725
+ function SolvenLoginModal(props) {
726
+ const {
727
+ isOpen,
728
+ onClose,
729
+ closeOnBackdrop = true,
730
+ closeOnEscape = true,
731
+ ...content
732
+ } = props;
733
+ const theme = useMemo(
734
+ () => ({ ...defaultTheme, ...props.theme ?? {} }),
735
+ [props.theme]
736
+ );
737
+ const dialogRef = useRef(null);
738
+ const previouslyFocused = useRef(null);
739
+ const [mounted, setMounted] = useState(false);
740
+ useEffect(() => {
741
+ setMounted(true);
742
+ }, []);
743
+ useEffect(() => {
744
+ if (!isOpen || !closeOnEscape) return;
745
+ const onKey = (e) => {
746
+ if (e.key === "Escape") {
747
+ e.stopPropagation();
748
+ onClose();
749
+ }
750
+ };
751
+ window.addEventListener("keydown", onKey);
752
+ return () => window.removeEventListener("keydown", onKey);
753
+ }, [isOpen, closeOnEscape, onClose]);
754
+ useEffect(() => {
755
+ if (!isOpen) return;
756
+ previouslyFocused.current = document.activeElement ?? null;
757
+ queueMicrotask(() => {
758
+ const focusable = dialogRef.current?.querySelector(
759
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
760
+ );
761
+ focusable?.focus();
762
+ });
763
+ const onKey = (e) => {
764
+ if (e.key !== "Tab" || !dialogRef.current) return;
765
+ const focusables = Array.from(
766
+ dialogRef.current.querySelectorAll(
767
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'
768
+ )
769
+ );
770
+ if (focusables.length === 0) return;
771
+ const first = focusables[0];
772
+ const last = focusables[focusables.length - 1];
773
+ if (e.shiftKey && document.activeElement === first) {
774
+ e.preventDefault();
775
+ last.focus();
776
+ } else if (!e.shiftKey && document.activeElement === last) {
777
+ e.preventDefault();
778
+ first.focus();
779
+ }
780
+ };
781
+ window.addEventListener("keydown", onKey);
782
+ const prevOverflow = document.body.style.overflow;
783
+ document.body.style.overflow = "hidden";
784
+ return () => {
785
+ window.removeEventListener("keydown", onKey);
786
+ document.body.style.overflow = prevOverflow;
787
+ previouslyFocused.current?.focus?.();
788
+ };
789
+ }, [isOpen]);
790
+ const handleBackdropClick = useCallback(
791
+ (e) => {
792
+ if (!closeOnBackdrop) return;
793
+ if (e.target === e.currentTarget) onClose();
794
+ },
795
+ [closeOnBackdrop, onClose]
796
+ );
797
+ if (!mounted || !isOpen) return null;
798
+ const node = /* @__PURE__ */ jsxs(
799
+ "div",
800
+ {
801
+ onClick: handleBackdropClick,
802
+ style: {
803
+ position: "fixed",
804
+ inset: 0,
805
+ background: "rgba(15, 12, 8, 0.55)",
806
+ backdropFilter: "blur(2px)",
807
+ display: "flex",
808
+ alignItems: "center",
809
+ justifyContent: "center",
810
+ padding: "1rem",
811
+ zIndex: 2147483e3,
812
+ animation: "solven-fade-in 140ms ease-out"
813
+ },
814
+ children: [
815
+ /* @__PURE__ */ jsxs(
816
+ "div",
817
+ {
818
+ ref: dialogRef,
819
+ role: "dialog",
820
+ "aria-modal": "true",
821
+ "aria-label": "Wallet sign-in",
822
+ style: {
823
+ background: theme.background,
824
+ color: theme.foreground,
825
+ fontFamily: theme.fontFamily,
826
+ padding: "1.5rem",
827
+ borderRadius: theme.radius,
828
+ boxShadow: "0 24px 80px rgba(0,0,0,0.22)",
829
+ maxWidth: 420,
830
+ width: "100%",
831
+ position: "relative",
832
+ animation: "solven-pop-in 160ms cubic-bezier(0.16, 1, 0.3, 1)"
833
+ },
834
+ children: [
835
+ /* @__PURE__ */ jsx(
836
+ "button",
837
+ {
838
+ type: "button",
839
+ "aria-label": "Close",
840
+ onClick: onClose,
841
+ style: {
842
+ position: "absolute",
843
+ top: 12,
844
+ right: 12,
845
+ border: "none",
846
+ background: "transparent",
847
+ color: theme.muted,
848
+ fontSize: "1.25rem",
849
+ lineHeight: 1,
850
+ cursor: "pointer",
851
+ padding: "0.25rem 0.5rem",
852
+ borderRadius: 8
853
+ },
854
+ children: "\xD7"
855
+ }
856
+ ),
857
+ /* @__PURE__ */ jsx(SolvenLoginContent, { ...content, onCloseAfterSignIn: onClose })
858
+ ]
859
+ }
860
+ ),
861
+ /* @__PURE__ */ jsx("style", { children: `
862
+ @keyframes solven-fade-in {
863
+ from { opacity: 0; }
864
+ to { opacity: 1; }
865
+ }
866
+ @keyframes solven-pop-in {
867
+ from { opacity: 0; transform: translateY(8px) scale(0.98); }
868
+ to { opacity: 1; transform: translateY(0) scale(1); }
869
+ }
870
+ ` })
871
+ ]
872
+ }
873
+ );
874
+ return createPortal(node, document.body);
875
+ }
876
+ var defaultTheme2 = {
877
+ background: "#ffffff",
878
+ foreground: "#1a1714",
879
+ muted: "#6b6359",
880
+ surface: "#f3ede2",
881
+ border: "#ece7dd",
882
+ primary: "#0f766e",
883
+ radius: "1rem",
884
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
885
+ };
886
+ function truncateAddress(address, chars) {
887
+ if (address.length <= chars * 2 + 3) return address;
888
+ return `${address.slice(0, chars)}...${address.slice(-chars)}`;
889
+ }
890
+ function SolvenAddress(props) {
891
+ const wallet = useSolvenWallet();
892
+ const theme = useMemo(() => ({ ...defaultTheme2, ...props.theme ?? {} }), [props.theme]);
893
+ const truncateChars = props.truncateChars ?? 6;
894
+ const showCopy = props.showCopy ?? true;
895
+ const label = props.label ?? "Wallet Address";
896
+ const [copied, setCopied] = useState(false);
897
+ const handleCopy = useCallback(async () => {
898
+ if (!wallet?.address) return;
899
+ try {
900
+ await navigator.clipboard.writeText(wallet.address);
901
+ setCopied(true);
902
+ setTimeout(() => setCopied(false), 2e3);
903
+ } catch {
904
+ const ta = document.createElement("textarea");
905
+ ta.value = wallet.address;
906
+ ta.style.position = "fixed";
907
+ ta.style.left = "-9999px";
908
+ document.body.appendChild(ta);
909
+ ta.select();
910
+ document.execCommand("copy");
911
+ document.body.removeChild(ta);
912
+ setCopied(true);
913
+ setTimeout(() => setCopied(false), 2e3);
914
+ }
915
+ }, [wallet?.address]);
916
+ if (!wallet) return null;
917
+ return /* @__PURE__ */ jsxs(
918
+ "div",
919
+ {
920
+ style: {
921
+ background: theme.background,
922
+ color: theme.foreground,
923
+ fontFamily: theme.fontFamily,
924
+ borderRadius: theme.radius,
925
+ padding: "1.25rem"
926
+ },
927
+ children: [
928
+ label && /* @__PURE__ */ jsx(
929
+ "div",
930
+ {
931
+ style: {
932
+ fontSize: "0.8125rem",
933
+ fontWeight: 600,
934
+ color: theme.muted,
935
+ marginBottom: "0.75rem",
936
+ textTransform: "uppercase",
937
+ letterSpacing: "0.04em"
938
+ },
939
+ children: label
940
+ }
941
+ ),
942
+ props.renderQr && /* @__PURE__ */ jsx("div", { style: { display: "flex", justifyContent: "center", marginBottom: "1rem" }, children: props.renderQr(wallet.address) }),
943
+ /* @__PURE__ */ jsxs(
944
+ "div",
945
+ {
946
+ style: {
947
+ display: "flex",
948
+ alignItems: "center",
949
+ gap: "0.5rem",
950
+ background: theme.surface,
951
+ padding: "0.625rem 0.875rem",
952
+ borderRadius: "0.75rem",
953
+ border: `1px solid ${theme.border}`
954
+ },
955
+ children: [
956
+ /* @__PURE__ */ jsx(
957
+ "span",
958
+ {
959
+ style: {
960
+ flex: 1,
961
+ fontFamily: "monospace",
962
+ fontSize: "0.875rem",
963
+ color: theme.foreground,
964
+ overflow: "hidden",
965
+ textOverflow: "ellipsis",
966
+ whiteSpace: "nowrap"
967
+ },
968
+ title: wallet.address,
969
+ children: truncateAddress(wallet.address, truncateChars)
970
+ }
971
+ ),
972
+ showCopy && /* @__PURE__ */ jsx(
973
+ "button",
974
+ {
975
+ type: "button",
976
+ onClick: handleCopy,
977
+ style: {
978
+ border: "none",
979
+ background: copied ? theme.primary : "transparent",
980
+ color: copied ? "#ffffff" : theme.muted,
981
+ fontSize: "0.75rem",
982
+ fontWeight: 600,
983
+ padding: "0.375rem 0.625rem",
984
+ borderRadius: "0.5rem",
985
+ cursor: "pointer",
986
+ transition: "all 150ms ease",
987
+ whiteSpace: "nowrap"
988
+ },
989
+ children: copied ? "Copied" : "Copy"
990
+ }
991
+ )
992
+ ]
993
+ }
994
+ )
995
+ ]
996
+ }
997
+ );
998
+ }
999
+ var defaultTheme3 = {
1000
+ background: "#ffffff",
1001
+ foreground: "#1a1714",
1002
+ muted: "#6b6359",
1003
+ surface: "#f3ede2",
1004
+ border: "#ece7dd",
1005
+ primary: "#0f766e",
1006
+ radius: "1rem",
1007
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
1008
+ };
1009
+ function formatTokenAmount(token) {
1010
+ const raw = BigInt(token.amount);
1011
+ if (token.decimals === 0) return raw.toString();
1012
+ const divisor = BigInt(10 ** token.decimals);
1013
+ const whole = raw / divisor;
1014
+ const fractional = raw % divisor;
1015
+ const fracStr = fractional.toString().padStart(token.decimals, "0");
1016
+ const trimmed = fracStr.slice(0, 4).replace(/0+$/, "");
1017
+ return trimmed ? `${whole}.${trimmed}` : whole.toString();
1018
+ }
1019
+ function getTokenLabel(token) {
1020
+ if (token.symbol) return token.symbol;
1021
+ if (token.mint === "SOL") return "SOL";
1022
+ return `${token.mint.slice(0, 4)}...${token.mint.slice(-4)}`;
1023
+ }
1024
+ function SolvenBalance(props) {
1025
+ const wallet = useSolvenWallet();
1026
+ const balance = useSolvenBalance(wallet?.id ?? null, props.pollMs);
1027
+ const theme = useMemo(() => ({ ...defaultTheme3, ...props.theme ?? {} }), [props.theme]);
1028
+ const showRefresh = props.showRefresh ?? true;
1029
+ const label = props.label ?? "Balance";
1030
+ const compact = props.compact ?? false;
1031
+ const [refreshing, setRefreshing] = useState(false);
1032
+ const tokens = useMemo(() => {
1033
+ if (!balance?.tokens) return [];
1034
+ if (!props.filterMints) return balance.tokens;
1035
+ const set = new Set(props.filterMints);
1036
+ return balance.tokens.filter((t) => set.has(t.mint));
1037
+ }, [balance?.tokens, props.filterMints]);
1038
+ const handleRefresh = useCallback(() => {
1039
+ setRefreshing(true);
1040
+ setTimeout(() => setRefreshing(false), 1500);
1041
+ }, []);
1042
+ if (!wallet) return null;
1043
+ if (compact) {
1044
+ const solToken = tokens.find((t) => t.mint === "SOL");
1045
+ return /* @__PURE__ */ jsx(
1046
+ "span",
1047
+ {
1048
+ style: {
1049
+ fontFamily: theme.fontFamily,
1050
+ fontSize: "0.9375rem",
1051
+ fontWeight: 600,
1052
+ color: theme.foreground
1053
+ },
1054
+ children: solToken ? `${formatTokenAmount(solToken)} SOL` : "\u2014"
1055
+ }
1056
+ );
1057
+ }
1058
+ const loading = !balance;
1059
+ return /* @__PURE__ */ jsxs(
1060
+ "div",
1061
+ {
1062
+ style: {
1063
+ background: theme.background,
1064
+ color: theme.foreground,
1065
+ fontFamily: theme.fontFamily,
1066
+ borderRadius: theme.radius,
1067
+ padding: "1.25rem"
1068
+ },
1069
+ children: [
1070
+ /* @__PURE__ */ jsxs(
1071
+ "div",
1072
+ {
1073
+ style: {
1074
+ display: "flex",
1075
+ alignItems: "center",
1076
+ justifyContent: "space-between",
1077
+ marginBottom: "0.75rem"
1078
+ },
1079
+ children: [
1080
+ /* @__PURE__ */ jsx(
1081
+ "span",
1082
+ {
1083
+ style: {
1084
+ fontSize: "0.8125rem",
1085
+ fontWeight: 600,
1086
+ color: theme.muted,
1087
+ textTransform: "uppercase",
1088
+ letterSpacing: "0.04em"
1089
+ },
1090
+ children: label
1091
+ }
1092
+ ),
1093
+ showRefresh && /* @__PURE__ */ jsx(
1094
+ "button",
1095
+ {
1096
+ type: "button",
1097
+ onClick: handleRefresh,
1098
+ disabled: refreshing,
1099
+ style: {
1100
+ border: "none",
1101
+ background: "transparent",
1102
+ color: theme.muted,
1103
+ fontSize: "0.75rem",
1104
+ fontWeight: 600,
1105
+ cursor: refreshing ? "default" : "pointer",
1106
+ opacity: refreshing ? 0.5 : 1,
1107
+ padding: "0.25rem 0.5rem",
1108
+ borderRadius: "0.375rem",
1109
+ transition: "opacity 150ms ease"
1110
+ },
1111
+ children: refreshing ? "Refreshing..." : "Refresh"
1112
+ }
1113
+ )
1114
+ ]
1115
+ }
1116
+ ),
1117
+ loading ? /* @__PURE__ */ jsx(
1118
+ "div",
1119
+ {
1120
+ style: {
1121
+ padding: "1rem 0",
1122
+ textAlign: "center",
1123
+ color: theme.muted,
1124
+ fontSize: "0.875rem"
1125
+ },
1126
+ children: "Loading balances..."
1127
+ }
1128
+ ) : tokens.length === 0 ? /* @__PURE__ */ jsx(
1129
+ "div",
1130
+ {
1131
+ style: {
1132
+ padding: "1rem 0",
1133
+ textAlign: "center",
1134
+ color: theme.muted,
1135
+ fontSize: "0.875rem"
1136
+ },
1137
+ children: "No tokens found"
1138
+ }
1139
+ ) : /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: tokens.map((token) => /* @__PURE__ */ jsxs(
1140
+ "div",
1141
+ {
1142
+ style: {
1143
+ display: "flex",
1144
+ alignItems: "center",
1145
+ justifyContent: "space-between",
1146
+ padding: "0.625rem 0.875rem",
1147
+ background: theme.surface,
1148
+ borderRadius: "0.75rem",
1149
+ border: `1px solid ${theme.border}`
1150
+ },
1151
+ children: [
1152
+ /* @__PURE__ */ jsx(
1153
+ "span",
1154
+ {
1155
+ style: {
1156
+ fontSize: "0.8125rem",
1157
+ fontWeight: 500,
1158
+ color: theme.muted
1159
+ },
1160
+ children: getTokenLabel(token)
1161
+ }
1162
+ ),
1163
+ /* @__PURE__ */ jsx(
1164
+ "span",
1165
+ {
1166
+ style: {
1167
+ fontSize: "1rem",
1168
+ fontWeight: 700,
1169
+ color: theme.foreground,
1170
+ fontFamily: "monospace"
1171
+ },
1172
+ children: formatTokenAmount(token)
1173
+ }
1174
+ )
1175
+ ]
1176
+ },
1177
+ token.mint
1178
+ )) }),
1179
+ balance?.asOf && /* @__PURE__ */ jsxs(
1180
+ "div",
1181
+ {
1182
+ style: {
1183
+ marginTop: "0.625rem",
1184
+ fontSize: "0.6875rem",
1185
+ color: theme.muted,
1186
+ textAlign: "right"
1187
+ },
1188
+ children: [
1189
+ "Updated ",
1190
+ new Date(balance.asOf).toLocaleTimeString()
1191
+ ]
1192
+ }
1193
+ )
1194
+ ]
1195
+ }
1196
+ );
1197
+ }
1198
+ var defaultTheme4 = {
1199
+ background: "#ffffff",
1200
+ foreground: "#1a1714",
1201
+ muted: "#6b6359",
1202
+ surface: "#f3ede2",
1203
+ border: "#ece7dd",
1204
+ primary: "#0f766e",
1205
+ danger: "#dc2626",
1206
+ radius: "1rem",
1207
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
1208
+ };
1209
+ function formatTokenAmount2(token) {
1210
+ const raw = BigInt(token.amount);
1211
+ if (token.decimals === 0) return raw.toString();
1212
+ const divisor = BigInt(10 ** token.decimals);
1213
+ const whole = raw / divisor;
1214
+ const fractional = raw % divisor;
1215
+ const fracStr = fractional.toString().padStart(token.decimals, "0");
1216
+ const trimmed = fracStr.slice(0, 4).replace(/0+$/, "");
1217
+ return trimmed ? `${whole}.${trimmed}` : whole.toString();
1218
+ }
1219
+ function getTokenLabel2(token) {
1220
+ if (token.symbol) return token.symbol;
1221
+ if (token.mint === "SOL") return "SOL";
1222
+ return `${token.mint.slice(0, 4)}...${token.mint.slice(-4)}`;
1223
+ }
1224
+ function parseAmountToBaseUnits(amount, decimals) {
1225
+ const parts = amount.split(".");
1226
+ const whole = parts[0] || "0";
1227
+ let frac = parts[1] || "";
1228
+ frac = frac.padEnd(decimals, "0").slice(0, decimals);
1229
+ const raw = BigInt(whole) * BigInt(10 ** decimals) + BigInt(frac);
1230
+ return raw.toString();
1231
+ }
1232
+ function isValidSolanaAddress(address) {
1233
+ return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
1234
+ }
1235
+ function SolvenWithdraw(props) {
1236
+ const wallet = useSolvenWallet();
1237
+ const balance = useSolvenBalance(wallet?.id ?? null);
1238
+ const { client } = useSolvenContext();
1239
+ const theme = useMemo(() => ({ ...defaultTheme4, ...props.theme ?? {} }), [props.theme]);
1240
+ const label = props.label ?? "Withdraw";
1241
+ const [toAddress, setToAddress] = useState("");
1242
+ const [amount, setAmount] = useState("");
1243
+ const [selectedMint, setSelectedMint] = useState(props.mint ?? "SOL");
1244
+ const [busy, setBusy] = useState(false);
1245
+ const [error, setError] = useState(null);
1246
+ const [successSig, setSuccessSig] = useState(null);
1247
+ const tokens = useMemo(() => {
1248
+ if (!balance?.tokens) return [];
1249
+ if (props.mint) return balance.tokens.filter((t) => t.mint === props.mint);
1250
+ return balance.tokens;
1251
+ }, [balance?.tokens, props.mint]);
1252
+ const selectedToken = useMemo(
1253
+ () => tokens.find((t) => t.mint === selectedMint) ?? null,
1254
+ [tokens, selectedMint]
1255
+ );
1256
+ const availableFormatted = selectedToken ? formatTokenAmount2(selectedToken) : "0";
1257
+ const handleMax = useCallback(() => {
1258
+ if (!selectedToken) return;
1259
+ if (selectedToken.mint === "SOL") {
1260
+ const raw = BigInt(selectedToken.amount);
1261
+ const reserve = BigInt(5e6);
1262
+ const maxRaw = raw > reserve ? raw - reserve : 0n;
1263
+ const divisor = BigInt(10 ** selectedToken.decimals);
1264
+ const whole = maxRaw / divisor;
1265
+ const fractional = maxRaw % divisor;
1266
+ const fracStr = fractional.toString().padStart(selectedToken.decimals, "0");
1267
+ const trimmed = fracStr.slice(0, selectedToken.decimals).replace(/0+$/, "");
1268
+ setAmount(trimmed ? `${whole}.${trimmed}` : whole.toString());
1269
+ } else {
1270
+ setAmount(formatTokenAmount2(selectedToken));
1271
+ }
1272
+ }, [selectedToken]);
1273
+ const handleSubmit = useCallback(async () => {
1274
+ setError(null);
1275
+ setSuccessSig(null);
1276
+ if (!wallet) {
1277
+ setError("No wallet connected");
1278
+ return;
1279
+ }
1280
+ if (!toAddress.trim()) {
1281
+ setError("Enter a destination address");
1282
+ return;
1283
+ }
1284
+ if (!isValidSolanaAddress(toAddress.trim())) {
1285
+ setError("Invalid Solana address");
1286
+ return;
1287
+ }
1288
+ if (!amount.trim() || parseFloat(amount) <= 0) {
1289
+ setError("Enter a valid amount");
1290
+ return;
1291
+ }
1292
+ if (!selectedToken) {
1293
+ setError("Token not found in wallet");
1294
+ return;
1295
+ }
1296
+ const baseUnits = parseAmountToBaseUnits(amount, selectedToken.decimals);
1297
+ if (BigInt(baseUnits) > BigInt(selectedToken.amount)) {
1298
+ setError("Insufficient balance");
1299
+ return;
1300
+ }
1301
+ setBusy(true);
1302
+ try {
1303
+ const result = await client.withdraw({
1304
+ walletId: wallet.id,
1305
+ toAddress: toAddress.trim(),
1306
+ mint: selectedMint,
1307
+ amount: baseUnits,
1308
+ idempotencyKey: `w-${wallet.id}-${Date.now()}`
1309
+ });
1310
+ setSuccessSig(result.signature);
1311
+ setAmount("");
1312
+ setToAddress("");
1313
+ props.onSuccess?.(result.signature);
1314
+ } catch (e) {
1315
+ const msg = e instanceof Error ? e.message : "Withdrawal failed";
1316
+ setError(msg);
1317
+ props.onError?.(e instanceof Error ? e : new Error(msg));
1318
+ } finally {
1319
+ setBusy(false);
1320
+ }
1321
+ }, [wallet, toAddress, amount, selectedMint, selectedToken, client, props]);
1322
+ if (!wallet) return null;
1323
+ const inputStyle = {
1324
+ width: "100%",
1325
+ padding: "0.625rem 0.875rem",
1326
+ borderRadius: "0.75rem",
1327
+ border: `1px solid ${theme.border}`,
1328
+ background: theme.surface,
1329
+ color: theme.foreground,
1330
+ fontFamily: theme.fontFamily,
1331
+ fontSize: "0.875rem",
1332
+ outline: "none",
1333
+ boxSizing: "border-box"
1334
+ };
1335
+ return /* @__PURE__ */ jsxs(
1336
+ "div",
1337
+ {
1338
+ style: {
1339
+ background: theme.background,
1340
+ color: theme.foreground,
1341
+ fontFamily: theme.fontFamily,
1342
+ borderRadius: theme.radius,
1343
+ padding: "1.25rem"
1344
+ },
1345
+ children: [
1346
+ /* @__PURE__ */ jsx(
1347
+ "div",
1348
+ {
1349
+ style: {
1350
+ fontSize: "0.8125rem",
1351
+ fontWeight: 600,
1352
+ color: theme.muted,
1353
+ marginBottom: "1rem",
1354
+ textTransform: "uppercase",
1355
+ letterSpacing: "0.04em"
1356
+ },
1357
+ children: label
1358
+ }
1359
+ ),
1360
+ !props.mint && tokens.length > 1 && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.75rem" }, children: [
1361
+ /* @__PURE__ */ jsx(
1362
+ "label",
1363
+ {
1364
+ style: { display: "block", fontSize: "0.75rem", color: theme.muted, marginBottom: "0.25rem" },
1365
+ children: "Token"
1366
+ }
1367
+ ),
1368
+ /* @__PURE__ */ jsx(
1369
+ "select",
1370
+ {
1371
+ value: selectedMint,
1372
+ onChange: (e) => {
1373
+ setSelectedMint(e.target.value);
1374
+ setAmount("");
1375
+ },
1376
+ style: {
1377
+ ...inputStyle,
1378
+ cursor: "pointer",
1379
+ appearance: "auto"
1380
+ },
1381
+ children: tokens.map((t) => /* @__PURE__ */ jsxs("option", { value: t.mint, children: [
1382
+ getTokenLabel2(t),
1383
+ " \u2014 ",
1384
+ formatTokenAmount2(t)
1385
+ ] }, t.mint))
1386
+ }
1387
+ )
1388
+ ] }),
1389
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.75rem" }, children: [
1390
+ /* @__PURE__ */ jsx(
1391
+ "label",
1392
+ {
1393
+ style: { display: "block", fontSize: "0.75rem", color: theme.muted, marginBottom: "0.25rem" },
1394
+ children: "To Address"
1395
+ }
1396
+ ),
1397
+ /* @__PURE__ */ jsx(
1398
+ "input",
1399
+ {
1400
+ type: "text",
1401
+ value: toAddress,
1402
+ onChange: (e) => setToAddress(e.target.value),
1403
+ placeholder: "Solana address (base58)",
1404
+ disabled: busy,
1405
+ style: inputStyle
1406
+ }
1407
+ )
1408
+ ] }),
1409
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "0.75rem" }, children: [
1410
+ /* @__PURE__ */ jsxs(
1411
+ "div",
1412
+ {
1413
+ style: {
1414
+ display: "flex",
1415
+ alignItems: "center",
1416
+ justifyContent: "space-between",
1417
+ marginBottom: "0.25rem"
1418
+ },
1419
+ children: [
1420
+ /* @__PURE__ */ jsx("label", { style: { fontSize: "0.75rem", color: theme.muted }, children: "Amount" }),
1421
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "0.6875rem", color: theme.muted }, children: [
1422
+ "Available: ",
1423
+ availableFormatted,
1424
+ " ",
1425
+ selectedToken ? getTokenLabel2(selectedToken) : ""
1426
+ ] })
1427
+ ]
1428
+ }
1429
+ ),
1430
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
1431
+ /* @__PURE__ */ jsx(
1432
+ "input",
1433
+ {
1434
+ type: "text",
1435
+ inputMode: "decimal",
1436
+ value: amount,
1437
+ onChange: (e) => {
1438
+ const v = e.target.value.replace(/[^0-9.]/g, "");
1439
+ if (v.split(".").length <= 2) setAmount(v);
1440
+ },
1441
+ placeholder: "0.00",
1442
+ disabled: busy,
1443
+ style: { ...inputStyle, paddingRight: "3.5rem" }
1444
+ }
1445
+ ),
1446
+ /* @__PURE__ */ jsx(
1447
+ "button",
1448
+ {
1449
+ type: "button",
1450
+ onClick: handleMax,
1451
+ disabled: busy,
1452
+ style: {
1453
+ position: "absolute",
1454
+ right: 8,
1455
+ top: "50%",
1456
+ transform: "translateY(-50%)",
1457
+ border: "none",
1458
+ background: theme.primary,
1459
+ color: "#ffffff",
1460
+ fontSize: "0.6875rem",
1461
+ fontWeight: 700,
1462
+ padding: "0.25rem 0.5rem",
1463
+ borderRadius: "0.375rem",
1464
+ cursor: "pointer"
1465
+ },
1466
+ children: "MAX"
1467
+ }
1468
+ )
1469
+ ] })
1470
+ ] }),
1471
+ /* @__PURE__ */ jsx(
1472
+ "button",
1473
+ {
1474
+ type: "button",
1475
+ onClick: handleSubmit,
1476
+ disabled: busy,
1477
+ style: {
1478
+ width: "100%",
1479
+ padding: "0.75rem",
1480
+ borderRadius: theme.radius,
1481
+ border: "none",
1482
+ background: theme.primary,
1483
+ color: "#ffffff",
1484
+ fontWeight: 700,
1485
+ fontSize: "0.9375rem",
1486
+ cursor: busy ? "default" : "pointer",
1487
+ opacity: busy ? 0.6 : 1,
1488
+ transition: "opacity 150ms ease",
1489
+ fontFamily: theme.fontFamily
1490
+ },
1491
+ children: busy ? "Withdrawing..." : "Withdraw"
1492
+ }
1493
+ ),
1494
+ error && /* @__PURE__ */ jsx(
1495
+ "div",
1496
+ {
1497
+ role: "alert",
1498
+ style: {
1499
+ marginTop: "0.75rem",
1500
+ padding: "0.625rem 0.75rem",
1501
+ borderRadius: "0.5rem",
1502
+ background: "#fee2e2",
1503
+ color: theme.danger,
1504
+ fontSize: "0.8125rem"
1505
+ },
1506
+ children: error
1507
+ }
1508
+ ),
1509
+ successSig && /* @__PURE__ */ jsxs(
1510
+ "div",
1511
+ {
1512
+ style: {
1513
+ marginTop: "0.75rem",
1514
+ padding: "0.625rem 0.75rem",
1515
+ borderRadius: "0.5rem",
1516
+ background: "#d1fae5",
1517
+ color: "#065f46",
1518
+ fontSize: "0.8125rem",
1519
+ wordBreak: "break-all"
1520
+ },
1521
+ children: [
1522
+ "Sent! Tx: ",
1523
+ successSig.slice(0, 20),
1524
+ "...",
1525
+ successSig.slice(-8)
1526
+ ]
1527
+ }
1528
+ )
1529
+ ]
1530
+ }
1531
+ );
1532
+ }
1533
+ var defaultTheme5 = {
1534
+ background: "#ffffff",
1535
+ foreground: "#1a1714",
1536
+ muted: "#6b6359",
1537
+ surface: "#f3ede2",
1538
+ border: "#ece7dd",
1539
+ primary: "#0f766e",
1540
+ danger: "#dc2626",
1541
+ radius: "1rem",
1542
+ fontFamily: 'system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
1543
+ };
1544
+ function formatLamports(lamports) {
1545
+ const raw = BigInt(lamports);
1546
+ const whole = raw / 1000000000n;
1547
+ const frac = raw % 1000000000n;
1548
+ const fracStr = frac.toString().padStart(9, "0").slice(0, 4).replace(/0+$/, "");
1549
+ return fracStr ? `${whole}.${fracStr}` : whole.toString();
1550
+ }
1551
+ function SolvenExport(props) {
1552
+ const wallet = useSolvenWallet();
1553
+ const { preflight, refreshPreflight, exportKey } = useSolvenExport(wallet?.id ?? null);
1554
+ const theme = useMemo(() => ({ ...defaultTheme5, ...props.theme ?? {} }), [props.theme]);
1555
+ const label = props.label ?? "Export Wallet";
1556
+ const [step, setStep] = useState("preflight");
1557
+ const [busy, setBusy] = useState(false);
1558
+ const [error, setError] = useState(null);
1559
+ const [exportedKey, setExportedKey] = useState(null);
1560
+ const [newAddress, setNewAddress] = useState(null);
1561
+ const [bootstrapSig, setBootstrapSig] = useState(null);
1562
+ const [keyCopied, setKeyCopied] = useState(false);
1563
+ const handleExport = useCallback(async () => {
1564
+ setError(null);
1565
+ setBusy(true);
1566
+ try {
1567
+ const result = await exportKey(`export-${wallet.id}-${Date.now()}`);
1568
+ setExportedKey(result.exportedPrivateKey);
1569
+ setNewAddress(result.newWalletAddress);
1570
+ setBootstrapSig(result.bootstrapTxSignature);
1571
+ setStep("revealed");
1572
+ props.onSuccess?.(result.newWalletAddress);
1573
+ } catch (e) {
1574
+ const msg = e instanceof Error ? e.message : "Export failed";
1575
+ setError(msg);
1576
+ props.onError?.(e instanceof Error ? e : new Error(msg));
1577
+ } finally {
1578
+ setBusy(false);
1579
+ }
1580
+ }, [wallet, exportKey, props]);
1581
+ const handleCopyKey = useCallback(async () => {
1582
+ if (!exportedKey) return;
1583
+ try {
1584
+ await navigator.clipboard.writeText(exportedKey);
1585
+ } catch {
1586
+ const ta = document.createElement("textarea");
1587
+ ta.value = exportedKey;
1588
+ ta.style.position = "fixed";
1589
+ ta.style.left = "-9999px";
1590
+ document.body.appendChild(ta);
1591
+ ta.select();
1592
+ document.execCommand("copy");
1593
+ document.body.removeChild(ta);
1594
+ }
1595
+ setKeyCopied(true);
1596
+ setTimeout(() => setKeyCopied(false), 3e3);
1597
+ }, [exportedKey]);
1598
+ if (!wallet) return null;
1599
+ const cardStyle = {
1600
+ background: theme.background,
1601
+ color: theme.foreground,
1602
+ fontFamily: theme.fontFamily,
1603
+ borderRadius: theme.radius,
1604
+ padding: "1.25rem"
1605
+ };
1606
+ const headerStyle = {
1607
+ fontSize: "0.8125rem",
1608
+ fontWeight: 600,
1609
+ color: theme.muted,
1610
+ marginBottom: "1rem",
1611
+ textTransform: "uppercase",
1612
+ letterSpacing: "0.04em"
1613
+ };
1614
+ if (step === "preflight") {
1615
+ return /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
1616
+ /* @__PURE__ */ jsx("div", { style: headerStyle, children: label }),
1617
+ /* @__PURE__ */ jsxs(
1618
+ "div",
1619
+ {
1620
+ style: {
1621
+ padding: "0.875rem",
1622
+ borderRadius: "0.75rem",
1623
+ background: theme.surface,
1624
+ border: `1px solid ${theme.border}`,
1625
+ marginBottom: "1rem",
1626
+ fontSize: "0.875rem",
1627
+ lineHeight: 1.6
1628
+ },
1629
+ children: [
1630
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, marginBottom: "0.5rem" }, children: "Exporting reveals your wallet's private key and creates a new wallet. Your remaining balance transfers to the new wallet automatically." }),
1631
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontWeight: 600, color: theme.danger }, children: "This action cannot be undone. Store the private key securely." })
1632
+ ]
1633
+ }
1634
+ ),
1635
+ !preflight ? /* @__PURE__ */ jsx("div", { style: { color: theme.muted, fontSize: "0.875rem", textAlign: "center", padding: "0.5rem 0" }, children: "Checking eligibility..." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1636
+ /* @__PURE__ */ jsxs(
1637
+ "div",
1638
+ {
1639
+ style: {
1640
+ display: "flex",
1641
+ flexDirection: "column",
1642
+ gap: "0.375rem",
1643
+ marginBottom: "1rem",
1644
+ fontSize: "0.8125rem"
1645
+ },
1646
+ children: [
1647
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1648
+ /* @__PURE__ */ jsx("span", { style: { color: theme.muted }, children: "Current balance" }),
1649
+ /* @__PURE__ */ jsxs("span", { style: { fontFamily: "monospace" }, children: [
1650
+ formatLamports(preflight.currentBalanceLamports),
1651
+ " SOL"
1652
+ ] })
1653
+ ] }),
1654
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1655
+ /* @__PURE__ */ jsx("span", { style: { color: theme.muted }, children: "Minimum required" }),
1656
+ /* @__PURE__ */ jsxs("span", { style: { fontFamily: "monospace" }, children: [
1657
+ formatLamports(preflight.minBalanceLamports),
1658
+ " SOL"
1659
+ ] })
1660
+ ] }),
1661
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1662
+ /* @__PURE__ */ jsx("span", { style: { color: theme.muted }, children: "Bootstrap to new wallet" }),
1663
+ /* @__PURE__ */ jsxs("span", { style: { fontFamily: "monospace" }, children: [
1664
+ formatLamports(preflight.bootstrapLamports),
1665
+ " SOL"
1666
+ ] })
1667
+ ] })
1668
+ ]
1669
+ }
1670
+ ),
1671
+ preflight.allowed ? /* @__PURE__ */ jsx(
1672
+ "button",
1673
+ {
1674
+ type: "button",
1675
+ onClick: () => setStep("confirm"),
1676
+ style: {
1677
+ width: "100%",
1678
+ padding: "0.75rem",
1679
+ borderRadius: theme.radius,
1680
+ border: "none",
1681
+ background: theme.danger,
1682
+ color: "#ffffff",
1683
+ fontWeight: 700,
1684
+ fontSize: "0.9375rem",
1685
+ cursor: "pointer",
1686
+ fontFamily: theme.fontFamily
1687
+ },
1688
+ children: "Continue to Export"
1689
+ }
1690
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1691
+ /* @__PURE__ */ jsx(
1692
+ "div",
1693
+ {
1694
+ style: {
1695
+ padding: "0.625rem 0.75rem",
1696
+ borderRadius: "0.5rem",
1697
+ background: "#fef3c7",
1698
+ color: "#92400e",
1699
+ fontSize: "0.8125rem",
1700
+ marginBottom: "0.75rem"
1701
+ },
1702
+ children: preflight.reason || "Balance is below the minimum required to export."
1703
+ }
1704
+ ),
1705
+ /* @__PURE__ */ jsx(
1706
+ "button",
1707
+ {
1708
+ type: "button",
1709
+ onClick: refreshPreflight,
1710
+ style: {
1711
+ width: "100%",
1712
+ padding: "0.625rem",
1713
+ borderRadius: theme.radius,
1714
+ border: `1px solid ${theme.border}`,
1715
+ background: "transparent",
1716
+ color: theme.foreground,
1717
+ fontWeight: 600,
1718
+ fontSize: "0.875rem",
1719
+ cursor: "pointer",
1720
+ fontFamily: theme.fontFamily
1721
+ },
1722
+ children: "Re-check Balance"
1723
+ }
1724
+ )
1725
+ ] })
1726
+ ] })
1727
+ ] });
1728
+ }
1729
+ if (step === "confirm") {
1730
+ return /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
1731
+ /* @__PURE__ */ jsx("div", { style: headerStyle, children: "Confirm Export" }),
1732
+ /* @__PURE__ */ jsxs(
1733
+ "div",
1734
+ {
1735
+ style: {
1736
+ padding: "0.875rem",
1737
+ borderRadius: "0.75rem",
1738
+ background: "#fef2f2",
1739
+ border: `1px solid #fecaca`,
1740
+ marginBottom: "1rem",
1741
+ fontSize: "0.875rem",
1742
+ color: theme.danger,
1743
+ lineHeight: 1.6
1744
+ },
1745
+ children: [
1746
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontWeight: 700, marginBottom: "0.375rem" }, children: "Are you sure?" }),
1747
+ /* @__PURE__ */ jsx("p", { style: { margin: 0 }, children: "Your current wallet's private key will be revealed and then deleted from our servers. A new wallet will be created for future use." })
1748
+ ]
1749
+ }
1750
+ ),
1751
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.625rem" }, children: [
1752
+ /* @__PURE__ */ jsx(
1753
+ "button",
1754
+ {
1755
+ type: "button",
1756
+ onClick: () => setStep("preflight"),
1757
+ disabled: busy,
1758
+ style: {
1759
+ flex: 1,
1760
+ padding: "0.75rem",
1761
+ borderRadius: theme.radius,
1762
+ border: `1px solid ${theme.border}`,
1763
+ background: "transparent",
1764
+ color: theme.foreground,
1765
+ fontWeight: 600,
1766
+ fontSize: "0.875rem",
1767
+ cursor: "pointer",
1768
+ fontFamily: theme.fontFamily
1769
+ },
1770
+ children: "Cancel"
1771
+ }
1772
+ ),
1773
+ /* @__PURE__ */ jsx(
1774
+ "button",
1775
+ {
1776
+ type: "button",
1777
+ onClick: handleExport,
1778
+ disabled: busy,
1779
+ style: {
1780
+ flex: 1,
1781
+ padding: "0.75rem",
1782
+ borderRadius: theme.radius,
1783
+ border: "none",
1784
+ background: theme.danger,
1785
+ color: "#ffffff",
1786
+ fontWeight: 700,
1787
+ fontSize: "0.875rem",
1788
+ cursor: busy ? "default" : "pointer",
1789
+ opacity: busy ? 0.6 : 1,
1790
+ fontFamily: theme.fontFamily
1791
+ },
1792
+ children: busy ? "Exporting..." : "Export Now"
1793
+ }
1794
+ )
1795
+ ] }),
1796
+ error && /* @__PURE__ */ jsx(
1797
+ "div",
1798
+ {
1799
+ role: "alert",
1800
+ style: {
1801
+ marginTop: "0.75rem",
1802
+ padding: "0.625rem 0.75rem",
1803
+ borderRadius: "0.5rem",
1804
+ background: "#fee2e2",
1805
+ color: theme.danger,
1806
+ fontSize: "0.8125rem"
1807
+ },
1808
+ children: error
1809
+ }
1810
+ )
1811
+ ] });
1812
+ }
1813
+ return /* @__PURE__ */ jsxs("div", { style: cardStyle, children: [
1814
+ /* @__PURE__ */ jsx("div", { style: headerStyle, children: "Export Complete" }),
1815
+ /* @__PURE__ */ jsx(
1816
+ "div",
1817
+ {
1818
+ style: {
1819
+ padding: "0.875rem",
1820
+ borderRadius: "0.75rem",
1821
+ background: "#d1fae5",
1822
+ border: "1px solid #a7f3d0",
1823
+ marginBottom: "1rem",
1824
+ fontSize: "0.8125rem",
1825
+ color: "#065f46",
1826
+ lineHeight: 1.5
1827
+ },
1828
+ children: "Your wallet has been exported. A new wallet has been created and your remaining balance has been transferred."
1829
+ }
1830
+ ),
1831
+ /* @__PURE__ */ jsxs("div", { style: { marginBottom: "1rem" }, children: [
1832
+ /* @__PURE__ */ jsx(
1833
+ "label",
1834
+ {
1835
+ style: { display: "block", fontSize: "0.75rem", color: theme.muted, marginBottom: "0.375rem", fontWeight: 600 },
1836
+ children: "Private Key (save this securely)"
1837
+ }
1838
+ ),
1839
+ /* @__PURE__ */ jsxs(
1840
+ "div",
1841
+ {
1842
+ style: {
1843
+ position: "relative",
1844
+ background: "#1a1714",
1845
+ borderRadius: "0.75rem",
1846
+ padding: "0.75rem"
1847
+ },
1848
+ children: [
1849
+ /* @__PURE__ */ jsx(
1850
+ "code",
1851
+ {
1852
+ style: {
1853
+ display: "block",
1854
+ fontFamily: "monospace",
1855
+ fontSize: "0.75rem",
1856
+ color: "#f5f5f4",
1857
+ wordBreak: "break-all",
1858
+ lineHeight: 1.5
1859
+ },
1860
+ children: exportedKey
1861
+ }
1862
+ ),
1863
+ /* @__PURE__ */ jsx(
1864
+ "button",
1865
+ {
1866
+ type: "button",
1867
+ onClick: handleCopyKey,
1868
+ style: {
1869
+ position: "absolute",
1870
+ top: 8,
1871
+ right: 8,
1872
+ border: "none",
1873
+ background: keyCopied ? "#065f46" : "rgba(255,255,255,0.15)",
1874
+ color: "#ffffff",
1875
+ fontSize: "0.6875rem",
1876
+ fontWeight: 600,
1877
+ padding: "0.25rem 0.5rem",
1878
+ borderRadius: "0.375rem",
1879
+ cursor: "pointer"
1880
+ },
1881
+ children: keyCopied ? "Copied!" : "Copy"
1882
+ }
1883
+ )
1884
+ ]
1885
+ }
1886
+ )
1887
+ ] }),
1888
+ newAddress && /* @__PURE__ */ jsx("div", { style: { marginBottom: "0.75rem", fontSize: "0.8125rem" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", marginBottom: "0.25rem" }, children: [
1889
+ /* @__PURE__ */ jsx("span", { style: { color: theme.muted }, children: "New wallet" }),
1890
+ /* @__PURE__ */ jsxs("span", { style: { fontFamily: "monospace", fontSize: "0.75rem" }, children: [
1891
+ newAddress.slice(0, 8),
1892
+ "...",
1893
+ newAddress.slice(-6)
1894
+ ] })
1895
+ ] }) }),
1896
+ bootstrapSig && /* @__PURE__ */ jsxs("div", { style: { fontSize: "0.75rem", color: theme.muted, wordBreak: "break-all" }, children: [
1897
+ "Bootstrap tx: ",
1898
+ bootstrapSig.slice(0, 16),
1899
+ "...",
1900
+ bootstrapSig.slice(-8)
1901
+ ] })
1902
+ ] });
1903
+ }
1904
+
1905
+ // ../../solven/sdk-client/src/transfer-in.ts
1906
+ async function buildSolTransferIn(deps, opts) {
1907
+ const { Connection, PublicKey, Transaction, SystemProgram } = deps;
1908
+ const conn = new Connection(opts.rpcUrl, "confirmed");
1909
+ const fromPk = new PublicKey(opts.fromAddress);
1910
+ const toPk = new PublicKey(opts.toAddress);
1911
+ const { blockhash } = await conn.getLatestBlockhash();
1912
+ const tx = new Transaction({ recentBlockhash: blockhash, feePayer: fromPk });
1913
+ tx.add(
1914
+ SystemProgram.transfer({
1915
+ fromPubkey: fromPk,
1916
+ toPubkey: toPk,
1917
+ lamports: Number(BigInt(opts.lamports))
1918
+ })
1919
+ );
1920
+ return Buffer.from(
1921
+ tx.serialize({ requireAllSignatures: false, verifySignatures: false })
1922
+ ).toString("base64");
1923
+ }
1924
+ async function buildSplTransferIn(deps, opts) {
1925
+ if (!deps.TOKEN_PROGRAM_ID || !deps.createTransferCheckedInstruction || !deps.getAssociatedTokenAddressSync || !deps.createAssociatedTokenAccountInstruction || !deps.getMint) {
1926
+ throw new Error("buildSplTransferIn requires the SPL-token helpers in deps.");
1927
+ }
1928
+ const {
1929
+ Connection,
1930
+ PublicKey,
1931
+ Transaction,
1932
+ TOKEN_PROGRAM_ID,
1933
+ createTransferCheckedInstruction,
1934
+ getAssociatedTokenAddressSync,
1935
+ createAssociatedTokenAccountInstruction,
1936
+ getMint
1937
+ } = deps;
1938
+ const conn = new Connection(opts.rpcUrl, "confirmed");
1939
+ const fromPk = new PublicKey(opts.fromAddress);
1940
+ const toPk = new PublicKey(opts.toAddress);
1941
+ const mintPk = new PublicKey(opts.mint);
1942
+ const mintInfo = await getMint(conn, mintPk);
1943
+ const fromAta = getAssociatedTokenAddressSync(mintPk, fromPk);
1944
+ const toAta = getAssociatedTokenAddressSync(mintPk, toPk);
1945
+ const destInfo = await conn.getAccountInfo(toAta).catch(() => null);
1946
+ const { blockhash } = await conn.getLatestBlockhash();
1947
+ const tx = new Transaction({ recentBlockhash: blockhash, feePayer: fromPk });
1948
+ if (!destInfo) {
1949
+ tx.add(
1950
+ createAssociatedTokenAccountInstruction(fromPk, toAta, toPk, mintPk)
1951
+ );
1952
+ }
1953
+ tx.add(
1954
+ createTransferCheckedInstruction(
1955
+ fromAta,
1956
+ mintPk,
1957
+ toAta,
1958
+ fromPk,
1959
+ BigInt(opts.amount),
1960
+ mintInfo.decimals,
1961
+ [],
1962
+ TOKEN_PROGRAM_ID
1963
+ )
1964
+ );
1965
+ return Buffer.from(
1966
+ tx.serialize({ requireAllSignatures: false, verifySignatures: false })
1967
+ ).toString("base64");
1968
+ }
1969
+
1970
+ // src/provider.tsx
1971
+ var IAMGameWalletProvider = SolvenProvider;
1972
+
1973
+ export { SolvenClient as IAMGameWalletClient, IAMGameWalletProvider, SolvenAddress as WalletAddress, SolvenBalance as WalletBalance, SolvenExport as WalletExport, SolvenLogin as WalletLogin, SolvenLoginModal as WalletLoginModal, SolvenSdkError as WalletSdkError, SolvenWithdraw as WalletWithdraw, backpackAdapter, buildSolTransferIn, buildSplTransferIn, getTelegramInitData, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, solflareAdapter, useSolvenWallet as useWallet, useSolvenAuth as useWalletAuth, useSolvenBalance as useWalletBalance, useSolvenExport as useWalletExport, useSolvenSign as useWalletSign, useSolvenTransferIn as useWalletTransferIn };