@swype-org/react-sdk 0.1.19 → 0.1.21

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
@@ -584,6 +584,26 @@ function resolveRootDomainFromHostname(hostname) {
584
584
  return parts.slice(-2).join(".");
585
585
  }
586
586
 
587
+ // src/signature.ts
588
+ function normalizeSignature(sig) {
589
+ const stripped = sig.startsWith("0x") ? sig.slice(2) : sig;
590
+ if (stripped.length === 130) {
591
+ return `0x${stripped}`;
592
+ }
593
+ if (stripped.length === 128) {
594
+ const r = stripped.slice(0, 64);
595
+ const yParityAndS = stripped.slice(64, 128);
596
+ const highByte = parseInt(yParityAndS.slice(0, 2), 16);
597
+ const v = (highByte & 128) !== 0 ? 28 : 27;
598
+ const sFirstByte = (highByte & 127).toString(16).padStart(2, "0");
599
+ const s = sFirstByte + yParityAndS.slice(2);
600
+ return `0x${r}${s}${v.toString(16)}`;
601
+ }
602
+ throw new Error(
603
+ `Invalid signature length: expected 65 or 64 bytes, got ${stripped.length / 2}`
604
+ );
605
+ }
606
+
587
607
  // src/hooks.ts
588
608
  var WALLET_CLIENT_MAX_ATTEMPTS = 15;
589
609
  var WALLET_CLIENT_POLL_MS = 200;
