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