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