@swype-org/react-sdk 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -9,6 +9,7 @@ var jsxRuntime = require('react/jsx-runtime');
9
9
  var viem = require('viem');
10
10
  var utils = require('viem/utils');
11
11
 
12
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
12
13
  var __defProp = Object.defineProperty;
13
14
  var __export = (target, all) => {
14
15
  for (var name in all)
@@ -120,6 +121,9 @@ function useSwypeConfig() {
120
121
  }
121
122
  return ctx;
122
123
  }
124
+ function useOptionalSwypeConfig() {
125
+ return react.useContext(SwypeContext);
126
+ }
123
127
  function useSwypeDepositAmount() {
124
128
  const ctx = react.useContext(SwypeContext);
125
129
  if (!ctx) {
@@ -144,6 +148,8 @@ __export(api_exports, {
144
148
  fetchChains: () => fetchChains,
145
149
  fetchProviders: () => fetchProviders,
146
150
  fetchTransfer: () => fetchTransfer,
151
+ fetchUserConfig: () => fetchUserConfig,
152
+ registerPasskey: () => registerPasskey,
147
153
  reportActionCompletion: () => reportActionCompletion,
148
154
  signTransfer: () => signTransfer,
149
155
  updateUserConfig: () => updateUserConfig,
@@ -207,19 +213,29 @@ async function createTransfer(apiBaseUrl, token, params) {
207
213
  if (!res.ok) await throwApiError(res);
208
214
  return await res.json();
209
215
  }
210
- async function fetchTransfer(apiBaseUrl, token, transferId) {
216
+ async function fetchTransfer(apiBaseUrl, token, transferId, authorizationSessionToken) {
217
+ if (!token && !authorizationSessionToken) {
218
+ throw new Error("Missing auth credentials for transfer fetch.");
219
+ }
211
220
  const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
212
- headers: { Authorization: `Bearer ${token}` }
221
+ headers: {
222
+ ...token ? { Authorization: `Bearer ${token}` } : {},
223
+ ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
224
+ }
213
225
  });
214
226
  if (!res.ok) await throwApiError(res);
215
227
  return await res.json();
216
228
  }
217
- async function signTransfer(apiBaseUrl, token, transferId, signedUserOp) {
229
+ async function signTransfer(apiBaseUrl, token, transferId, signedUserOp, authorizationSessionToken) {
230
+ if (!token && !authorizationSessionToken) {
231
+ throw new Error("Missing auth credentials for transfer signing.");
232
+ }
218
233
  const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}`, {
219
234
  method: "PATCH",
220
235
  headers: {
221
236
  "Content-Type": "application/json",
222
- Authorization: `Bearer ${token}`
237
+ ...token ? { Authorization: `Bearer ${token}` } : {},
238
+ ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
223
239
  },
224
240
  body: JSON.stringify({ signedUserOp })
225
241
  });
@@ -233,6 +249,24 @@ async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
233
249
  if (!res.ok) await throwApiError(res);
234
250
  return await res.json();
235
251
  }
252
+ async function registerPasskey(apiBaseUrl, token, credentialId, publicKey) {
253
+ const res = await fetch(`${apiBaseUrl}/v1/users/config/passkey`, {
254
+ method: "POST",
255
+ headers: {
256
+ "Content-Type": "application/json",
257
+ Authorization: `Bearer ${token}`
258
+ },
259
+ body: JSON.stringify({ credentialId, publicKey })
260
+ });
261
+ if (!res.ok) await throwApiError(res);
262
+ }
263
+ async function fetchUserConfig(apiBaseUrl, token) {
264
+ const res = await fetch(`${apiBaseUrl}/v1/users/config`, {
265
+ headers: { Authorization: `Bearer ${token}` }
266
+ });
267
+ if (!res.ok) await throwApiError(res);
268
+ return await res.json();
269
+ }
236
270
  async function updateUserConfig(apiBaseUrl, token, config) {
237
271
  const res = await fetch(`${apiBaseUrl}/v1/users`, {
238
272
  method: "PATCH",
@@ -527,24 +561,78 @@ async function getWalletClient(config, parameters = {}) {
527
561
  const client = await getConnectorClient(config, parameters);
528
562
  return client.extend(viem.walletActions);
529
563
  }
530
- async function waitForWalletClient(wagmiConfig2, walletClientParams = {}, maxAttempts = 15, intervalMs = 200) {
531
- for (let i = 0; i < maxAttempts; i++) {
564
+ var WALLET_CLIENT_MAX_ATTEMPTS = 15;
565
+ var WALLET_CLIENT_POLL_MS = 200;
566
+ var ACTION_POLL_INTERVAL_MS = 500;
567
+ var ACTION_POLL_MAX_RETRIES = 20;
568
+ var SIGN_PERMIT2_POLL_MS = 1e3;
569
+ var SIGN_PERMIT2_MAX_POLLS = 15;
570
+ var TRANSFER_SIGN_MAX_POLLS = 60;
571
+ function actionSuccess(action, message, data) {
572
+ return { actionId: action.id, type: action.type, status: "success", message, data };
573
+ }
574
+ function actionError(action, message) {
575
+ return { actionId: action.id, type: action.type, status: "error", message };
576
+ }
577
+ function isUserRejection(msg) {
578
+ const lower = msg.toLowerCase();
579
+ return lower.includes("rejected") || lower.includes("denied");
580
+ }
581
+ function hexToBytes(hex) {
582
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
583
+ const bytes = clean.match(/.{1,2}/g).map((b) => parseInt(b, 16));
584
+ return new Uint8Array(bytes);
585
+ }
586
+ function toBase64(buffer) {
587
+ return btoa(String.fromCharCode(...new Uint8Array(buffer)));
588
+ }
589
+ function base64ToBytes(value) {
590
+ const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
591
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
592
+ const raw = atob(padded);
593
+ const bytes = new Uint8Array(raw.length);
594
+ for (let i = 0; i < raw.length; i++) {
595
+ bytes[i] = raw.charCodeAt(i);
596
+ }
597
+ return bytes;
598
+ }
599
+ function readEnvValue(name) {
600
+ const meta = ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) });
601
+ const metaValue = meta.env?.[name];
602
+ if (typeof metaValue === "string" && metaValue.trim().length > 0) {
603
+ return metaValue.trim();
604
+ }
605
+ const processValue = globalThis.process?.env?.[name];
606
+ if (typeof processValue === "string" && processValue.trim().length > 0) {
607
+ return processValue.trim();
608
+ }
609
+ return void 0;
610
+ }
611
+ function resolvePasskeyRpId() {
612
+ const configuredDomain = readEnvValue("VITE_DOMAIN") ?? readEnvValue("SWYPE_DOMAIN");
613
+ if (configuredDomain) {
614
+ return configuredDomain.replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\./, "").trim();
615
+ }
616
+ if (typeof window !== "undefined") {
617
+ return window.location.hostname;
618
+ }
619
+ return "localhost";
620
+ }
621
+ async function waitForWalletClient(wagmiConfig2, params = {}) {
622
+ for (let i = 0; i < WALLET_CLIENT_MAX_ATTEMPTS; i++) {
532
623
  try {
533
- return await getWalletClient(wagmiConfig2, walletClientParams);
624
+ return await getWalletClient(wagmiConfig2, params);
534
625
  } catch {
535
- if (i === maxAttempts - 1) {
626
+ if (i === WALLET_CLIENT_MAX_ATTEMPTS - 1) {
536
627
  throw new Error("Wallet not ready. Please try again.");
537
628
  }
538
- await new Promise((r) => setTimeout(r, intervalMs));
629
+ await new Promise((r) => setTimeout(r, WALLET_CLIENT_POLL_MS));
539
630
  }
540
631
  }
541
632
  throw new Error("Wallet not ready. Please try again.");
542
633
  }
543
634
  function parseSignTypedDataPayload(typedData) {
544
- const domain = typedData.domain;
545
- const types = typedData.types;
546
- const primaryType = typedData.primaryType;
547
- const message = typedData.message;
635
+ const { domain, types, primaryType, message } = typedData;
548
636
  if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
549
637
  throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
550
638
  }
@@ -564,6 +652,46 @@ function parseSignTypedDataPayload(typedData) {
564
652
  message
565
653
  };
566
654
  }
655
+ function getPendingActions(session, completedIds) {
656
+ return session.actions.filter((a) => a.status === "PENDING" && !completedIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
657
+ }
658
+ async function createPasskeyCredential(userIdentifier) {
659
+ const challenge = new Uint8Array(32);
660
+ crypto.getRandomValues(challenge);
661
+ const rpId = resolvePasskeyRpId();
662
+ const credential = await navigator.credentials.create({
663
+ publicKey: {
664
+ challenge,
665
+ rp: { name: "Swype", id: rpId },
666
+ user: {
667
+ id: new TextEncoder().encode(userIdentifier),
668
+ name: userIdentifier,
669
+ displayName: "Swype User"
670
+ },
671
+ pubKeyCredParams: [
672
+ { alg: -7, type: "public-key" },
673
+ // ES256 (P-256)
674
+ { alg: -257, type: "public-key" }
675
+ // RS256
676
+ ],
677
+ authenticatorSelection: {
678
+ authenticatorAttachment: "platform",
679
+ residentKey: "preferred",
680
+ userVerification: "required"
681
+ },
682
+ timeout: 6e4
683
+ }
684
+ });
685
+ if (!credential) {
686
+ throw new Error("Passkey creation was cancelled.");
687
+ }
688
+ const response = credential.response;
689
+ const publicKeyBytes = response.getPublicKey?.();
690
+ return {
691
+ credentialId: toBase64(credential.rawId),
692
+ publicKey: publicKeyBytes ? toBase64(publicKeyBytes) : ""
693
+ };
694
+ }
567
695
  function useTransferPolling(intervalMs = 3e3) {
568
696
  const { apiBaseUrl } = useSwypeConfig();
569
697
  const { getAccessToken } = reactAuth.usePrivy();
@@ -612,8 +740,247 @@ function useTransferPolling(intervalMs = 3e3) {
612
740
  react.useEffect(() => () => stopPolling(), [stopPolling]);
613
741
  return { transfer, error, isPolling, startPolling, stopPolling };
614
742
  }
615
- function useAuthorizationExecutor() {
616
- const { apiBaseUrl } = useSwypeConfig();
743
+ async function executeOpenProvider(action, wagmiConfig2, connectors, connectAsync) {
744
+ try {
745
+ const account = getAccount(wagmiConfig2);
746
+ if (account.isConnected && account.address) {
747
+ const hexChainId2 = account.chainId ? `0x${account.chainId.toString(16)}` : void 0;
748
+ return actionSuccess(
749
+ action,
750
+ `Connected. Account: ${account.address}, Chain: ${hexChainId2}`,
751
+ { accounts: [account.address], chainId: hexChainId2 }
752
+ );
753
+ }
754
+ const targetId = action.metadata?.wagmiConnectorId;
755
+ const metaMaskConnector = connectors.find((c) => {
756
+ const id = c.id.toLowerCase();
757
+ const name = c.name.toLowerCase();
758
+ return id.includes("metamask") || name.includes("metamask");
759
+ });
760
+ const connector = targetId ? connectors.find((c) => c.id === targetId) ?? metaMaskConnector ?? connectors[0] : metaMaskConnector ?? connectors[0];
761
+ if (!connector) {
762
+ return actionError(action, "No wallet connector found. Please install a supported wallet.");
763
+ }
764
+ const result = await connectAsync({ connector });
765
+ const hexChainId = `0x${result.chainId.toString(16)}`;
766
+ return actionSuccess(
767
+ action,
768
+ `Connected to ${connector.name}. Account: ${result.accounts[0]}, Chain: ${hexChainId}`,
769
+ { accounts: [...result.accounts], chainId: hexChainId }
770
+ );
771
+ } catch (err) {
772
+ return actionError(
773
+ action,
774
+ err instanceof Error ? err.message : "Failed to connect wallet"
775
+ );
776
+ }
777
+ }
778
+ async function executeSelectSource(action, waitForSelection) {
779
+ try {
780
+ const options = action.metadata?.options;
781
+ const recommended = action.metadata?.recommended;
782
+ if (!options || options.length === 0) {
783
+ return actionError(
784
+ action,
785
+ "No selectable source options returned by backend."
786
+ );
787
+ }
788
+ const selection = await waitForSelection(action);
789
+ const isValidSelection = options.some(
790
+ (option) => option.chainName === selection.chainName && option.tokenSymbol === selection.tokenSymbol
791
+ );
792
+ if (!isValidSelection) {
793
+ return actionError(
794
+ action,
795
+ "Invalid source selection. Please choose one of the provided options."
796
+ );
797
+ }
798
+ return actionSuccess(
799
+ action,
800
+ `Selected ${selection.tokenSymbol} on ${selection.chainName}.`,
801
+ { selectedChainName: selection.chainName, selectedTokenSymbol: selection.tokenSymbol }
802
+ );
803
+ } catch (err) {
804
+ return actionError(
805
+ action,
806
+ err instanceof Error ? err.message : "Failed to select source"
807
+ );
808
+ }
809
+ }
810
+ async function executeSwitchChain(action, wagmiConfig2, switchChainAsync) {
811
+ try {
812
+ const account = getAccount(wagmiConfig2);
813
+ const targetChainIdHex = action.metadata?.targetChainId;
814
+ if (!targetChainIdHex) {
815
+ return actionError(action, "No targetChainId in action metadata.");
816
+ }
817
+ if (!/^0x[0-9a-fA-F]+$/.test(targetChainIdHex)) {
818
+ return actionError(
819
+ action,
820
+ `Invalid targetChainId in action metadata: ${targetChainIdHex}`
821
+ );
822
+ }
823
+ const targetChainIdNum = parseInt(targetChainIdHex, 16);
824
+ if (Number.isNaN(targetChainIdNum)) {
825
+ return actionError(
826
+ action,
827
+ `Invalid targetChainId in action metadata: ${targetChainIdHex}`
828
+ );
829
+ }
830
+ const hexChainId = `0x${targetChainIdNum.toString(16)}`;
831
+ if (account.chainId === targetChainIdNum) {
832
+ return actionSuccess(
833
+ action,
834
+ `Already on chain ${hexChainId}. Skipped.`,
835
+ { chainId: hexChainId, switched: false }
836
+ );
837
+ }
838
+ await switchChainAsync({ chainId: targetChainIdNum });
839
+ return actionSuccess(
840
+ action,
841
+ `Switched to chain ${hexChainId}.`,
842
+ { chainId: hexChainId, switched: true }
843
+ );
844
+ } catch (err) {
845
+ return actionError(
846
+ action,
847
+ err instanceof Error ? err.message : "Failed to switch chain"
848
+ );
849
+ }
850
+ }
851
+ async function executeApprovePermit2(action, wagmiConfig2) {
852
+ try {
853
+ const walletClient = await waitForWalletClient(wagmiConfig2);
854
+ const account = getAccount(wagmiConfig2);
855
+ const sender = account.address ?? walletClient.account?.address;
856
+ const expectedWallet = action.metadata?.walletAddress;
857
+ if (!sender) {
858
+ throw new Error("Wallet account not available. Please connect your wallet.");
859
+ }
860
+ if (expectedWallet && sender.toLowerCase() !== expectedWallet.toLowerCase()) {
861
+ return actionError(
862
+ action,
863
+ `Connected wallet ${sender} does not match the required source wallet ${expectedWallet}. Please switch accounts in your wallet and retry.`
864
+ );
865
+ }
866
+ const to = action.metadata?.to;
867
+ const data = action.metadata?.data;
868
+ const tokenSymbol = action.metadata?.tokenSymbol;
869
+ if (!to || !data) {
870
+ return actionError(
871
+ action,
872
+ "APPROVE_PERMIT2 metadata is missing transaction parameters (to, data)."
873
+ );
874
+ }
875
+ const txHash = await walletClient.request({
876
+ method: "eth_sendTransaction",
877
+ params: [{
878
+ from: sender,
879
+ to,
880
+ data,
881
+ value: "0x0"
882
+ }]
883
+ });
884
+ console.info(
885
+ `[swype-sdk][approve-permit2] ERC-20 approve tx sent. token=${tokenSymbol ?? to}, txHash=${txHash}`
886
+ );
887
+ return actionSuccess(
888
+ action,
889
+ `Approved Permit2 to spend ${tokenSymbol ?? "tokens"}.`,
890
+ { txHash }
891
+ );
892
+ } catch (err) {
893
+ const msg = err instanceof Error ? err.message : "Failed to approve Permit2";
894
+ return actionError(
895
+ action,
896
+ isUserRejection(msg) ? "You rejected the approval transaction. Please approve the Permit2 spending allowance in your wallet to continue." : msg
897
+ );
898
+ }
899
+ }
900
+ async function executeSignPermit2(action, wagmiConfig2, apiBaseUrl, sessionId) {
901
+ try {
902
+ const expectedWallet = action.metadata?.walletAddress;
903
+ const walletClient = await waitForWalletClient(
904
+ wagmiConfig2,
905
+ expectedWallet ? { account: expectedWallet } : {}
906
+ );
907
+ const account = getAccount(wagmiConfig2);
908
+ const connectedAddress = account.address ?? walletClient.account?.address;
909
+ const sender = expectedWallet ?? connectedAddress;
910
+ if (!sender) {
911
+ throw new Error("Wallet account not available. Please connect your wallet.");
912
+ }
913
+ if (expectedWallet && connectedAddress && connectedAddress.toLowerCase() !== expectedWallet.toLowerCase()) {
914
+ return actionError(
915
+ action,
916
+ `Connected wallet ${connectedAddress} does not match the required source wallet ${expectedWallet}. Please switch accounts in your wallet and retry.`
917
+ );
918
+ }
919
+ let typedData = action.metadata?.typedData;
920
+ const tokenSymbol = action.metadata?.tokenSymbol;
921
+ if (!typedData && sessionId) {
922
+ for (let i = 0; i < SIGN_PERMIT2_MAX_POLLS; i++) {
923
+ await new Promise((r) => setTimeout(r, SIGN_PERMIT2_POLL_MS));
924
+ const session = await fetchAuthorizationSession(apiBaseUrl, sessionId);
925
+ const updated = session.actions.find((a) => a.id === action.id);
926
+ typedData = updated?.metadata?.typedData;
927
+ if (typedData) break;
928
+ }
929
+ }
930
+ if (!typedData) {
931
+ return actionError(
932
+ action,
933
+ "SIGN_PERMIT2 metadata is missing typedData. The server may still be preparing the signing payload."
934
+ );
935
+ }
936
+ const parsed = parseSignTypedDataPayload(typedData);
937
+ console.info(
938
+ `[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")}`
939
+ );
940
+ const signature = await walletClient.signTypedData({
941
+ account: sender,
942
+ domain: parsed.domain,
943
+ types: parsed.types,
944
+ primaryType: parsed.primaryType,
945
+ message: parsed.message
946
+ });
947
+ const recoverInput = {
948
+ domain: parsed.domain,
949
+ types: parsed.types,
950
+ primaryType: parsed.primaryType,
951
+ message: parsed.message,
952
+ signature
953
+ };
954
+ const recoveredSigner = await viem.recoverTypedDataAddress(recoverInput);
955
+ const expectedSigner = (expectedWallet ?? sender).toLowerCase();
956
+ console.info(
957
+ `[swype-sdk][sign-permit2] Signature recovered. recoveredSigner=${recoveredSigner}, expectedSigner=${expectedSigner}`
958
+ );
959
+ if (recoveredSigner.toLowerCase() !== expectedSigner) {
960
+ return actionError(
961
+ action,
962
+ `Wallet signed with ${recoveredSigner}, but source wallet is ${expectedWallet ?? sender}. Please switch to the source wallet in MetaMask and retry.`
963
+ );
964
+ }
965
+ console.info(
966
+ `[swype-sdk][sign-permit2] Permit2 EIP-712 signature obtained. token=${tokenSymbol ?? "unknown"}`
967
+ );
968
+ return actionSuccess(
969
+ action,
970
+ `Permit2 allowance signed for ${tokenSymbol ?? "tokens"}.`,
971
+ { signature }
972
+ );
973
+ } catch (err) {
974
+ const msg = err instanceof Error ? err.message : "Failed to sign Permit2 allowance";
975
+ return actionError(
976
+ action,
977
+ isUserRejection(msg) ? "You rejected the Permit2 signature request. Please approve the signature in your wallet to allow fund transfers." : msg
978
+ );
979
+ }
980
+ }
981
+ function useAuthorizationExecutor(options) {
982
+ const swypeConfig = useOptionalSwypeConfig();
983
+ const apiBaseUrl = options?.apiBaseUrl ?? swypeConfig?.apiBaseUrl;
617
984
  const wagmiConfig2 = wagmi.useConfig();
618
985
  const { connectAsync, connectors } = wagmi.useConnect();
619
986
  const { switchChainAsync } = wagmi.useSwitchChain();
@@ -624,6 +991,7 @@ function useAuthorizationExecutor() {
624
991
  const executingRef = react.useRef(false);
625
992
  const [pendingSelectSource, setPendingSelectSource] = react.useState(null);
626
993
  const selectSourceResolverRef = react.useRef(null);
994
+ const sessionIdRef = react.useRef(null);
627
995
  const resolveSelectSource = react.useCallback((selection) => {
628
996
  if (selectSourceResolverRef.current) {
629
997
  selectSourceResolverRef.current(selection);
@@ -631,487 +999,82 @@ function useAuthorizationExecutor() {
631
999
  setPendingSelectSource(null);
632
1000
  }
633
1001
  }, []);
634
- const sessionIdRef = react.useRef(null);
635
- const executeOpenProvider = react.useCallback(
636
- async (action) => {
637
- try {
638
- const account = getAccount(wagmiConfig2);
639
- if (account.isConnected && account.address) {
640
- const hexChainId2 = account.chainId ? `0x${account.chainId.toString(16)}` : void 0;
641
- return {
642
- actionId: action.id,
643
- type: action.type,
644
- status: "success",
645
- message: `Connected. Account: ${account.address}, Chain: ${hexChainId2}`,
646
- data: { accounts: [account.address], chainId: hexChainId2 }
647
- };
648
- }
649
- const targetId = action.metadata?.wagmiConnectorId;
650
- const metaMaskConnector = connectors.find((c) => {
651
- const id = c.id.toLowerCase();
652
- const name = c.name.toLowerCase();
653
- return id.includes("metamask") || name.includes("metamask");
654
- });
655
- const connector = targetId ? connectors.find((c) => c.id === targetId) ?? metaMaskConnector ?? connectors[0] : metaMaskConnector ?? connectors[0];
656
- if (!connector) {
657
- return {
658
- actionId: action.id,
659
- type: action.type,
660
- status: "error",
661
- message: "No wallet connector found. Please install a supported wallet."
662
- };
663
- }
664
- const result = await connectAsync({ connector });
665
- const hexChainId = `0x${result.chainId.toString(16)}`;
666
- return {
667
- actionId: action.id,
668
- type: action.type,
669
- status: "success",
670
- message: `Connected to ${connector.name}. Account: ${result.accounts[0]}, Chain: ${hexChainId}`,
671
- data: { accounts: [...result.accounts], chainId: hexChainId }
672
- };
673
- } catch (err) {
674
- return {
675
- actionId: action.id,
676
- type: action.type,
677
- status: "error",
678
- message: err instanceof Error ? err.message : "Failed to connect wallet"
679
- };
680
- }
681
- },
682
- [wagmiConfig2, connectors, connectAsync]
683
- );
684
- const executeSelectSource = react.useCallback(
685
- async (action) => {
686
- try {
687
- const options = action.metadata?.options;
688
- const recommended = action.metadata?.recommended;
689
- if (!options || options.length <= 1) {
690
- const selection2 = recommended ?? { chainName: "Base", tokenSymbol: "USDC" };
691
- return {
692
- actionId: action.id,
693
- type: action.type,
694
- status: "success",
695
- message: `Auto-selected ${selection2.tokenSymbol} on ${selection2.chainName}.`,
696
- data: {
697
- selectedChainName: selection2.chainName,
698
- selectedTokenSymbol: selection2.tokenSymbol
699
- }
700
- };
701
- }
702
- const selection = await new Promise((resolve) => {
703
- selectSourceResolverRef.current = resolve;
704
- setPendingSelectSource(action);
705
- });
706
- return {
707
- actionId: action.id,
708
- type: action.type,
709
- status: "success",
710
- message: `Selected ${selection.tokenSymbol} on ${selection.chainName}.`,
711
- data: {
712
- selectedChainName: selection.chainName,
713
- selectedTokenSymbol: selection.tokenSymbol
714
- }
715
- };
716
- } catch (err) {
717
- return {
718
- actionId: action.id,
719
- type: action.type,
720
- status: "error",
721
- message: err instanceof Error ? err.message : "Failed to select source"
722
- };
723
- }
724
- },
725
- []
726
- );
727
- const executeSwitchChain = react.useCallback(
728
- async (action) => {
729
- try {
730
- const account = getAccount(wagmiConfig2);
731
- const targetChainIdHex = action.metadata?.targetChainId;
732
- if (!targetChainIdHex) {
733
- return {
734
- actionId: action.id,
735
- type: action.type,
736
- status: "error",
737
- message: "No targetChainId in action metadata."
738
- };
739
- }
740
- const targetChainIdNum = parseInt(targetChainIdHex, 16);
741
- const hexChainId = `0x${targetChainIdNum.toString(16)}`;
742
- if (account.chainId === targetChainIdNum) {
743
- return {
744
- actionId: action.id,
745
- type: action.type,
746
- status: "success",
747
- message: `Already on chain ${hexChainId}. Skipped.`,
748
- data: { chainId: hexChainId, switched: false }
749
- };
750
- }
751
- await switchChainAsync({ chainId: targetChainIdNum });
752
- return {
753
- actionId: action.id,
754
- type: action.type,
755
- status: "success",
756
- message: `Switched to chain ${hexChainId}.`,
757
- data: { chainId: hexChainId, switched: true }
758
- };
759
- } catch (err) {
760
- return {
761
- actionId: action.id,
762
- type: action.type,
763
- status: "error",
764
- message: err instanceof Error ? err.message : "Failed to switch chain"
765
- };
766
- }
767
- },
768
- [wagmiConfig2, switchChainAsync]
769
- );
770
- const executeRegisterPasskey = react.useCallback(
771
- async (action) => {
772
- try {
773
- const account = getAccount(wagmiConfig2);
774
- const challenge = new Uint8Array(32);
775
- crypto.getRandomValues(challenge);
776
- const credential = await navigator.credentials.create({
777
- publicKey: {
778
- challenge,
779
- rp: {
780
- name: "Swype",
781
- id: window.location.hostname
782
- },
783
- user: {
784
- id: new TextEncoder().encode(account.address ?? "user"),
785
- name: account.address ?? "Swype User",
786
- displayName: "Swype User"
787
- },
788
- pubKeyCredParams: [
789
- { alg: -7, type: "public-key" },
790
- // ES256 (P-256)
791
- { alg: -257, type: "public-key" }
792
- // RS256
793
- ],
794
- authenticatorSelection: {
795
- authenticatorAttachment: "platform",
796
- residentKey: "preferred",
797
- userVerification: "required"
798
- },
799
- timeout: 6e4
800
- }
801
- });
802
- if (!credential) {
803
- return {
804
- actionId: action.id,
805
- type: action.type,
806
- status: "error",
807
- message: "Passkey creation was cancelled."
808
- };
809
- }
810
- const response = credential.response;
811
- const publicKeyBytes = response.getPublicKey?.();
812
- const credentialId = btoa(
813
- String.fromCharCode(...new Uint8Array(credential.rawId))
814
- );
815
- const publicKey = publicKeyBytes ? btoa(String.fromCharCode(...new Uint8Array(publicKeyBytes))) : "";
816
- return {
817
- actionId: action.id,
818
- type: action.type,
819
- status: "success",
820
- message: "Passkey created successfully.",
821
- data: {
822
- credentialId,
823
- publicKey
824
- }
825
- };
826
- } catch (err) {
827
- return {
828
- actionId: action.id,
829
- type: action.type,
830
- status: "error",
831
- message: err instanceof Error ? err.message : "Failed to create passkey"
832
- };
833
- }
834
- },
835
- [wagmiConfig2]
836
- );
837
- const executeCreateSmartAccount = react.useCallback(
838
- async (action) => {
839
- return {
840
- actionId: action.id,
841
- type: action.type,
842
- status: "success",
843
- message: "Smart account creation acknowledged. Server is deploying.",
844
- data: {}
845
- };
846
- },
1002
+ const waitForSelection = react.useCallback(
1003
+ (action) => new Promise((resolve) => {
1004
+ selectSourceResolverRef.current = resolve;
1005
+ setPendingSelectSource(action);
1006
+ }),
847
1007
  []
848
1008
  );
849
- const executeApprovePermit2 = react.useCallback(
850
- async (action) => {
851
- try {
852
- const walletClient = await waitForWalletClient(wagmiConfig2);
853
- const account = getAccount(wagmiConfig2);
854
- const sender = account.address ?? walletClient.account?.address;
855
- const expectedWalletAddress = action.metadata?.walletAddress;
856
- if (!sender) {
857
- throw new Error("Wallet account not available. Please connect your wallet.");
858
- }
859
- if (expectedWalletAddress && sender.toLowerCase() !== expectedWalletAddress.toLowerCase()) {
860
- return {
861
- actionId: action.id,
862
- type: action.type,
863
- status: "error",
864
- message: `Connected wallet ${sender} does not match the required source wallet ${expectedWalletAddress}. Please switch accounts in your wallet and retry.`
865
- };
866
- }
867
- const to = action.metadata?.to;
868
- const data = action.metadata?.data;
869
- const tokenSymbol = action.metadata?.tokenSymbol;
870
- if (!to || !data) {
871
- return {
872
- actionId: action.id,
873
- type: action.type,
874
- status: "error",
875
- message: "APPROVE_PERMIT2 metadata is missing transaction parameters (to, data)."
876
- };
877
- }
878
- const txHash = await walletClient.request({
879
- method: "eth_sendTransaction",
880
- params: [
881
- {
882
- from: sender,
883
- to,
884
- data,
885
- value: "0x0"
886
- }
887
- ]
888
- });
889
- console.info(
890
- `[swype-sdk][approve-permit2] ERC-20 approve tx sent. token=${tokenSymbol ?? to}, txHash=${txHash}`
891
- );
892
- return {
893
- actionId: action.id,
894
- type: action.type,
895
- status: "success",
896
- message: `Approved Permit2 to spend ${tokenSymbol ?? "tokens"}.`,
897
- data: { txHash }
898
- };
899
- } catch (err) {
900
- const message = err instanceof Error ? err.message : "Failed to approve Permit2";
901
- const isRejected = message.includes("rejected") || message.includes("denied") || message.includes("user rejected");
902
- return {
903
- actionId: action.id,
904
- type: action.type,
905
- status: "error",
906
- message: isRejected ? "You rejected the approval transaction. Please approve the Permit2 spending allowance in your wallet to continue." : message
907
- };
908
- }
909
- },
910
- [wagmiConfig2]
911
- );
912
- const executeSignPermit2 = react.useCallback(
913
- async (action) => {
914
- try {
915
- const expectedWalletAddress = action.metadata?.walletAddress;
916
- const walletClient = await waitForWalletClient(
917
- wagmiConfig2,
918
- expectedWalletAddress ? { account: expectedWalletAddress } : {}
919
- );
920
- const account = getAccount(wagmiConfig2);
921
- const connectedAddress = account.address ?? walletClient.account?.address;
922
- const sender = expectedWalletAddress ?? connectedAddress;
923
- if (!sender) {
924
- throw new Error("Wallet account not available. Please connect your wallet.");
925
- }
926
- if (expectedWalletAddress && connectedAddress && connectedAddress.toLowerCase() !== expectedWalletAddress.toLowerCase()) {
927
- return {
928
- actionId: action.id,
929
- type: action.type,
930
- status: "error",
931
- message: `Connected wallet ${sender} does not match the required source wallet ${expectedWalletAddress}. Please switch accounts in your wallet and retry.`
932
- };
933
- }
934
- let typedData = action.metadata?.typedData;
935
- const tokenSymbol = action.metadata?.tokenSymbol;
936
- if (!typedData && sessionIdRef.current) {
937
- const POLL_INTERVAL_MS = 1e3;
938
- const MAX_POLLS = 15;
939
- for (let i = 0; i < MAX_POLLS; i++) {
940
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
941
- const session = await fetchAuthorizationSession(
942
- apiBaseUrl,
943
- sessionIdRef.current
944
- );
945
- const updatedAction = session.actions.find((a) => a.id === action.id);
946
- typedData = updatedAction?.metadata?.typedData;
947
- if (typedData) break;
948
- }
949
- }
950
- if (!typedData) {
951
- return {
952
- actionId: action.id,
953
- type: action.type,
954
- status: "error",
955
- message: "SIGN_PERMIT2 metadata is missing typedData. The server may still be preparing the signing payload."
956
- };
957
- }
958
- const parsedTypedData = parseSignTypedDataPayload(typedData);
959
- console.info(
960
- `[swype-sdk][sign-permit2] Signing typed data. expectedOwner=${expectedWalletAddress ?? "N/A"}, senderParam=${sender}, connectedAddress=${connectedAddress ?? "N/A"}, primaryType=${parsedTypedData.primaryType}, domainChainId=${String(parsedTypedData.domain.chainId ?? "N/A")}, verifyingContract=${String(parsedTypedData.domain.verifyingContract ?? "N/A")}`
961
- );
962
- const signature = await walletClient.signTypedData({
963
- account: sender,
964
- domain: parsedTypedData.domain,
965
- types: parsedTypedData.types,
966
- primaryType: parsedTypedData.primaryType,
967
- message: parsedTypedData.message
968
- });
969
- const recoverInput = {
970
- domain: parsedTypedData.domain,
971
- types: parsedTypedData.types,
972
- primaryType: parsedTypedData.primaryType,
973
- message: parsedTypedData.message,
974
- signature
975
- };
976
- const recoveredSigner = await viem.recoverTypedDataAddress(recoverInput);
977
- const expectedSigner = (expectedWalletAddress ?? sender).toLowerCase();
978
- console.info(
979
- `[swype-sdk][sign-permit2] Signature recovered. recoveredSigner=${recoveredSigner}, expectedSigner=${expectedSigner}`
980
- );
981
- if (recoveredSigner.toLowerCase() !== expectedSigner) {
982
- return {
983
- actionId: action.id,
984
- type: action.type,
985
- status: "error",
986
- message: `Wallet signed with ${recoveredSigner}, but source wallet is ${expectedWalletAddress ?? sender}. Please switch to the source wallet in MetaMask and retry.`
987
- };
988
- }
989
- console.info(
990
- `[swype-sdk][sign-permit2] Permit2 EIP-712 signature obtained. token=${tokenSymbol ?? "unknown"}`
991
- );
992
- return {
993
- actionId: action.id,
994
- type: action.type,
995
- status: "success",
996
- message: `Permit2 allowance signed for ${tokenSymbol ?? "tokens"}.`,
997
- data: { signature }
998
- };
999
- } catch (err) {
1000
- const message = err instanceof Error ? err.message : "Failed to sign Permit2 allowance";
1001
- const isRejected = message.includes("rejected") || message.includes("denied") || message.includes("user rejected");
1002
- return {
1003
- actionId: action.id,
1004
- type: action.type,
1005
- status: "error",
1006
- message: isRejected ? "You rejected the Permit2 signature request. Please approve the signature in your wallet to allow fund transfers." : message
1007
- };
1008
- }
1009
- },
1010
- [wagmiConfig2, apiBaseUrl]
1011
- );
1012
- const executeAction = react.useCallback(
1009
+ const dispatchAction = react.useCallback(
1013
1010
  async (action) => {
1014
1011
  setCurrentAction(action);
1015
1012
  switch (action.type) {
1016
1013
  case "OPEN_PROVIDER":
1017
- return executeOpenProvider(action);
1014
+ return executeOpenProvider(action, wagmiConfig2, connectors, connectAsync);
1018
1015
  case "SELECT_SOURCE":
1019
- return executeSelectSource(action);
1016
+ return executeSelectSource(action, waitForSelection);
1020
1017
  case "SWITCH_CHAIN":
1021
- return executeSwitchChain(action);
1022
- case "REGISTER_PASSKEY":
1023
- return executeRegisterPasskey(action);
1024
- case "CREATE_SMART_ACCOUNT":
1025
- return executeCreateSmartAccount(action);
1018
+ return executeSwitchChain(action, wagmiConfig2, switchChainAsync);
1026
1019
  case "APPROVE_PERMIT2":
1027
- return executeApprovePermit2(action);
1020
+ return executeApprovePermit2(action, wagmiConfig2);
1028
1021
  case "SIGN_PERMIT2":
1029
- return executeSignPermit2(action);
1022
+ return executeSignPermit2(action, wagmiConfig2, apiBaseUrl ?? "", sessionIdRef.current);
1030
1023
  default:
1031
- return {
1032
- actionId: action.id,
1033
- type: action.type,
1034
- status: "error",
1035
- message: `Unsupported action type: ${action.type}`
1036
- };
1024
+ return actionError(action, `Unsupported action type: ${action.type}`);
1037
1025
  }
1038
1026
  },
1039
- [executeOpenProvider, executeSelectSource, executeSwitchChain, executeRegisterPasskey, executeCreateSmartAccount, executeApprovePermit2, executeSignPermit2]
1027
+ [wagmiConfig2, connectors, connectAsync, switchChainAsync, apiBaseUrl, waitForSelection]
1040
1028
  );
1041
- const executeSession = react.useCallback(
1042
- async (transfer) => {
1029
+ const executeSessionById = react.useCallback(
1030
+ async (sessionId) => {
1043
1031
  if (executingRef.current) return;
1044
1032
  executingRef.current = true;
1045
- if (!transfer.authorizationSessions || transfer.authorizationSessions.length === 0) {
1033
+ if (!sessionId) {
1046
1034
  executingRef.current = false;
1047
- throw new Error("No authorization sessions available.");
1035
+ throw new Error("No authorization session id provided.");
1036
+ }
1037
+ if (!apiBaseUrl) {
1038
+ executingRef.current = false;
1039
+ throw new Error("Missing apiBaseUrl. Provide useAuthorizationExecutor({ apiBaseUrl }) or wrap in <SwypeProvider>.");
1048
1040
  }
1049
- const sessionId = transfer.authorizationSessions[0].id;
1050
1041
  sessionIdRef.current = sessionId;
1051
1042
  setExecuting(true);
1052
1043
  setError(null);
1053
1044
  setResults([]);
1054
1045
  try {
1055
- let currentSession = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1046
+ let session = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1056
1047
  const allResults = [];
1057
- const completedActionIds = /* @__PURE__ */ new Set();
1058
- let pendingActions = currentSession.actions.filter((a) => a.status === "PENDING").sort((a, b) => a.orderIndex - b.orderIndex);
1059
- const ACTION_POLL_INTERVAL_MS = 500;
1060
- const ACTION_POLL_MAX_RETRIES = 20;
1061
- let actionPollRetries = 0;
1062
- while (pendingActions.length === 0 && currentSession.status !== "AUTHORIZED" && actionPollRetries < ACTION_POLL_MAX_RETRIES) {
1048
+ const completedIds = /* @__PURE__ */ new Set();
1049
+ let pending = getPendingActions(session, completedIds);
1050
+ let retries = 0;
1051
+ while (pending.length === 0 && session.status !== "AUTHORIZED" && retries < ACTION_POLL_MAX_RETRIES) {
1063
1052
  await new Promise((r) => setTimeout(r, ACTION_POLL_INTERVAL_MS));
1064
- currentSession = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1065
- pendingActions = currentSession.actions.filter((a) => a.status === "PENDING").sort((a, b) => a.orderIndex - b.orderIndex);
1066
- actionPollRetries++;
1053
+ session = await fetchAuthorizationSession(apiBaseUrl, sessionId);
1054
+ pending = getPendingActions(session, completedIds);
1055
+ retries++;
1067
1056
  }
1068
- if (pendingActions.length === 0 && currentSession.status !== "AUTHORIZED") {
1057
+ if (pending.length === 0 && session.status !== "AUTHORIZED") {
1069
1058
  throw new Error("Authorization actions were not created in time. Please try again.");
1070
1059
  }
1071
- while (pendingActions.length > 0) {
1072
- const action = pendingActions[0];
1073
- if (completedActionIds.has(action.id)) break;
1074
- const result = await executeAction(action);
1060
+ while (pending.length > 0) {
1061
+ const action = pending[0];
1062
+ if (completedIds.has(action.id)) break;
1063
+ const result = await dispatchAction(action);
1075
1064
  if (result.status === "error") {
1076
1065
  allResults.push(result);
1077
1066
  setResults([...allResults]);
1078
1067
  throw new Error(result.message);
1079
1068
  }
1080
- completedActionIds.add(action.id);
1081
- const updatedSession = await reportActionCompletion(
1069
+ completedIds.add(action.id);
1070
+ allResults.push(result);
1071
+ session = await reportActionCompletion(
1082
1072
  apiBaseUrl,
1083
1073
  action.id,
1084
1074
  result.data ?? {}
1085
1075
  );
1086
- currentSession = updatedSession;
1087
- pendingActions = currentSession.actions.filter((a) => a.status === "PENDING" && !completedActionIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
1088
- if (action.type === "OPEN_PROVIDER" && pendingActions.length > 0) {
1089
- const chainResults = [result];
1090
- while (pendingActions.length > 0) {
1091
- const nextAction = pendingActions[0];
1092
- const nextResult = await executeAction(nextAction);
1093
- if (nextResult.status === "error") {
1094
- chainResults.push(nextResult);
1095
- allResults.push(...chainResults);
1096
- setResults([...allResults]);
1097
- throw new Error(nextResult.message);
1098
- }
1099
- completedActionIds.add(nextAction.id);
1100
- const nextSession = await reportActionCompletion(
1101
- apiBaseUrl,
1102
- nextAction.id,
1103
- nextResult.data ?? {}
1104
- );
1105
- currentSession = nextSession;
1106
- chainResults.push(nextResult);
1107
- pendingActions = currentSession.actions.filter((a) => a.status === "PENDING" && !completedActionIds.has(a.id)).sort((a, b) => a.orderIndex - b.orderIndex);
1108
- }
1109
- allResults.push(...chainResults);
1110
- setResults([...allResults]);
1111
- continue;
1112
- }
1113
- allResults.push(result);
1114
1076
  setResults([...allResults]);
1077
+ pending = getPendingActions(session, completedIds);
1115
1078
  }
1116
1079
  } catch (err) {
1117
1080
  const msg = err instanceof Error ? err.message : "Authorization failed";
@@ -1123,7 +1086,16 @@ function useAuthorizationExecutor() {
1123
1086
  executingRef.current = false;
1124
1087
  }
1125
1088
  },
1126
- [apiBaseUrl, executeAction]
1089
+ [apiBaseUrl, dispatchAction]
1090
+ );
1091
+ const executeSession = react.useCallback(
1092
+ async (transfer) => {
1093
+ if (!transfer.authorizationSessions?.length) {
1094
+ throw new Error("No authorization sessions available.");
1095
+ }
1096
+ await executeSessionById(transfer.authorizationSessions[0].id);
1097
+ },
1098
+ [executeSessionById]
1127
1099
  );
1128
1100
  return {
1129
1101
  executing,
@@ -1132,12 +1104,21 @@ function useAuthorizationExecutor() {
1132
1104
  currentAction,
1133
1105
  pendingSelectSource,
1134
1106
  resolveSelectSource,
1107
+ executeSessionById,
1135
1108
  executeSession
1136
1109
  };
1137
1110
  }
1138
- function useTransferSigning(pollIntervalMs = 2e3) {
1139
- const { apiBaseUrl } = useSwypeConfig();
1140
- const { getAccessToken } = reactAuth.usePrivy();
1111
+ function useTransferSigning(pollIntervalMs = 2e3, options) {
1112
+ const swypeConfig = useOptionalSwypeConfig();
1113
+ const apiBaseUrl = options?.apiBaseUrl ?? swypeConfig?.apiBaseUrl;
1114
+ const authorizationSessionToken = options?.authorizationSessionToken;
1115
+ let privyGetAccessToken;
1116
+ try {
1117
+ ({ getAccessToken: privyGetAccessToken } = reactAuth.usePrivy());
1118
+ } catch {
1119
+ privyGetAccessToken = void 0;
1120
+ }
1121
+ const getAccessToken = options?.getAccessToken ?? privyGetAccessToken;
1141
1122
  const [signing, setSigning] = react.useState(false);
1142
1123
  const [signPayload, setSignPayload] = react.useState(null);
1143
1124
  const [error, setError] = react.useState(null);
@@ -1147,14 +1128,24 @@ function useTransferSigning(pollIntervalMs = 2e3) {
1147
1128
  setError(null);
1148
1129
  setSignPayload(null);
1149
1130
  try {
1150
- const token = await getAccessToken();
1151
- if (!token) {
1131
+ if (!apiBaseUrl) {
1132
+ throw new Error("Missing apiBaseUrl. Provide useTransferSigning(_, { apiBaseUrl }) or wrap in <SwypeProvider>.");
1133
+ }
1134
+ if (!getAccessToken && !authorizationSessionToken) {
1135
+ throw new Error("Missing getAccessToken provider. Provide useTransferSigning(_, { getAccessToken }).");
1136
+ }
1137
+ const token = getAccessToken ? await getAccessToken() : null;
1138
+ if (!token && !authorizationSessionToken) {
1152
1139
  throw new Error("Could not get access token");
1153
1140
  }
1154
- const MAX_POLLS = 60;
1155
1141
  let payload = null;
1156
- for (let i = 0; i < MAX_POLLS; i++) {
1157
- const transfer = await fetchTransfer(apiBaseUrl, token, transferId);
1142
+ for (let i = 0; i < TRANSFER_SIGN_MAX_POLLS; i++) {
1143
+ const transfer = await fetchTransfer(
1144
+ apiBaseUrl,
1145
+ token ?? "",
1146
+ transferId,
1147
+ authorizationSessionToken
1148
+ );
1158
1149
  if (transfer.signPayload) {
1159
1150
  payload = transfer.signPayload;
1160
1151
  setSignPayload(payload);
@@ -1168,14 +1159,16 @@ function useTransferSigning(pollIntervalMs = 2e3) {
1168
1159
  if (!payload) {
1169
1160
  throw new Error("Timed out waiting for sign payload. Please try again.");
1170
1161
  }
1171
- const userOpHashHex = payload.userOpHash;
1172
- const hashBytes = new Uint8Array(
1173
- (userOpHashHex.startsWith("0x") ? userOpHashHex.slice(2) : userOpHashHex).match(/.{1,2}/g).map((byte) => parseInt(byte, 16))
1174
- );
1162
+ const hashBytes = hexToBytes(payload.userOpHash);
1163
+ const allowCredentials = payload.passkeyCredentialId ? [{
1164
+ type: "public-key",
1165
+ id: base64ToBytes(payload.passkeyCredentialId)
1166
+ }] : void 0;
1175
1167
  const assertion = await navigator.credentials.get({
1176
1168
  publicKey: {
1177
1169
  challenge: hashBytes,
1178
- rpId: window.location.hostname,
1170
+ rpId: resolvePasskeyRpId(),
1171
+ allowCredentials,
1179
1172
  userVerification: "required",
1180
1173
  timeout: 6e4
1181
1174
  }
@@ -1184,32 +1177,20 @@ function useTransferSigning(pollIntervalMs = 2e3) {
1184
1177
  throw new Error("Passkey authentication was cancelled.");
1185
1178
  }
1186
1179
  const response = assertion.response;
1187
- const signature = btoa(
1188
- String.fromCharCode(...new Uint8Array(response.signature))
1189
- );
1190
- const authenticatorData = btoa(
1191
- String.fromCharCode(
1192
- ...new Uint8Array(response.authenticatorData)
1193
- )
1194
- );
1195
- const clientDataJSON = btoa(
1196
- String.fromCharCode(
1197
- ...new Uint8Array(response.clientDataJSON)
1198
- )
1199
- );
1200
1180
  const signedUserOp = {
1201
1181
  ...payload.userOp,
1202
- signature,
1203
- authenticatorData,
1204
- clientDataJSON
1182
+ credentialId: toBase64(assertion.rawId),
1183
+ signature: toBase64(response.signature),
1184
+ authenticatorData: toBase64(response.authenticatorData),
1185
+ clientDataJSON: toBase64(response.clientDataJSON)
1205
1186
  };
1206
- const updatedTransfer = await signTransfer(
1187
+ return await signTransfer(
1207
1188
  apiBaseUrl,
1208
- token,
1189
+ token ?? "",
1209
1190
  transferId,
1210
- signedUserOp
1191
+ signedUserOp,
1192
+ authorizationSessionToken
1211
1193
  );
1212
- return updatedTransfer;
1213
1194
  } catch (err) {
1214
1195
  const msg = err instanceof Error ? err.message : "Failed to sign transfer";
1215
1196
  setError(msg);
@@ -1218,7 +1199,7 @@ function useTransferSigning(pollIntervalMs = 2e3) {
1218
1199
  setSigning(false);
1219
1200
  }
1220
1201
  },
1221
- [apiBaseUrl, getAccessToken, pollIntervalMs]
1202
+ [apiBaseUrl, getAccessToken, pollIntervalMs, authorizationSessionToken]
1222
1203
  );
1223
1204
  return { signing, signPayload, error, signTransfer: signTransfer2 };
1224
1205
  }
@@ -1994,6 +1975,37 @@ function computeSmartDefaults(accts, transferAmount) {
1994
1975
  }
1995
1976
  return { accountId: accts[0].id, walletId: null };
1996
1977
  }
1978
+ function parseRawBalance(rawBalance, decimals) {
1979
+ const parsed = Number(rawBalance);
1980
+ if (!Number.isFinite(parsed)) return 0;
1981
+ return parsed / 10 ** decimals;
1982
+ }
1983
+ function buildSelectSourceChoices(options) {
1984
+ const chainChoices = [];
1985
+ const chainIndexByName = /* @__PURE__ */ new Map();
1986
+ for (const option of options) {
1987
+ const chainName = option.chainName;
1988
+ const tokenSymbol = option.tokenSymbol;
1989
+ const balance = parseRawBalance(option.rawBalance, option.decimals);
1990
+ let chainChoice;
1991
+ const existingChainIdx = chainIndexByName.get(chainName);
1992
+ if (existingChainIdx === void 0) {
1993
+ chainChoice = { chainName, balance: 0, tokens: [] };
1994
+ chainIndexByName.set(chainName, chainChoices.length);
1995
+ chainChoices.push(chainChoice);
1996
+ } else {
1997
+ chainChoice = chainChoices[existingChainIdx];
1998
+ }
1999
+ chainChoice.balance += balance;
2000
+ const existingToken = chainChoice.tokens.find((token) => token.tokenSymbol === tokenSymbol);
2001
+ if (existingToken) {
2002
+ existingToken.balance += balance;
2003
+ } else {
2004
+ chainChoice.tokens.push({ tokenSymbol, balance });
2005
+ }
2006
+ }
2007
+ return chainChoices;
2008
+ }
1997
2009
  function SwypePayment({
1998
2010
  destination,
1999
2011
  onComplete,
@@ -2020,8 +2032,12 @@ function SwypePayment({
2020
2032
  });
2021
2033
  const [transfer, setTransfer] = react.useState(null);
2022
2034
  const [creatingTransfer, setCreatingTransfer] = react.useState(false);
2035
+ const [registeringPasskey, setRegisteringPasskey] = react.useState(false);
2023
2036
  const [mobileFlow, setMobileFlow] = react.useState(false);
2024
2037
  const pollingTransferIdRef = react.useRef(null);
2038
+ const [selectSourceChainName, setSelectSourceChainName] = react.useState("");
2039
+ const [selectSourceTokenSymbol, setSelectSourceTokenSymbol] = react.useState("");
2040
+ const initializedSelectSourceActionRef = react.useRef(null);
2025
2041
  const authExecutor = useAuthorizationExecutor();
2026
2042
  const polling = useTransferPolling();
2027
2043
  const transferSigning = useTransferSigning();
@@ -2033,14 +2049,36 @@ function SwypePayment({
2033
2049
  }
2034
2050
  }, [depositAmount]);
2035
2051
  react.useEffect(() => {
2036
- if (ready && authenticated && step === "login") {
2037
- if (depositAmount != null && depositAmount > 0) {
2038
- setStep("ready");
2039
- } else {
2040
- setStep("enter-amount");
2052
+ if (!ready || !authenticated || step !== "login") return;
2053
+ let cancelled = false;
2054
+ const checkPasskey = async () => {
2055
+ try {
2056
+ const token = await getAccessToken();
2057
+ if (!token || cancelled) return;
2058
+ const { config } = await fetchUserConfig(apiBaseUrl, token);
2059
+ if (cancelled) return;
2060
+ if (!config.passkey) {
2061
+ setStep("register-passkey");
2062
+ } else if (depositAmount != null && depositAmount > 0) {
2063
+ setStep("ready");
2064
+ } else {
2065
+ setStep("enter-amount");
2066
+ }
2067
+ } catch {
2068
+ if (!cancelled) {
2069
+ if (depositAmount != null && depositAmount > 0) {
2070
+ setStep("ready");
2071
+ } else {
2072
+ setStep("enter-amount");
2073
+ }
2074
+ }
2041
2075
  }
2042
- }
2043
- }, [ready, authenticated, step, depositAmount]);
2076
+ };
2077
+ checkPasskey();
2078
+ return () => {
2079
+ cancelled = true;
2080
+ };
2081
+ }, [ready, authenticated, step, depositAmount, apiBaseUrl, getAccessToken]);
2044
2082
  const loadingDataRef = react.useRef(false);
2045
2083
  react.useEffect(() => {
2046
2084
  if (!authenticated) return;
@@ -2114,61 +2152,43 @@ function SwypePayment({
2114
2152
  document.removeEventListener("visibilitychange", handleVisibility);
2115
2153
  };
2116
2154
  }, [mobileFlow, polling]);
2155
+ const pendingSelectSourceAction = authExecutor.pendingSelectSource;
2156
+ const selectSourceChoices = react.useMemo(() => {
2157
+ if (!pendingSelectSourceAction) return [];
2158
+ const options = pendingSelectSourceAction.metadata?.options ?? [];
2159
+ return buildSelectSourceChoices(options);
2160
+ }, [pendingSelectSourceAction]);
2161
+ const selectSourceRecommended = react.useMemo(() => {
2162
+ if (!pendingSelectSourceAction) return null;
2163
+ return pendingSelectSourceAction.metadata?.recommended ?? null;
2164
+ }, [pendingSelectSourceAction]);
2117
2165
  react.useEffect(() => {
2118
- if (!authExecutor.pendingSelectSource) return;
2119
- const action = authExecutor.pendingSelectSource;
2120
- const hasAdvancedOverride = advancedSettings.asset !== null || advancedSettings.chain !== null;
2121
- if (hasAdvancedOverride) {
2122
- const options = action.metadata?.options ?? [];
2123
- const recommended = action.metadata?.recommended;
2124
- const match = options.find(
2125
- (opt) => (advancedSettings.chain === null || opt.chainName === advancedSettings.chain) && (advancedSettings.asset === null || opt.tokenSymbol === advancedSettings.asset)
2126
- );
2127
- if (match) {
2128
- authExecutor.resolveSelectSource({
2129
- chainName: match.chainName,
2130
- tokenSymbol: match.tokenSymbol
2131
- });
2132
- } else if (recommended) {
2133
- authExecutor.resolveSelectSource({
2134
- chainName: recommended.chainName,
2135
- tokenSymbol: recommended.tokenSymbol
2136
- });
2137
- }
2166
+ if (!pendingSelectSourceAction) {
2167
+ initializedSelectSourceActionRef.current = null;
2168
+ setSelectSourceChainName("");
2169
+ setSelectSourceTokenSymbol("");
2170
+ return;
2171
+ }
2172
+ if (initializedSelectSourceActionRef.current === pendingSelectSourceAction.id) {
2173
+ return;
2174
+ }
2175
+ const hasRecommendedOption = !!selectSourceRecommended && selectSourceChoices.some(
2176
+ (chain) => chain.chainName === selectSourceRecommended.chainName && chain.tokens.some(
2177
+ (token) => token.tokenSymbol === selectSourceRecommended.tokenSymbol
2178
+ )
2179
+ );
2180
+ if (hasRecommendedOption && selectSourceRecommended) {
2181
+ setSelectSourceChainName(selectSourceRecommended.chainName);
2182
+ setSelectSourceTokenSymbol(selectSourceRecommended.tokenSymbol);
2183
+ } else if (selectSourceChoices.length > 0 && selectSourceChoices[0].tokens.length > 0) {
2184
+ setSelectSourceChainName(selectSourceChoices[0].chainName);
2185
+ setSelectSourceTokenSymbol(selectSourceChoices[0].tokens[0].tokenSymbol);
2138
2186
  } else {
2139
- const options = action.metadata?.options ?? [];
2140
- const recommended = action.metadata?.recommended;
2141
- const selWallet = selectedWalletId ? accounts.find((a) => a.id === selectedAccountId)?.wallets.find((w) => w.id === selectedWalletId) : null;
2142
- if (selWallet) {
2143
- const walletMatch = options.find(
2144
- (opt) => opt.chainName === selWallet.chain.name
2145
- );
2146
- if (walletMatch) {
2147
- authExecutor.resolveSelectSource({
2148
- chainName: walletMatch.chainName,
2149
- tokenSymbol: walletMatch.tokenSymbol
2150
- });
2151
- return;
2152
- }
2153
- }
2154
- if (recommended) {
2155
- authExecutor.resolveSelectSource({
2156
- chainName: recommended.chainName,
2157
- tokenSymbol: recommended.tokenSymbol
2158
- });
2159
- } else if (options.length > 0) {
2160
- authExecutor.resolveSelectSource({
2161
- chainName: options[0].chainName,
2162
- tokenSymbol: options[0].tokenSymbol
2163
- });
2164
- } else {
2165
- authExecutor.resolveSelectSource({
2166
- chainName: "Base",
2167
- tokenSymbol: "USDC"
2168
- });
2169
- }
2187
+ setSelectSourceChainName("Base");
2188
+ setSelectSourceTokenSymbol("USDC");
2170
2189
  }
2171
- }, [authExecutor, authExecutor.pendingSelectSource, advancedSettings, selectedWalletId, selectedAccountId, accounts]);
2190
+ initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
2191
+ }, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
2172
2192
  const handlePay = react.useCallback(async () => {
2173
2193
  const parsedAmount = parseFloat(amount);
2174
2194
  if (isNaN(parsedAmount) || parsedAmount <= 0) {
@@ -2331,6 +2351,37 @@ function SwypePayment({
2331
2351
  ]
2332
2352
  }
2333
2353
  );
2354
+ const displayedSelectSourceChoices = selectSourceChoices.length > 0 ? selectSourceChoices : [
2355
+ {
2356
+ chainName: "Base",
2357
+ balance: 0,
2358
+ tokens: [{ tokenSymbol: "USDC", balance: 0 }]
2359
+ }
2360
+ ];
2361
+ const selectedChainChoice = displayedSelectSourceChoices.find(
2362
+ (choice) => choice.chainName === selectSourceChainName
2363
+ ) ?? displayedSelectSourceChoices[0];
2364
+ const selectSourceTokenChoices = selectedChainChoice?.tokens ?? [];
2365
+ const resolvedSelectSourceChainName = selectedChainChoice?.chainName ?? selectSourceChainName;
2366
+ const resolvedSelectSourceTokenSymbol = selectSourceTokenChoices.find(
2367
+ (token) => token.tokenSymbol === selectSourceTokenSymbol
2368
+ )?.tokenSymbol ?? selectSourceTokenChoices[0]?.tokenSymbol ?? "";
2369
+ const canConfirmSelectSource = !!resolvedSelectSourceChainName && !!resolvedSelectSourceTokenSymbol;
2370
+ const handleSelectSourceChainChange = (chainName) => {
2371
+ setSelectSourceChainName(chainName);
2372
+ const nextChain = displayedSelectSourceChoices.find(
2373
+ (choice) => choice.chainName === chainName
2374
+ );
2375
+ if (!nextChain || nextChain.tokens.length === 0) {
2376
+ setSelectSourceTokenSymbol("");
2377
+ return;
2378
+ }
2379
+ const recommendedTokenForChain = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
2380
+ const hasRecommendedToken = !!recommendedTokenForChain && nextChain.tokens.some((token) => token.tokenSymbol === recommendedTokenForChain);
2381
+ setSelectSourceTokenSymbol(
2382
+ hasRecommendedToken && recommendedTokenForChain ? recommendedTokenForChain : nextChain.tokens[0].tokenSymbol
2383
+ );
2384
+ };
2334
2385
  if (!ready) {
2335
2386
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "24px 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Initializing..." }) }) });
2336
2387
  }
@@ -2374,6 +2425,73 @@ function SwypePayment({
2374
2425
  /* @__PURE__ */ jsxRuntime.jsx("button", { style: btnPrimary, onClick: login, children: "Connect to Swype" })
2375
2426
  ] }) });
2376
2427
  }
2428
+ if (step === "register-passkey") {
2429
+ const handleRegisterPasskey = async () => {
2430
+ setRegisteringPasskey(true);
2431
+ setError(null);
2432
+ try {
2433
+ const token = await getAccessToken();
2434
+ if (!token) throw new Error("Not authenticated");
2435
+ const { credentialId, publicKey } = await createPasskeyCredential("Swype User");
2436
+ await registerPasskey(apiBaseUrl, token, credentialId, publicKey);
2437
+ if (depositAmount != null && depositAmount > 0) {
2438
+ setStep("ready");
2439
+ } else {
2440
+ setStep("enter-amount");
2441
+ }
2442
+ } catch (err) {
2443
+ const msg = err instanceof Error ? err.message : "Failed to register passkey";
2444
+ setError(msg);
2445
+ } finally {
2446
+ setRegisteringPasskey(false);
2447
+ }
2448
+ };
2449
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center" }, children: [
2450
+ /* @__PURE__ */ jsxRuntime.jsxs(
2451
+ "svg",
2452
+ {
2453
+ width: "48",
2454
+ height: "48",
2455
+ viewBox: "0 0 48 48",
2456
+ fill: "none",
2457
+ style: { margin: "0 auto 16px" },
2458
+ children: [
2459
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { width: "48", height: "48", rx: "12", fill: tokens.accent + "20" }),
2460
+ /* @__PURE__ */ jsxRuntime.jsx(
2461
+ "path",
2462
+ {
2463
+ d: "M24 16c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-4.42 0-8 1.79-8 4v2h16v-2c0-2.21-3.58-4-8-4z",
2464
+ fill: tokens.accent
2465
+ }
2466
+ )
2467
+ ]
2468
+ }
2469
+ ),
2470
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { ...headingStyle, marginBottom: "8px" }, children: "Set Up Passkey" }),
2471
+ /* @__PURE__ */ jsxRuntime.jsx(
2472
+ "p",
2473
+ {
2474
+ style: {
2475
+ fontSize: "0.875rem",
2476
+ color: tokens.textSecondary,
2477
+ margin: "0 0 24px 0",
2478
+ lineHeight: 1.5
2479
+ },
2480
+ children: "Create a passkey for secure, one-touch payments. This only needs to be done once."
2481
+ }
2482
+ ),
2483
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorStyle, children: error }),
2484
+ /* @__PURE__ */ jsxRuntime.jsx(
2485
+ "button",
2486
+ {
2487
+ style: registeringPasskey ? btnDisabled : btnPrimary,
2488
+ disabled: registeringPasskey,
2489
+ onClick: handleRegisterPasskey,
2490
+ children: registeringPasskey ? "Creating passkey..." : "Create Passkey"
2491
+ }
2492
+ )
2493
+ ] }) });
2494
+ }
2377
2495
  if (step === "enter-amount") {
2378
2496
  const parsedAmount = parseFloat(amount);
2379
2497
  const canContinue = !isNaN(parsedAmount) && parsedAmount > 0;
@@ -2692,6 +2810,142 @@ function SwypePayment({
2692
2810
  ] });
2693
2811
  }
2694
2812
  if (step === "processing") {
2813
+ if (pendingSelectSourceAction) {
2814
+ const chainValue = resolvedSelectSourceChainName;
2815
+ const tokenValue = resolvedSelectSourceTokenSymbol;
2816
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: cardStyle, children: [
2817
+ stepBadge("Select source"),
2818
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", marginBottom: "16px" }, children: [
2819
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { ...headingStyle, marginBottom: "8px" }, children: "Select payment source" }),
2820
+ /* @__PURE__ */ jsxRuntime.jsx(
2821
+ "p",
2822
+ {
2823
+ style: {
2824
+ fontSize: "0.85rem",
2825
+ color: tokens.textSecondary,
2826
+ margin: 0,
2827
+ lineHeight: 1.5
2828
+ },
2829
+ children: "Confirm the chain and token to use for this transfer."
2830
+ }
2831
+ )
2832
+ ] }),
2833
+ /* @__PURE__ */ jsxRuntime.jsxs(
2834
+ "div",
2835
+ {
2836
+ style: {
2837
+ fontSize: "0.825rem",
2838
+ color: tokens.textSecondary,
2839
+ marginBottom: "16px",
2840
+ padding: "14px",
2841
+ background: tokens.bgInput,
2842
+ borderRadius: tokens.radius,
2843
+ border: `1px solid ${tokens.border}`
2844
+ },
2845
+ children: [
2846
+ /* @__PURE__ */ jsxRuntime.jsx(
2847
+ "label",
2848
+ {
2849
+ htmlFor: "swype-select-source-chain",
2850
+ style: {
2851
+ display: "block",
2852
+ fontSize: "0.75rem",
2853
+ fontWeight: 600,
2854
+ marginBottom: "6px",
2855
+ color: tokens.textMuted,
2856
+ textTransform: "uppercase",
2857
+ letterSpacing: "0.04em"
2858
+ },
2859
+ children: "Chain"
2860
+ }
2861
+ ),
2862
+ /* @__PURE__ */ jsxRuntime.jsx(
2863
+ "select",
2864
+ {
2865
+ id: "swype-select-source-chain",
2866
+ value: chainValue,
2867
+ onChange: (event) => handleSelectSourceChainChange(event.target.value),
2868
+ style: {
2869
+ width: "100%",
2870
+ marginBottom: "12px",
2871
+ padding: "10px 12px",
2872
+ borderRadius: tokens.radius,
2873
+ border: `1px solid ${tokens.border}`,
2874
+ background: tokens.bgCard,
2875
+ color: tokens.text,
2876
+ fontFamily: "inherit",
2877
+ fontSize: "0.875rem",
2878
+ outline: "none"
2879
+ },
2880
+ children: displayedSelectSourceChoices.map((chainChoice) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: chainChoice.chainName, children: [
2881
+ chainChoice.chainName,
2882
+ " ($",
2883
+ chainChoice.balance.toFixed(2),
2884
+ ")"
2885
+ ] }, chainChoice.chainName))
2886
+ }
2887
+ ),
2888
+ /* @__PURE__ */ jsxRuntime.jsx(
2889
+ "label",
2890
+ {
2891
+ htmlFor: "swype-select-source-token",
2892
+ style: {
2893
+ display: "block",
2894
+ fontSize: "0.75rem",
2895
+ fontWeight: 600,
2896
+ marginBottom: "6px",
2897
+ color: tokens.textMuted,
2898
+ textTransform: "uppercase",
2899
+ letterSpacing: "0.04em"
2900
+ },
2901
+ children: "Token"
2902
+ }
2903
+ ),
2904
+ /* @__PURE__ */ jsxRuntime.jsx(
2905
+ "select",
2906
+ {
2907
+ id: "swype-select-source-token",
2908
+ value: tokenValue,
2909
+ onChange: (event) => setSelectSourceTokenSymbol(event.target.value),
2910
+ style: {
2911
+ width: "100%",
2912
+ padding: "10px 12px",
2913
+ borderRadius: tokens.radius,
2914
+ border: `1px solid ${tokens.border}`,
2915
+ background: tokens.bgCard,
2916
+ color: tokens.text,
2917
+ fontFamily: "inherit",
2918
+ fontSize: "0.875rem",
2919
+ outline: "none"
2920
+ },
2921
+ children: selectSourceTokenChoices.map((tokenChoice) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: tokenChoice.tokenSymbol, children: [
2922
+ tokenChoice.tokenSymbol,
2923
+ " ($",
2924
+ tokenChoice.balance.toFixed(2),
2925
+ ")"
2926
+ ] }, tokenChoice.tokenSymbol))
2927
+ }
2928
+ )
2929
+ ]
2930
+ }
2931
+ ),
2932
+ /* @__PURE__ */ jsxRuntime.jsx(
2933
+ "button",
2934
+ {
2935
+ style: canConfirmSelectSource ? btnPrimary : btnDisabled,
2936
+ disabled: !canConfirmSelectSource,
2937
+ onClick: () => {
2938
+ if (!canConfirmSelectSource) return;
2939
+ authExecutor.resolveSelectSource({
2940
+ chainName: resolvedSelectSourceChainName,
2941
+ tokenSymbol: resolvedSelectSourceTokenSymbol
2942
+ });
2943
+ },
2944
+ children: "Confirm source"
2945
+ }
2946
+ )
2947
+ ] });
2948
+ }
2695
2949
  if (transferSigning.signing && transferSigning.signPayload) {
2696
2950
  const payload = transferSigning.signPayload;
2697
2951
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "16px 0" }, children: [
@@ -2732,11 +2986,6 @@ function SwypePayment({
2732
2986
  const currentActionType = authExecutor.currentAction?.type;
2733
2987
  const getRegistrationMessage = () => {
2734
2988
  switch (currentActionType) {
2735
- case "REGISTER_PASSKEY":
2736
- return {
2737
- label: "Creating your passkey...",
2738
- description: "Set up a passkey for secure, one-touch payments."
2739
- };
2740
2989
  case "CREATE_SMART_ACCOUNT":
2741
2990
  return {
2742
2991
  label: "Creating your smart account...",
@@ -2935,6 +3184,7 @@ function SwypePayment({
2935
3184
 
2936
3185
  exports.SwypePayment = SwypePayment;
2937
3186
  exports.SwypeProvider = SwypeProvider;
3187
+ exports.createPasskeyCredential = createPasskeyCredential;
2938
3188
  exports.darkTheme = darkTheme;
2939
3189
  exports.getTheme = getTheme;
2940
3190
  exports.lightTheme = lightTheme;
@@ -2943,5 +3193,6 @@ exports.useAuthorizationExecutor = useAuthorizationExecutor;
2943
3193
  exports.useSwypeConfig = useSwypeConfig;
2944
3194
  exports.useSwypeDepositAmount = useSwypeDepositAmount;
2945
3195
  exports.useTransferPolling = useTransferPolling;
3196
+ exports.useTransferSigning = useTransferSigning;
2946
3197
  //# sourceMappingURL=index.cjs.map
2947
3198
  //# sourceMappingURL=index.cjs.map