@swype-org/react-sdk 0.1.47 → 0.1.49

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.d.cts CHANGED
@@ -198,7 +198,7 @@ interface UserConfig {
198
198
  /** Theme mode */
199
199
  type ThemeMode = 'light' | 'dark';
200
200
  /** Steps in the payment flow */
201
- type PaymentStep = 'login' | 'otp-verify' | 'create-passkey' | 'wallet-picker' | 'deposit' | 'low-balance' | 'processing' | 'select-source' | 'success';
201
+ type PaymentStep = 'login' | 'otp-verify' | 'create-passkey' | 'wallet-picker' | 'open-wallet' | 'deposit' | 'low-balance' | 'processing' | 'select-source' | 'success';
202
202
  /** User-selected advanced settings for chain/asset override */
203
203
  interface AdvancedSettings {
204
204
  /** Override asset (e.g. 'USDC', 'USDT'). Null = let backend decide. */
@@ -377,7 +377,7 @@ interface SwypePaymentProps {
377
377
  /** Seconds to count down on the success screen before auto-dismissing. */
378
378
  autoCloseSeconds?: number;
379
379
  }
380
- declare function SwypePayment({ destination, onComplete, onError, useWalletConnector, idempotencyKey, merchantAuthorization, merchantName, onBack, onDismiss, autoCloseSeconds, }: SwypePaymentProps): react_jsx_runtime.JSX.Element | null;
380
+ declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.Element;
381
381
 
382
382
  type AccessTokenGetter = () => Promise<string | null | undefined>;
383
383
  /**
package/dist/index.d.ts CHANGED
@@ -198,7 +198,7 @@ interface UserConfig {
198
198
  /** Theme mode */
199
199
  type ThemeMode = 'light' | 'dark';
200
200
  /** Steps in the payment flow */
201
- type PaymentStep = 'login' | 'otp-verify' | 'create-passkey' | 'wallet-picker' | 'deposit' | 'low-balance' | 'processing' | 'select-source' | 'success';
201
+ type PaymentStep = 'login' | 'otp-verify' | 'create-passkey' | 'wallet-picker' | 'open-wallet' | 'deposit' | 'low-balance' | 'processing' | 'select-source' | 'success';
202
202
  /** User-selected advanced settings for chain/asset override */
203
203
  interface AdvancedSettings {
204
204
  /** Override asset (e.g. 'USDC', 'USDT'). Null = let backend decide. */
@@ -377,7 +377,7 @@ interface SwypePaymentProps {
377
377
  /** Seconds to count down on the success screen before auto-dismissing. */
378
378
  autoCloseSeconds?: number;
379
379
  }
380
- declare function SwypePayment({ destination, onComplete, onError, useWalletConnector, idempotencyKey, merchantAuthorization, merchantName, onBack, onDismiss, autoCloseSeconds, }: SwypePaymentProps): react_jsx_runtime.JSX.Element | null;
380
+ declare function SwypePayment(props: SwypePaymentProps): react_jsx_runtime.JSX.Element;
381
381
 
382
382
  type AccessTokenGetter = () => Promise<string | null | undefined>;
383
383
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect } from 'react';
1
+ import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect, Component } from 'react';
2
2
  import { PrivyProvider, usePrivy, useLoginWithEmail, useLoginWithSms, useLoginWithOAuth } from '@privy-io/react-auth';
3
3
  import { createConfig, http, WagmiProvider, useConfig, useConnect, useSwitchChain } from 'wagmi';
4
4
  import { mainnet, arbitrum, base } from 'wagmi/chains';
@@ -666,13 +666,20 @@ function isInCrossOriginIframe() {
666
666
  }
667
667
  }
668
668
  var delegationCounter = 0;
