@waaskey/react 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.cjs ADDED
@@ -0,0 +1,733 @@
1
+ 'use strict';
2
+
3
+ var sdk = require('@waaskey/sdk');
4
+ var react = require('react');
5
+
6
+ // src/provider.ts
7
+ var WaaskeyContext = react.createContext(null);
8
+
9
+ // src/theme.ts
10
+ var lightTheme = {
11
+ accent: "#6366f1",
12
+ accentForeground: "#ffffff",
13
+ background: "#ffffff",
14
+ foreground: "#0a0d14",
15
+ muted: "#6b7280",
16
+ border: "#e5e7eb",
17
+ danger: "#dc2626",
18
+ radius: "12px",
19
+ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
20
+ };
21
+ var darkTheme = {
22
+ accent: "#818cf8",
23
+ accentForeground: "#0a0d14",
24
+ background: "#0f1117",
25
+ foreground: "#f3f4f6",
26
+ muted: "#9ca3af",
27
+ border: "#272b36",
28
+ danger: "#f87171",
29
+ radius: "12px",
30
+ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif"
31
+ };
32
+ var defaultTheme = lightTheme;
33
+ function resolveTheme(override, base = defaultTheme) {
34
+ return override ? { ...base, ...override } : base;
35
+ }
36
+ var ThemeContext = react.createContext(defaultTheme);
37
+ function useTheme() {
38
+ return react.useContext(ThemeContext);
39
+ }
40
+
41
+ // src/provider.ts
42
+ function WaaskeyProvider(props) {
43
+ const source = "client" in props ? props.client : props.options;
44
+ const client = react.useMemo(() => "client" in props ? props.client : new sdk.Waaskey(props.options), [source]);
45
+ const theme = react.useMemo(() => resolveTheme(props.theme), [props.theme]);
46
+ return react.createElement(WaaskeyContext.Provider, { value: client }, react.createElement(ThemeContext.Provider, { value: theme }, props.children));
47
+ }
48
+ var WaasProvider = WaaskeyProvider;
49
+ function useWaaskey() {
50
+ const client = react.useContext(WaaskeyContext);
51
+ if (!client) throw new Error("useWaaskey must be used within a <WaaskeyProvider>.");
52
+ return client;
53
+ }
54
+ function useWaas() {
55
+ const client = useWaaskey();
56
+ const theme = useTheme();
57
+ return { client, theme, auth: client.auth };
58
+ }
59
+ function useCreateWallet() {
60
+ const waaskey = useWaaskey();
61
+ const [state, setState] = react.useState({ loading: false });
62
+ const create = react.useCallback(
63
+ async (params, options) => {
64
+ setState({ loading: true });
65
+ try {
66
+ const wallet = await waaskey.wallets.create(params, options);
67
+ setState({ loading: false, data: wallet });
68
+ return wallet;
69
+ } catch (error) {
70
+ setState({ loading: false, error });
71
+ throw error;
72
+ }
73
+ },
74
+ [waaskey]
75
+ );
76
+ return { create, wallet: state.data, error: state.error, isPending: state.loading };
77
+ }
78
+ function useWallet(id) {
79
+ const waaskey = useWaaskey();
80
+ const [state, setState] = react.useState({ loading: Boolean(id) });
81
+ const refresh = react.useCallback(async () => {
82
+ if (!id) return;
83
+ setState({ loading: true });
84
+ try {
85
+ setState({ loading: false, data: await waaskey.wallets.get(id) });
86
+ } catch (error) {
87
+ setState({ loading: false, error });
88
+ }
89
+ }, [waaskey, id]);
90
+ react.useEffect(() => {
91
+ void refresh();
92
+ }, [refresh]);
93
+ return { ...state, refresh };
94
+ }
95
+ function useBalance(chain, address) {
96
+ const waaskey = useWaaskey();
97
+ const [state, setState] = react.useState({ loading: Boolean(chain && address) });
98
+ const refresh = react.useCallback(async () => {
99
+ if (!chain || !address) return;
100
+ setState({ loading: true });
101
+ try {
102
+ setState({ loading: false, data: await waaskey.balances.getBalance(chain, address) });
103
+ } catch (error) {
104
+ setState({ loading: false, error });
105
+ }
106
+ }, [waaskey, chain, address]);
107
+ react.useEffect(() => {
108
+ void refresh();
109
+ }, [refresh]);
110
+ return { ...state, refresh };
111
+ }
112
+ function useBalances(chains, address) {
113
+ const waaskey = useWaaskey();
114
+ const [state, setState] = react.useState({ loading: Boolean(address && chains.length) });
115
+ const key = chains.join(",");
116
+ const refresh = react.useCallback(async () => {
117
+ if (!address || !key) return;
118
+ setState({ loading: true });
119
+ try {
120
+ const data = await Promise.all(key.split(",").map((chain) => waaskey.balances.getBalance(chain, address)));
121
+ setState({ loading: false, data });
122
+ } catch (error) {
123
+ setState({ loading: false, error });
124
+ }
125
+ }, [waaskey, key, address]);
126
+ react.useEffect(() => {
127
+ void refresh();
128
+ }, [refresh]);
129
+ return { ...state, refresh };
130
+ }
131
+ function useSend(walletId) {
132
+ const waaskey = useWaaskey();
133
+ const [status, setStatus] = react.useState("idle");
134
+ const [result, setResult] = react.useState();
135
+ const [error, setError] = react.useState();
136
+ const send = react.useCallback(
137
+ async (params) => {
138
+ if (!walletId) throw new Error("useSend: no wallet id.");
139
+ setStatus("pending");
140
+ setError(void 0);
141
+ try {
142
+ const wallet = await waaskey.wallets.get(walletId);
143
+ const res = await wallet.send(params);
144
+ setResult(res);
145
+ setStatus("sent");
146
+ return res;
147
+ } catch (e) {
148
+ setError(e);
149
+ setStatus("error");
150
+ throw e;
151
+ }
152
+ },
153
+ [waaskey, walletId]
154
+ );
155
+ const reset = react.useCallback(() => {
156
+ setStatus("idle");
157
+ setResult(void 0);
158
+ setError(void 0);
159
+ }, []);
160
+ return { send, status, result, error, reset };
161
+ }
162
+ function useSignatures(walletId) {
163
+ const waaskey = useWaaskey();
164
+ const [state, setState] = react.useState({ loading: Boolean(walletId) });
165
+ const refresh = react.useCallback(async () => {
166
+ if (!walletId) return;
167
+ setState({ loading: true });
168
+ try {
169
+ const wallet = await waaskey.wallets.get(walletId);
170
+ const page = await wallet.signatures();
171
+ setState({ loading: false, data: page.items });
172
+ } catch (error) {
173
+ setState({ loading: false, error });
174
+ }
175
+ }, [waaskey, walletId]);
176
+ react.useEffect(() => {
177
+ void refresh();
178
+ }, [refresh]);
179
+ return { ...state, refresh };
180
+ }
181
+ function useLogin() {
182
+ const waaskey = useWaaskey();
183
+ const [step, setStep] = react.useState("email");
184
+ const [email, setEmail] = react.useState("");
185
+ const [session, setSession] = react.useState();
186
+ const [loading, setLoading] = react.useState(false);
187
+ const [error, setError] = react.useState();
188
+ const start = react.useCallback(
189
+ async (e) => {
190
+ setLoading(true);
191
+ setError(void 0);
192
+ try {
193
+ await waaskey.auth.email.start(e);
194
+ setEmail(e);
195
+ setStep("code");
196
+ } catch (err) {
197
+ setError(err);
198
+ } finally {
199
+ setLoading(false);
200
+ }
201
+ },
202
+ [waaskey]
203
+ );
204
+ const verify = react.useCallback(
205
+ async (code) => {
206
+ setLoading(true);
207
+ setError(void 0);
208
+ try {
209
+ const s = await waaskey.auth.email.verify(email, code);
210
+ setSession(s);
211
+ setStep("done");
212
+ } catch (err) {
213
+ setError(err);
214
+ } finally {
215
+ setLoading(false);
216
+ }
217
+ },
218
+ [waaskey, email]
219
+ );
220
+ const reset = react.useCallback(() => {
221
+ setStep("email");
222
+ setError(void 0);
223
+ }, []);
224
+ return { step, email, session, loading, error, start, verify, reset };
225
+ }
226
+ function useQrCode(text) {
227
+ const [dataUrl, setDataUrl] = react.useState();
228
+ react.useEffect(() => {
229
+ let cancelled = false;
230
+ setDataUrl(void 0);
231
+ if (!text) return;
232
+ void (async () => {
233
+ try {
234
+ const qr = await import('qrcode');
235
+ const url = await qr.toDataURL(text, { margin: 1, width: 192 });
236
+ if (!cancelled) setDataUrl(url);
237
+ } catch {
238
+ }
239
+ })();
240
+ return () => {
241
+ cancelled = true;
242
+ };
243
+ }, [text]);
244
+ return dataUrl;
245
+ }
246
+ function ConnectModal({ open, onClose, onConnect, title = "Sign in", theme }) {
247
+ const base = useTheme();
248
+ const t = { ...base, ...theme };
249
+ const { step, email, session, loading, error, start, verify, reset } = useLogin();
250
+ const [emailInput, setEmailInput] = react.useState("");
251
+ const [codeInput, setCodeInput] = react.useState("");
252
+ const firstFieldRef = react.useRef(null);
253
+ react.useEffect(() => {
254
+ if (step === "done" && session) onConnect?.(session);
255
+ }, [step, session, onConnect]);
256
+ react.useEffect(() => {
257
+ if (!open) return;
258
+ const onKey = (e) => {
259
+ if (e.key === "Escape") onClose();
260
+ };
261
+ document.addEventListener("keydown", onKey);
262
+ firstFieldRef.current?.focus();
263
+ return () => document.removeEventListener("keydown", onKey);
264
+ }, [open, step, onClose]);
265
+ if (!open) return null;
266
+ const overlay = { position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1e3 };
267
+ const card = {
268
+ background: t.background,
269
+ color: t.foreground,
270
+ borderRadius: t.radius,
271
+ padding: 24,
272
+ width: 360,
273
+ maxWidth: "90vw",
274
+ boxShadow: "0 10px 40px rgba(0,0,0,0.2)"
275
+ };
276
+ const input = { width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1px solid ${t.border}`, borderRadius: "8px", fontSize: 14, marginTop: 8 };
277
+ const button = {
278
+ width: "100%",
279
+ padding: "10px 12px",
280
+ background: t.accent,
281
+ color: t.accentForeground,
282
+ border: "none",
283
+ borderRadius: "8px",
284
+ fontSize: 14,
285
+ fontWeight: 600,
286
+ marginTop: 12,
287
+ cursor: loading ? "default" : "pointer",
288
+ opacity: loading ? 0.6 : 1
289
+ };
290
+ const link = { background: "none", border: "none", color: t.muted, fontSize: 13, marginTop: 12, cursor: "pointer" };
291
+ const errText = { color: t.danger, fontSize: 13, marginTop: 8 };
292
+ const onSubmitEmail = (e) => {
293
+ e.preventDefault();
294
+ if (emailInput.trim()) void start(emailInput.trim());
295
+ };
296
+ const onSubmitCode = (e) => {
297
+ e.preventDefault();
298
+ if (codeInput.trim()) void verify(codeInput.trim());
299
+ };
300
+ const close = () => {
301
+ onClose();
302
+ };
303
+ let body;
304
+ if (step === "email") {
305
+ body = react.createElement(
306
+ "form",
307
+ { onSubmit: onSubmitEmail, key: "email" },
308
+ react.createElement("label", { style: { fontSize: 13, color: t.muted }, htmlFor: "waaskey-email" }, "Email"),
309
+ react.createElement("input", {
310
+ id: "waaskey-email",
311
+ ref: firstFieldRef,
312
+ style: input,
313
+ type: "email",
314
+ placeholder: "you@example.com",
315
+ value: emailInput,
316
+ onChange: (e) => setEmailInput(e.target.value),
317
+ disabled: loading
318
+ }),
319
+ error ? react.createElement("div", { style: errText, role: "alert" }, error.message) : null,
320
+ react.createElement("button", { style: button, type: "submit", disabled: loading }, loading ? "Sending\u2026" : "Send code")
321
+ );
322
+ } else if (step === "code") {
323
+ body = react.createElement(
324
+ "form",
325
+ { onSubmit: onSubmitCode, key: "code" },
326
+ react.createElement("div", { style: { fontSize: 13, color: t.muted } }, `Enter the code sent to ${email}`),
327
+ react.createElement("input", {
328
+ id: "waaskey-code",
329
+ ref: firstFieldRef,
330
+ style: input,
331
+ inputMode: "numeric",
332
+ placeholder: "123456",
333
+ value: codeInput,
334
+ onChange: (e) => setCodeInput(e.target.value),
335
+ disabled: loading
336
+ }),
337
+ error ? react.createElement("div", { style: errText, role: "alert" }, error.message) : null,
338
+ react.createElement("button", { style: button, type: "submit", disabled: loading }, loading ? "Verifying\u2026" : "Verify"),
339
+ react.createElement(
340
+ "button",
341
+ {
342
+ style: link,
343
+ type: "button",
344
+ onClick: () => {
345
+ setCodeInput("");
346
+ reset();
347
+ }
348
+ },
349
+ "\u2190 Use a different email"
350
+ )
351
+ );
352
+ } else {
353
+ body = react.createElement(
354
+ "div",
355
+ { key: "done", style: { textAlign: "center", padding: "12px 0" } },
356
+ react.createElement("div", { style: { fontSize: 15, fontWeight: 600 } }, "Connected"),
357
+ react.createElement("button", { style: button, type: "button", onClick: close }, "Continue")
358
+ );
359
+ }
360
+ return react.createElement(
361
+ "div",
362
+ { style: overlay, onClick: close },
363
+ react.createElement(
364
+ "div",
365
+ { style: card, role: "dialog", "aria-modal": true, "aria-label": title, onClick: (e) => e.stopPropagation() },
366
+ react.createElement(
367
+ "div",
368
+ { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8 } },
369
+ react.createElement("strong", { style: { fontSize: 16 } }, title),
370
+ react.createElement("button", { style: { ...link, marginTop: 0 }, type: "button", "aria-label": "Close", onClick: close }, "\u2715")
371
+ ),
372
+ body
373
+ )
374
+ );
375
+ }
376
+ function WalletWidget({ walletId, chains, sendChainId = "evm:1", sendDecimals = 18, onSent, theme }) {
377
+ const base = useTheme();
378
+ const t = { ...base, ...theme };
379
+ const [tab, setTab] = react.useState("assets");
380
+ const wallet = useWallet(walletId);
381
+ const address = wallet.data?.address;
382
+ const s = react.useMemo(() => styles(t), [t]);
383
+ const tabs = [
384
+ ["assets", "Assets"],
385
+ ["receive", "Receive"],
386
+ ["send", "Send"],
387
+ ["activity", "Activity"]
388
+ ];
389
+ return react.createElement(
390
+ "div",
391
+ { style: s.card, "data-testid": "waaskey-wallet-widget" },
392
+ react.createElement(
393
+ "div",
394
+ { style: s.tabbar },
395
+ ...tabs.map(([id, label]) => react.createElement("button", { key: id, type: "button", onClick: () => setTab(id), style: { ...s.tab, ...tab === id ? s.tabActive : {} } }, label))
396
+ ),
397
+ react.createElement(
398
+ "div",
399
+ { style: s.body },
400
+ tab === "assets" && react.createElement(AssetsTab, { chains, address, loading: wallet.loading, s }),
401
+ tab === "receive" && react.createElement(ReceiveTab, { address, s }),
402
+ tab === "send" && react.createElement(SendTab, { walletId, sendChainId, sendDecimals, onSent, s }),
403
+ tab === "activity" && react.createElement(ActivityTab, { walletId, s })
404
+ )
405
+ );
406
+ }
407
+ function AssetsTab({ chains, address, loading, s }) {
408
+ const { data, error, loading: balLoading } = useBalances(chains, address);
409
+ if (loading || balLoading) return react.createElement("div", { style: s.muted }, "Loading balances\u2026");
410
+ if (error) return react.createElement("div", { style: s.error, role: "alert" }, error.message);
411
+ if (!data || data.length === 0) return react.createElement("div", { style: s.empty }, "No assets yet.");
412
+ return react.createElement(
413
+ "ul",
414
+ { style: s.list },
415
+ ...data.map(
416
+ (b, i) => react.createElement("li", { key: chains[i] ?? i, style: s.row }, react.createElement("span", { style: { fontWeight: 600 } }, chains[i] ?? ""), react.createElement("span", {}, `${b.formatted} ${b.symbol ?? ""}`.trim()))
417
+ )
418
+ );
419
+ }
420
+ function ReceiveTab({ address, s }) {
421
+ const qr = useQrCode(address);
422
+ if (!address) return react.createElement("div", { style: s.muted }, "Address not available yet.");
423
+ return react.createElement(
424
+ "div",
425
+ { style: { textAlign: "center" } },
426
+ qr ? react.createElement("img", { src: qr, alt: "Wallet address QR", width: 192, height: 192, style: { borderRadius: 8 } }) : null,
427
+ react.createElement("div", { style: { ...s.muted, marginTop: 12, wordBreak: "break-all", fontFamily: "monospace" } }, address),
428
+ react.createElement("button", { type: "button", style: s.secondaryButton, onClick: () => void navigator.clipboard?.writeText(address) }, "Copy address")
429
+ );
430
+ }
431
+ function SendTab({
432
+ walletId,
433
+ sendChainId,
434
+ sendDecimals,
435
+ onSent,
436
+ s
437
+ }) {
438
+ const { send, status, result, error, reset } = useSend(walletId);
439
+ const [to, setTo] = react.useState("");
440
+ const [amount, setAmount] = react.useState("");
441
+ const onSubmit = (e) => {
442
+ e.preventDefault();
443
+ if (!to.trim() || !amount.trim()) return;
444
+ void send({ chainId: sendChainId, to: to.trim(), value: parseUnits(amount.trim(), sendDecimals) }).then((r) => onSent?.(r));
445
+ };
446
+ if (status === "sent" && result) {
447
+ return react.createElement(
448
+ "div",
449
+ { style: { textAlign: "center" } },
450
+ react.createElement("div", { style: { fontWeight: 600, marginBottom: 8 } }, "Signed \u2713"),
451
+ react.createElement("div", { style: { ...s.muted, wordBreak: "break-all", fontFamily: "monospace" } }, result.txHash),
452
+ react.createElement("button", { type: "button", style: s.secondaryButton, onClick: reset }, "Send another")
453
+ );
454
+ }
455
+ const pending = status === "pending";
456
+ return react.createElement(
457
+ "form",
458
+ { onSubmit },
459
+ react.createElement("label", { style: s.label, htmlFor: "waaskey-send-to" }, "Recipient"),
460
+ react.createElement("input", { id: "waaskey-send-to", style: s.input, placeholder: "0x\u2026", value: to, onChange: (e) => setTo(e.target.value), disabled: pending }),
461
+ react.createElement("label", { style: { ...s.label, marginTop: 12 }, htmlFor: "waaskey-send-amount" }, "Amount"),
462
+ react.createElement("input", {
463
+ id: "waaskey-send-amount",
464
+ style: s.input,
465
+ inputMode: "decimal",
466
+ placeholder: "0.0",
467
+ value: amount,
468
+ onChange: (e) => setAmount(e.target.value),
469
+ disabled: pending
470
+ }),
471
+ status === "error" && error ? react.createElement("div", { style: { ...s.error, marginTop: 8 }, role: "alert" }, error.message) : null,
472
+ react.createElement("button", { type: "submit", style: s.button, disabled: pending }, pending ? "Sending\u2026" : "Send")
473
+ );
474
+ }
475
+ function ActivityTab({ walletId, s }) {
476
+ const { data, error, loading } = useSignatures(walletId);
477
+ if (loading) return react.createElement("div", { style: s.muted }, "Loading activity\u2026");
478
+ if (error) return react.createElement("div", { style: s.error, role: "alert" }, error.message);
479
+ if (!data || data.length === 0) return react.createElement("div", { style: s.empty }, "No signing activity yet.");
480
+ return react.createElement(
481
+ "ul",
482
+ { style: s.list },
483
+ ...data.map(
484
+ (sig) => react.createElement(
485
+ "li",
486
+ { key: sig.id, style: s.row },
487
+ react.createElement("span", { style: { fontFamily: "monospace" } }, shorten(sig.txHash ?? sig.to ?? sig.digest ?? sig.kind)),
488
+ react.createElement("span", { style: { ...s.badge, ...statusStyle(sig.status) } }, sig.status ?? sig.kind)
489
+ )
490
+ )
491
+ );
492
+ }
493
+ function parseUnits(value, decimals) {
494
+ const [whole, frac = ""] = value.split(".");
495
+ const fracPadded = (frac + "0".repeat(decimals)).slice(0, decimals);
496
+ const combined = `${whole}${fracPadded}`.replace(/^0+(?=\d)/, "");
497
+ return combined === "" ? "0" : combined;
498
+ }
499
+ function shorten(v) {
500
+ return v.length > 14 ? `${v.slice(0, 8)}\u2026${v.slice(-4)}` : v;
501
+ }
502
+ function statusStyle(status) {
503
+ if (status === "signed") return { background: "#dcfce7", color: "#166534" };
504
+ if (status === "failed") return { background: "#fee2e2", color: "#991b1b" };
505
+ return { background: "#fef9c3", color: "#854d0e" };
506
+ }
507
+ function styles(t) {
508
+ return {
509
+ card: {
510
+ background: t.background,
511
+ color: t.foreground,
512
+ border: `1px solid ${t.border}`,
513
+ borderRadius: t.radius,
514
+ width: 340,
515
+ maxWidth: "90vw",
516
+ overflow: "hidden",
517
+ fontSize: 14,
518
+ fontFamily: t.fontFamily
519
+ },
520
+ tabbar: { display: "flex", borderBottom: `1px solid ${t.border}` },
521
+ tab: { flex: 1, padding: "10px 8px", background: "none", border: "none", color: t.muted, cursor: "pointer", fontSize: 13 },
522
+ tabActive: { color: t.accent, fontWeight: 600, boxShadow: `inset 0 -2px 0 ${t.accent}` },
523
+ body: { padding: 16, minHeight: 160 },
524
+ list: { listStyle: "none", margin: 0, padding: 0 },
525
+ row: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", borderBottom: `1px solid ${t.border}` },
526
+ label: { display: "block", fontSize: 13, color: t.muted },
527
+ input: { width: "100%", boxSizing: "border-box", padding: "10px 12px", border: `1px solid ${t.border}`, borderRadius: 8, fontSize: 14, marginTop: 6 },
528
+ button: {
529
+ width: "100%",
530
+ padding: "10px 12px",
531
+ background: t.accent,
532
+ color: t.accentForeground,
533
+ border: "none",
534
+ borderRadius: 8,
535
+ fontSize: 14,
536
+ fontWeight: 600,
537
+ marginTop: 16,
538
+ cursor: "pointer"
539
+ },
540
+ secondaryButton: { padding: "8px 14px", background: "none", color: t.accent, border: `1px solid ${t.accent}`, borderRadius: 8, fontSize: 13, marginTop: 12, cursor: "pointer" },
541
+ muted: { color: t.muted },
542
+ empty: { color: t.muted, textAlign: "center", padding: "24px 0" },
543
+ error: { color: t.danger, fontSize: 13 },
544
+ badge: { fontSize: 12, padding: "2px 8px", borderRadius: 999 }
545
+ };
546
+ }
547
+ function useSignPrompt() {
548
+ const waaskey = useWaaskey();
549
+ const [req, setReq] = react.useState();
550
+ const [status, setStatus] = react.useState("idle");
551
+ const [error, setError] = react.useState();
552
+ const deferred = react.useRef(void 0);
553
+ const requestSignature = react.useCallback((next) => {
554
+ setReq(next);
555
+ setStatus("idle");
556
+ setError(void 0);
557
+ return new Promise((resolve, reject2) => {
558
+ deferred.current = { resolve, reject: reject2 };
559
+ });
560
+ }, []);
561
+ const close = react.useCallback(() => {
562
+ setReq(void 0);
563
+ setStatus("idle");
564
+ setError(void 0);
565
+ deferred.current = void 0;
566
+ }, []);
567
+ const approve = react.useCallback(async () => {
568
+ if (!req || !deferred.current) return;
569
+ setStatus("pending");
570
+ setError(void 0);
571
+ try {
572
+ const wallet = await waaskey.wallets.get(req.walletId);
573
+ const outcome = req.kind === "sign" ? await wallet.sign(req.digest) : await wallet.send(req.tx);
574
+ deferred.current.resolve(outcome);
575
+ close();
576
+ } catch (e) {
577
+ setError(e);
578
+ setStatus("error");
579
+ }
580
+ }, [req, waaskey, close]);
581
+ const reject = react.useCallback(() => {
582
+ deferred.current?.reject(new Error("Signature request rejected by user"));
583
+ close();
584
+ }, [close]);
585
+ const prompt = req ? react.createElement(SignPrompt, { request: req, status, error, onApprove: () => void approve(), onReject: reject }) : null;
586
+ return { requestSignature, prompt };
587
+ }
588
+ function SignPrompt({ request, status, error, onApprove, onReject }) {
589
+ const t = useTheme();
590
+ const pending = status === "pending";
591
+ const overlay = { position: "fixed", inset: 0, background: "rgba(0,0,0,0.4)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1e3 };
592
+ const card = {
593
+ background: t.background,
594
+ color: t.foreground,
595
+ borderRadius: t.radius,
596
+ padding: 24,
597
+ width: 360,
598
+ maxWidth: "90vw",
599
+ fontFamily: t.fontFamily,
600
+ boxShadow: "0 10px 40px rgba(0,0,0,0.2)"
601
+ };
602
+ const rowStyle = { display: "flex", justifyContent: "space-between", gap: 12, padding: "6px 0", borderBottom: `1px solid ${t.border}`, fontSize: 14 };
603
+ const key = { color: t.muted };
604
+ const val = { fontFamily: "monospace", wordBreak: "break-all", textAlign: "right" };
605
+ const primary = {
606
+ flex: 1,
607
+ padding: "10px 12px",
608
+ background: t.accent,
609
+ color: t.accentForeground,
610
+ border: "none",
611
+ borderRadius: 8,
612
+ fontSize: 14,
613
+ fontWeight: 600,
614
+ cursor: "pointer"
615
+ };
616
+ const secondary = {
617
+ flex: 1,
618
+ padding: "10px 12px",
619
+ background: "none",
620
+ color: t.muted,
621
+ border: `1px solid ${t.border}`,
622
+ borderRadius: 8,
623
+ fontSize: 14,
624
+ cursor: "pointer"
625
+ };
626
+ const rows = request.kind === "send" ? [
627
+ ["Network", request.tx.chainId],
628
+ ["To", request.tx.to],
629
+ ["Amount", request.tx.value ?? "0"]
630
+ ] : [["Message digest", request.digest]];
631
+ return react.createElement(
632
+ "div",
633
+ { style: overlay, role: "dialog", "aria-modal": true, "aria-label": request.title ?? "Confirm" },
634
+ react.createElement(
635
+ "div",
636
+ { style: card },
637
+ react.createElement("strong", { style: { fontSize: 16 } }, request.title ?? (request.kind === "send" ? "Confirm transaction" : "Confirm signature")),
638
+ react.createElement("div", { style: { marginTop: 12 } }, ...rows.map(([k, v]) => react.createElement("div", { key: k, style: rowStyle }, react.createElement("span", { style: key }, k), react.createElement("span", { style: val }, v)))),
639
+ error ? react.createElement("div", { style: { color: t.danger, fontSize: 13, marginTop: 12 }, role: "alert" }, error.message) : null,
640
+ react.createElement(
641
+ "div",
642
+ { style: { display: "flex", gap: 12, marginTop: 20 } },
643
+ react.createElement("button", { type: "button", style: secondary, onClick: onReject, disabled: pending }, "Reject"),
644
+ react.createElement("button", { type: "button", style: primary, onClick: onApprove, disabled: pending }, pending ? "Confirming\u2026" : status === "error" ? "Retry" : "Approve")
645
+ )
646
+ )
647
+ );
648
+ }
649
+ function FundWidget({ walletAddress, chainId, cryptoCurrency = "ETH", fiatCurrency, fiatAmount, label = "Add funds", onComplete }) {
650
+ const waaskey = useWaaskey();
651
+ const t = useTheme();
652
+ const [status, setStatus] = react.useState("idle");
653
+ const [error, setError] = react.useState();
654
+ const [provider, setProvider] = react.useState();
655
+ const launch = react.useCallback(async () => {
656
+ setStatus("loading");
657
+ setError(void 0);
658
+ try {
659
+ const { url, provider: p } = await waaskey.onramp.widgetUrl({ walletAddress, cryptoCurrency, chainId, fiatCurrency, fiatAmount });
660
+ setProvider(p);
661
+ window.open(url, "_blank", "noopener,noreferrer");
662
+ setStatus("open");
663
+ } catch (e) {
664
+ setError(e);
665
+ setStatus("error");
666
+ }
667
+ }, [waaskey, walletAddress, cryptoCurrency, chainId, fiatCurrency, fiatAmount]);
668
+ const done = react.useCallback(() => {
669
+ setStatus("idle");
670
+ onComplete?.();
671
+ }, [onComplete]);
672
+ const button = {
673
+ width: "100%",
674
+ padding: "10px 12px",
675
+ background: t.accent,
676
+ color: t.accentForeground,
677
+ border: "none",
678
+ borderRadius: 8,
679
+ fontSize: 14,
680
+ fontWeight: 600,
681
+ cursor: status === "loading" ? "default" : "pointer",
682
+ opacity: status === "loading" ? 0.6 : 1,
683
+ fontFamily: t.fontFamily
684
+ };
685
+ const secondary = { ...button, background: "none", color: t.accent, border: `1px solid ${t.accent}`, marginTop: 10 };
686
+ if (status === "open") {
687
+ return react.createElement(
688
+ "div",
689
+ { style: { fontFamily: t.fontFamily, color: t.foreground, textAlign: "center" } },
690
+ react.createElement("div", { style: { fontSize: 14 } }, `Complete your purchase in the ${provider ?? "provider"} window.`),
691
+ react.createElement("button", { type: "button", style: secondary, onClick: done }, "I've finished")
692
+ );
693
+ }
694
+ return react.createElement(
695
+ "div",
696
+ { style: { fontFamily: t.fontFamily } },
697
+ react.createElement("button", { type: "button", style: button, onClick: () => void launch(), disabled: status === "loading" }, status === "loading" ? "Opening\u2026" : label),
698
+ status === "error" && error ? react.createElement("div", { style: { color: t.danger, fontSize: 13, marginTop: 8 }, role: "alert" }, error.message) : null
699
+ );
700
+ }
701
+
702
+ exports.ConnectModal = ConnectModal;
703
+ exports.FundWidget = FundWidget;
704
+ exports.SignPrompt = SignPrompt;
705
+ exports.ThemeContext = ThemeContext;
706
+ exports.WaasProvider = WaasProvider;
707
+ exports.WaaskeyContext = WaaskeyContext;
708
+ exports.WaaskeyProvider = WaaskeyProvider;
709
+ exports.WalletWidget = WalletWidget;
710
+ exports.darkTheme = darkTheme;
711
+ exports.defaultTheme = defaultTheme;
712
+ exports.lightTheme = lightTheme;
713
+ exports.resolveTheme = resolveTheme;
714
+ exports.useBalance = useBalance;
715
+ exports.useBalances = useBalances;
716
+ exports.useCreateWallet = useCreateWallet;
717
+ exports.useLogin = useLogin;
718
+ exports.useQrCode = useQrCode;
719
+ exports.useSend = useSend;
720
+ exports.useSignPrompt = useSignPrompt;
721
+ exports.useSignatures = useSignatures;
722
+ exports.useTheme = useTheme;
723
+ exports.useWaas = useWaas;
724
+ exports.useWaaskey = useWaaskey;
725
+ exports.useWallet = useWallet;
726
+ Object.keys(sdk).forEach(function (k) {
727
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
728
+ enumerable: true,
729
+ get: function () { return sdk[k]; }
730
+ });
731
+ });
732
+ //# sourceMappingURL=index.cjs.map
733
+ //# sourceMappingURL=index.cjs.map