@swype-org/react-sdk 0.1.3 → 0.1.4
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 +169 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.js +170 -211
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -38,7 +38,7 @@ interface Wallet {
|
|
|
38
38
|
id: string;
|
|
39
39
|
name: string;
|
|
40
40
|
};
|
|
41
|
-
/** The smart account type (e.g. 'metamask'), if
|
|
41
|
+
/** The smart account type (e.g. 'metamask'), if a smart account has been created. */
|
|
42
42
|
smartAccountType?: string | null;
|
|
43
43
|
createDate: string;
|
|
44
44
|
updateDate: string;
|
|
@@ -293,10 +293,17 @@ interface UseAuthorizationExecutorResult {
|
|
|
293
293
|
}
|
|
294
294
|
/**
|
|
295
295
|
* Executes the full authorization flow for a transfer's first session:
|
|
296
|
-
* fetches the session, walks through PENDING actions
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
296
|
+
* fetches the session, walks through PENDING actions, reports each
|
|
297
|
+
* completion to the server, and chains new actions that appear.
|
|
298
|
+
*
|
|
299
|
+
* Supported action types:
|
|
300
|
+
* - OPEN_PROVIDER — connect the user's wallet
|
|
301
|
+
* - SELECT_SOURCE — pause for user chain+token selection
|
|
302
|
+
* - SWITCH_CHAIN — switch the wallet to the required chain
|
|
303
|
+
* - REGISTER_PASSKEY — create a WebAuthn passkey credential
|
|
304
|
+
* - CREATE_SMART_ACCOUNT — server-side smart account deployment
|
|
305
|
+
* - APPROVE_PERMIT2 — ERC-20 approve(permit2, maxUint256) from EOA
|
|
306
|
+
* - SIGN_PERMIT2 — EIP-712 Permit2 allowance signature from EOA
|
|
300
307
|
*
|
|
301
308
|
* Transfer signing (passkey) is handled separately via
|
|
302
309
|
* `useTransferSigning()` after the authorization flow completes.
|
package/dist/index.d.ts
CHANGED
|
@@ -38,7 +38,7 @@ interface Wallet {
|
|
|
38
38
|
id: string;
|
|
39
39
|
name: string;
|
|
40
40
|
};
|
|
41
|
-
/** The smart account type (e.g. 'metamask'), if
|
|
41
|
+
/** The smart account type (e.g. 'metamask'), if a smart account has been created. */
|
|
42
42
|
smartAccountType?: string | null;
|
|
43
43
|
createDate: string;
|
|
44
44
|
updateDate: string;
|
|
@@ -293,10 +293,17 @@ interface UseAuthorizationExecutorResult {
|
|
|
293
293
|
}
|
|
294
294
|
/**
|
|
295
295
|
* Executes the full authorization flow for a transfer's first session:
|
|
296
|
-
* fetches the session, walks through PENDING actions
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
296
|
+
* fetches the session, walks through PENDING actions, reports each
|
|
297
|
+
* completion to the server, and chains new actions that appear.
|
|
298
|
+
*
|
|
299
|
+
* Supported action types:
|
|
300
|
+
* - OPEN_PROVIDER — connect the user's wallet
|
|
301
|
+
* - SELECT_SOURCE — pause for user chain+token selection
|
|
302
|
+
* - SWITCH_CHAIN — switch the wallet to the required chain
|
|
303
|
+
* - REGISTER_PASSKEY — create a WebAuthn passkey credential
|
|
304
|
+
* - CREATE_SMART_ACCOUNT — server-side smart account deployment
|
|
305
|
+
* - APPROVE_PERMIT2 — ERC-20 approve(permit2, maxUint256) from EOA
|
|
306
|
+
* - SIGN_PERMIT2 — EIP-712 Permit2 allowance signature from EOA
|
|
300
307
|
*
|
|
301
308
|
* Transfer signing (passkey) is handled separately via
|
|
302
309
|
* `useTransferSigning()` after the authorization flow completes.
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createConfig, http, WagmiProvider, useConfig, useConnect, useSwitchChai
|
|
|
4
4
|
import { mainnet, arbitrum, base } from 'wagmi/chains';
|
|
5
5
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { walletActions, createClient, custom } from 'viem';
|
|
7
|
+
import { recoverTypedDataAddress, walletActions, createClient, custom } from 'viem';
|
|
8
8
|
import { parseAccount, getAddress } from 'viem/utils';
|
|
9
9
|
|
|
10
10
|
var __defProp = Object.defineProperty;
|
|
@@ -525,160 +525,10 @@ async function getWalletClient(config, parameters = {}) {
|
|
|
525
525
|
const client = await getConnectorClient(config, parameters);
|
|
526
526
|
return client.extend(walletActions);
|
|
527
527
|
}
|
|
528
|
-
|
|
529
|
-
// src/authorization/upgrade.ts
|
|
530
|
-
function isNonEmptyString(value) {
|
|
531
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
532
|
-
}
|
|
533
|
-
function isValidChainId(value) {
|
|
534
|
-
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
535
|
-
}
|
|
536
|
-
function isRpcClient(value) {
|
|
537
|
-
return typeof value === "object" && value !== null && "request" in value && typeof value.request === "function";
|
|
538
|
-
}
|
|
539
|
-
function sleep(ms) {
|
|
540
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
541
|
-
}
|
|
542
|
-
async function pollCallsStatus(client, bundleId, label, timeoutMs = 12e4, intervalMs = 3e3) {
|
|
543
|
-
const deadline = Date.now() + timeoutMs;
|
|
544
|
-
while (Date.now() < deadline) {
|
|
545
|
-
const raw = await client.request({
|
|
546
|
-
method: "wallet_getCallsStatus",
|
|
547
|
-
params: [bundleId]
|
|
548
|
-
});
|
|
549
|
-
const result = raw;
|
|
550
|
-
const status = result.status;
|
|
551
|
-
console.info(`[swype-sdk][upgrade] ${label} status:`, status);
|
|
552
|
-
if (status === 200 || status === "CONFIRMED" || status === "confirmed") {
|
|
553
|
-
const txHash = result.receipts?.[0]?.transactionHash ?? "";
|
|
554
|
-
if (!txHash) {
|
|
555
|
-
console.warn(`[swype-sdk][upgrade] ${label}: confirmed but no receipt txHash.`);
|
|
556
|
-
}
|
|
557
|
-
return txHash;
|
|
558
|
-
}
|
|
559
|
-
if (typeof status === "number" && status >= 400) {
|
|
560
|
-
throw new Error(
|
|
561
|
-
`${label} failed with status ${status}. Receipts: ${JSON.stringify(result.receipts ?? [])}`
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
if (typeof status === "string" && (status.toLowerCase().includes("fail") || status.toLowerCase().includes("error"))) {
|
|
565
|
-
throw new Error(
|
|
566
|
-
`${label} failed: ${status}. Receipts: ${JSON.stringify(result.receipts ?? [])}`
|
|
567
|
-
);
|
|
568
|
-
}
|
|
569
|
-
await sleep(intervalMs);
|
|
570
|
-
}
|
|
571
|
-
throw new Error(`${label} timed out after ${timeoutMs / 1e3}s.`);
|
|
572
|
-
}
|
|
573
|
-
async function sendCalls(client, callsParams, label) {
|
|
574
|
-
try {
|
|
575
|
-
const result = await client.request({
|
|
576
|
-
method: "wallet_sendCalls",
|
|
577
|
-
params: [callsParams]
|
|
578
|
-
});
|
|
579
|
-
const bundleId = typeof result === "string" ? result : result?.id;
|
|
580
|
-
if (!bundleId) {
|
|
581
|
-
throw new Error(`${label}: wallet_sendCalls returned an unexpected result: ${JSON.stringify(result)}`);
|
|
582
|
-
}
|
|
583
|
-
return bundleId;
|
|
584
|
-
} catch (err) {
|
|
585
|
-
const msg = err.message ?? "";
|
|
586
|
-
if (msg.includes("Unauthorized") || msg.includes("-32006")) {
|
|
587
|
-
throw new Error(
|
|
588
|
-
"MetaMask smart accounts may not be enabled or may not be supported on this chain. Please ensure you are using a supported network and that smart accounts are enabled in MetaMask Settings \u2192 Experimental."
|
|
589
|
-
);
|
|
590
|
-
}
|
|
591
|
-
throw new Error(`${label} failed: ${msg}`);
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
async function submitUpgradeTransaction(params) {
|
|
595
|
-
const { walletClient, sender, metadata } = params;
|
|
596
|
-
const addKeyCalldata = metadata?.addKeyCalldata;
|
|
597
|
-
if (!isNonEmptyString(addKeyCalldata)) {
|
|
598
|
-
throw new Error("Incomplete upgrade metadata: missing addKeyCalldata.");
|
|
599
|
-
}
|
|
600
|
-
const resolvedSmartAccountAddress = metadata?.smartAccountAddress ?? sender;
|
|
601
|
-
if (!isNonEmptyString(resolvedSmartAccountAddress)) {
|
|
602
|
-
throw new Error("No connected account address found for smart account upgrade.");
|
|
603
|
-
}
|
|
604
|
-
const chainId = metadata?.upgradePayload?.authorization?.chainId;
|
|
605
|
-
if (!isValidChainId(chainId)) {
|
|
606
|
-
throw new Error("Invalid or missing chainId in upgrade metadata.");
|
|
607
|
-
}
|
|
608
|
-
if (!isRpcClient(walletClient)) {
|
|
609
|
-
throw new Error(
|
|
610
|
-
"Connected wallet client does not support request(). EIP-7702 upgrade requires a wallet client with raw RPC access."
|
|
611
|
-
);
|
|
612
|
-
}
|
|
613
|
-
const hexChainId = `0x${chainId.toString(16)}`;
|
|
614
|
-
console.info(
|
|
615
|
-
"[swype-sdk][upgrade] Step 1/2: Triggering EIP-7702 upgrade via wallet_sendCalls.",
|
|
616
|
-
{ from: resolvedSmartAccountAddress, chainId: hexChainId }
|
|
617
|
-
);
|
|
618
|
-
const upgradeBundleId = await sendCalls(
|
|
619
|
-
walletClient,
|
|
620
|
-
{
|
|
621
|
-
version: "2.0.0",
|
|
622
|
-
from: resolvedSmartAccountAddress,
|
|
623
|
-
chainId: hexChainId,
|
|
624
|
-
atomicRequired: false,
|
|
625
|
-
calls: [{ to: resolvedSmartAccountAddress, value: "0x0" }]
|
|
626
|
-
},
|
|
627
|
-
"EIP-7702 upgrade"
|
|
628
|
-
);
|
|
629
|
-
console.info("[swype-sdk][upgrade] Upgrade bundle submitted. Polling for confirmation\u2026", {
|
|
630
|
-
bundleId: upgradeBundleId
|
|
631
|
-
});
|
|
632
|
-
const upgradeTxHash = await pollCallsStatus(
|
|
633
|
-
walletClient,
|
|
634
|
-
upgradeBundleId,
|
|
635
|
-
"EIP-7702 upgrade"
|
|
636
|
-
);
|
|
637
|
-
console.info("[swype-sdk][upgrade] Step 1/2 complete: account upgraded.", {
|
|
638
|
-
txHash: upgradeTxHash
|
|
639
|
-
});
|
|
640
|
-
console.info(
|
|
641
|
-
"[swype-sdk][upgrade] Step 2/2: Registering passkey via wallet_sendCalls (addKey)."
|
|
642
|
-
);
|
|
643
|
-
const addKeyBundleId = await sendCalls(
|
|
644
|
-
walletClient,
|
|
645
|
-
{
|
|
646
|
-
version: "2.0.0",
|
|
647
|
-
from: resolvedSmartAccountAddress,
|
|
648
|
-
chainId: hexChainId,
|
|
649
|
-
atomicRequired: false,
|
|
650
|
-
calls: [
|
|
651
|
-
{
|
|
652
|
-
to: resolvedSmartAccountAddress,
|
|
653
|
-
value: "0x0",
|
|
654
|
-
data: addKeyCalldata
|
|
655
|
-
}
|
|
656
|
-
]
|
|
657
|
-
},
|
|
658
|
-
"Passkey registration (addKey)"
|
|
659
|
-
);
|
|
660
|
-
console.info("[swype-sdk][upgrade] addKey bundle submitted. Polling for confirmation\u2026", {
|
|
661
|
-
bundleId: addKeyBundleId
|
|
662
|
-
});
|
|
663
|
-
const addKeyTxHash = await pollCallsStatus(
|
|
664
|
-
walletClient,
|
|
665
|
-
addKeyBundleId,
|
|
666
|
-
"Passkey registration"
|
|
667
|
-
);
|
|
668
|
-
console.info("[swype-sdk][upgrade] Step 2/2 complete: passkey registered.", {
|
|
669
|
-
txHash: addKeyTxHash
|
|
670
|
-
});
|
|
671
|
-
return {
|
|
672
|
-
txHash: addKeyTxHash || upgradeTxHash,
|
|
673
|
-
smartAccountAddress: resolvedSmartAccountAddress
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// src/hooks.ts
|
|
678
|
-
async function waitForWalletClient(wagmiConfig2, maxAttempts = 15, intervalMs = 200) {
|
|
528
|
+
async function waitForWalletClient(wagmiConfig2, walletClientParams = {}, maxAttempts = 15, intervalMs = 200) {
|
|
679
529
|
for (let i = 0; i < maxAttempts; i++) {
|
|
680
530
|
try {
|
|
681
|
-
return await getWalletClient(wagmiConfig2);
|
|
531
|
+
return await getWalletClient(wagmiConfig2, walletClientParams);
|
|
682
532
|
} catch {
|
|
683
533
|
if (i === maxAttempts - 1) {
|
|
684
534
|
throw new Error("Wallet not ready. Please try again.");
|
|
@@ -688,19 +538,29 @@ async function waitForWalletClient(wagmiConfig2, maxAttempts = 15, intervalMs =
|
|
|
688
538
|
}
|
|
689
539
|
throw new Error("Wallet not ready. Please try again.");
|
|
690
540
|
}
|
|
691
|
-
function
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
541
|
+
function parseSignTypedDataPayload(typedData) {
|
|
542
|
+
const domain = typedData.domain;
|
|
543
|
+
const types = typedData.types;
|
|
544
|
+
const primaryType = typedData.primaryType;
|
|
545
|
+
const message = typedData.message;
|
|
546
|
+
if (!domain || typeof domain !== "object" || Array.isArray(domain)) {
|
|
547
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid domain object.");
|
|
697
548
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
549
|
+
if (!types || typeof types !== "object" || Array.isArray(types)) {
|
|
550
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid types object.");
|
|
551
|
+
}
|
|
552
|
+
if (typeof primaryType !== "string") {
|
|
553
|
+
throw new Error("SIGN_PERMIT2 typedData is missing primaryType.");
|
|
554
|
+
}
|
|
555
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
556
|
+
throw new Error("SIGN_PERMIT2 typedData is missing a valid message object.");
|
|
557
|
+
}
|
|
558
|
+
return {
|
|
559
|
+
domain,
|
|
560
|
+
types,
|
|
561
|
+
primaryType,
|
|
562
|
+
message
|
|
563
|
+
};
|
|
704
564
|
}
|
|
705
565
|
function useTransferPolling(intervalMs = 3e3) {
|
|
706
566
|
const { apiBaseUrl } = useSwypeConfig();
|
|
@@ -972,88 +832,180 @@ function useAuthorizationExecutor() {
|
|
|
972
832
|
},
|
|
973
833
|
[wagmiConfig2]
|
|
974
834
|
);
|
|
975
|
-
const
|
|
835
|
+
const executeCreateSmartAccount = useCallback(
|
|
836
|
+
async (action) => {
|
|
837
|
+
return {
|
|
838
|
+
actionId: action.id,
|
|
839
|
+
type: action.type,
|
|
840
|
+
status: "success",
|
|
841
|
+
message: "Smart account creation acknowledged. Server is deploying.",
|
|
842
|
+
data: {}
|
|
843
|
+
};
|
|
844
|
+
},
|
|
845
|
+
[]
|
|
846
|
+
);
|
|
847
|
+
const executeApprovePermit2 = useCallback(
|
|
976
848
|
async (action) => {
|
|
977
849
|
try {
|
|
978
|
-
const account = getAccount(wagmiConfig2);
|
|
979
850
|
const walletClient = await waitForWalletClient(wagmiConfig2);
|
|
851
|
+
const account = getAccount(wagmiConfig2);
|
|
980
852
|
const sender = account.address ?? walletClient.account?.address;
|
|
853
|
+
const expectedWalletAddress = action.metadata?.walletAddress;
|
|
981
854
|
if (!sender) {
|
|
982
855
|
throw new Error("Wallet account not available. Please connect your wallet.");
|
|
983
856
|
}
|
|
984
|
-
if (
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
857
|
+
if (expectedWalletAddress && sender.toLowerCase() !== expectedWalletAddress.toLowerCase()) {
|
|
858
|
+
return {
|
|
859
|
+
actionId: action.id,
|
|
860
|
+
type: action.type,
|
|
861
|
+
status: "error",
|
|
862
|
+
message: `Connected wallet ${sender} does not match the required source wallet ${expectedWalletAddress}. Please switch accounts in your wallet and retry.`
|
|
863
|
+
};
|
|
988
864
|
}
|
|
989
|
-
const
|
|
990
|
-
const
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
865
|
+
const to = action.metadata?.to;
|
|
866
|
+
const data = action.metadata?.data;
|
|
867
|
+
const tokenSymbol = action.metadata?.tokenSymbol;
|
|
868
|
+
if (!to || !data) {
|
|
869
|
+
return {
|
|
870
|
+
actionId: action.id,
|
|
871
|
+
type: action.type,
|
|
872
|
+
status: "error",
|
|
873
|
+
message: "APPROVE_PERMIT2 metadata is missing transaction parameters (to, data)."
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
const txHash = await walletClient.request({
|
|
877
|
+
method: "eth_sendTransaction",
|
|
878
|
+
params: [
|
|
879
|
+
{
|
|
880
|
+
from: sender,
|
|
881
|
+
to,
|
|
882
|
+
data,
|
|
883
|
+
value: "0x0"
|
|
884
|
+
}
|
|
885
|
+
]
|
|
994
886
|
});
|
|
887
|
+
console.info(
|
|
888
|
+
`[swype-sdk][approve-permit2] ERC-20 approve tx sent. token=${tokenSymbol ?? to}, txHash=${txHash}`
|
|
889
|
+
);
|
|
995
890
|
return {
|
|
996
891
|
actionId: action.id,
|
|
997
892
|
type: action.type,
|
|
998
893
|
status: "success",
|
|
999
|
-
message:
|
|
1000
|
-
data: {
|
|
1001
|
-
txHash,
|
|
1002
|
-
smartAccountAddress
|
|
1003
|
-
}
|
|
894
|
+
message: `Approved Permit2 to spend ${tokenSymbol ?? "tokens"}.`,
|
|
895
|
+
data: { txHash }
|
|
1004
896
|
};
|
|
1005
897
|
} catch (err) {
|
|
1006
|
-
|
|
1007
|
-
const message = extractErrorMessage(err);
|
|
898
|
+
const message = err instanceof Error ? err.message : "Failed to approve Permit2";
|
|
1008
899
|
const isRejected = message.includes("rejected") || message.includes("denied") || message.includes("user rejected");
|
|
1009
900
|
return {
|
|
1010
901
|
actionId: action.id,
|
|
1011
902
|
type: action.type,
|
|
1012
903
|
status: "error",
|
|
1013
|
-
message: isRejected ? "You rejected the
|
|
904
|
+
message: isRejected ? "You rejected the approval transaction. Please approve the Permit2 spending allowance in your wallet to continue." : message
|
|
1014
905
|
};
|
|
1015
906
|
}
|
|
1016
907
|
},
|
|
1017
908
|
[wagmiConfig2]
|
|
1018
909
|
);
|
|
1019
|
-
const
|
|
910
|
+
const executeSignPermit2 = useCallback(
|
|
1020
911
|
async (action) => {
|
|
1021
912
|
try {
|
|
1022
|
-
const
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
913
|
+
const expectedWalletAddress = action.metadata?.walletAddress;
|
|
914
|
+
const walletClient = await waitForWalletClient(
|
|
915
|
+
wagmiConfig2,
|
|
916
|
+
expectedWalletAddress ? { account: expectedWalletAddress } : {}
|
|
917
|
+
);
|
|
918
|
+
const account = getAccount(wagmiConfig2);
|
|
919
|
+
const connectedAddress = account.address ?? walletClient.account?.address;
|
|
920
|
+
const sender = expectedWalletAddress ?? connectedAddress;
|
|
921
|
+
if (!sender) {
|
|
922
|
+
throw new Error("Wallet account not available. Please connect your wallet.");
|
|
923
|
+
}
|
|
924
|
+
if (expectedWalletAddress && connectedAddress && connectedAddress.toLowerCase() !== expectedWalletAddress.toLowerCase()) {
|
|
925
|
+
return {
|
|
926
|
+
actionId: action.id,
|
|
927
|
+
type: action.type,
|
|
928
|
+
status: "error",
|
|
929
|
+
message: `Connected wallet ${sender} does not match the required source wallet ${expectedWalletAddress}. Please switch accounts in your wallet and retry.`
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
let typedData = action.metadata?.typedData;
|
|
933
|
+
const tokenSymbol = action.metadata?.tokenSymbol;
|
|
934
|
+
if (!typedData && sessionIdRef.current) {
|
|
935
|
+
const POLL_INTERVAL_MS = 1e3;
|
|
936
|
+
const MAX_POLLS = 15;
|
|
937
|
+
for (let i = 0; i < MAX_POLLS; i++) {
|
|
938
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
939
|
+
const session = await fetchAuthorizationSession(
|
|
940
|
+
apiBaseUrl,
|
|
941
|
+
sessionIdRef.current
|
|
942
|
+
);
|
|
943
|
+
const updatedAction = session.actions.find((a) => a.id === action.id);
|
|
944
|
+
typedData = updatedAction?.metadata?.typedData;
|
|
945
|
+
if (typedData) break;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (!typedData) {
|
|
1026
949
|
return {
|
|
1027
950
|
actionId: action.id,
|
|
1028
951
|
type: action.type,
|
|
1029
952
|
status: "error",
|
|
1030
|
-
message: "
|
|
953
|
+
message: "SIGN_PERMIT2 metadata is missing typedData. The server may still be preparing the signing payload."
|
|
1031
954
|
};
|
|
1032
955
|
}
|
|
1033
|
-
const
|
|
1034
|
-
|
|
956
|
+
const parsedTypedData = parseSignTypedDataPayload(typedData);
|
|
957
|
+
console.info(
|
|
958
|
+
`[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")}`
|
|
959
|
+
);
|
|
960
|
+
const signature = await walletClient.signTypedData({
|
|
961
|
+
account: sender,
|
|
962
|
+
domain: parsedTypedData.domain,
|
|
963
|
+
types: parsedTypedData.types,
|
|
964
|
+
primaryType: parsedTypedData.primaryType,
|
|
965
|
+
message: parsedTypedData.message
|
|
1035
966
|
});
|
|
1036
|
-
const
|
|
967
|
+
const recoverInput = {
|
|
968
|
+
domain: parsedTypedData.domain,
|
|
969
|
+
types: parsedTypedData.types,
|
|
970
|
+
primaryType: parsedTypedData.primaryType,
|
|
971
|
+
message: parsedTypedData.message,
|
|
972
|
+
signature
|
|
973
|
+
};
|
|
974
|
+
const recoveredSigner = await recoverTypedDataAddress(recoverInput);
|
|
975
|
+
const expectedSigner = (expectedWalletAddress ?? sender).toLowerCase();
|
|
976
|
+
console.info(
|
|
977
|
+
`[swype-sdk][sign-permit2] Signature recovered. recoveredSigner=${recoveredSigner}, expectedSigner=${expectedSigner}`
|
|
978
|
+
);
|
|
979
|
+
if (recoveredSigner.toLowerCase() !== expectedSigner) {
|
|
980
|
+
return {
|
|
981
|
+
actionId: action.id,
|
|
982
|
+
type: action.type,
|
|
983
|
+
status: "error",
|
|
984
|
+
message: `Wallet signed with ${recoveredSigner}, but source wallet is ${expectedWalletAddress ?? sender}. Please switch to the source wallet in MetaMask and retry.`
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
console.info(
|
|
988
|
+
`[swype-sdk][sign-permit2] Permit2 EIP-712 signature obtained. token=${tokenSymbol ?? "unknown"}`
|
|
989
|
+
);
|
|
1037
990
|
return {
|
|
1038
991
|
actionId: action.id,
|
|
1039
992
|
type: action.type,
|
|
1040
993
|
status: "success",
|
|
1041
|
-
message: `
|
|
1042
|
-
data: {
|
|
1043
|
-
signedDelegation: signature,
|
|
1044
|
-
tokens
|
|
1045
|
-
}
|
|
994
|
+
message: `Permit2 allowance signed for ${tokenSymbol ?? "tokens"}.`,
|
|
995
|
+
data: { signature }
|
|
1046
996
|
};
|
|
1047
997
|
} catch (err) {
|
|
998
|
+
const message = err instanceof Error ? err.message : "Failed to sign Permit2 allowance";
|
|
999
|
+
const isRejected = message.includes("rejected") || message.includes("denied") || message.includes("user rejected");
|
|
1048
1000
|
return {
|
|
1049
1001
|
actionId: action.id,
|
|
1050
1002
|
type: action.type,
|
|
1051
1003
|
status: "error",
|
|
1052
|
-
message:
|
|
1004
|
+
message: isRejected ? "You rejected the Permit2 signature request. Please approve the signature in your wallet to allow fund transfers." : message
|
|
1053
1005
|
};
|
|
1054
1006
|
}
|
|
1055
1007
|
},
|
|
1056
|
-
[wagmiConfig2]
|
|
1008
|
+
[wagmiConfig2, apiBaseUrl]
|
|
1057
1009
|
);
|
|
1058
1010
|
const executeAction = useCallback(
|
|
1059
1011
|
async (action) => {
|
|
@@ -1067,10 +1019,12 @@ function useAuthorizationExecutor() {
|
|
|
1067
1019
|
return executeSwitchChain(action);
|
|
1068
1020
|
case "REGISTER_PASSKEY":
|
|
1069
1021
|
return executeRegisterPasskey(action);
|
|
1070
|
-
case "
|
|
1071
|
-
return
|
|
1072
|
-
case "
|
|
1073
|
-
return
|
|
1022
|
+
case "CREATE_SMART_ACCOUNT":
|
|
1023
|
+
return executeCreateSmartAccount(action);
|
|
1024
|
+
case "APPROVE_PERMIT2":
|
|
1025
|
+
return executeApprovePermit2(action);
|
|
1026
|
+
case "SIGN_PERMIT2":
|
|
1027
|
+
return executeSignPermit2(action);
|
|
1074
1028
|
default:
|
|
1075
1029
|
return {
|
|
1076
1030
|
actionId: action.id,
|
|
@@ -1080,7 +1034,7 @@ function useAuthorizationExecutor() {
|
|
|
1080
1034
|
};
|
|
1081
1035
|
}
|
|
1082
1036
|
},
|
|
1083
|
-
[executeOpenProvider, executeSelectSource, executeSwitchChain, executeRegisterPasskey,
|
|
1037
|
+
[executeOpenProvider, executeSelectSource, executeSwitchChain, executeRegisterPasskey, executeCreateSmartAccount, executeApprovePermit2, executeSignPermit2]
|
|
1084
1038
|
);
|
|
1085
1039
|
const executeSession = useCallback(
|
|
1086
1040
|
async (transfer) => {
|
|
@@ -2781,15 +2735,20 @@ function SwypePayment({
|
|
|
2781
2735
|
label: "Creating your passkey...",
|
|
2782
2736
|
description: "Set up a passkey for secure, one-touch payments."
|
|
2783
2737
|
};
|
|
2784
|
-
case "
|
|
2738
|
+
case "CREATE_SMART_ACCOUNT":
|
|
2739
|
+
return {
|
|
2740
|
+
label: "Creating your smart account...",
|
|
2741
|
+
description: "Setting up your smart account for gasless payments."
|
|
2742
|
+
};
|
|
2743
|
+
case "APPROVE_PERMIT2":
|
|
2785
2744
|
return {
|
|
2786
|
-
label: "
|
|
2787
|
-
description: "Approve the
|
|
2745
|
+
label: "Approving token access...",
|
|
2746
|
+
description: "Approve the prompt in your wallet to allow secure token transfers."
|
|
2788
2747
|
};
|
|
2789
|
-
case "
|
|
2748
|
+
case "SIGN_PERMIT2":
|
|
2790
2749
|
return {
|
|
2791
|
-
label: "
|
|
2792
|
-
description: "
|
|
2750
|
+
label: "Signing transfer permission...",
|
|
2751
|
+
description: "Sign the permit to allow your smart account to transfer tokens on your behalf."
|
|
2793
2752
|
};
|
|
2794
2753
|
default:
|
|
2795
2754
|
return { label: "", description: "" };
|