669
+ var DELEGATION_CREATE_TIMEOUT_MS = 6e4;
670
+ var DELEGATION_GET_TIMEOUT_MS = 3e4;
669
671
  function delegatePasskeyCreate(options) {
670
672
  return new Promise((resolve, reject) => {
671
673
  const id = `pc-${++delegationCounter}-${Date.now()}`;
674
+ const timer = setTimeout(() => {
675
+ window.removeEventListener("message", handler);
676
+ reject(new Error("Passkey creation timed out. Please try again."));
677
+ }, DELEGATION_CREATE_TIMEOUT_MS);
672
678
  const handler = (event) => {
673
679
  const data = event.data;
674
680
  if (!data || typeof data !== "object") return;
675
681
  if (data.type !== "swype:passkey-create-response" || data.id !== id) return;
682
+ clearTimeout(timer);
676
683
  window.removeEventListener("message", handler);
677
684
  if (data.error) {
678
685
  reject(new Error(data.error));
@@ -689,10 +696,15 @@ function delegatePasskeyCreate(options) {
689
696
  function delegatePasskeyGet(options) {
690
697
  return new Promise((resolve, reject) => {
691
698
  const id = `pg-${++delegationCounter}-${Date.now()}`;
699
+ const timer = setTimeout(() => {
700
+ window.removeEventListener("message", handler);
701
+ reject(new Error("Passkey verification timed out. Please try again."));
702
+ }, DELEGATION_GET_TIMEOUT_MS);
692
703
  const handler = (event) => {
693
704
  const data = event.data;
694
705
  if (!data || typeof data !== "object") return;
695
706
  if (data.type !== "swype:passkey-get-response" || data.id !== id) return;
707
+ clearTimeout(timer);
696
708
  window.removeEventListener("message", handler);
697
709
  if (data.error) {
698
710
  reject(new Error(data.error));
@@ -2644,7 +2656,6 @@ var hintStyle = (color) => ({
2644
2656
  });
2645
2657
  function CreatePasskeyScreen({
2646
2658
  onCreatePasskey,
2647
- onSkip,
2648
2659
  onBack,
2649
2660
  creating,
2650
2661
  error
@@ -2655,7 +2666,6 @@ function CreatePasskeyScreen({
2655
2666
  {
2656
2667
  footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2657
2668
  /* @__PURE__ */ jsx(PrimaryButton, { onClick: onCreatePasskey, disabled: creating, loading: creating, children: "Create passkey" }),
2658
- /* @__PURE__ */ jsx("button", { type: "button", onClick: onSkip, style: skipStyle(tokens.textMuted), disabled: creating, children: "Skip for now" }),
2659
2669
  /* @__PURE__ */ jsx(PoweredByFooter, {})
2660
2670
  ] }),
2661
2671
  children: [
@@ -2710,19 +2720,6 @@ var errorBannerStyle2 = (tokens) => ({
2710
2720
  width: "100%",
2711
2721
  textAlign: "left"
2712
2722
  });
2713
- var skipStyle = (color) => ({
2714
- background: "transparent",
2715
- border: "none",
2716
- color,
2717
- cursor: "pointer",
2718
- fontFamily: "inherit",
2719
- fontSize: "0.88rem",
2720
- fontWeight: 500,
2721
- display: "block",
2722
- width: "100%",
2723
- textAlign: "center",
2724
- padding: "12px 0 0"
2725
- });
2726
2723
  var WALLET_EMOJIS = {
2727
2724
  rabby: "\u{1F430}",
2728
2725
  ora: "\u2666\uFE0F",
@@ -2735,6 +2732,7 @@ function truncateAddress(address) {
2735
2732
  function WalletPickerScreen({
2736
2733
  providers,
2737
2734
  pendingConnections,
2735
+ loading,
2738
2736
  onSelectProvider,
2739
2737
  onContinueConnection,
2740
2738
  onBack
@@ -2748,6 +2746,12 @@ function WalletPickerScreen({
2748
2746
  { id: "ora", name: "Ora" },
2749
2747
  { id: "phantom", name: "Phantom" }
2750
2748
  ];
2749
+ if (loading) {
2750
+ return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
2751
+ /* @__PURE__ */ jsx(ScreenHeader, { title: "Set up Swype", onBack }),
2752
+ /* @__PURE__ */ jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(Spinner, { label: "Connecting..." }) })
2753
+ ] });
2754
+ }
2751
2755
  return /* @__PURE__ */ jsxs(
2752
2756
  ScreenLayout,
2753
2757
  {
@@ -3848,6 +3852,167 @@ var waitHintStyle = (color) => ({
3848
3852
  color,
3849
3853
  margin: 0
3850
3854
  });
3855
+ function OpenWalletScreen({
3856
+ walletName,
3857
+ deeplinkUri,
3858
+ loading,
3859
+ onLogout
3860
+ }) {
3861
+ const { tokens } = useSwypeConfig();
3862
+ const displayName = walletName ?? "your wallet";
3863
+ const logoSrc = walletName ? KNOWN_LOGOS[walletName.toLowerCase()] : void 0;
3864
+ const handleOpen = useCallback(() => {
3865
+ const opened = window.open(deeplinkUri, "_blank");
3866
+ if (!opened) {
3867
+ window.location.href = deeplinkUri;
3868
+ }
3869
+ }, [deeplinkUri]);
3870
+ return /* @__PURE__ */ jsxs(
3871
+ ScreenLayout,
3872
+ {
3873
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
3874
+ !loading && /* @__PURE__ */ jsxs(PrimaryButton, { onClick: handleOpen, children: [
3875
+ "Open ",
3876
+ displayName
3877
+ ] }),
3878
+ /* @__PURE__ */ jsx("p", { style: hintStyle3(tokens.textMuted), children: loading ? "Preparing authorization..." : "Tap the button to authorize in your wallet app" })
3879
+ ] }),
3880
+ children: [
3881
+ /* @__PURE__ */ jsx(ScreenHeader, { right: /* @__PURE__ */ jsx(SettingsMenu, { onLogout }) }),
3882
+ /* @__PURE__ */ jsxs("div", { style: contentStyle6, children: [
3883
+ logoSrc ? /* @__PURE__ */ jsx("img", { src: logoSrc, alt: displayName, style: logoStyle }) : /* @__PURE__ */ jsx(Spinner, { size: 48 }),
3884
+ /* @__PURE__ */ jsx("h2", { style: headingStyle8(tokens.text), children: loading ? "Connecting..." : `Open ${displayName}` }),
3885
+ /* @__PURE__ */ jsx("p", { style: subtitleStyle8(tokens.textSecondary), children: loading ? "Creating transfer and preparing your wallet link..." : `Continue in ${displayName} to authorize this connection.` }),
3886
+ !loading && /* @__PURE__ */ jsxs("div", { style: waitingBadgeStyle(tokens), children: [
3887
+ /* @__PURE__ */ jsx(Spinner, { size: 14 }),
3888
+ /* @__PURE__ */ jsx("span", { children: "Waiting for authorization..." })
3889
+ ] })
3890
+ ] })
3891
+ ]
3892
+ }
3893
+ );
3894
+ }
3895
+ var contentStyle6 = {
3896
+ flex: 1,
3897
+ display: "flex",
3898
+ flexDirection: "column",
3899
+ alignItems: "center",
3900
+ justifyContent: "center",
3901
+ textAlign: "center",
3902
+ padding: "0 24px"
3903
+ };
3904
+ var logoStyle = {
3905
+ width: 56,
3906
+ height: 56,
3907
+ borderRadius: 14,
3908
+ objectFit: "contain"
3909
+ };
3910
+ var headingStyle8 = (color) => ({
3911
+ fontSize: "1.45rem",
3912
+ fontWeight: 700,
3913
+ letterSpacing: "-0.02em",
3914
+ color,
3915
+ margin: "20px 0 8px"
3916
+ });
3917
+ var subtitleStyle8 = (color) => ({
3918
+ fontSize: "0.9rem",
3919
+ color,
3920
+ margin: "0 0 24px",
3921
+ lineHeight: 1.5,
3922
+ maxWidth: 280
3923
+ });
3924
+ var waitingBadgeStyle = (tokens) => ({
3925
+ display: "inline-flex",
3926
+ alignItems: "center",
3927
+ gap: 8,
3928
+ padding: "8px 16px",
3929
+ borderRadius: 20,
3930
+ background: tokens.bgInput,
3931
+ border: `1px solid ${tokens.border}`,
3932
+ color: tokens.textMuted,
3933
+ fontSize: "0.82rem"
3934
+ });
3935
+ var hintStyle3 = (color) => ({
3936
+ textAlign: "center",
3937
+ fontSize: "0.82rem",
3938
+ color,
3939
+ margin: "8px 0 0"
3940
+ });
3941
+ var PaymentErrorBoundary = class extends Component {
3942
+ constructor(props) {
3943
+ super(props);
3944
+ this.state = { hasError: false };
3945
+ }
3946
+ static getDerivedStateFromError() {
3947
+ return { hasError: true };
3948
+ }
3949
+ componentDidCatch(error, info) {
3950
+ console.error("[SwypePayment] Uncaught error:", error, info.componentStack);
3951
+ }
3952
+ handleReset = () => {
3953
+ this.setState({ hasError: false });
3954
+ this.props.onReset();
3955
+ };
3956
+ render() {
3957
+ if (!this.state.hasError) {
3958
+ return this.props.children;
3959
+ }
3960
+ return /* @__PURE__ */ jsxs("div", { style: containerStyle8, children: [
3961
+ /* @__PURE__ */ jsx("div", { style: iconStyle4, children: /* @__PURE__ */ jsxs("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", children: [
3962
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#ef4444", strokeWidth: "1.5" }),
3963
+ /* @__PURE__ */ jsx("path", { d: "M12 8v5", stroke: "#ef4444", strokeWidth: "1.5", strokeLinecap: "round" }),
3964
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "16", r: "0.75", fill: "#ef4444" })
3965
+ ] }) }),
3966
+ /* @__PURE__ */ jsx("h2", { style: headingStyle9, children: "Something went wrong" }),
3967
+ /* @__PURE__ */ jsx("p", { style: messageStyle, children: "An unexpected error occurred. Please try again." }),
3968
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: this.handleReset, style: buttonStyle3, children: "Try again" })
3969
+ ] });
3970
+ }
3971
+ };
3972
+ var containerStyle8 = {
3973
+ display: "flex",
3974
+ flexDirection: "column",
3975
+ alignItems: "center",
3976
+ justifyContent: "center",
3977
+ textAlign: "center",
3978
+ padding: "48px 24px",
3979
+ minHeight: "100%",
3980
+ maxWidth: 420,
3981
+ margin: "0 auto"
3982
+ };
3983
+ var iconStyle4 = {
3984
+ marginBottom: 20
3985
+ };
3986
+ var headingStyle9 = {
3987
+ fontSize: "1.25rem",
3988
+ fontWeight: 700,
3989
+ color: "#1a1a1a",
3990
+ margin: "0 0 8px"
3991
+ };
3992
+ var messageStyle = {
3993
+ fontSize: "0.9rem",
3994
+ color: "#666",
3995
+ margin: "0 0 28px",
3996
+ lineHeight: 1.5
3997
+ };
3998
+ var buttonStyle3 = {
3999
+ background: "#1a1a1a",
4000
+ color: "#fff",
4001
+ border: "none",
4002
+ borderRadius: 12,
4003
+ padding: "12px 32px",
4004
+ fontSize: "0.95rem",
4005
+ fontWeight: 600,
4006
+ fontFamily: "inherit",
4007
+ cursor: "pointer"
4008
+ };
4009
+ function isInIframe() {
4010
+ try {
4011
+ return typeof window !== "undefined" && window !== window.top;
4012
+ } catch {
4013
+ return true;
4014
+ }
4015
+ }
3851
4016
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
3852
4017
  var MIN_SEND_AMOUNT_USD = 0.25;
3853
4018
  function computeSmartDefaults(accts, transferAmount) {
@@ -3918,7 +4083,14 @@ function buildSelectSourceChoices(options) {
3918
4083
  }
3919
4084
  return chainChoices;
3920
4085
  }
3921
- function SwypePayment({
4086
+ function SwypePayment(props) {
4087
+ const resetKey = useRef(0);
4088
+ const handleBoundaryReset = useCallback(() => {
4089
+ resetKey.current += 1;
4090
+ }, []);
4091
+ return /* @__PURE__ */ jsx(PaymentErrorBoundary, { onReset: handleBoundaryReset, children: /* @__PURE__ */ jsx(SwypePaymentInner, { ...props }) }, resetKey.current);
4092
+ }
4093
+ function SwypePaymentInner({
3922
4094
  destination,
3923
4095
  onComplete,
3924
4096
  onError,
@@ -3968,6 +4140,7 @@ function SwypePayment({
3968
4140
  const [otpCode, setOtpCode] = useState("");
3969
4141
  const [oneTapLimit, setOneTapLimit] = useState(100);
3970
4142
  const [mobileFlow, setMobileFlow] = useState(false);
4143
+ const [deeplinkUri, setDeeplinkUri] = useState(null);
3971
4144
  const pollingTransferIdRef = useRef(null);
3972
4145
  const mobileSigningTransferIdRef = useRef(null);
3973
4146
  const mobileSetupFlowRef = useRef(false);
@@ -4108,7 +4281,7 @@ function SwypePayment({
4108
4281
  }
4109
4282
  setStep("create-passkey");
4110
4283
  } catch {
4111
- if (!cancelled) setStep("deposit");
4284
+ if (!cancelled) setStep("create-passkey");
4112
4285
  }
4113
4286
  };
4114
4287
  checkPasskey();
@@ -4215,6 +4388,7 @@ function SwypePayment({
4215
4388
  if (mobileSetupFlowRef.current) {
4216
4389
  mobileSetupFlowRef.current = false;
4217
4390
  setMobileFlow(false);
4391
+ setDeeplinkUri(null);
4218
4392
  polling.stopPolling();
4219
4393
  setTransfer(polledTransfer);
4220
4394
  reloadAccounts().catch(() => {
@@ -4355,12 +4529,15 @@ function SwypePayment({
4355
4529
  return;
4356
4530
  }
4357
4531
  const isSetupRedirect = mobileSetupFlowRef.current;
4358
- if (!isSetupRedirect) {
4532
+ if (isSetupRedirect) {
4533
+ setStep("open-wallet");
4534
+ } else {
4359
4535
  setStep("processing");
4360
4536
  }
4361
4537
  processingStartedAtRef.current = Date.now();
4362
4538
  setError(null);
4363
4539
  setCreatingTransfer(true);
4540
+ setDeeplinkUri(null);
4364
4541
  setMobileFlow(false);
4365
4542
  try {
4366
4543
  if (transfer?.status === "AUTHORIZED") {
@@ -4384,7 +4561,7 @@ function SwypePayment({
4384
4561
  const isActiveWallet = effectiveSourceType === "walletId" && accounts.some(
4385
4562
  (a) => a.wallets.some((w) => w.id === effectiveSourceId && w.status === "ACTIVE")
4386
4563
  );
4387
- if (!isActiveWallet) {
4564
+ if (!isActiveWallet && !isSetupRedirect) {
4388
4565
  let found = false;
4389
4566
  for (const acct of accounts) {
4390
4567
  for (const wallet of acct.wallets) {
@@ -4414,11 +4591,14 @@ function SwypePayment({
4414
4591
  userAgent: typeof navigator === "undefined" ? void 0 : navigator.userAgent
4415
4592
  });
4416
4593
  if (!shouldUseConnector) {
4594
+ const uri = t.authorizationSessions[0].uri;
4417
4595
  setMobileFlow(true);
4418
4596
  pollingTransferIdRef.current = t.id;
4419
4597
  polling.startPolling(t.id);
4420
- if (!isActiveWallet) {
4421
- window.location.href = t.authorizationSessions[0].uri;
4598
+ setDeeplinkUri(uri);
4599
+ setStep("open-wallet");
4600
+ if (!isInIframe()) {
4601
+ window.location.href = uri;
4422
4602
  }
4423
4603
  return;
4424
4604
  } else {
@@ -4432,7 +4612,7 @@ function SwypePayment({
4432
4612
  const msg = err instanceof Error ? err.message : "Transfer failed";
4433
4613
  setError(msg);
4434
4614
  onError?.(msg);
4435
- setStep("deposit");
4615
+ setStep(isSetupRedirect ? "wallet-picker" : "deposit");
4436
4616
  } finally {
4437
4617
  setCreatingTransfer(false);
4438
4618
  }
@@ -4481,16 +4661,6 @@ function SwypePayment({
4481
4661
  setRegisteringPasskey(false);
4482
4662
  }
4483
4663
  }, [getAccessToken, user, apiBaseUrl, accounts]);
4484
- const handleSkipPasskey = useCallback(() => {
4485
- const hasActiveWallet = accounts.some(
4486
- (a) => a.wallets.some((w) => w.status === "ACTIVE")
4487
- );
4488
- if (accounts.length === 0 || !hasActiveWallet) {
4489
- setStep("wallet-picker");
4490
- } else {
4491
- setStep("deposit");
4492
- }
4493
- }, [accounts]);
4494
4664
  const handleSelectProvider = useCallback((providerId) => {
4495
4665
  setSelectedProviderId(providerId);
4496
4666
  setSelectedAccountId(null);
@@ -4523,6 +4693,7 @@ function SwypePayment({
4523
4693
  setError(null);
4524
4694
  setAmount(depositAmount != null ? depositAmount.toString() : "");
4525
4695
  setMobileFlow(false);
4696
+ setDeeplinkUri(null);
4526
4697
  processingStartedAtRef.current = null;
4527
4698
  pollingTransferIdRef.current = null;
4528
4699
  mobileSigningTransferIdRef.current = null;
@@ -4554,6 +4725,7 @@ function SwypePayment({
4554
4725
  setConnectingNewAccount(false);
4555
4726
  setAmount(depositAmount != null ? depositAmount.toString() : "");
4556
4727
  setMobileFlow(false);
4728
+ setDeeplinkUri(null);
4557
4729
  resetHeadlessLogin();
4558
4730
  }, [logout, polling, depositAmount, resetHeadlessLogin]);
4559
4731
  if (!ready) {
@@ -4604,8 +4776,7 @@ function SwypePayment({
4604
4776
  CreatePasskeyScreen,
4605
4777
  {
4606
4778
  onCreatePasskey: handleRegisterPasskey,
4607
- onSkip: handleSkipPasskey,
4608
- onBack: () => setStep("login"),
4779
+ onBack: handleLogout,
4609
4780
  creating: registeringPasskey,
4610
4781
  error
4611
4782
  }
@@ -4617,9 +4788,22 @@ function SwypePayment({
4617
4788
  {
4618
4789
  providers,
4619
4790
  pendingConnections,
4791
+ loading: creatingTransfer,
4620
4792
  onSelectProvider: handleSelectProvider,
4621
4793
  onContinueConnection: handleContinueConnection,
4622
- onBack: () => setStep("create-passkey")
4794
+ onBack: () => setStep(activeCredentialId ? "deposit" : "create-passkey")
4795
+ }
4796
+ );
4797
+ }
4798
+ if (step === "open-wallet") {
4799
+ const providerName = providers.find((p) => p.id === selectedProviderId)?.name ?? null;
4800
+ return /* @__PURE__ */ jsx(
4801
+ OpenWalletScreen,
4802
+ {
4803
+ walletName: providerName,
4804
+ deeplinkUri: deeplinkUri ?? "",
4805
+ loading: creatingTransfer || !deeplinkUri,
4806
+ onLogout: handleLogout
4623
4807
  }
4624
4808
  );
4625
4809
  }