@swype-org/react-sdk 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useRef, useState, useCallback, useMemo, useContext, useEffect } from 'react';
2
- import { PrivyProvider, usePrivy, useLoginWithEmail, useLoginWithSms } from '@privy-io/react-auth';
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';
5
5
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -1448,8 +1448,20 @@ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
1448
1448
  if (!processingStartedAtMs) return false;
1449
1449
  return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
1450
1450
  }
1451
+ var STATUS_DISPLAY_LABELS = {
1452
+ CREATED: "created",
1453
+ AUTHORIZED: "authorized",
1454
+ SENDING: "sending",
1455
+ SENT: "confirming delivery",
1456
+ COMPLETED: "completed",
1457
+ FAILED: "failed"
1458
+ };
1459
+ function getStatusDisplayLabel(status) {
1460
+ return STATUS_DISPLAY_LABELS[status] ?? status;
1461
+ }
1451
1462
  function buildProcessingTimeoutMessage(status) {
1452
- return `Payment is taking longer than expected (status: ${status}). Please try again.`;
1463
+ const label = getStatusDisplayLabel(status);
1464
+ return `Payment is taking longer than expected (status: ${label}). Please try again.`;
1453
1465
  }
1454
1466
 
1455
1467
  // src/walletFlow.ts
@@ -1712,7 +1724,7 @@ var circleStyle = (bg, size) => ({
1712
1724
  function OtpInput({ value, onChange, length = 6, disabled }) {
1713
1725
  const { tokens } = useSwypeConfig();
1714
1726
  const inputsRef = useRef([]);
1715
- const digits = value.padEnd(length, "").split("").slice(0, length);
1727
+ const digits = value.padEnd(length).split("").slice(0, length);
1716
1728
  const focusInput = useCallback((index) => {
1717
1729
  const clamped = Math.max(0, Math.min(index, length - 1));
1718
1730
  inputsRef.current[clamped]?.focus();
@@ -2115,34 +2127,50 @@ function LoginScreen({
2115
2127
  onSubmit,
2116
2128
  sending,
2117
2129
  error,
2118
- onBack
2130
+ onBack,
2131
+ merchantInitials,
2132
+ onSocialLogin
2119
2133
  }) {
2120
2134
  const { tokens } = useSwypeConfig();
2121
2135
  const disabled = authInput.trim().length === 0 || sending;
2122
- const walletLogos = ["MM", "R", "O", "P", "+"];
2123
2136
  return /* @__PURE__ */ jsxs(
2124
2137
  ScreenLayout,
2125
2138
  {
2126
2139
  footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2127
2140
  /* @__PURE__ */ jsx(PrimaryButton, { onClick: onSubmit, disabled, loading: sending, children: "Continue" }),
2128
- /* @__PURE__ */ jsxs("div", { style: dividerStyle(tokens), children: [
2129
- /* @__PURE__ */ jsx("div", { style: dividerLineStyle(tokens.border) }),
2130
- /* @__PURE__ */ jsx("span", { children: "works with" }),
2131
- /* @__PURE__ */ jsx("div", { style: dividerLineStyle(tokens.border) })
2141
+ onSocialLogin && /* @__PURE__ */ jsxs(Fragment, { children: [
2142
+ /* @__PURE__ */ jsxs("div", { style: dividerStyle(tokens), children: [
2143
+ /* @__PURE__ */ jsx("div", { style: dividerLineStyle(tokens.border) }),
2144
+ /* @__PURE__ */ jsx("span", { children: "or continue with" }),
2145
+ /* @__PURE__ */ jsx("div", { style: dividerLineStyle(tokens.border) })
2146
+ ] }),
2147
+ /* @__PURE__ */ jsx("div", { style: socialRowStyle, children: ["google", "apple", "twitter"].map((provider) => /* @__PURE__ */ jsx(
2148
+ "button",
2149
+ {
2150
+ type: "button",
2151
+ onClick: () => onSocialLogin(provider),
2152
+ style: socialButtonStyle(tokens),
2153
+ children: socialLabel(provider)
2154
+ },
2155
+ provider
2156
+ )) })
2157
+ ] }),
2158
+ /* @__PURE__ */ jsxs("div", { style: walletSectionStyle, children: [
2159
+ /* @__PURE__ */ jsx("span", { style: walletLabelStyle(tokens.textMuted), children: "Works with" }),
2160
+ /* @__PURE__ */ jsx("div", { style: walletLogosStyle, children: walletIcons.map(({ key, emoji }) => /* @__PURE__ */ jsx("span", { style: walletEmojiStyle, children: emoji }, key)) })
2132
2161
  ] }),
2133
- /* @__PURE__ */ jsx("div", { style: logosRowStyle, children: walletLogos.map((label) => /* @__PURE__ */ jsx("div", { style: logoCircleStyle(tokens), children: label }, label)) }),
2134
2162
  /* @__PURE__ */ jsx(PoweredByFooter, {})
2135
2163
  ] }),
2136
2164
  children: [
2137
- /* @__PURE__ */ jsx(ScreenHeader, { onBack }),
2165
+ /* @__PURE__ */ jsx(
2166
+ ScreenHeader,
2167
+ {
2168
+ onBack,
2169
+ right: merchantInitials ? /* @__PURE__ */ jsx("div", { style: avatarStyle(tokens), children: merchantInitials }) : void 0
2170
+ }
2171
+ ),
2138
2172
  /* @__PURE__ */ jsxs("div", { style: contentStyle, children: [
2139
- /* @__PURE__ */ jsx(IconCircle, { variant: "accent", size: 56, children: /* @__PURE__ */ jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx(
2140
- "path",
2141
- {
2142
- d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z",
2143
- fill: tokens.accent
2144
- }
2145
- ) }) }),
2173
+ /* @__PURE__ */ jsx("div", { style: iconBoxStyle(tokens.accent), children: /* @__PURE__ */ jsx("span", { style: iconLetterStyle, children: "S" }) }),
2146
2174
  /* @__PURE__ */ jsx("h2", { style: headingStyle(tokens.text), children: "One-time setup.\nOne-tap deposits after." }),
2147
2175
  /* @__PURE__ */ jsx("p", { style: subtitleStyle(tokens.textSecondary), children: "Protected by Face ID." }),
2148
2176
  error && /* @__PURE__ */ jsx("div", { style: errorStyle(tokens), children: error }),
@@ -2167,6 +2195,23 @@ function LoginScreen({
2167
2195
  }
2168
2196
  );
2169
2197
  }
2198
+ var walletIcons = [
2199
+ { key: "metamask", emoji: "\u{1F98A}" },
2200
+ { key: "rabby", emoji: "\u{1F430}" },
2201
+ { key: "phantom", emoji: "\u25C6" },
2202
+ { key: "rainbow", emoji: "\u{1F439}" },
2203
+ { key: "coinbase", emoji: "\u{1F535}" }
2204
+ ];
2205
+ function socialLabel(provider) {
2206
+ switch (provider) {
2207
+ case "google":
2208
+ return "Google";
2209
+ case "apple":
2210
+ return "Apple";
2211
+ case "twitter":
2212
+ return "X";
2213
+ }
2214
+ }
2170
2215
  var contentStyle = {
2171
2216
  textAlign: "center",
2172
2217
  flex: 1,
@@ -2175,10 +2220,25 @@ var contentStyle = {
2175
2220
  alignItems: "center",
2176
2221
  paddingTop: 24
2177
2222
  };
2223
+ var iconBoxStyle = (accent) => ({
2224
+ width: 56,
2225
+ height: 56,
2226
+ borderRadius: 16,
2227
+ background: accent,
2228
+ display: "flex",
2229
+ alignItems: "center",
2230
+ justifyContent: "center"
2231
+ });
2232
+ var iconLetterStyle = {
2233
+ color: "#fff",
2234
+ fontSize: "1.5rem",
2235
+ fontWeight: 700,
2236
+ lineHeight: 1
2237
+ };
2178
2238
  var headingStyle = (color) => ({
2179
- fontSize: "1.65rem",
2239
+ fontSize: "1.45rem",
2180
2240
  fontWeight: 700,
2181
- lineHeight: 1.2,
2241
+ lineHeight: 1.25,
2182
2242
  letterSpacing: "-0.02em",
2183
2243
  color,
2184
2244
  margin: "20px 0 8px",
@@ -2227,24 +2287,55 @@ var dividerLineStyle = (color) => ({
2227
2287
  height: 1,
2228
2288
  background: color
2229
2289
  });
2230
- var logosRowStyle = {
2290
+ var socialRowStyle = {
2291
+ display: "flex",
2292
+ gap: 10,
2293
+ marginBottom: 20
2294
+ };
2295
+ var socialButtonStyle = (tokens) => ({
2296
+ flex: 1,
2297
+ padding: "12px 0",
2298
+ borderRadius: 12,
2299
+ border: `1px solid ${tokens.border}`,
2300
+ background: "transparent",
2301
+ color: tokens.text,
2302
+ fontSize: "0.88rem",
2303
+ fontWeight: 600,
2304
+ fontFamily: "inherit",
2305
+ cursor: "pointer"
2306
+ });
2307
+ var walletSectionStyle = {
2308
+ textAlign: "center",
2309
+ marginBottom: 8,
2310
+ marginTop: 4
2311
+ };
2312
+ var walletLabelStyle = (color) => ({
2313
+ fontSize: "0.78rem",
2314
+ color,
2315
+ display: "block",
2316
+ marginBottom: 10
2317
+ });
2318
+ var walletLogosStyle = {
2231
2319
  display: "flex",
2232
2320
  justifyContent: "center",
2233
- gap: 12,
2234
- marginBottom: 8
2321
+ gap: 16
2235
2322
  };
2236
- var logoCircleStyle = (tokens) => ({
2237
- width: 34,
2238
- height: 34,
2239
- borderRadius: 999,
2323
+ var walletEmojiStyle = {
2324
+ fontSize: "1.4rem",
2325
+ lineHeight: 1
2326
+ };
2327
+ var avatarStyle = (tokens) => ({
2328
+ width: 28,
2329
+ height: 28,
2330
+ borderRadius: "50%",
2240
2331
  border: `1px solid ${tokens.border}`,
2241
- background: tokens.bgInput,
2242
- color: tokens.textMuted,
2243
2332
  display: "flex",
2244
2333
  alignItems: "center",
2245
2334
  justifyContent: "center",
2246
- fontSize: "0.72rem",
2247
- fontWeight: 600
2335
+ fontSize: "0.6rem",
2336
+ fontWeight: 700,
2337
+ color: tokens.textMuted,
2338
+ background: "transparent"
2248
2339
  });
2249
2340
  var RESEND_COOLDOWN_SECONDS = 30;
2250
2341
  function OtpVerifyScreen({
@@ -2896,7 +2987,7 @@ var swipeHintStyle = (color) => ({
2896
2987
  color,
2897
2988
  margin: "12px 0 0"
2898
2989
  });
2899
- var MIN_DEPOSIT = 1;
2990
+ var MIN_DEPOSIT = 0.25;
2900
2991
  function DepositScreen({
2901
2992
  merchantName,
2902
2993
  sourceName,
@@ -2917,8 +3008,7 @@ function DepositScreen({
2917
3008
  onLogout
2918
3009
  }) {
2919
3010
  const { tokens } = useSwypeConfig();
2920
- const [amount, setAmount] = useState(initialAmount);
2921
- const sliderMax = Math.min(remainingLimit, availableBalance, 500);
3011
+ const amount = initialAmount;
2922
3012
  const isLowBalance = availableBalance < MIN_DEPOSIT;
2923
3013
  const canDeposit = amount >= MIN_DEPOSIT && amount <= remainingLimit && !isLowBalance && !processing;
2924
3014
  const headerTitle = merchantName ? `Deposit to ${merchantName}` : "Deposit";
@@ -3030,17 +3120,6 @@ function DepositScreen({
3030
3120
  /* @__PURE__ */ jsx("span", { style: chevronStyle2, children: ">" })
3031
3121
  ] })
3032
3122
  ] }),
3033
- /* @__PURE__ */ jsx(
3034
- LimitSlider,
3035
- {
3036
- value: amount,
3037
- min: MIN_DEPOSIT,
3038
- max: sliderMax > MIN_DEPOSIT ? sliderMax : 20,
3039
- step: 0.5,
3040
- ticks: [MIN_DEPOSIT, 5, 10, 20].filter((t) => t <= sliderMax || t <= 20),
3041
- onChange: setAmount
3042
- }
3043
- ),
3044
3123
  /* @__PURE__ */ jsxs("div", { style: detailsStyle, children: [
3045
3124
  /* @__PURE__ */ jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
3046
3125
  "Remaining limit: ",
@@ -3049,13 +3128,13 @@ function DepositScreen({
3049
3128
  remainingLimit.toFixed(2)
3050
3129
  ] })
3051
3130
  ] }),
3052
- estimatedFeeUsd != null && estimatedFeePct != null && /* @__PURE__ */ jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
3131
+ estimatedFeeUsd != null && estimatedFeePct != null ? /* @__PURE__ */ jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
3053
3132
  "Fee: ~$",
3054
3133
  estimatedFeeUsd.toFixed(2),
3055
3134
  " (",
3056
3135
  estimatedFeePct.toFixed(1),
3057
3136
  "%)"
3058
- ] })
3137
+ ] }) : /* @__PURE__ */ jsx("div", { style: detailRowStyle(tokens.textMuted), children: "Fees calculated at time of transfer" })
3059
3138
  ] }),
3060
3139
  error && /* @__PURE__ */ jsx("div", { style: errorBannerStyle5(tokens), children: error })
3061
3140
  ]
@@ -3165,14 +3244,45 @@ function SuccessScreen({
3165
3244
  onDone,
3166
3245
  onLogout,
3167
3246
  onIncreaseLimits,
3168
- onManageAccount
3247
+ onManageAccount,
3248
+ autoCloseSeconds
3169
3249
  }) {
3170
3250
  const { tokens } = useSwypeConfig();
3251
+ const [countdown, setCountdown] = useState(autoCloseSeconds ?? 0);
3252
+ const doneCalledRef = useRef(false);
3253
+ const handleDone = useCallback(() => {
3254
+ if (doneCalledRef.current) return;
3255
+ doneCalledRef.current = true;
3256
+ onDone();
3257
+ }, [onDone]);
3258
+ useEffect(() => {
3259
+ if (!autoCloseSeconds || autoCloseSeconds <= 0) return;
3260
+ const intervalId = window.setInterval(() => {
3261
+ setCountdown((prev) => {
3262
+ if (prev <= 1) {
3263
+ window.clearInterval(intervalId);
3264
+ return 0;
3265
+ }
3266
+ return prev - 1;
3267
+ });
3268
+ }, 1e3);
3269
+ return () => window.clearInterval(intervalId);
3270
+ }, [autoCloseSeconds]);
3271
+ useEffect(() => {
3272
+ if (autoCloseSeconds && countdown === 0) {
3273
+ handleDone();
3274
+ }
3275
+ }, [autoCloseSeconds, countdown, handleDone]);
3171
3276
  return /* @__PURE__ */ jsxs(
3172
3277
  ScreenLayout,
3173
3278
  {
3174
3279
  footer: /* @__PURE__ */ jsxs(Fragment, { children: [
3175
- /* @__PURE__ */ jsx(PrimaryButton, { onClick: onDone, children: "Done" }),
3280
+ /* @__PURE__ */ jsx(PrimaryButton, { onClick: handleDone, children: "Done" }),
3281
+ autoCloseSeconds != null && autoCloseSeconds > 0 && /* @__PURE__ */ jsxs("p", { style: countdownStyle(tokens.textMuted), children: [
3282
+ "Returning to app in ",
3283
+ countdown,
3284
+ "s\u2026"
3285
+ ] }),
3176
3286
  onManageAccount && /* @__PURE__ */ jsx("button", { type: "button", onClick: onManageAccount, style: manageStyle(tokens.textMuted), children: "Manage Swype account \u2192" }),
3177
3287
  /* @__PURE__ */ jsx(PoweredByFooter, {})
3178
3288
  ] }),
@@ -3296,6 +3406,12 @@ var upsellLinkStyle = (color) => ({
3296
3406
  fontFamily: "inherit",
3297
3407
  padding: 0
3298
3408
  });
3409
+ var countdownStyle = (color) => ({
3410
+ fontSize: "0.82rem",
3411
+ color,
3412
+ margin: "10px 0 0",
3413
+ textAlign: "center"
3414
+ });
3299
3415
  var manageStyle = (color) => ({
3300
3416
  background: "transparent",
3301
3417
  border: "none",
@@ -3309,6 +3425,233 @@ var manageStyle = (color) => ({
3309
3425
  textAlign: "center",
3310
3426
  padding: "12px 0 0"
3311
3427
  });
3428
+ function SelectSourceScreen({
3429
+ choices,
3430
+ selectedChainName,
3431
+ selectedTokenSymbol,
3432
+ recommended,
3433
+ onChainChange,
3434
+ onTokenChange,
3435
+ onConfirm,
3436
+ onLogout
3437
+ }) {
3438
+ const { tokens } = useSwypeConfig();
3439
+ const selectedChain = choices.find((c) => c.chainName === selectedChainName) ?? choices[0];
3440
+ const tokenChoices = selectedChain?.tokens ?? [];
3441
+ const canConfirm = !!selectedChainName && !!selectedTokenSymbol;
3442
+ return /* @__PURE__ */ jsxs(
3443
+ ScreenLayout,
3444
+ {
3445
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
3446
+ /* @__PURE__ */ jsx(PrimaryButton, { onClick: onConfirm, disabled: !canConfirm, children: "Confirm source" }),
3447
+ /* @__PURE__ */ jsx(PoweredByFooter, {})
3448
+ ] }),
3449
+ children: [
3450
+ /* @__PURE__ */ jsx(
3451
+ ScreenHeader,
3452
+ {
3453
+ title: "Select Source",
3454
+ right: /* @__PURE__ */ jsx(SettingsMenu, { onLogout })
3455
+ }
3456
+ ),
3457
+ /* @__PURE__ */ jsx("p", { style: subtitleStyle7(tokens.textMuted), children: "Choose which chain and token to pay from." }),
3458
+ /* @__PURE__ */ jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Chain" }),
3459
+ /* @__PURE__ */ jsx("div", { style: optionListStyle, children: choices.map((chain) => {
3460
+ const isSelected = chain.chainName === selectedChainName;
3461
+ const isRecommended = chain.chainName === recommended?.chainName;
3462
+ return /* @__PURE__ */ jsxs(
3463
+ "button",
3464
+ {
3465
+ type: "button",
3466
+ onClick: () => onChainChange(chain.chainName),
3467
+ style: optionButtonStyle(tokens, isSelected),
3468
+ children: [
3469
+ /* @__PURE__ */ jsxs("div", { style: optionContentStyle, children: [
3470
+ /* @__PURE__ */ jsxs("span", { style: optionNameStyle(tokens.text), children: [
3471
+ chain.chainName,
3472
+ isRecommended && /* @__PURE__ */ jsx("span", { style: recommendedBadgeStyle(tokens.accent), children: " recommended" })
3473
+ ] }),
3474
+ /* @__PURE__ */ jsxs("span", { style: optionBalanceStyle(tokens.textMuted), children: [
3475
+ "$",
3476
+ chain.balance.toFixed(2)
3477
+ ] })
3478
+ ] }),
3479
+ /* @__PURE__ */ jsx("div", { style: radioStyle(tokens, isSelected), children: isSelected && /* @__PURE__ */ jsx("div", { style: radioDotStyle(tokens.accent) }) })
3480
+ ]
3481
+ },
3482
+ chain.chainName
3483
+ );
3484
+ }) }),
3485
+ tokenChoices.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
3486
+ /* @__PURE__ */ jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Token" }),
3487
+ /* @__PURE__ */ jsx("div", { style: optionListStyle, children: tokenChoices.map((token) => {
3488
+ const isSelected = token.tokenSymbol === selectedTokenSymbol;
3489
+ return /* @__PURE__ */ jsxs(
3490
+ "button",
3491
+ {
3492
+ type: "button",
3493
+ onClick: () => onTokenChange(token.tokenSymbol),
3494
+ style: optionButtonStyle(tokens, isSelected),
3495
+ children: [
3496
+ /* @__PURE__ */ jsxs("div", { style: optionContentStyle, children: [
3497
+ /* @__PURE__ */ jsx("span", { style: optionNameStyle(tokens.text), children: token.tokenSymbol }),
3498
+ /* @__PURE__ */ jsxs("span", { style: optionBalanceStyle(tokens.textMuted), children: [
3499
+ "$",
3500
+ token.balance.toFixed(2)
3501
+ ] })
3502
+ ] }),
3503
+ /* @__PURE__ */ jsx("div", { style: radioStyle(tokens, isSelected), children: isSelected && /* @__PURE__ */ jsx("div", { style: radioDotStyle(tokens.accent) }) })
3504
+ ]
3505
+ },
3506
+ token.tokenSymbol
3507
+ );
3508
+ }) })
3509
+ ] })
3510
+ ]
3511
+ }
3512
+ );
3513
+ }
3514
+ var subtitleStyle7 = (color) => ({
3515
+ fontSize: "0.85rem",
3516
+ color,
3517
+ margin: "0 0 20px",
3518
+ lineHeight: 1.5
3519
+ });
3520
+ var labelStyle2 = (color) => ({
3521
+ display: "block",
3522
+ fontSize: "0.75rem",
3523
+ fontWeight: 600,
3524
+ color,
3525
+ textTransform: "uppercase",
3526
+ letterSpacing: "0.05em",
3527
+ marginBottom: 8
3528
+ });
3529
+ var optionListStyle = {
3530
+ display: "flex",
3531
+ flexDirection: "column",
3532
+ gap: 8,
3533
+ marginBottom: 20
3534
+ };
3535
+ var optionButtonStyle = (tokens, selected) => ({
3536
+ display: "flex",
3537
+ alignItems: "center",
3538
+ justifyContent: "space-between",
3539
+ padding: "14px 16px",
3540
+ background: tokens.bgInput,
3541
+ border: `1.5px solid ${selected ? tokens.accent : tokens.border}`,
3542
+ borderRadius: 14,
3543
+ cursor: "pointer",
3544
+ fontFamily: "inherit",
3545
+ textAlign: "left",
3546
+ transition: "border-color 0.15s ease"
3547
+ });
3548
+ var optionContentStyle = {
3549
+ display: "flex",
3550
+ flexDirection: "column",
3551
+ gap: 2
3552
+ };
3553
+ var optionNameStyle = (color) => ({
3554
+ fontSize: "0.9rem",
3555
+ fontWeight: 600,
3556
+ color
3557
+ });
3558
+ var recommendedBadgeStyle = (color) => ({
3559
+ fontSize: "0.7rem",
3560
+ fontWeight: 500,
3561
+ color,
3562
+ marginLeft: 6
3563
+ });
3564
+ var optionBalanceStyle = (color) => ({
3565
+ fontSize: "0.78rem",
3566
+ color
3567
+ });
3568
+ var radioStyle = (tokens, selected) => ({
3569
+ width: 20,
3570
+ height: 20,
3571
+ borderRadius: "50%",
3572
+ border: `2px solid ${selected ? tokens.accent : tokens.border}`,
3573
+ display: "flex",
3574
+ alignItems: "center",
3575
+ justifyContent: "center",
3576
+ flexShrink: 0
3577
+ });
3578
+ var radioDotStyle = (color) => ({
3579
+ width: 10,
3580
+ height: 10,
3581
+ borderRadius: "50%",
3582
+ background: color
3583
+ });
3584
+ var STEP_LABELS = ["Creating transfer", "Verifying", "Sent", "Done"];
3585
+ var PHASE_ACTIVE_INDEX = {
3586
+ creating: 0,
3587
+ verifying: 1,
3588
+ sent: 2,
3589
+ done: 4
3590
+ };
3591
+ function buildSteps(phase) {
3592
+ const activeIdx = PHASE_ACTIVE_INDEX[phase];
3593
+ return STEP_LABELS.map((label, i) => ({
3594
+ label,
3595
+ status: i < activeIdx ? "complete" : i === activeIdx ? "active" : "pending"
3596
+ }));
3597
+ }
3598
+ function TransferStatusScreen({
3599
+ phase,
3600
+ error,
3601
+ onLogout
3602
+ }) {
3603
+ const { tokens } = useSwypeConfig();
3604
+ const steps = buildSteps(phase);
3605
+ return /* @__PURE__ */ jsxs(ScreenLayout, { children: [
3606
+ /* @__PURE__ */ jsx(ScreenHeader, { right: /* @__PURE__ */ jsx(SettingsMenu, { onLogout }) }),
3607
+ /* @__PURE__ */ jsxs("div", { style: contentStyle6, children: [
3608
+ /* @__PURE__ */ jsx(Spinner, { size: 48 }),
3609
+ /* @__PURE__ */ jsx("h2", { style: headingStyle8(tokens.text), children: "Processing Transfer..." }),
3610
+ error && /* @__PURE__ */ jsx("div", { style: errorBannerStyle6(tokens), children: error }),
3611
+ /* @__PURE__ */ jsx("div", { style: stepsWrapStyle2, children: /* @__PURE__ */ jsx(StepList, { steps }) }),
3612
+ /* @__PURE__ */ jsx("p", { style: waitHintStyle2(tokens.textMuted), children: "Usually takes a few seconds" })
3613
+ ] })
3614
+ ] });
3615
+ }
3616
+ var contentStyle6 = {
3617
+ flex: 1,
3618
+ display: "flex",
3619
+ flexDirection: "column",
3620
+ alignItems: "center",
3621
+ justifyContent: "center",
3622
+ textAlign: "center",
3623
+ padding: "0 24px"
3624
+ };
3625
+ var headingStyle8 = (color) => ({
3626
+ fontSize: "1.45rem",
3627
+ fontWeight: 700,
3628
+ letterSpacing: "-0.02em",
3629
+ color,
3630
+ margin: "20px 0 16px"
3631
+ });
3632
+ var errorBannerStyle6 = (tokens) => ({
3633
+ background: tokens.errorBg,
3634
+ border: `1px solid ${tokens.error}66`,
3635
+ borderRadius: 16,
3636
+ padding: "11px 14px",
3637
+ color: tokens.error,
3638
+ fontSize: "0.84rem",
3639
+ marginBottom: 14,
3640
+ lineHeight: 1.5,
3641
+ width: "100%",
3642
+ textAlign: "left"
3643
+ });
3644
+ var stepsWrapStyle2 = {
3645
+ width: "100%",
3646
+ maxWidth: 280,
3647
+ textAlign: "left",
3648
+ marginBottom: 16
3649
+ };
3650
+ var waitHintStyle2 = (color) => ({
3651
+ fontSize: "0.82rem",
3652
+ color,
3653
+ margin: 0
3654
+ });
3312
3655
  var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
3313
3656
  var MIN_SEND_AMOUNT_USD = 0.25;
3314
3657
  function computeSmartDefaults(accts, transferAmount) {
@@ -3387,7 +3730,9 @@ function SwypePayment({
3387
3730
  idempotencyKey,
3388
3731
  merchantAuthorization,
3389
3732
  merchantName,
3390
- onBack
3733
+ onBack,
3734
+ onDismiss,
3735
+ autoCloseSeconds
3391
3736
  }) {
3392
3737
  const { apiBaseUrl, tokens, depositAmount } = useSwypeConfig();
3393
3738
  const { ready, authenticated, user, logout, getAccessToken } = usePrivy();
@@ -3401,6 +3746,7 @@ function SwypePayment({
3401
3746
  loginWithCode: loginWithSmsCode,
3402
3747
  state: smsLoginState
3403
3748
  } = useLoginWithSms();
3749
+ const { initOAuth } = useLoginWithOAuth();
3404
3750
  const [step, setStep] = useState("login");
3405
3751
  const [error, setError] = useState(null);
3406
3752
  const [providers, setProviders] = useState([]);
@@ -3449,6 +3795,14 @@ function SwypePayment({
3449
3795
  setVerificationTarget(null);
3450
3796
  setOtpCode("");
3451
3797
  }, []);
3798
+ const handleSocialLogin = useCallback(async (provider) => {
3799
+ setError(null);
3800
+ try {
3801
+ await initOAuth({ provider });
3802
+ } catch (err) {
3803
+ setError(err instanceof Error ? err.message : "Social login failed");
3804
+ }
3805
+ }, [initOAuth]);
3452
3806
  const activeOtpStatus = verificationTarget?.kind === "email" ? emailLoginState.status : verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
3453
3807
  const activeOtpErrorMessage = verificationTarget?.kind === "email" && emailLoginState.status === "error" ? emailLoginState.error?.message ?? "Failed to continue with email." : verificationTarget?.kind === "phone" && smsLoginState.status === "error" ? smsLoginState.error?.message ?? "Failed to continue with phone number." : null;
3454
3808
  useEffect(() => {
@@ -3517,6 +3871,9 @@ function SwypePayment({
3517
3871
  if (!token || cancelled) return;
3518
3872
  const { config } = await fetchUserConfig(apiBaseUrl, token);
3519
3873
  if (cancelled) return;
3874
+ if (config.defaultAllowance != null) {
3875
+ setOneTapLimit(config.defaultAllowance);
3876
+ }
3520
3877
  const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
3521
3878
  if (allPasskeys.length === 0) {
3522
3879
  setStep("create-passkey");
@@ -3701,6 +4058,26 @@ function SwypePayment({
3701
4058
  }
3702
4059
  initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
3703
4060
  }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
4061
+ useEffect(() => {
4062
+ if (pendingSelectSourceAction && step === "processing") {
4063
+ setStep("select-source");
4064
+ } else if (!pendingSelectSourceAction && step === "select-source") {
4065
+ setStep("processing");
4066
+ }
4067
+ }, [pendingSelectSourceAction, step]);
4068
+ const handleSelectSourceChainChange = useCallback(
4069
+ (chainName) => {
4070
+ setSelectSourceChainName(chainName);
4071
+ const chain = selectSourceChoices.find((c) => c.chainName === chainName);
4072
+ if (!chain || chain.tokens.length === 0) return;
4073
+ const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
4074
+ const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
4075
+ setSelectSourceTokenSymbol(
4076
+ hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
4077
+ );
4078
+ },
4079
+ [selectSourceChoices, selectSourceRecommended]
4080
+ );
3704
4081
  const selectedAccount = accounts.find((a) => a.id === selectedAccountId);
3705
4082
  const selectedWallet = selectedAccount?.wallets.find((w) => w.id === selectedWalletId);
3706
4083
  const sourceName = selectedAccount?.name ?? selectedWallet?.chain.name ?? "Wallet";
@@ -3876,6 +4253,7 @@ function SwypePayment({
3876
4253
  }
3877
4254
  const setupAmount = depositAmount ?? MIN_SEND_AMOUNT_USD;
3878
4255
  const t = await createTransfer(apiBaseUrl, token, {
4256
+ id: idempotencyKey,
3879
4257
  credentialId: activeCredentialId ?? "",
3880
4258
  merchantAuthorization,
3881
4259
  sourceType,
@@ -3904,6 +4282,7 @@ function SwypePayment({
3904
4282
  }
3905
4283
  }, [
3906
4284
  getAccessToken,
4285
+ idempotencyKey,
3907
4286
  sourceId,
3908
4287
  sourceType,
3909
4288
  activeCredentialId,
@@ -3974,7 +4353,9 @@ function SwypePayment({
3974
4353
  onSubmit: handleSendLoginCode,
3975
4354
  sending: activeOtpStatus === "sending-code",
3976
4355
  error,
3977
- onBack
4356
+ onBack,
4357
+ merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0,
4358
+ onSocialLogin: handleSocialLogin
3978
4359
  }
3979
4360
  );
3980
4361
  }
@@ -4001,6 +4382,9 @@ function SwypePayment({
4001
4382
  }
4002
4383
  );
4003
4384
  }
4385
+ if ((step === "login" || step === "otp-verify") && authenticated) {
4386
+ return /* @__PURE__ */ jsx(ScreenLayout, { children: /* @__PURE__ */ jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(Spinner, { label: "Verifying your passkey..." }) }) });
4387
+ }
4004
4388
  if (step === "create-passkey") {
4005
4389
  return /* @__PURE__ */ jsx(
4006
4390
  CreatePasskeyScreen,
@@ -4066,11 +4450,9 @@ function SwypePayment({
4066
4450
  sourceAddress,
4067
4451
  sourceVerified,
4068
4452
  availableBalance: maxSourceBalance,
4069
- remainingLimit: oneTapLimit,
4453
+ remainingLimit: selectedAccount?.remainingAllowance ?? oneTapLimit,
4070
4454
  tokenCount,
4071
4455
  initialAmount: parsedAmt,
4072
- estimatedFeePct: 0.6,
4073
- estimatedFeeUsd: parsedAmt * 6e-3,
4074
4456
  processing: creatingTransfer,
4075
4457
  error,
4076
4458
  onDeposit: handlePay,
@@ -4082,24 +4464,36 @@ function SwypePayment({
4082
4464
  );
4083
4465
  }
4084
4466
  if (step === "processing") {
4085
- const currentActionType = authExecutor.currentAction?.type;
4086
- const statusLabel = creatingTransfer ? "Creating Transfer" : mobileFlow ? "Waiting for Authorization" : authExecutor.executing ? currentActionType?.replace(/_/g, " ") ?? "Authorizing" : transferSigning.signing ? "Sending transfer" : polling.isPolling ? "Transfer Sent" : "Please wait...";
4087
- return /* @__PURE__ */ jsx(ScreenLayout, { children: /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }, children: [
4088
- /* @__PURE__ */ jsx(Spinner, { size: 48 }),
4089
- /* @__PURE__ */ jsx("h2", { style: { fontSize: "1.4rem", fontWeight: 700, color: tokens.text, marginTop: 20, marginBottom: 8 }, children: statusLabel }),
4090
- (error || authExecutor.error || transferSigning.error || polling.error) && /* @__PURE__ */ jsx("div", { style: {
4091
- background: tokens.errorBg,
4092
- border: `1px solid ${tokens.error}66`,
4093
- borderRadius: 16,
4094
- padding: "11px 14px",
4095
- color: tokens.error,
4096
- fontSize: "0.84rem",
4097
- marginTop: 16,
4098
- lineHeight: 1.5,
4099
- textAlign: "left",
4100
- maxWidth: 340
4101
- }, children: error || authExecutor.error || transferSigning.error || polling.error })
4102
- ] }) });
4467
+ const polledStatus = polling.transfer?.status;
4468
+ const transferPhase = creatingTransfer ? "creating" : mobileFlow || authExecutor.executing || transferSigning.signing ? "verifying" : polledStatus === "SENDING" || polledStatus === "SENT" || polling.isPolling ? "sent" : "creating";
4469
+ return /* @__PURE__ */ jsx(
4470
+ TransferStatusScreen,
4471
+ {
4472
+ phase: transferPhase,
4473
+ error: error || authExecutor.error || transferSigning.error || polling.error,
4474
+ onLogout: handleLogout
4475
+ }
4476
+ );
4477
+ }
4478
+ if (step === "select-source") {
4479
+ return /* @__PURE__ */ jsx(
4480
+ SelectSourceScreen,
4481
+ {
4482
+ choices: selectSourceChoices,
4483
+ selectedChainName: selectSourceChainName,
4484
+ selectedTokenSymbol: selectSourceTokenSymbol,
4485
+ recommended: selectSourceRecommended,
4486
+ onChainChange: handleSelectSourceChainChange,
4487
+ onTokenChange: setSelectSourceTokenSymbol,
4488
+ onConfirm: () => {
4489
+ authExecutor.resolveSelectSource({
4490
+ chainName: selectSourceChainName,
4491
+ tokenSymbol: selectSourceTokenSymbol
4492
+ });
4493
+ },
4494
+ onLogout: handleLogout
4495
+ }
4496
+ );
4103
4497
  }
4104
4498
  if (step === "success") {
4105
4499
  transfer?.status === "COMPLETED";
@@ -4112,9 +4506,13 @@ function SwypePayment({
4112
4506
  currency: displayCurrency,
4113
4507
  merchantName,
4114
4508
  sourceName,
4115
- remainingLimit: oneTapLimit > displayAmount ? oneTapLimit - displayAmount : 0,
4116
- onDone: handleNewPayment,
4117
- onLogout: handleLogout
4509
+ remainingLimit: (() => {
4510
+ const limit = selectedAccount?.remainingAllowance ?? oneTapLimit;
4511
+ return limit > displayAmount ? limit - displayAmount : 0;
4512
+ })(),
4513
+ onDone: onDismiss ?? handleNewPayment,
4514
+ onLogout: handleLogout,
4515
+ autoCloseSeconds
4118
4516
  }
4119
4517
  );
4120
4518
  }
@@ -4127,7 +4525,7 @@ function SwypePayment({
4127
4525
  sourceAddress,
4128
4526
  sourceVerified,
4129
4527
  availableBalance: 0,
4130
- remainingLimit: oneTapLimit,
4528
+ remainingLimit: selectedAccount?.remainingAllowance ?? oneTapLimit,
4131
4529
  tokenCount,
4132
4530
  initialAmount: depositAmount ?? 5,
4133
4531
  processing: false,