@@ -701,7 +721,7 @@ function parseSignTypedDataPayload(typedData) {
701
721
  function getPendingActions(session, completedIds) {
702
722
  return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
703
723
  }
704
- async function createPasskeyCredential(userIdentifier) {
724
+ async function createPasskeyCredential(params) {
705
725
  const challenge = new Uint8Array(32);
706
726
  crypto.getRandomValues(challenge);
707
727
  const rpId = resolvePasskeyRpId();
@@ -711,9 +731,9 @@ async function createPasskeyCredential(userIdentifier) {
711
731
  challenge,
712
732
  rp: { name: "Swype", id: rpId },
713
733
  user: {
714
- id: new TextEncoder().encode(userIdentifier),
715
- name: userIdentifier,
716
- displayName: "Swype User"
734
+ id: new TextEncoder().encode(params.userId),
735
+ name: params.displayName,
736
+ displayName: params.displayName
717
737
  },
718
738
  pubKeyCredParams: [
719
739
  { alg: -7, type: "public-key" },
@@ -1006,13 +1026,14 @@ async function executeSignPermit2(action, wagmiConfig2, apiBaseUrl, sessionId) {
1006
1026
  console.info(
1007
1027
  `[swype-sdk][sign-permit2] Signing typed data. expectedOwner=${expectedWallet ?? "N/A"}, senderParam=${sender}, connectedAddress=${connectedAddress ?? "N/A"}, primaryType=${parsed.primaryType}, domainChainId=${String(parsed.domain.chainId ?? "N/A")}, verifyingContract=${String(parsed.domain.verifyingContract ?? "N/A")}`
1008
1028
  );
1009
- const signature = await walletClient.signTypedData({
1029
+ const rawSignature = await walletClient.signTypedData({
1010
1030
  account: sender,
1011
1031
  domain: parsed.domain,
1012
1032
  types: parsed.types,
1013
1033
  primaryType: parsed.primaryType,
1014
1034
  message: parsed.message
1015
1035
  });
1036
+ const signature = normalizeSignature(rawSignature);
1016
1037
  const recoverInput = {
1017
1038
  domain: parsed.domain,
1018
1039
  types: parsed.types,
@@ -1393,15 +1414,19 @@ function ProviderCard({ provider, selected, onClick }) {
1393
1414
  }
1394
1415
  );
1395
1416
  }
1396
- function AccountDropdown({
1417
+ function FundingSourceDropdown({
1397
1418
  accounts,
1419
+ providers,
1398
1420
  selectedAccountId,
1399
- onSelect,
1400
1421
  selectedWalletId,
1401
- onWalletSelect
1422
+ connectingNewAccount,
1423
+ onSelectAccount,
1424
+ onSelectWallet,
1425
+ onConnectNewAccount
1402
1426
  }) {
1403
1427
  const { tokens } = useSwypeConfig();
1404
1428
  const [open, setOpen] = react.useState(false);
1429
+ const [showProviders, setShowProviders] = react.useState(false);
1405
1430
  const containerRef = react.useRef(null);
1406
1431
  const selected = accounts.find((a) => a.id === selectedAccountId);
1407
1432
  const selectedWallet = selected?.wallets.find(
@@ -1412,41 +1437,15 @@ function AccountDropdown({
1412
1437
  const handleClick = (e) => {
1413
1438
  if (containerRef.current && !containerRef.current.contains(e.target)) {
1414
1439
  setOpen(false);
1440
+ setShowProviders(false);
1415
1441
  }
1416
1442
  };
1417
1443
  document.addEventListener("mousedown", handleClick);
1418
1444
  return () => document.removeEventListener("mousedown", handleClick);
1419
1445
  }, [open]);
1420
- if (accounts.length === 0) return null;
1421
- const hasMultipleChoices = accounts.length > 1 || accounts.length === 1 && onWalletSelect && accounts[0].wallets.filter((w) => w.balance.available.amount > 0).length > 1;
1422
- if (!hasMultipleChoices) {
1423
- return /* @__PURE__ */ jsxRuntime.jsxs(
1424
- "div",
1425
- {
1426
- style: {
1427
- display: "flex",
1428
- alignItems: "center",
1429
- gap: "6px",
1430
- fontSize: "0.85rem",
1431
- color: tokens.textSecondary
1432
- },
1433
- children: [
1434
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: accounts[0].name }),
1435
- (selectedWallet ?? accounts[0].wallets[0]) && /* @__PURE__ */ jsxRuntime.jsx(
1436
- "span",
1437
- {
1438
- style: {
1439
- fontSize: "0.75rem",
1440
- color: tokens.textMuted,
1441
- fontFamily: '"SF Mono", "Fira Code", monospace'
1442
- },
1443
- children: selectedWallet ? `${selectedWallet.chain.name} \xB7 ${selectedWallet.name}` : accounts[0].wallets[0]?.name
1444
- }
1445
- )
1446
- ]
1447
- }
1448
- );
1449
- }
1446
+ react.useEffect(() => {
1447
+ if (!open) setShowProviders(false);
1448
+ }, [open]);
1450
1449
  const getAccountBalance = (account) => {
1451
1450
  let total = 0;
1452
1451
  for (const w of account.wallets) {
@@ -1455,6 +1454,27 @@ function AccountDropdown({
1455
1454
  return total > 0 ? `$${total.toFixed(2)}` : "";
1456
1455
  };
1457
1456
  const hasActiveWallet = (account) => account.wallets.some((w) => w.status === "ACTIVE");
1457
+ let triggerLabel;
1458
+ if (connectingNewAccount) {
1459
+ triggerLabel = /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: tokens.textMuted, fontSize: "0.85rem" }, children: "Connecting\u2026" });
1460
+ } else if (selected) {
1461
+ triggerLabel = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1462
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: tokens.text }, children: selected.name }),
1463
+ (selectedWallet ?? selected.wallets[0]) && /* @__PURE__ */ jsxRuntime.jsx(
1464
+ "span",
1465
+ {
1466
+ style: {
1467
+ fontSize: "0.75rem",
1468
+ color: tokens.textMuted,
1469
+ fontFamily: '"SF Mono", "Fira Code", monospace'
1470
+ },
1471
+ children: selectedWallet ? `${selectedWallet.chain.name} \xB7 ${selectedWallet.name}` : selected.wallets[0].name
1472
+ }
1473
+ )
1474
+ ] });
1475
+ } else {
1476
+ triggerLabel = /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: tokens.textMuted }, children: "Select a source" });
1477
+ }
1458
1478
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: containerRef, style: { position: "relative" }, children: [
1459
1479
  /* @__PURE__ */ jsxRuntime.jsxs(
1460
1480
  "button",
@@ -1475,18 +1495,7 @@ function AccountDropdown({
1475
1495
  outline: "none"
1476
1496
  },
1477
1497
  children: [
1478
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: tokens.text }, children: selected?.name ?? "Select account" }),
1479
- (selectedWallet ?? selected?.wallets?.[0]) && /* @__PURE__ */ jsxRuntime.jsx(
1480
- "span",
1481
- {
1482
- style: {
1483
- fontSize: "0.75rem",
1484
- color: tokens.textMuted,
1485
- fontFamily: '"SF Mono", "Fira Code", monospace'
1486
- },
1487
- children: selectedWallet ? `${selectedWallet.chain.name} \xB7 ${selectedWallet.name}` : selected.wallets[0].name
1488
- }
1489
- ),
1498
+ triggerLabel,
1490
1499
  /* @__PURE__ */ jsxRuntime.jsx(
1491
1500
  "svg",
1492
1501
  {
@@ -1514,13 +1523,12 @@ function AccountDropdown({
1514
1523
  ]
1515
1524
  }
1516
1525
  ),
1517
- open && /* @__PURE__ */ jsxRuntime.jsx(
1526
+ open && /* @__PURE__ */ jsxRuntime.jsxs(
1518
1527
  "div",
1519
1528
  {
1520
1529
  style: {
1521
1530
  position: "absolute",
1522
1531
  top: "100%",
1523
- left: 0,
1524
1532
  right: 0,
1525
1533
  marginTop: "4px",
1526
1534
  background: tokens.bgCard,
@@ -1529,190 +1537,93 @@ function AccountDropdown({
1529
1537
  boxShadow: tokens.shadowLg,
1530
1538
  zIndex: 50,
1531
1539
  overflow: "hidden",
1532
- minWidth: "220px"
1540
+ minWidth: "260px"
1533
1541
  },
1534
- children: accounts.map((account) => {
1535
- const isAcctSelected = account.id === selectedAccountId;
1536
- const balance = getAccountBalance(account);
1537
- const active = hasActiveWallet(account);
1538
- const walletsWithBalance = account.wallets.filter(
1539
- (w) => w.balance.available.amount > 0
1540
- );
1541
- const showWallets = onWalletSelect && walletsWithBalance.length > 0;
1542
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1543
- /* @__PURE__ */ jsxRuntime.jsxs(
1544
- "button",
1545
- {
1546
- onClick: () => {
1547
- onSelect(account.id);
1548
- if (!showWallets) setOpen(false);
1549
- },
1550
- style: {
1551
- display: "flex",
1552
- alignItems: "center",
1553
- justifyContent: "space-between",
1554
- width: "100%",
1555
- padding: "10px 14px",
1556
- background: isAcctSelected && !selectedWalletId ? tokens.accent + "12" : "transparent",
1557
- border: "none",
1558
- borderBottom: showWallets ? "none" : `1px solid ${tokens.border}`,
1559
- cursor: "pointer",
1560
- color: tokens.text,
1561
- fontFamily: "inherit",
1562
- fontSize: "0.85rem",
1563
- textAlign: "left",
1564
- outline: "none",
1565
- transition: "background 0.1s ease"
1566
- },
1567
- children: [
1568
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
1569
- /* @__PURE__ */ jsxRuntime.jsxs(
1570
- "div",
1571
- {
1572
- style: {
1573
- display: "flex",
1574
- alignItems: "center",
1575
- gap: "6px"
1576
- },
1577
- children: [
1578
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: account.name }),
1579
- active && /* @__PURE__ */ jsxRuntime.jsx(
1580
- "span",
1581
- {
1582
- style: {
1583
- fontSize: "0.6rem",
1584
- fontWeight: 600,
1585
- color: tokens.success,
1586
- background: tokens.successBg,
1587
- padding: "2px 7px",
1588
- borderRadius: "999px",
1589
- textTransform: "uppercase",
1590
- letterSpacing: "0.03em"
1591
- },
1592
- children: "Active"
1593
- }
1594
- )
1595
- ]
1596
- }
1597
- ),
1598
- balance && /* @__PURE__ */ jsxRuntime.jsx(
1599
- "div",
1600
- {
1601
- style: {
1602
- fontSize: "0.75rem",
1603
- color: tokens.textMuted,
1604
- marginTop: "2px"
1605
- },
1606
- children: balance
1607
- }
1608
- )
1609
- ] }),
1610
- isAcctSelected && !selectedWalletId && /* @__PURE__ */ jsxRuntime.jsx(
1611
- "svg",
1612
- {
1613
- width: "14",
1614
- height: "14",
1615
- viewBox: "0 0 24 24",
1616
- fill: "none",
1617
- style: { flexShrink: 0, marginLeft: "8px" },
1618
- children: /* @__PURE__ */ jsxRuntime.jsx(
1619
- "path",
1620
- {
1621
- d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z",
1622
- fill: tokens.accent
1623
- }
1624
- )
1625
- }
1626
- )
1627
- ]
1628
- }
1629
- ),
1630
- showWallets && walletsWithBalance.map((wallet, wIdx) => {
1631
- const isWalletSelected = isAcctSelected && wallet.id === selectedWalletId;
1632
- const walletBal = wallet.balance.available.amount > 0 ? `$${wallet.balance.available.amount.toFixed(2)}` : "";
1633
- const isLastWallet = wIdx === walletsWithBalance.length - 1;
1634
- return /* @__PURE__ */ jsxRuntime.jsxs(
1542
+ children: [
1543
+ accounts.map((account) => {
1544
+ const isAcctSelected = account.id === selectedAccountId;
1545
+ const balance = getAccountBalance(account);
1546
+ const active = hasActiveWallet(account);
1547
+ const walletsWithBalance = account.wallets.filter(
1548
+ (w) => w.balance.available.amount > 0
1549
+ );
1550
+ const showWallets = walletsWithBalance.length > 0;
1551
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1552
+ /* @__PURE__ */ jsxRuntime.jsxs(
1635
1553
  "button",
1636
1554
  {
1637
1555
  onClick: () => {
1638
- onWalletSelect(account.id, wallet.id);
1639
- setOpen(false);
1556
+ onSelectAccount(account.id);
1557
+ if (!showWallets) setOpen(false);
1640
1558
  },
1641
1559
  style: {
1642
1560
  display: "flex",
1643
1561
  alignItems: "center",
1644
1562
  justifyContent: "space-between",
1645
1563
  width: "100%",
1646
- padding: "8px 14px 8px 28px",
1647
- background: isWalletSelected ? tokens.accent + "12" : "transparent",
1564
+ padding: "10px 14px",
1565
+ background: isAcctSelected && !selectedWalletId ? tokens.accent + "12" : "transparent",
1648
1566
  border: "none",
1649
- borderBottom: isLastWallet ? `1px solid ${tokens.border}` : "none",
1567
+ borderBottom: showWallets ? "none" : `1px solid ${tokens.border}`,
1650
1568
  cursor: "pointer",
1651
1569
  color: tokens.text,
1652
1570
  fontFamily: "inherit",
1653
- fontSize: "0.8rem",
1571
+ fontSize: "0.85rem",
1654
1572
  textAlign: "left",
1655
1573
  outline: "none",
1656
1574
  transition: "background 0.1s ease"
1657
1575
  },
1658
1576
  children: [
1659
- /* @__PURE__ */ jsxRuntime.jsxs(
1660
- "div",
1661
- {
1662
- style: {
1663
- display: "flex",
1664
- alignItems: "center",
1665
- gap: "6px",
1666
- minWidth: 0,
1667
- flex: 1
1668
- },
1669
- children: [
1670
- /* @__PURE__ */ jsxRuntime.jsx(
1671
- "span",
1672
- {
1673
- style: {
1674
- fontWeight: 500,
1675
- fontSize: "0.8rem"
1676
- },
1677
- children: wallet.chain.name
1678
- }
1679
- ),
1680
- /* @__PURE__ */ jsxRuntime.jsx(
1681
- "span",
1682
- {
1683
- style: {
1684
- fontSize: "0.7rem",
1685
- color: tokens.textMuted,
1686
- fontFamily: '"SF Mono", "Fira Code", monospace'
1687
- },
1688
- children: wallet.name
1689
- }
1690
- ),
1691
- walletBal && /* @__PURE__ */ jsxRuntime.jsx(
1692
- "span",
1693
- {
1694
- style: {
1695
- fontSize: "0.7rem",
1696
- color: tokens.textMuted,
1697
- marginLeft: "auto"
1698
- },
1699
- children: walletBal
1700
- }
1701
- )
1702
- ]
1703
- }
1704
- ),
1705
- isWalletSelected && /* @__PURE__ */ jsxRuntime.jsx(
1577
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
1578
+ /* @__PURE__ */ jsxRuntime.jsxs(
1579
+ "div",
1580
+ {
1581
+ style: {
1582
+ display: "flex",
1583
+ alignItems: "center",
1584
+ gap: "6px"
1585
+ },
1586
+ children: [
1587
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: account.name }),
1588
+ active && /* @__PURE__ */ jsxRuntime.jsx(
1589
+ "span",
1590
+ {
1591
+ style: {
1592
+ fontSize: "0.6rem",
1593
+ fontWeight: 600,
1594
+ color: tokens.success,
1595
+ background: tokens.successBg,
1596
+ padding: "2px 7px",
1597
+ borderRadius: "999px",
1598
+ textTransform: "uppercase",
1599
+ letterSpacing: "0.03em"
1600
+ },
1601
+ children: "Active"
1602
+ }
1603
+ )
1604
+ ]
1605
+ }
1606
+ ),
1607
+ balance && /* @__PURE__ */ jsxRuntime.jsx(
1608
+ "div",
1609
+ {
1610
+ style: {
1611
+ fontSize: "0.75rem",
1612
+ color: tokens.textMuted,
1613
+ marginTop: "2px"
1614
+ },
1615
+ children: balance
1616
+ }
1617
+ )
1618
+ ] }),
1619
+ isAcctSelected && !selectedWalletId && /* @__PURE__ */ jsxRuntime.jsx(
1706
1620
  "svg",
1707
1621
  {
1708
- width: "12",
1709
- height: "12",
1622
+ width: "14",
1623
+ height: "14",
1710
1624
  viewBox: "0 0 24 24",
1711
1625
  fill: "none",
1712
- style: {
1713
- flexShrink: 0,
1714
- marginLeft: "8px"
1715
- },
1626
+ style: { flexShrink: 0, marginLeft: "8px" },
1716
1627
  children: /* @__PURE__ */ jsxRuntime.jsx(
1717
1628
  "path",
1718
1629
  {
@@ -1723,12 +1634,214 @@ function AccountDropdown({
1723
1634
  }
1724
1635
  )
1725
1636
  ]
1637
+ }
1638
+ ),
1639
+ showWallets && walletsWithBalance.map((wallet, wIdx) => {
1640
+ const isWalletSelected = isAcctSelected && wallet.id === selectedWalletId;
1641
+ const walletBal = wallet.balance.available.amount > 0 ? `$${wallet.balance.available.amount.toFixed(2)}` : "";
1642
+ const isLastWallet = wIdx === walletsWithBalance.length - 1;
1643
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1644
+ "button",
1645
+ {
1646
+ onClick: () => {
1647
+ onSelectWallet(account.id, wallet.id);
1648
+ setOpen(false);
1649
+ },
1650
+ style: {
1651
+ display: "flex",
1652
+ alignItems: "center",
1653
+ justifyContent: "space-between",
1654
+ width: "100%",
1655
+ padding: "8px 14px 8px 28px",
1656
+ background: isWalletSelected ? tokens.accent + "12" : "transparent",
1657
+ border: "none",
1658
+ borderBottom: isLastWallet ? `1px solid ${tokens.border}` : "none",
1659
+ cursor: "pointer",
1660
+ color: tokens.text,
1661
+ fontFamily: "inherit",
1662
+ fontSize: "0.8rem",
1663
+ textAlign: "left",
1664
+ outline: "none",
1665
+ transition: "background 0.1s ease"
1666
+ },
1667
+ children: [
1668
+ /* @__PURE__ */ jsxRuntime.jsxs(
1669
+ "div",
1670
+ {
1671
+ style: {
1672
+ display: "flex",
1673
+ alignItems: "center",
1674
+ gap: "6px",
1675
+ minWidth: 0,
1676
+ flex: 1
1677
+ },
1678
+ children: [
1679
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, fontSize: "0.8rem" }, children: wallet.chain.name }),
1680
+ /* @__PURE__ */ jsxRuntime.jsx(
1681
+ "span",
1682
+ {
1683
+ style: {
1684
+ fontSize: "0.7rem",
1685
+ color: tokens.textMuted,
1686
+ fontFamily: '"SF Mono", "Fira Code", monospace'
1687
+ },
1688
+ children: wallet.name
1689
+ }
1690
+ ),
1691
+ walletBal && /* @__PURE__ */ jsxRuntime.jsx(
1692
+ "span",
1693
+ {
1694
+ style: {
1695
+ fontSize: "0.7rem",
1696
+ color: tokens.textMuted,
1697
+ marginLeft: "auto"
1698
+ },
1699
+ children: walletBal
1700
+ }
1701
+ )
1702
+ ]
1703
+ }
1704
+ ),
1705
+ isWalletSelected && /* @__PURE__ */ jsxRuntime.jsx(
1706
+ "svg",
1707
+ {
1708
+ width: "12",
1709
+ height: "12",
1710
+ viewBox: "0 0 24 24",
1711
+ fill: "none",
1712
+ style: { flexShrink: 0, marginLeft: "8px" },
1713
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1714
+ "path",
1715
+ {
1716
+ d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z",
1717
+ fill: tokens.accent
1718
+ }
1719
+ )
1720
+ }
1721
+ )
1722
+ ]
1723
+ },
1724
+ wallet.id
1725
+ );
1726
+ })
1727
+ ] }, account.id);
1728
+ }),
1729
+ accounts.length > 0 && !showProviders && /* @__PURE__ */ jsxRuntime.jsx(
1730
+ "div",
1731
+ {
1732
+ style: {
1733
+ height: "1px",
1734
+ background: tokens.border,
1735
+ margin: "0"
1736
+ }
1737
+ }
1738
+ ),
1739
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px" }, children: !showProviders ? /* @__PURE__ */ jsxRuntime.jsxs(
1740
+ "button",
1741
+ {
1742
+ onClick: () => setShowProviders(true),
1743
+ disabled: connectingNewAccount,
1744
+ style: {
1745
+ display: "flex",
1746
+ alignItems: "center",
1747
+ gap: "8px",
1748
+ background: "transparent",
1749
+ border: `1px dashed ${tokens.border}`,
1750
+ borderRadius: tokens.radius,
1751
+ padding: "10px 12px",
1752
+ width: "100%",
1753
+ cursor: connectingNewAccount ? "not-allowed" : "pointer",
1754
+ color: tokens.textSecondary,
1755
+ fontFamily: "inherit",
1756
+ fontSize: "0.825rem",
1757
+ fontWeight: 500,
1758
+ outline: "none",
1759
+ opacity: connectingNewAccount ? 0.5 : 1,
1760
+ transition: "opacity 0.15s ease"
1761
+ },
1762
+ children: [
1763
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1764
+ "path",
1765
+ {
1766
+ d: "M12 5v14M5 12h14",
1767
+ stroke: tokens.textMuted,
1768
+ strokeWidth: "2",
1769
+ strokeLinecap: "round"
1770
+ }
1771
+ ) }),
1772
+ "Add a source"
1773
+ ]
1774
+ }
1775
+ ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1776
+ /* @__PURE__ */ jsxRuntime.jsxs(
1777
+ "div",
1778
+ {
1779
+ style: {
1780
+ display: "flex",
1781
+ alignItems: "center",
1782
+ justifyContent: "space-between",
1783
+ marginBottom: "8px",
1784
+ padding: "0 4px"
1726
1785
  },
1727
- wallet.id
1728
- );
1729
- })
1730
- ] }, account.id);
1731
- })
1786
+ children: [
1787
+ /* @__PURE__ */ jsxRuntime.jsx(
1788
+ "label",
1789
+ {
1790
+ style: {
1791
+ fontSize: "0.7rem",
1792
+ fontWeight: 600,
1793
+ color: tokens.textMuted,
1794
+ textTransform: "uppercase",
1795
+ letterSpacing: "0.05em"
1796
+ },
1797
+ children: "Select provider"
1798
+ }
1799
+ ),
1800
+ /* @__PURE__ */ jsxRuntime.jsx(
1801
+ "button",
1802
+ {
1803
+ onClick: () => setShowProviders(false),
1804
+ style: {
1805
+ background: "transparent",
1806
+ border: "none",
1807
+ cursor: "pointer",
1808
+ color: tokens.textMuted,
1809
+ fontSize: "0.75rem",
1810
+ fontFamily: "inherit",
1811
+ outline: "none",
1812
+ padding: "2px 4px"
1813
+ },
1814
+ children: "Cancel"
1815
+ }
1816
+ )
1817
+ ]
1818
+ }
1819
+ ),
1820
+ /* @__PURE__ */ jsxRuntime.jsx(
1821
+ "div",
1822
+ {
1823
+ style: {
1824
+ display: "flex",
1825
+ flexDirection: "column",
1826
+ gap: "6px"
1827
+ },
1828
+ children: providers.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1829
+ ProviderCard,
1830
+ {
1831
+ provider: p,
1832
+ selected: false,
1833
+ onClick: () => {
1834
+ onConnectNewAccount(p.id);
1835
+ setOpen(false);
1836
+ setShowProviders(false);
1837
+ }
1838
+ },
1839
+ p.id
1840
+ ))
1841
+ }
1842
+ )
1843
+ ] }) })
1844
+ ]
1732
1845
  }
