@iamgame/wallet-sdk 0.1.3 → 0.1.5

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 CHANGED
@@ -1,9 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var react = require('react');
3
+ var React = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var reactDom = require('react-dom');
6
6
 
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+
7
11
  // ../../solven/sdk-client/src/errors.ts
8
12
  var SolvenSdkError = class _SolvenSdkError extends Error {
9
13
  constructor(args) {
@@ -249,6 +253,9 @@ function getTelegram() {
249
253
  if (typeof window === "undefined") return void 0;
250
254
  return window.Telegram;
251
255
  }
256
+ function getTelegramWebApp() {
257
+ return getTelegram()?.WebApp ?? null;
258
+ }
252
259
  function getTelegramInitData() {
253
260
  const data = getTelegram()?.WebApp?.initData;
254
261
  if (!data || typeof data !== "string" || data.length === 0) return null;
@@ -265,22 +272,48 @@ function notifyTelegramReady() {
265
272
  } catch {
266
273
  }
267
274
  }
268
- var SolvenContext = react.createContext(null);
275
+ var SolvenContext = React.createContext(null);
269
276
  function useSolvenContext() {
270
- const ctx = react.useContext(SolvenContext);
277
+ const ctx = React.useContext(SolvenContext);
271
278
  if (!ctx) throw new Error("useSolvenContext must be inside a <SolvenProvider />");
272
279
  return ctx;
273
280
  }
274
- function SolvenProvider({ children, autoTelegram = true, ...opts }) {
275
- const client = react.useMemo(() => new SolvenClient(opts), [
281
+ function useSolvenReportError() {
282
+ return useSolvenContext().reportError;
283
+ }
284
+ var SolvenErrorBoundary = class extends React__default.default.Component {
285
+ constructor() {
286
+ super(...arguments);
287
+ this.state = { hasError: false };
288
+ }
289
+ static getDerivedStateFromError() {
290
+ return { hasError: true };
291
+ }
292
+ componentDidCatch(error) {
293
+ this.props.onError(error, { operation: "render" });
294
+ }
295
+ render() {
296
+ return this.state.hasError ? null : this.props.children;
297
+ }
298
+ };
299
+ function SolvenProvider({ children, autoTelegram = true, onError, ...opts }) {
300
+ const client = React.useMemo(() => new SolvenClient(opts), [
276
301
  opts.publishableKey,
277
302
  opts.baseUrl,
278
303
  opts.storage,
279
304
  opts.fetchImpl
280
305
  ]);
281
- const [session, setSession] = react.useState(client.getSession());
282
- const [status, setStatus] = react.useState("loading");
283
- react.useEffect(() => {
306
+ const [session, setSession] = React.useState(client.getSession());
307
+ const [status, setStatus] = React.useState("loading");
308
+ const onErrorRef = React.useRef(onError);
309
+ onErrorRef.current = onError;
310
+ const reportError = React.useCallback((error, context) => {
311
+ try {
312
+ onErrorRef.current?.(error, context);
313
+ } catch {
314
+ }
315
+ }, []);
316
+ React.useEffect(() => {
284
317
  let cancelled = false;
285
318
  (async () => {
286
319
  const stored = client.getSession();
@@ -312,8 +345,8 @@ function SolvenProvider({ children, autoTelegram = true, ...opts }) {
312
345
  cancelled = true;
313
346
  };
314
347
  }, [client]);
315
- const triedTelegram = react.useRef(false);
316
- react.useEffect(() => {
348
+ const triedTelegram = React.useRef(false);
349
+ React.useEffect(() => {
317
350
  if (!autoTelegram || status !== "anonymous" || triedTelegram.current) return;
318
351
  triedTelegram.current = true;
319
352
  let cancelled = false;
@@ -345,15 +378,16 @@ function SolvenProvider({ children, autoTelegram = true, ...opts }) {
345
378
  setSession: (s) => {
346
379
  setSession(s);
347
380
  setStatus(s ? "authenticated" : "anonymous");
348
- }
381
+ },
382
+ reportError
349
383
  };
350
- return /* @__PURE__ */ jsxRuntime.jsx(SolvenContext.Provider, { value, children });
384
+ return /* @__PURE__ */ jsxRuntime.jsx(SolvenContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(SolvenErrorBoundary, { onError: reportError, children }) });
351
385
  }
352
386
 
353
387
  // ../../solven/sdk-client/src/hooks.ts
354
388
  function useSolvenAuth() {
355
389
  const { client, session, status, setSession } = useSolvenContext();
356
- const connectExternal = react.useCallback(
390
+ const connectExternal = React.useCallback(
357
391
  async (adapter) => {
358
392
  const { publicKey } = await adapter.connect();
359
393
  const challenge = await client.requestSiwsChallenge(publicKey);
@@ -364,27 +398,27 @@ function useSolvenAuth() {
364
398
  },
365
399
  [client, setSession]
366
400
  );
367
- const connectTelegram = react.useCallback(async () => {
401
+ const connectTelegram = React.useCallback(async () => {
368
402
  const initData = getTelegramInitData();
369
403
  if (!initData) throw new Error("Not running inside a Telegram Mini App.");
370
404
  const session2 = await client.verifyTelegram({ initData });
371
405
  setSession(session2);
372
406
  }, [client, setSession]);
373
- const requestEmailOtp = react.useCallback(
407
+ const requestEmailOtp = React.useCallback(
374
408
  async (email) => {
375
409
  const { expiresAt } = await client.initiateEmail(email);
376
410
  return { expiresAt };
377
411
  },
378
412
  [client]
379
413
  );
380
- const connectEmail = react.useCallback(
414
+ const connectEmail = React.useCallback(
381
415
  async (email, code) => {
382
416
  const session2 = await client.verifyEmail(email, code);
383
417
  setSession(session2);
384
418
  },
385
419
  [client, setSession]
386
420
  );
387
- const logout = react.useCallback(async () => {
421
+ const logout = React.useCallback(async () => {
388
422
  await client.logout();
389
423
  setSession(null);
390
424
  }, [client, setSession]);
@@ -400,25 +434,29 @@ function useSolvenAuth() {
400
434
  };
401
435
  }
402
436
  function useSolvenWallet() {
403
- const { client, status } = useSolvenContext();
404
- const [wallet, setWallet] = react.useState(null);
405
- react.useEffect(() => {
437
+ const { client, status, reportError } = useSolvenContext();
438
+ const [wallet, setWallet] = React.useState(null);
439
+ React.useEffect(() => {
406
440
  if (status !== "authenticated") {
407
441
  setWallet(null);
408
442
  return;
409
443
  }
410
444
  let cancelled = false;
411
- client.getMyWallet().then((w) => !cancelled && setWallet(w)).catch(() => !cancelled && setWallet(null));
445
+ client.getMyWallet().then((w) => !cancelled && setWallet(w)).catch((e) => {
446
+ if (cancelled) return;
447
+ reportError(e, { operation: "wallet:load" });
448
+ setWallet(null);
449
+ });
412
450
  return () => {
413
451
  cancelled = true;
414
452
  };
415
- }, [client, status]);
453
+ }, [client, status, reportError]);
416
454
  return wallet;
417
455
  }
418
456
  function useSolvenBalance(walletId, pollMs = 15e3) {
419
457
  const { client, status } = useSolvenContext();
420
- const [balance, setBalance] = react.useState(null);
421
- react.useEffect(() => {
458
+ const [balance, setBalance] = React.useState(null);
459
+ React.useEffect(() => {
422
460
  if (status !== "authenticated" || !walletId) {
423
461
  setBalance(null);
424
462
  return;
@@ -441,25 +479,32 @@ function useSolvenBalance(walletId, pollMs = 15e3) {
441
479
  return balance;
442
480
  }
443
481
  function useSolvenSign() {
444
- const { client } = useSolvenContext();
445
- const signAction = react.useCallback(
446
- ({ walletId, txBase64, label, idempotencyKey }) => client.signAction({ walletId, txBase64, label }, idempotencyKey),
447
- [client]
482
+ const { client, reportError } = useSolvenContext();
483
+ const signAction = React.useCallback(
484
+ async ({ walletId, txBase64, label, idempotencyKey }) => {
485
+ try {
486
+ return await client.signAction({ walletId, txBase64, label }, idempotencyKey);
487
+ } catch (e) {
488
+ reportError(e, { operation: "sign" });
489
+ throw e;
490
+ }
491
+ },
492
+ [client, reportError]
448
493
  );
449
494
  return { signAction };
450
495
  }
451
496
  function useSolvenExport(walletId) {
452
497
  const { client } = useSolvenContext();
453
- const [preflight, setPreflight] = react.useState(null);
454
- const refreshPreflight = react.useCallback(async () => {
498
+ const [preflight, setPreflight] = React.useState(null);
499
+ const refreshPreflight = React.useCallback(async () => {
455
500
  if (!walletId) return;
456
501
  const p = await client.exportPreflight(walletId);
457
502
  setPreflight(p);
458
503
  }, [client, walletId]);
459
- react.useEffect(() => {
504
+ React.useEffect(() => {
460
505
  if (walletId) void refreshPreflight();
461
506
  }, [walletId, refreshPreflight]);
462
- const exportKey = react.useCallback(
507
+ const exportKey = React.useCallback(
463
508
  async (idempotencyKey) => {
464
509
  if (!walletId) throw new Error("No wallet selected for export");
465
510
  return client.exportWallet(walletId, idempotencyKey);
@@ -472,6 +517,13 @@ function useSolvenTransferIn() {
472
517
  const wallet = useSolvenWallet();
473
518
  return { destinationAddress: wallet?.address ?? null };
474
519
  }
520
+ function useIsTelegram() {
521
+ const [inTelegram, setInTelegram] = React.useState(false);
522
+ React.useEffect(() => {
523
+ setInTelegram(isTelegramMiniApp());
524
+ }, []);
525
+ return inTelegram;
526
+ }
475
527
 
476
528
  // ../../solven/sdk-client/src/wallet-adapter.ts
477
529
  function phantomAdapter() {
@@ -588,20 +640,21 @@ var defaultTheme = {
588
640
  };
589
641
  function SolvenLoginContent(props) {
590
642
  const { connectExternal, connectTelegram, requestEmailOtp, connectEmail, status, user } = useSolvenAuth();
591
- const [busy, setBusy] = react.useState(null);
592
- const [error, setError] = react.useState(null);
593
- const [email, setEmail] = react.useState("");
594
- const [otp, setOtp] = react.useState("");
595
- const [otpSent, setOtpSent] = react.useState(false);
643
+ const reportError = useSolvenReportError();
644
+ const [busy, setBusy] = React.useState(null);
645
+ const [error, setError] = React.useState(null);
646
+ const [email, setEmail] = React.useState("");
647
+ const [otp, setOtp] = React.useState("");
648
+ const [otpSent, setOtpSent] = React.useState(false);
596
649
  const showEmail = props.showEmail ?? true;
597
- const theme = react.useMemo(
650
+ const theme = React.useMemo(
598
651
  () => ({ ...defaultTheme, ...props.theme ?? {} }),
599
652
  [props.theme]
600
653
  );
601
- const inTma = react.useMemo(() => isTelegramMiniApp(), []);
654
+ const inTma = React.useMemo(() => isTelegramMiniApp(), []);
602
655
  const autoTelegram = props.autoTelegram ?? true;
603
- const wallets = react.useMemo(listSupportedWallets, []);
604
- react.useEffect(() => {
656
+ const wallets = React.useMemo(listSupportedWallets, []);
657
+ React.useEffect(() => {
605
658
  notifyTelegramReady();
606
659
  if (!inTma || !autoTelegram) return;
607
660
  if (status === "authenticated") return;
@@ -616,6 +669,7 @@ function SolvenLoginContent(props) {
616
669
  props.onCloseAfterSignIn?.();
617
670
  } catch (e) {
618
671
  if (!cancelled) {
672
+ reportError(e, { operation: "login:telegram" });
619
673
  setError(e instanceof Error ? e.message : "Telegram sign-in failed");
620
674
  }
621
675
  } finally {
@@ -642,6 +696,7 @@ function SolvenLoginContent(props) {
642
696
  props.onSignIn?.();
643
697
  props.onCloseAfterSignIn?.();
644
698
  } catch (e) {
699
+ reportError(e, { operation: "login:wallet" });
645
700
  setError(e instanceof Error ? e.message : "Wallet sign-in failed");
646
701
  } finally {
647
702
  setBusy(null);
@@ -655,6 +710,7 @@ function SolvenLoginContent(props) {
655
710
  props.onSignIn?.();
656
711
  props.onCloseAfterSignIn?.();
657
712
  } catch (e) {
713
+ reportError(e, { operation: "login:telegram" });
658
714
  setError(e instanceof Error ? e.message : "Telegram sign-in failed");
659
715
  } finally {
660
716
  setBusy(null);
@@ -667,6 +723,7 @@ function SolvenLoginContent(props) {
667
723
  await requestEmailOtp(email.trim());
668
724
  setOtpSent(true);
669
725
  } catch (e) {
726
+ reportError(e, { operation: "login:email" });
670
727
  setError(e instanceof Error ? e.message : "Couldn't send the code");
671
728
  } finally {
672
729
  setBusy(null);
@@ -680,6 +737,7 @@ function SolvenLoginContent(props) {
680
737
  props.onSignIn?.();
681
738
  props.onCloseAfterSignIn?.();
682
739
  } catch (e) {
740
+ reportError(e, { operation: "login:email" });
683
741
  setError(e instanceof Error ? e.message : "Couldn't verify the code");
684
742
  } finally {
685
743
  setBusy(null);
@@ -917,7 +975,7 @@ function SolvenLoginContent(props) {
917
975
  ] });
918
976
  }
919
977
  function SolvenLogin(props) {
920
- const theme = react.useMemo(
978
+ const theme = React.useMemo(
921
979
  () => ({ ...defaultTheme, ...props.theme ?? {} }),
922
980
  [props.theme]
923
981
  );
@@ -950,17 +1008,17 @@ function SolvenLoginModal(props) {
950
1008
  closeOnEscape = true,
951
1009
  ...content
952
1010
  } = props;
953
- const theme = react.useMemo(
1011
+ const theme = React.useMemo(
954
1012
  () => ({ ...defaultTheme, ...props.theme ?? {} }),
955
1013
  [props.theme]
956
1014
  );
957
- const dialogRef = react.useRef(null);
958
- const previouslyFocused = react.useRef(null);
959
- const [mounted, setMounted] = react.useState(false);
960
- react.useEffect(() => {
1015
+ const dialogRef = React.useRef(null);
1016
+ const previouslyFocused = React.useRef(null);
1017
+ const [mounted, setMounted] = React.useState(false);
1018
+ React.useEffect(() => {
961
1019
  setMounted(true);
962
1020
  }, []);
963
- react.useEffect(() => {
1021
+ React.useEffect(() => {
964
1022
  if (!isOpen || !closeOnEscape) return;
965
1023
  const onKey = (e) => {
966
1024
  if (e.key === "Escape") {
@@ -971,7 +1029,7 @@ function SolvenLoginModal(props) {
971
1029
  window.addEventListener("keydown", onKey);
972
1030
  return () => window.removeEventListener("keydown", onKey);
973
1031
  }, [isOpen, closeOnEscape, onClose]);
974
- react.useEffect(() => {
1032
+ React.useEffect(() => {
975
1033
  if (!isOpen) return;
976
1034
  previouslyFocused.current = document.activeElement ?? null;
977
1035
  queueMicrotask(() => {
@@ -1007,7 +1065,7 @@ function SolvenLoginModal(props) {
1007
1065
  previouslyFocused.current?.focus?.();
1008
1066
  };
1009
1067
  }, [isOpen]);
1010
- const handleBackdropClick = react.useCallback(
1068
+ const handleBackdropClick = React.useCallback(
1011
1069
  (e) => {
1012
1070
  if (!closeOnBackdrop) return;
1013
1071
  if (e.target === e.currentTarget) onClose();
@@ -1109,12 +1167,12 @@ function truncateAddress(address, chars) {
1109
1167
  }
1110
1168
  function SolvenAddress(props) {
1111
1169
  const wallet = useSolvenWallet();
1112
- const theme = react.useMemo(() => ({ ...defaultTheme2, ...props.theme ?? {} }), [props.theme]);
1170
+ const theme = React.useMemo(() => ({ ...defaultTheme2, ...props.theme ?? {} }), [props.theme]);
1113
1171
  const truncateChars = props.truncateChars ?? 6;
1114
1172
  const showCopy = props.showCopy ?? true;
1115
1173
  const label = props.label ?? "Wallet Address";
1116
- const [copied, setCopied] = react.useState(false);
1117
- const handleCopy = react.useCallback(async () => {
1174
+ const [copied, setCopied] = React.useState(false);
1175
+ const handleCopy = React.useCallback(async () => {
1118
1176
  if (!wallet?.address) return;
1119
1177
  try {
1120
1178
  await navigator.clipboard.writeText(wallet.address);
@@ -1244,18 +1302,18 @@ function getTokenLabel(token) {
1244
1302
  function SolvenBalance(props) {
1245
1303
  const wallet = useSolvenWallet();
1246
1304
  const balance = useSolvenBalance(wallet?.id ?? null, props.pollMs);
1247
- const theme = react.useMemo(() => ({ ...defaultTheme3, ...props.theme ?? {} }), [props.theme]);
1305
+ const theme = React.useMemo(() => ({ ...defaultTheme3, ...props.theme ?? {} }), [props.theme]);
1248
1306
  const showRefresh = props.showRefresh ?? true;
1249
1307
  const label = props.label ?? "Balance";
1250
1308
  const compact = props.compact ?? false;
1251
- const [refreshing, setRefreshing] = react.useState(false);
1252
- const tokens = react.useMemo(() => {
1309
+ const [refreshing, setRefreshing] = React.useState(false);
1310
+ const tokens = React.useMemo(() => {
1253
1311
  if (!balance?.tokens) return [];
1254
1312
  if (!props.filterMints) return balance.tokens;
1255
1313
  const set = new Set(props.filterMints);
1256
1314
  return balance.tokens.filter((t) => set.has(t.mint));
1257
1315
  }, [balance?.tokens, props.filterMints]);
1258
- const handleRefresh = react.useCallback(() => {
1316
+ const handleRefresh = React.useCallback(() => {
1259
1317
  setRefreshing(true);
1260
1318
  setTimeout(() => setRefreshing(false), 1500);
1261
1319
  }, []);
@@ -1455,26 +1513,26 @@ function isValidSolanaAddress(address) {
1455
1513
  function SolvenWithdraw(props) {
1456
1514
  const wallet = useSolvenWallet();
1457
1515
  const balance = useSolvenBalance(wallet?.id ?? null);
1458
- const { client } = useSolvenContext();
1459
- const theme = react.useMemo(() => ({ ...defaultTheme4, ...props.theme ?? {} }), [props.theme]);
1516
+ const { client, reportError } = useSolvenContext();
1517
+ const theme = React.useMemo(() => ({ ...defaultTheme4, ...props.theme ?? {} }), [props.theme]);
1460
1518
  const label = props.label ?? "Withdraw";
1461
- const [toAddress, setToAddress] = react.useState("");
1462
- const [amount, setAmount] = react.useState("");
1463
- const [selectedMint, setSelectedMint] = react.useState(props.mint ?? "SOL");
1464
- const [busy, setBusy] = react.useState(false);
1465
- const [error, setError] = react.useState(null);
1466
- const [successSig, setSuccessSig] = react.useState(null);
1467
- const tokens = react.useMemo(() => {
1519
+ const [toAddress, setToAddress] = React.useState("");
1520
+ const [amount, setAmount] = React.useState("");
1521
+ const [selectedMint, setSelectedMint] = React.useState(props.mint ?? "SOL");
1522
+ const [busy, setBusy] = React.useState(false);
1523
+ const [error, setError] = React.useState(null);
1524
+ const [successSig, setSuccessSig] = React.useState(null);
1525
+ const tokens = React.useMemo(() => {
1468
1526
  if (!balance?.tokens) return [];
1469
1527
  if (props.mint) return balance.tokens.filter((t) => t.mint === props.mint);
1470
1528
  return balance.tokens;
1471
1529
  }, [balance?.tokens, props.mint]);
1472
- const selectedToken = react.useMemo(
1530
+ const selectedToken = React.useMemo(
1473
1531
  () => tokens.find((t) => t.mint === selectedMint) ?? null,
1474
1532
  [tokens, selectedMint]
1475
1533
  );
1476
1534
  const availableFormatted = selectedToken ? formatTokenAmount2(selectedToken) : "0";
1477
- const handleMax = react.useCallback(() => {
1535
+ const handleMax = React.useCallback(() => {
1478
1536
  if (!selectedToken) return;
1479
1537
  if (selectedToken.mint === "SOL") {
1480
1538
  const raw = BigInt(selectedToken.amount);
@@ -1490,7 +1548,7 @@ function SolvenWithdraw(props) {
1490
1548
  setAmount(formatTokenAmount2(selectedToken));
1491
1549
  }
1492
1550
  }, [selectedToken]);
1493
- const handleSubmit = react.useCallback(async () => {
1551
+ const handleSubmit = React.useCallback(async () => {
1494
1552
  setError(null);
1495
1553
  setSuccessSig(null);
1496
1554
  if (!wallet) {
@@ -1533,6 +1591,7 @@ function SolvenWithdraw(props) {
1533
1591
  props.onSuccess?.(result.signature);
1534
1592
  } catch (e) {
1535
1593
  const msg = e instanceof Error ? e.message : "Withdrawal failed";
1594
+ reportError(e, { operation: "withdraw" });
1536
1595
  setError(msg);
1537
1596
  props.onError?.(e instanceof Error ? e : new Error(msg));
1538
1597
  } finally {
@@ -1771,16 +1830,17 @@ function formatLamports(lamports) {
1771
1830
  function SolvenExport(props) {
1772
1831
  const wallet = useSolvenWallet();
1773
1832
  const { preflight, refreshPreflight, exportKey } = useSolvenExport(wallet?.id ?? null);
1774
- const theme = react.useMemo(() => ({ ...defaultTheme5, ...props.theme ?? {} }), [props.theme]);
1833
+ const reportError = useSolvenReportError();
1834
+ const theme = React.useMemo(() => ({ ...defaultTheme5, ...props.theme ?? {} }), [props.theme]);
1775
1835
  const label = props.label ?? "Export Wallet";
1776
- const [step, setStep] = react.useState("preflight");
1777
- const [busy, setBusy] = react.useState(false);
1778
- const [error, setError] = react.useState(null);
1779
- const [exportedKey, setExportedKey] = react.useState(null);
1780
- const [newAddress, setNewAddress] = react.useState(null);
1781
- const [bootstrapSig, setBootstrapSig] = react.useState(null);
1782
- const [keyCopied, setKeyCopied] = react.useState(false);
1783
- const handleExport = react.useCallback(async () => {
1836
+ const [step, setStep] = React.useState("preflight");
1837
+ const [busy, setBusy] = React.useState(false);
1838
+ const [error, setError] = React.useState(null);
1839
+ const [exportedKey, setExportedKey] = React.useState(null);
1840
+ const [newAddress, setNewAddress] = React.useState(null);
1841
+ const [bootstrapSig, setBootstrapSig] = React.useState(null);
1842
+ const [keyCopied, setKeyCopied] = React.useState(false);
1843
+ const handleExport = React.useCallback(async () => {
1784
1844
  setError(null);
1785
1845
  setBusy(true);
1786
1846
  try {
@@ -1792,13 +1852,14 @@ function SolvenExport(props) {
1792
1852
  props.onSuccess?.(result.newWalletAddress);
1793
1853
  } catch (e) {
1794
1854
  const msg = e instanceof Error ? e.message : "Export failed";
1855
+ reportError(e, { operation: "export" });
1795
1856
  setError(msg);
1796
1857
  props.onError?.(e instanceof Error ? e : new Error(msg));
1797
1858
  } finally {
1798
1859
  setBusy(false);
1799
1860
  }
1800
1861
  }, [wallet, exportKey, props]);
1801
- const handleCopyKey = react.useCallback(async () => {
1862
+ const handleCopyKey = React.useCallback(async () => {
1802
1863
  if (!exportedKey) return;
1803
1864
  try {
1804
1865
  await navigator.clipboard.writeText(exportedKey);
@@ -2186,6 +2247,31 @@ async function buildSplTransferIn(deps, opts) {
2186
2247
  tx.serialize({ requireAllSignatures: false, verifySignatures: false })
2187
2248
  ).toString("base64");
2188
2249
  }
2250
+ var detectors = [
2251
+ { platform: "telegram", detect: () => isTelegramMiniApp() }
2252
+ // Future hosts drop in here (or via registerPlatform), e.g.:
2253
+ // { platform: "mewe", detect: () => typeof window !== "undefined" && !!(window as { MeWe?: unknown }).MeWe },
2254
+ ];
2255
+ function registerPlatform(detector) {
2256
+ detectors.unshift(detector);
2257
+ }
2258
+ function detectPlatform() {
2259
+ if (typeof window === "undefined") return "web";
2260
+ for (const d of detectors) {
2261
+ try {
2262
+ if (d.detect()) return d.platform;
2263
+ } catch {
2264
+ }
2265
+ }
2266
+ return "web";
2267
+ }
2268
+ function usePlatform() {
2269
+ const [platform, setPlatform] = React.useState("web");
2270
+ React.useEffect(() => {
2271
+ setPlatform(detectPlatform());
2272
+ }, []);
2273
+ return platform;
2274
+ }
2189
2275
 
2190
2276
  // src/provider.tsx
2191
2277
  var IAMGameWalletProvider = SolvenProvider;
@@ -2202,17 +2288,23 @@ exports.WalletWithdraw = SolvenWithdraw;
2202
2288
  exports.backpackAdapter = backpackAdapter;
2203
2289
  exports.buildSolTransferIn = buildSolTransferIn;
2204
2290
  exports.buildSplTransferIn = buildSplTransferIn;
2291
+ exports.detectPlatform = detectPlatform;
2205
2292
  exports.getTelegramInitData = getTelegramInitData;
2293
+ exports.getTelegramWebApp = getTelegramWebApp;
2206
2294
  exports.inMemorySession = inMemorySession;
2207
2295
  exports.isTelegramMiniApp = isTelegramMiniApp;
2208
2296
  exports.listSupportedWallets = listSupportedWallets;
2209
2297
  exports.localStorageSession = localStorageSession;
2210
2298
  exports.notifyTelegramReady = notifyTelegramReady;
2211
2299
  exports.phantomAdapter = phantomAdapter;
2300
+ exports.registerPlatform = registerPlatform;
2212
2301
  exports.solflareAdapter = solflareAdapter;
2302
+ exports.useIsTelegram = useIsTelegram;
2303
+ exports.usePlatform = usePlatform;
2213
2304
  exports.useWallet = useSolvenWallet;
2214
2305
  exports.useWalletAuth = useSolvenAuth;
2215
2306
  exports.useWalletBalance = useSolvenBalance;
2216
2307
  exports.useWalletExport = useSolvenExport;
2308
+ exports.useWalletReportError = useSolvenReportError;
2217
2309
  exports.useWalletSign = useSolvenSign;
2218
2310
  exports.useWalletTransferIn = useSolvenTransferIn;
package/dist/index.d.cts CHANGED
@@ -302,6 +302,22 @@ interface IUseIAMGameTransferIn {
302
302
  destinationAddress: string | null;
303
303
  }
304
304
  declare function useIAMGameTransferIn(): IUseIAMGameTransferIn;
305
+ /**
306
+ * True when the app is running inside a Telegram Mini App. SSR-safe: returns false
307
+ * on the server and during the first client render, then the real value after mount —
308
+ * so it never causes a hydration mismatch. For imperative checks use isTelegramMiniApp();
309
+ * for the WebApp object (haptics etc.) use getTelegramWebApp().
310
+ */
311
+ declare function useIsTelegram(): boolean;
312
+
313
+ /** Context passed to onError describing which wallet operation failed. */
314
+ interface IAMGameErrorContext {
315
+ /** e.g. "login:telegram" | "login:wallet" | "login:email" | "sign" | "withdraw" | "export" | "wallet:load" | "render". */
316
+ operation: string;
317
+ }
318
+ type IAMGameErrorReporter = (error: unknown, context: IAMGameErrorContext) => void;
319
+ /** Report a wallet failure from inside SDK components/hooks (wired to the provider's onError). */
320
+ declare function useIAMGameReportError(): IAMGameErrorReporter;
305
321
 
306
322
  interface IAMGameLoginTheme {
307
323
  primary?: string;
@@ -497,13 +513,62 @@ declare function buildSolTransferIn(deps: TransferInDeps, opts: BuildSolTransfer
497
513
  */
498
514
  declare function buildSplTransferIn(deps: TransferInDeps, opts: BuildSplTransferOpts): Promise<string>;
499
515
 
516
+ /** Minimal shape of the Telegram WebApp object — enough for detection + common UI
517
+ * affordances (haptics, theme, buttons). It is the live Telegram object, so other
518
+ * fields exist at runtime; cast if you need them. */
519
+ interface TelegramWebApp {
520
+ initData?: string;
521
+ version?: string;
522
+ platform?: string;
523
+ colorScheme?: "light" | "dark";
524
+ ready?: () => void;
525
+ expand?: () => void;
526
+ close?: () => void;
527
+ HapticFeedback?: {
528
+ impactOccurred?: (style: "light" | "medium" | "heavy" | "rigid" | "soft") => void;
529
+ notificationOccurred?: (type: "error" | "success" | "warning") => void;
530
+ selectionChanged?: () => void;
531
+ };
532
+ [key: string]: unknown;
533
+ }
534
+ /** The live Telegram WebApp object when running inside Telegram, else null.
535
+ * Use it for haptics etc.: getTelegramWebApp()?.HapticFeedback?.impactOccurred("medium"). */
536
+ declare function getTelegramWebApp(): TelegramWebApp | null;
500
537
  declare function getTelegramInitData(): string | null;
501
538
  declare function isTelegramMiniApp(): boolean;
502
539
  declare function notifyTelegramReady(): void;
503
540
 
541
+ /** Known hosts. Kept an open string so a new platform (e.g. "mewe") works before a
542
+ * release — register a detector for it and it just flows through everywhere. */
543
+ type Platform = "web" | "telegram" | "mewe" | (string & {});
544
+ interface PlatformDetector {
545
+ platform: Platform;
546
+ /** True when the app is running inside this host. Runs only in the browser. */
547
+ detect: () => boolean;
548
+ }
549
+ /**
550
+ * Register a host detector the SDK doesn't know yet (checked before the built-ins).
551
+ * Call once at app start. This is the seam for launching on a new platform — no SDK
552
+ * release required.
553
+ */
554
+ declare function registerPlatform(detector: PlatformDetector): void;
555
+ /** The current host platform. "web" on the server and as the default. */
556
+ declare function detectPlatform(): Platform;
557
+ /** SSR-safe reactive platform: "web" on the server + first client render, then the
558
+ * real value after mount — so it never causes a hydration mismatch. */
559
+ declare function usePlatform(): Platform;
560
+
504
561
  interface IAMGameWalletProviderProps extends IAMGameClientOptions {
505
562
  children: React.ReactNode;
563
+ /** Zero-tap Telegram login inside a Mini App. Defaults to true. */
564
+ autoTelegram?: boolean;
565
+ /**
566
+ * Called whenever a wallet operation fails in the UI — login, sign, withdraw,
567
+ * export, wallet load, or a render crash. Wire to your error tracker, e.g.
568
+ * `onError={(err, { operation }) => Sentry.captureException(err, { tags: { wallet_op: operation } })}`.
569
+ */
570
+ onError?: IAMGameErrorReporter;
506
571
  }
507
572
  declare const IAMGameWalletProvider: React.FC<IAMGameWalletProviderProps>;
508
573
 
509
- export { type AuthMethod, type BuildSolTransferOpts, type BuildSplTransferOpts, type ComplianceModuleType, type ComplianceStatus, IAMGameClient as IAMGameWalletClient, type IAMGameClientOptions as IAMGameWalletClientOptions, IAMGameWalletProvider, type IAMGameWalletProviderProps, type IComplianceState, type IExportInitiateRequest, type IExportInitiateResponse, type IExportPreflightResponse, type IExternalWalletAdapter, type ISession, type ISessionStorage, type ISignActionRequest, type ISignActionResponse, type ISiwsChallenge, type ISiwsVerifyRequest, type ITelegramVerifyRequest, type ITokenBalance, type IUseIAMGameAuth as IUseWalletAuth, type IUseIAMGameExport as IUseWalletExport, type IUseIAMGameSign as IUseWalletSign, type IUseIAMGameTransferIn as IUseWalletTransferIn, type IUser, type IUserIdentity, type IWallet, type IWalletBalance, type IIAMGameErrorEnvelope as IWalletErrorEnvelope, type IWebhookEnvelope, type IWebhookSignatureHeader, type IWithdrawRequest, type IWithdrawResponse, type TransferInDeps, IAMGameAddress as WalletAddress, type IAMGameAddressProps as WalletAddressProps, type IAMGameAddressTheme as WalletAddressTheme, IAMGameBalance as WalletBalance, type IAMGameBalanceProps as WalletBalanceProps, type IAMGameBalanceTheme as WalletBalanceTheme, type WalletCustody, type WalletDescriptor, type IAMGameErrorCode as WalletErrorCode, IAMGameExport as WalletExport, type IAMGameExportProps as WalletExportProps, type IAMGameExportTheme as WalletExportTheme, type WalletId, IAMGameLogin as WalletLogin, IAMGameLoginModal as WalletLoginModal, type IAMGameLoginModalProps as WalletLoginModalProps, type IAMGameLoginProps as WalletLoginProps, type IAMGameLoginTheme as WalletLoginTheme, IAMGameSdkError as WalletSdkError, type WalletStatus, IAMGameWithdraw as WalletWithdraw, type IAMGameWithdrawProps as WalletWithdrawProps, type IAMGameWithdrawTheme as WalletWithdrawTheme, type WebhookEventType, backpackAdapter, buildSolTransferIn, buildSplTransferIn, getTelegramInitData, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, solflareAdapter, useIAMGameWallet as useWallet, useIAMGameAuth as useWalletAuth, useIAMGameBalance as useWalletBalance, useIAMGameExport as useWalletExport, useIAMGameSign as useWalletSign, useIAMGameTransferIn as useWalletTransferIn };
574
+ export { type AuthMethod, type BuildSolTransferOpts, type BuildSplTransferOpts, type ComplianceModuleType, type ComplianceStatus, IAMGameClient as IAMGameWalletClient, type IAMGameClientOptions as IAMGameWalletClientOptions, IAMGameWalletProvider, type IAMGameWalletProviderProps, type IComplianceState, type IExportInitiateRequest, type IExportInitiateResponse, type IExportPreflightResponse, type IExternalWalletAdapter, type ISession, type ISessionStorage, type ISignActionRequest, type ISignActionResponse, type ISiwsChallenge, type ISiwsVerifyRequest, type ITelegramVerifyRequest, type ITokenBalance, type IUseIAMGameAuth as IUseWalletAuth, type IUseIAMGameExport as IUseWalletExport, type IUseIAMGameSign as IUseWalletSign, type IUseIAMGameTransferIn as IUseWalletTransferIn, type IUser, type IUserIdentity, type IWallet, type IWalletBalance, type IIAMGameErrorEnvelope as IWalletErrorEnvelope, type IWebhookEnvelope, type IWebhookSignatureHeader, type IWithdrawRequest, type IWithdrawResponse, type Platform, type PlatformDetector, type TelegramWebApp, type TransferInDeps, IAMGameAddress as WalletAddress, type IAMGameAddressProps as WalletAddressProps, type IAMGameAddressTheme as WalletAddressTheme, IAMGameBalance as WalletBalance, type IAMGameBalanceProps as WalletBalanceProps, type IAMGameBalanceTheme as WalletBalanceTheme, type WalletCustody, type WalletDescriptor, type IAMGameErrorCode as WalletErrorCode, type IAMGameErrorContext as WalletErrorContext, type IAMGameErrorReporter as WalletErrorReporter, IAMGameExport as WalletExport, type IAMGameExportProps as WalletExportProps, type IAMGameExportTheme as WalletExportTheme, type WalletId, IAMGameLogin as WalletLogin, IAMGameLoginModal as WalletLoginModal, type IAMGameLoginModalProps as WalletLoginModalProps, type IAMGameLoginProps as WalletLoginProps, type IAMGameLoginTheme as WalletLoginTheme, IAMGameSdkError as WalletSdkError, type WalletStatus, IAMGameWithdraw as WalletWithdraw, type IAMGameWithdrawProps as WalletWithdrawProps, type IAMGameWithdrawTheme as WalletWithdrawTheme, type WebhookEventType, backpackAdapter, buildSolTransferIn, buildSplTransferIn, detectPlatform, getTelegramInitData, getTelegramWebApp, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, registerPlatform, solflareAdapter, useIsTelegram, usePlatform, useIAMGameWallet as useWallet, useIAMGameAuth as useWalletAuth, useIAMGameBalance as useWalletBalance, useIAMGameExport as useWalletExport, useIAMGameReportError as useWalletReportError, useIAMGameSign as useWalletSign, useIAMGameTransferIn as useWalletTransferIn };
package/dist/index.d.ts CHANGED
@@ -302,6 +302,22 @@ interface IUseIAMGameTransferIn {
302
302
  destinationAddress: string | null;
303
303
  }
304
304
  declare function useIAMGameTransferIn(): IUseIAMGameTransferIn;
305
+ /**
306
+ * True when the app is running inside a Telegram Mini App. SSR-safe: returns false
307
+ * on the server and during the first client render, then the real value after mount —
308
+ * so it never causes a hydration mismatch. For imperative checks use isTelegramMiniApp();
309
+ * for the WebApp object (haptics etc.) use getTelegramWebApp().
310
+ */
311
+ declare function useIsTelegram(): boolean;
312
+
313
+ /** Context passed to onError describing which wallet operation failed. */
314
+ interface IAMGameErrorContext {
315
+ /** e.g. "login:telegram" | "login:wallet" | "login:email" | "sign" | "withdraw" | "export" | "wallet:load" | "render". */
316
+ operation: string;
317
+ }
318
+ type IAMGameErrorReporter = (error: unknown, context: IAMGameErrorContext) => void;
319
+ /** Report a wallet failure from inside SDK components/hooks (wired to the provider's onError). */
320
+ declare function useIAMGameReportError(): IAMGameErrorReporter;
305
321
 
306
322
  interface IAMGameLoginTheme {
307
323
  primary?: string;
@@ -497,13 +513,62 @@ declare function buildSolTransferIn(deps: TransferInDeps, opts: BuildSolTransfer
497
513
  */
498
514
  declare function buildSplTransferIn(deps: TransferInDeps, opts: BuildSplTransferOpts): Promise<string>;
499
515
 
516
+ /** Minimal shape of the Telegram WebApp object — enough for detection + common UI
517
+ * affordances (haptics, theme, buttons). It is the live Telegram object, so other
518
+ * fields exist at runtime; cast if you need them. */
519
+ interface TelegramWebApp {
520
+ initData?: string;
521
+ version?: string;
522
+ platform?: string;
523
+ colorScheme?: "light" | "dark";
524
+ ready?: () => void;
525
+ expand?: () => void;
526
+ close?: () => void;
527
+ HapticFeedback?: {
528
+ impactOccurred?: (style: "light" | "medium" | "heavy" | "rigid" | "soft") => void;
529
+ notificationOccurred?: (type: "error" | "success" | "warning") => void;
530
+ selectionChanged?: () => void;
531
+ };
532
+ [key: string]: unknown;
533
+ }
534
+ /** The live Telegram WebApp object when running inside Telegram, else null.
535
+ * Use it for haptics etc.: getTelegramWebApp()?.HapticFeedback?.impactOccurred("medium"). */
536
+ declare function getTelegramWebApp(): TelegramWebApp | null;
500
537
  declare function getTelegramInitData(): string | null;
501
538
  declare function isTelegramMiniApp(): boolean;
502
539
  declare function notifyTelegramReady(): void;
503
540
 
541
+ /** Known hosts. Kept an open string so a new platform (e.g. "mewe") works before a
542
+ * release — register a detector for it and it just flows through everywhere. */
543
+ type Platform = "web" | "telegram" | "mewe" | (string & {});
544
+ interface PlatformDetector {
545
+ platform: Platform;
546
+ /** True when the app is running inside this host. Runs only in the browser. */
547
+ detect: () => boolean;
548
+ }
549
+ /**
550
+ * Register a host detector the SDK doesn't know yet (checked before the built-ins).
551
+ * Call once at app start. This is the seam for launching on a new platform — no SDK
552
+ * release required.
553
+ */
554
+ declare function registerPlatform(detector: PlatformDetector): void;
555
+ /** The current host platform. "web" on the server and as the default. */
556
+ declare function detectPlatform(): Platform;
557
+ /** SSR-safe reactive platform: "web" on the server + first client render, then the
558
+ * real value after mount — so it never causes a hydration mismatch. */
559
+ declare function usePlatform(): Platform;
560
+
504
561
  interface IAMGameWalletProviderProps extends IAMGameClientOptions {
505
562
  children: React.ReactNode;
563
+ /** Zero-tap Telegram login inside a Mini App. Defaults to true. */
564
+ autoTelegram?: boolean;
565
+ /**
566
+ * Called whenever a wallet operation fails in the UI — login, sign, withdraw,
567
+ * export, wallet load, or a render crash. Wire to your error tracker, e.g.
568
+ * `onError={(err, { operation }) => Sentry.captureException(err, { tags: { wallet_op: operation } })}`.
569
+ */
570
+ onError?: IAMGameErrorReporter;
506
571
  }
507
572
  declare const IAMGameWalletProvider: React.FC<IAMGameWalletProviderProps>;
508
573
 
509
- export { type AuthMethod, type BuildSolTransferOpts, type BuildSplTransferOpts, type ComplianceModuleType, type ComplianceStatus, IAMGameClient as IAMGameWalletClient, type IAMGameClientOptions as IAMGameWalletClientOptions, IAMGameWalletProvider, type IAMGameWalletProviderProps, type IComplianceState, type IExportInitiateRequest, type IExportInitiateResponse, type IExportPreflightResponse, type IExternalWalletAdapter, type ISession, type ISessionStorage, type ISignActionRequest, type ISignActionResponse, type ISiwsChallenge, type ISiwsVerifyRequest, type ITelegramVerifyRequest, type ITokenBalance, type IUseIAMGameAuth as IUseWalletAuth, type IUseIAMGameExport as IUseWalletExport, type IUseIAMGameSign as IUseWalletSign, type IUseIAMGameTransferIn as IUseWalletTransferIn, type IUser, type IUserIdentity, type IWallet, type IWalletBalance, type IIAMGameErrorEnvelope as IWalletErrorEnvelope, type IWebhookEnvelope, type IWebhookSignatureHeader, type IWithdrawRequest, type IWithdrawResponse, type TransferInDeps, IAMGameAddress as WalletAddress, type IAMGameAddressProps as WalletAddressProps, type IAMGameAddressTheme as WalletAddressTheme, IAMGameBalance as WalletBalance, type IAMGameBalanceProps as WalletBalanceProps, type IAMGameBalanceTheme as WalletBalanceTheme, type WalletCustody, type WalletDescriptor, type IAMGameErrorCode as WalletErrorCode, IAMGameExport as WalletExport, type IAMGameExportProps as WalletExportProps, type IAMGameExportTheme as WalletExportTheme, type WalletId, IAMGameLogin as WalletLogin, IAMGameLoginModal as WalletLoginModal, type IAMGameLoginModalProps as WalletLoginModalProps, type IAMGameLoginProps as WalletLoginProps, type IAMGameLoginTheme as WalletLoginTheme, IAMGameSdkError as WalletSdkError, type WalletStatus, IAMGameWithdraw as WalletWithdraw, type IAMGameWithdrawProps as WalletWithdrawProps, type IAMGameWithdrawTheme as WalletWithdrawTheme, type WebhookEventType, backpackAdapter, buildSolTransferIn, buildSplTransferIn, getTelegramInitData, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, solflareAdapter, useIAMGameWallet as useWallet, useIAMGameAuth as useWalletAuth, useIAMGameBalance as useWalletBalance, useIAMGameExport as useWalletExport, useIAMGameSign as useWalletSign, useIAMGameTransferIn as useWalletTransferIn };
574
+ export { type AuthMethod, type BuildSolTransferOpts, type BuildSplTransferOpts, type ComplianceModuleType, type ComplianceStatus, IAMGameClient as IAMGameWalletClient, type IAMGameClientOptions as IAMGameWalletClientOptions, IAMGameWalletProvider, type IAMGameWalletProviderProps, type IComplianceState, type IExportInitiateRequest, type IExportInitiateResponse, type IExportPreflightResponse, type IExternalWalletAdapter, type ISession, type ISessionStorage, type ISignActionRequest, type ISignActionResponse, type ISiwsChallenge, type ISiwsVerifyRequest, type ITelegramVerifyRequest, type ITokenBalance, type IUseIAMGameAuth as IUseWalletAuth, type IUseIAMGameExport as IUseWalletExport, type IUseIAMGameSign as IUseWalletSign, type IUseIAMGameTransferIn as IUseWalletTransferIn, type IUser, type IUserIdentity, type IWallet, type IWalletBalance, type IIAMGameErrorEnvelope as IWalletErrorEnvelope, type IWebhookEnvelope, type IWebhookSignatureHeader, type IWithdrawRequest, type IWithdrawResponse, type Platform, type PlatformDetector, type TelegramWebApp, type TransferInDeps, IAMGameAddress as WalletAddress, type IAMGameAddressProps as WalletAddressProps, type IAMGameAddressTheme as WalletAddressTheme, IAMGameBalance as WalletBalance, type IAMGameBalanceProps as WalletBalanceProps, type IAMGameBalanceTheme as WalletBalanceTheme, type WalletCustody, type WalletDescriptor, type IAMGameErrorCode as WalletErrorCode, type IAMGameErrorContext as WalletErrorContext, type IAMGameErrorReporter as WalletErrorReporter, IAMGameExport as WalletExport, type IAMGameExportProps as WalletExportProps, type IAMGameExportTheme as WalletExportTheme, type WalletId, IAMGameLogin as WalletLogin, IAMGameLoginModal as WalletLoginModal, type IAMGameLoginModalProps as WalletLoginModalProps, type IAMGameLoginProps as WalletLoginProps, type IAMGameLoginTheme as WalletLoginTheme, IAMGameSdkError as WalletSdkError, type WalletStatus, IAMGameWithdraw as WalletWithdraw, type IAMGameWithdrawProps as WalletWithdrawProps, type IAMGameWithdrawTheme as WalletWithdrawTheme, type WebhookEventType, backpackAdapter, buildSolTransferIn, buildSplTransferIn, detectPlatform, getTelegramInitData, getTelegramWebApp, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, registerPlatform, solflareAdapter, useIsTelegram, usePlatform, useIAMGameWallet as useWallet, useIAMGameAuth as useWalletAuth, useIAMGameBalance as useWalletBalance, useIAMGameExport as useWalletExport, useIAMGameReportError as useWalletReportError, useIAMGameSign as useWalletSign, useIAMGameTransferIn as useWalletTransferIn };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useMemo, useState, useEffect, useRef, useCallback, useContext } from 'react';
1
+ import React, { createContext, useMemo, useState, useRef, useCallback, useEffect, useContext } from 'react';
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { createPortal } from 'react-dom';
4
4
 
@@ -247,6 +247,9 @@ function getTelegram() {
247
247
  if (typeof window === "undefined") return void 0;
248
248
  return window.Telegram;
249
249
  }
250
+ function getTelegramWebApp() {
251
+ return getTelegram()?.WebApp ?? null;
252
+ }
250
253
  function getTelegramInitData() {
251
254
  const data = getTelegram()?.WebApp?.initData;
252
255
  if (!data || typeof data !== "string" || data.length === 0) return null;
@@ -269,7 +272,25 @@ function useSolvenContext() {
269
272
  if (!ctx) throw new Error("useSolvenContext must be inside a <SolvenProvider />");
270
273
  return ctx;
271
274
  }
272
- function SolvenProvider({ children, autoTelegram = true, ...opts }) {
275
+ function useSolvenReportError() {
276
+ return useSolvenContext().reportError;
277
+ }
278
+ var SolvenErrorBoundary = class extends React.Component {
279
+ constructor() {
280
+ super(...arguments);
281
+ this.state = { hasError: false };
282
+ }
283
+ static getDerivedStateFromError() {
284
+ return { hasError: true };
285
+ }
286
+ componentDidCatch(error) {
287
+ this.props.onError(error, { operation: "render" });
288
+ }
289
+ render() {
290
+ return this.state.hasError ? null : this.props.children;
291
+ }
292
+ };
293
+ function SolvenProvider({ children, autoTelegram = true, onError, ...opts }) {
273
294
  const client = useMemo(() => new SolvenClient(opts), [
274
295
  opts.publishableKey,
275
296
  opts.baseUrl,
@@ -278,6 +299,14 @@ function SolvenProvider({ children, autoTelegram = true, ...opts }) {
278
299
  ]);
279
300
  const [session, setSession] = useState(client.getSession());
280
301
  const [status, setStatus] = useState("loading");
302
+ const onErrorRef = useRef(onError);
303
+ onErrorRef.current = onError;
304
+ const reportError = useCallback((error, context) => {
305
+ try {
306
+ onErrorRef.current?.(error, context);
307
+ } catch {
308
+ }
309
+ }, []);
281
310
  useEffect(() => {
282
311
  let cancelled = false;
283
312
  (async () => {
@@ -343,9 +372,10 @@ function SolvenProvider({ children, autoTelegram = true, ...opts }) {
343
372
  setSession: (s) => {
344
373
  setSession(s);
345
374
  setStatus(s ? "authenticated" : "anonymous");
346
- }
375
+ },
376
+ reportError
347
377
  };
348
- return /* @__PURE__ */ jsx(SolvenContext.Provider, { value, children });
378
+ return /* @__PURE__ */ jsx(SolvenContext.Provider, { value, children: /* @__PURE__ */ jsx(SolvenErrorBoundary, { onError: reportError, children }) });
349
379
  }
350
380
 
351
381
  // ../../solven/sdk-client/src/hooks.ts
@@ -398,7 +428,7 @@ function useSolvenAuth() {
398
428
  };
399
429
  }
400
430
  function useSolvenWallet() {
401
- const { client, status } = useSolvenContext();
431
+ const { client, status, reportError } = useSolvenContext();
402
432
  const [wallet, setWallet] = useState(null);
403
433
  useEffect(() => {
404
434
  if (status !== "authenticated") {
@@ -406,11 +436,15 @@ function useSolvenWallet() {
406
436
  return;
407
437
  }
408
438
  let cancelled = false;
409
- client.getMyWallet().then((w) => !cancelled && setWallet(w)).catch(() => !cancelled && setWallet(null));
439
+ client.getMyWallet().then((w) => !cancelled && setWallet(w)).catch((e) => {
440
+ if (cancelled) return;
441
+ reportError(e, { operation: "wallet:load" });
442
+ setWallet(null);
443
+ });
410
444
  return () => {
411
445
  cancelled = true;
412
446
  };
413
- }, [client, status]);
447
+ }, [client, status, reportError]);
414
448
  return wallet;
415
449
  }
416
450
  function useSolvenBalance(walletId, pollMs = 15e3) {
@@ -439,10 +473,17 @@ function useSolvenBalance(walletId, pollMs = 15e3) {
439
473
  return balance;
440
474
  }
441
475
  function useSolvenSign() {
442
- const { client } = useSolvenContext();
476
+ const { client, reportError } = useSolvenContext();
443
477
  const signAction = useCallback(
444
- ({ walletId, txBase64, label, idempotencyKey }) => client.signAction({ walletId, txBase64, label }, idempotencyKey),
445
- [client]
478
+ async ({ walletId, txBase64, label, idempotencyKey }) => {
479
+ try {
480
+ return await client.signAction({ walletId, txBase64, label }, idempotencyKey);
481
+ } catch (e) {
482
+ reportError(e, { operation: "sign" });
483
+ throw e;
484
+ }
485
+ },
486
+ [client, reportError]
446
487
  );
447
488
  return { signAction };
448
489
  }
@@ -470,6 +511,13 @@ function useSolvenTransferIn() {
470
511
  const wallet = useSolvenWallet();
471
512
  return { destinationAddress: wallet?.address ?? null };
472
513
  }
514
+ function useIsTelegram() {
515
+ const [inTelegram, setInTelegram] = useState(false);
516
+ useEffect(() => {
517
+ setInTelegram(isTelegramMiniApp());
518
+ }, []);
519
+ return inTelegram;
520
+ }
473
521
 
474
522
  // ../../solven/sdk-client/src/wallet-adapter.ts
475
523
  function phantomAdapter() {
@@ -586,6 +634,7 @@ var defaultTheme = {
586
634
  };
587
635
  function SolvenLoginContent(props) {
588
636
  const { connectExternal, connectTelegram, requestEmailOtp, connectEmail, status, user } = useSolvenAuth();
637
+ const reportError = useSolvenReportError();
589
638
  const [busy, setBusy] = useState(null);
590
639
  const [error, setError] = useState(null);
591
640
  const [email, setEmail] = useState("");
@@ -614,6 +663,7 @@ function SolvenLoginContent(props) {
614
663
  props.onCloseAfterSignIn?.();
615
664
  } catch (e) {
616
665
  if (!cancelled) {
666
+ reportError(e, { operation: "login:telegram" });
617
667
  setError(e instanceof Error ? e.message : "Telegram sign-in failed");
618
668
  }
619
669
  } finally {
@@ -640,6 +690,7 @@ function SolvenLoginContent(props) {
640
690
  props.onSignIn?.();
641
691
  props.onCloseAfterSignIn?.();
642
692
  } catch (e) {
693
+ reportError(e, { operation: "login:wallet" });
643
694
  setError(e instanceof Error ? e.message : "Wallet sign-in failed");
644
695
  } finally {
645
696
  setBusy(null);
@@ -653,6 +704,7 @@ function SolvenLoginContent(props) {
653
704
  props.onSignIn?.();
654
705
  props.onCloseAfterSignIn?.();
655
706
  } catch (e) {
707
+ reportError(e, { operation: "login:telegram" });
656
708
  setError(e instanceof Error ? e.message : "Telegram sign-in failed");
657
709
  } finally {
658
710
  setBusy(null);
@@ -665,6 +717,7 @@ function SolvenLoginContent(props) {
665
717
  await requestEmailOtp(email.trim());
666
718
  setOtpSent(true);
667
719
  } catch (e) {
720
+ reportError(e, { operation: "login:email" });
668
721
  setError(e instanceof Error ? e.message : "Couldn't send the code");
669
722
  } finally {
670
723
  setBusy(null);
@@ -678,6 +731,7 @@ function SolvenLoginContent(props) {
678
731
  props.onSignIn?.();
679
732
  props.onCloseAfterSignIn?.();
680
733
  } catch (e) {
734
+ reportError(e, { operation: "login:email" });
681
735
  setError(e instanceof Error ? e.message : "Couldn't verify the code");
682
736
  } finally {
683
737
  setBusy(null);
@@ -1453,7 +1507,7 @@ function isValidSolanaAddress(address) {
1453
1507
  function SolvenWithdraw(props) {
1454
1508
  const wallet = useSolvenWallet();
1455
1509
  const balance = useSolvenBalance(wallet?.id ?? null);
1456
- const { client } = useSolvenContext();
1510
+ const { client, reportError } = useSolvenContext();
1457
1511
  const theme = useMemo(() => ({ ...defaultTheme4, ...props.theme ?? {} }), [props.theme]);
1458
1512
  const label = props.label ?? "Withdraw";
1459
1513
  const [toAddress, setToAddress] = useState("");
@@ -1531,6 +1585,7 @@ function SolvenWithdraw(props) {
1531
1585
  props.onSuccess?.(result.signature);
1532
1586
  } catch (e) {
1533
1587
  const msg = e instanceof Error ? e.message : "Withdrawal failed";
1588
+ reportError(e, { operation: "withdraw" });
1534
1589
  setError(msg);
1535
1590
  props.onError?.(e instanceof Error ? e : new Error(msg));
1536
1591
  } finally {
@@ -1769,6 +1824,7 @@ function formatLamports(lamports) {
1769
1824
  function SolvenExport(props) {
1770
1825
  const wallet = useSolvenWallet();
1771
1826
  const { preflight, refreshPreflight, exportKey } = useSolvenExport(wallet?.id ?? null);
1827
+ const reportError = useSolvenReportError();
1772
1828
  const theme = useMemo(() => ({ ...defaultTheme5, ...props.theme ?? {} }), [props.theme]);
1773
1829
  const label = props.label ?? "Export Wallet";
1774
1830
  const [step, setStep] = useState("preflight");
@@ -1790,6 +1846,7 @@ function SolvenExport(props) {
1790
1846
  props.onSuccess?.(result.newWalletAddress);
1791
1847
  } catch (e) {
1792
1848
  const msg = e instanceof Error ? e.message : "Export failed";
1849
+ reportError(e, { operation: "export" });
1793
1850
  setError(msg);
1794
1851
  props.onError?.(e instanceof Error ? e : new Error(msg));
1795
1852
  } finally {
@@ -2184,8 +2241,33 @@ async function buildSplTransferIn(deps, opts) {
2184
2241
  tx.serialize({ requireAllSignatures: false, verifySignatures: false })
2185
2242
  ).toString("base64");
2186
2243
  }
2244
+ var detectors = [
2245
+ { platform: "telegram", detect: () => isTelegramMiniApp() }
2246
+ // Future hosts drop in here (or via registerPlatform), e.g.:
2247
+ // { platform: "mewe", detect: () => typeof window !== "undefined" && !!(window as { MeWe?: unknown }).MeWe },
2248
+ ];
2249
+ function registerPlatform(detector) {
2250
+ detectors.unshift(detector);
2251
+ }
2252
+ function detectPlatform() {
2253
+ if (typeof window === "undefined") return "web";
2254
+ for (const d of detectors) {
2255
+ try {
2256
+ if (d.detect()) return d.platform;
2257
+ } catch {
2258
+ }
2259
+ }
2260
+ return "web";
2261
+ }
2262
+ function usePlatform() {
2263
+ const [platform, setPlatform] = useState("web");
2264
+ useEffect(() => {
2265
+ setPlatform(detectPlatform());
2266
+ }, []);
2267
+ return platform;
2268
+ }
2187
2269
 
2188
2270
  // src/provider.tsx
2189
2271
  var IAMGameWalletProvider = SolvenProvider;
2190
2272
 
2191
- export { SolvenClient as IAMGameWalletClient, IAMGameWalletProvider, SolvenAddress as WalletAddress, SolvenBalance as WalletBalance, SolvenExport as WalletExport, SolvenLogin as WalletLogin, SolvenLoginModal as WalletLoginModal, SolvenSdkError as WalletSdkError, SolvenWithdraw as WalletWithdraw, backpackAdapter, buildSolTransferIn, buildSplTransferIn, getTelegramInitData, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, solflareAdapter, useSolvenWallet as useWallet, useSolvenAuth as useWalletAuth, useSolvenBalance as useWalletBalance, useSolvenExport as useWalletExport, useSolvenSign as useWalletSign, useSolvenTransferIn as useWalletTransferIn };
2273
+ export { SolvenClient as IAMGameWalletClient, IAMGameWalletProvider, SolvenAddress as WalletAddress, SolvenBalance as WalletBalance, SolvenExport as WalletExport, SolvenLogin as WalletLogin, SolvenLoginModal as WalletLoginModal, SolvenSdkError as WalletSdkError, SolvenWithdraw as WalletWithdraw, backpackAdapter, buildSolTransferIn, buildSplTransferIn, detectPlatform, getTelegramInitData, getTelegramWebApp, inMemorySession, isTelegramMiniApp, listSupportedWallets, localStorageSession, notifyTelegramReady, phantomAdapter, registerPlatform, solflareAdapter, useIsTelegram, usePlatform, useSolvenWallet as useWallet, useSolvenAuth as useWalletAuth, useSolvenBalance as useWalletBalance, useSolvenExport as useWalletExport, useSolvenReportError as useWalletReportError, useSolvenSign as useWalletSign, useSolvenTransferIn as useWalletTransferIn };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iamgame/wallet-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "IAMGame Wallet browser SDK — Telegram & Solana wallet auth, balances, server-side signing, and key export. Drop-in React provider for game frontends.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://wallet.iamgame.com",