1733
1846
  )
1734
1847
  ] });
@@ -1737,14 +1850,10 @@ var ASSETS = ["USDC", "USDT"];
1737
1850
  function AdvancedSettings({
1738
1851
  settings,
1739
1852
  onChange,
1740
- chains,
1741
- providers,
1742
- onConnectNewAccount,
1743
- connectingNewAccount
1853
+ chains
1744
1854
  }) {
1745
1855
  const { tokens } = useSwypeConfig();
1746
1856
  const [open, setOpen] = react.useState(false);
1747
- const [showProviders, setShowProviders] = react.useState(false);
1748
1857
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: "12px" }, children: [
1749
1858
  /* @__PURE__ */ jsxRuntime.jsxs(
1750
1859
  "button",
@@ -1848,7 +1957,7 @@ function AdvancedSettings({
1848
1957
  );
1849
1958
  }) })
1850
1959
  ] }),
1851
- chains.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "14px" }, children: [
1960
+ chains.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1852
1961
  /* @__PURE__ */ jsxRuntime.jsx(
1853
1962
  "label",
1854
1963
  {
@@ -1891,119 +2000,7 @@ function AdvancedSettings({
1891
2000
  chain.id
1892
2001
  );
1893
2002
  }) })
1894
- ] }),
1895
- /* @__PURE__ */ jsxRuntime.jsx("div", { children: !showProviders ? /* @__PURE__ */ jsxRuntime.jsxs(
1896
- "button",
1897
- {
1898
- onClick: () => setShowProviders(true),
1899
- disabled: connectingNewAccount,
1900
- style: {
1901
- display: "flex",
1902
- alignItems: "center",
1903
- gap: "6px",
1904
- background: tokens.bgCard,
1905
- border: `1px dashed ${tokens.border}`,
1906
- borderRadius: "999px",
1907
- padding: "10px 14px",
1908
- width: "100%",
1909
- cursor: connectingNewAccount ? "not-allowed" : "pointer",
1910
- color: tokens.textSecondary,
1911
- fontFamily: "inherit",
1912
- fontSize: "0.825rem",
1913
- fontWeight: 500,
1914
- outline: "none",
1915
- opacity: connectingNewAccount ? 0.5 : 1,
1916
- transition: "opacity 0.1s ease"
1917
- },
1918
- children: [
1919
- /* @__PURE__ */ jsxRuntime.jsx(
1920
- "svg",
1921
- {
1922
- width: "14",
1923
- height: "14",
1924
- viewBox: "0 0 24 24",
1925
- fill: "none",
1926
- children: /* @__PURE__ */ jsxRuntime.jsx(
1927
- "path",
1928
- {
1929
- d: "M12 5v14M5 12h14",
1930
- stroke: tokens.textMuted,
1931
- strokeWidth: "2",
1932
- strokeLinecap: "round"
1933
- }
1934
- )
1935
- }
1936
- ),
1937
- "Connect new account"
1938
- ]
1939
- }
1940
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1941
- /* @__PURE__ */ jsxRuntime.jsxs(
1942
- "div",
1943
- {
1944
- style: {
1945
- display: "flex",
1946
- alignItems: "center",
1947
- justifyContent: "space-between",
1948
- marginBottom: "8px"
1949
- },
1950
- children: [
1951
- /* @__PURE__ */ jsxRuntime.jsx(
1952
- "label",
1953
- {
1954
- style: {
1955
- fontSize: "0.7rem",
1956
- fontWeight: 600,
1957
- color: tokens.textMuted,
1958
- textTransform: "uppercase",
1959
- letterSpacing: "0.05em"
1960
- },
1961
- children: "Select provider"
1962
- }
1963
- ),
1964
- /* @__PURE__ */ jsxRuntime.jsx(
1965
- "button",
1966
- {
1967
- onClick: () => setShowProviders(false),
1968
- style: {
1969
- background: "transparent",
1970
- border: "none",
1971
- cursor: "pointer",
1972
- color: tokens.textMuted,
1973
- fontSize: "0.75rem",
1974
- fontFamily: "inherit",
1975
- outline: "none",
1976
- padding: "2px 4px"
1977
- },
1978
- children: "Cancel"
1979
- }
1980
- )
1981
- ]
1982
- }
1983
- ),
1984
- /* @__PURE__ */ jsxRuntime.jsx(
1985
- "div",
1986
- {
1987
- style: {
1988
- display: "flex",
1989
- flexDirection: "column",
1990
- gap: "6px"
1991
- },
1992
- children: providers.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
1993
- ProviderCard,
1994
- {
1995
- provider: p,
1996
- selected: false,
1997
- onClick: () => {
1998
- onConnectNewAccount(p.id);
1999
- setShowProviders(false);
2000
- }
2001
- },
2002
- p.id
2003
- ))
2004
- }
2005
- )
2006
- ] }) })
2003
+ ] })
2007
2004
  ]
2008
2005
  }
2009
2006
  )
@@ -2118,7 +2115,7 @@ function SwypePayment({
2118
2115
  useWalletConnector
2119
2116
  }) {
2120
2117
  const { apiBaseUrl, tokens, depositAmount } = useSwypeConfig();
2121
- const { ready, authenticated, login, logout, getAccessToken } = reactAuth.usePrivy();
2118
+ const { ready, authenticated, user, login, logout, getAccessToken } = reactAuth.usePrivy();
2122
2119
  const [step, setStep] = react.useState("login");
2123
2120
  const [error, setError] = react.useState(null);
2124
2121
  const [providers, setProviders] = react.useState([]);
@@ -2169,28 +2166,34 @@ function SwypePayment({
2169
2166
  if (!token || cancelled) return;
2170
2167
  const { config } = await fetchUserConfig(apiBaseUrl, token);
2171
2168
  if (cancelled) return;
2172
- if (config.passkey?.credentialId) {
2173
- const hasKey = activeCredentialId ? activeCredentialId === config.passkey.credentialId : await deviceHasPasskey(config.passkey.credentialId);
2169
+ const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
2170
+ if (allPasskeys.length === 0) {
2171
+ setStep("register-passkey");
2172
+ return;
2173
+ }
2174
+ if (activeCredentialId && allPasskeys.some((p) => p.credentialId === activeCredentialId)) {
2175
+ if (depositAmount != null && depositAmount > 0) {
2176
+ setStep("ready");
2177
+ } else {
2178
+ setStep("enter-amount");
2179
+ }
2180
+ return;
2181
+ }
2182
+ for (const pk of allPasskeys) {
2174
2183
  if (cancelled) return;
2175
- if (hasKey) {
2176
- if (!activeCredentialId) {
2177
- setActiveCredentialId(config.passkey.credentialId);
2178
- window.localStorage.setItem(
2179
- ACTIVE_CREDENTIAL_STORAGE_KEY,
2180
- config.passkey.credentialId
2181
- );
2182
- }
2184
+ if (await deviceHasPasskey(pk.credentialId)) {
2185
+ setActiveCredentialId(pk.credentialId);
2186
+ window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, pk.credentialId);
2183
2187
  if (depositAmount != null && depositAmount > 0) {
2184
2188
  setStep("ready");
2185
2189
  } else {
2186
2190
  setStep("enter-amount");
2187
2191
  }
2188
- } else {
2189
- setStep("register-passkey");
2192
+ return;
2190
2193
  }
2191
- } else {
2192
- setStep("register-passkey");
2193
2194
  }
2195
+ if (cancelled) return;
2196
+ setStep("register-passkey");
2194
2197
  } catch {
2195
2198
  if (!cancelled) {
2196
2199
  if (depositAmount != null && depositAmount > 0) {
@@ -2516,11 +2519,6 @@ function SwypePayment({
2516
2519
  pollingTransferIdRef.current = null;
2517
2520
  mobileSigningTransferIdRef.current = null;
2518
2521
  }, [logout, polling, depositAmount]);
2519
- const handleConnectNewAccount = (providerId) => {
2520
- setSelectedProviderId(providerId);
2521
- setSelectedAccountId(null);
2522
- setConnectingNewAccount(true);
2523
- };
2524
2522
  const cardStyle = {
2525
2523
  background: tokens.bgCard,
2526
2524
  borderRadius: tokens.radiusLg,
@@ -2705,7 +2703,11 @@ function SwypePayment({
2705
2703
  try {
2706
2704
  const token = await getAccessToken();
2707
2705
  if (!token) throw new Error("Not authenticated");
2708
- const { credentialId, publicKey } = await createPasskeyCredential("Swype User");
2706
+ const passkeyDisplayName = user?.email?.address ?? user?.google?.name ?? user?.id ?? "Swype User";
2707
+ const { credentialId, publicKey } = await createPasskeyCredential({
2708
+ userId: user?.id ?? "unknown",
2709
+ displayName: passkeyDisplayName
2710
+ });
2709
2711
  await registerPasskey(apiBaseUrl, token, credentialId, publicKey);
2710
2712
  setActiveCredentialId(credentialId);
2711
2713
  window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
@@ -2888,7 +2890,6 @@ function SwypePayment({
2888
2890
  if (step === "ready") {
2889
2891
  const parsedAmount = parseFloat(amount);
2890
2892
  const canPay = !isNaN(parsedAmount) && parsedAmount >= MIN_SEND_AMOUNT_USD && !!sourceId && !loadingData;
2891
- const noAccounts = !loadingData && accounts.length === 0;
2892
2893
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: cardStyle, children: [
2893
2894
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
2894
2895
  stepBadge("Review & pay"),
@@ -3019,23 +3020,30 @@ function SwypePayment({
3019
3020
  },
3020
3021
  children: [
3021
3022
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "From" }),
3022
- noAccounts ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: tokens.textMuted }, children: "New account" }) : /* @__PURE__ */ jsxRuntime.jsx(
3023
- AccountDropdown,
3023
+ /* @__PURE__ */ jsxRuntime.jsx(
3024
+ FundingSourceDropdown,
3024
3025
  {
3025
3026
  accounts,
3027
+ providers,
3026
3028
  selectedAccountId,
3027
3029
  selectedWalletId,
3028
- onSelect: (id) => {
3030
+ connectingNewAccount,
3031
+ onSelectAccount: (id) => {
3029
3032
  setSelectedAccountId(id);
3030
3033
  setSelectedWalletId(null);
3031
3034
  setConnectingNewAccount(false);
3032
3035
  setSelectedProviderId(null);
3033
3036
  },
3034
- onWalletSelect: (accountId, walletId) => {
3037
+ onSelectWallet: (accountId, walletId) => {
3035
3038
  setSelectedAccountId(accountId);
3036
3039
  setSelectedWalletId(walletId);
3037
3040
  setConnectingNewAccount(false);
3038
3041
  setSelectedProviderId(null);
3042
+ },
3043
+ onConnectNewAccount: (providerId) => {
3044
+ setSelectedProviderId(providerId);
3045
+ setSelectedAccountId(null);
3046
+ setConnectingNewAccount(true);
3039
3047
  }
3040
3048
  }
3041
3049
  )
@@ -3045,46 +3053,6 @@ function SwypePayment({
3045
3053
  ]
3046
3054
  }
3047
3055
  ),
3048
- noAccounts && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
3049
- /* @__PURE__ */ jsxRuntime.jsx(
3050
- "label",
3051
- {
3052
- style: {
3053
- display: "block",
3054
- fontSize: "0.8rem",
3055
- color: tokens.textMuted,
3056
- marginBottom: "8px",
3057
- fontWeight: 500,
3058
- textTransform: "uppercase",
3059
- letterSpacing: "0.05em"
3060
- },
3061
- children: "Connect a wallet"
3062
- }
3063
- ),
3064
- /* @__PURE__ */ jsxRuntime.jsx(
3065
- "div",
3066
- {
3067
- style: {
3068
- display: "flex",
3069
- flexDirection: "column",
3070
- gap: "8px"
3071
- },
3072
- children: providers.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
3073
- ProviderCard,
3074
- {
3075
- provider: p,
3076
- selected: selectedProviderId === p.id,
3077
- onClick: () => {
3078
- setSelectedProviderId(p.id);
3079
- setSelectedAccountId(null);
3080
- setConnectingNewAccount(false);
3081
- }
3082
- },
3083
- p.id
3084
- ))
3085
- }
3086
- )
3087
- ] }),
3088
3056
  /* @__PURE__ */ jsxRuntime.jsxs(
3089
3057
  "button",
3090
3058
  {
@@ -3097,15 +3065,12 @@ function SwypePayment({
3097
3065
  ]
3098
3066
  }
3099
3067
  ),
3100
- !noAccounts && /* @__PURE__ */ jsxRuntime.jsx(
3068
+ /* @__PURE__ */ jsxRuntime.jsx(
3101
3069
  AdvancedSettings,
3102
3070
  {
3103
3071
  settings: advancedSettings,
3104
3072
  onChange: setAdvancedSettings,
3105
- chains,
3106
- providers,
3107
- onConnectNewAccount: handleConnectNewAccount,
3108
- connectingNewAccount
3073
+ chains
3109
3074
  }
3110
3075
  )
3111
3076
  ] })