@b3dotfun/sdk 0.1.69-alpha.4 → 0.1.69-alpha.6
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/cjs/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -4
- package/dist/cjs/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +4 -6
- package/dist/cjs/anyspend/react/hooks/useKycStatus.d.ts +3 -1
- package/dist/cjs/anyspend/react/hooks/useKycStatus.js +11 -7
- package/dist/cjs/global-account/react/components/B3Provider/B3Provider.native.js +2 -1
- package/dist/cjs/global-account/react/hooks/useAuth.js +2 -4
- package/dist/cjs/global-account/react/hooks/useAuthentication.js +2 -4
- package/dist/cjs/global-account/react/utils/createWagmiConfig.d.ts +0 -18
- package/dist/cjs/global-account/react/utils/createWagmiConfig.js +0 -17
- package/dist/esm/anyspend/react/components/checkout/CheckoutPaymentPanel.js +2 -4
- package/dist/esm/anyspend/react/hooks/useAnyspendCreateOnrampOrder.js +5 -7
- package/dist/esm/anyspend/react/hooks/useKycStatus.d.ts +3 -1
- package/dist/esm/anyspend/react/hooks/useKycStatus.js +9 -5
- package/dist/esm/global-account/react/components/B3Provider/B3Provider.native.js +2 -1
- package/dist/esm/global-account/react/hooks/useAuth.js +4 -6
- package/dist/esm/global-account/react/hooks/useAuthentication.js +3 -5
- package/dist/esm/global-account/react/utils/createWagmiConfig.d.ts +0 -18
- package/dist/esm/global-account/react/utils/createWagmiConfig.js +0 -16
- package/dist/types/anyspend/react/hooks/useKycStatus.d.ts +3 -1
- package/dist/types/global-account/react/utils/createWagmiConfig.d.ts +0 -18
- package/package.json +1 -1
- package/src/anyspend/react/components/checkout/CheckoutPaymentPanel.tsx +2 -4
- package/src/anyspend/react/hooks/useAnyspendCreateOnrampOrder.ts +5 -7
- package/src/anyspend/react/hooks/useKycStatus.ts +8 -5
- package/src/global-account/react/components/B3Provider/B3Provider.native.tsx +2 -1
- package/src/global-account/react/hooks/useAuth.ts +4 -6
- package/src/global-account/react/hooks/useAuthentication.ts +3 -5
- package/src/global-account/react/utils/createWagmiConfig.tsx +0 -18
|
@@ -56,9 +56,7 @@ function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress, desti
|
|
|
56
56
|
if (activeOrderId) {
|
|
57
57
|
return ((0, jsx_runtime_1.jsx)(CheckoutOrderStatus_1.CheckoutOrderStatus, { orderId: activeOrderId, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, onRetry: handleRetry, showPoints: showPoints, showOrderId: showOrderId, classes: classes }));
|
|
58
58
|
}
|
|
59
|
-
const accordionButtonClass = (active) => (0, cn_1.cn)("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", active
|
|
60
|
-
|
|
61
|
-
: "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
|
|
62
|
-
const expandedPanelClass = (0, cn_1.cn)("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
|
|
59
|
+
const accordionButtonClass = (active) => (0, cn_1.cn)("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", !active && "hover:bg-gray-50 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
|
|
60
|
+
const expandedPanelClass = (0, cn_1.cn)("anyspend-payment-method-panel border-t border-gray-100 px-4 py-4 dark:border-neutral-800");
|
|
63
61
|
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [(0, jsx_runtime_1.jsx)("h2", { className: (0, cn_1.cn)("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [(0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-crypto", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "crypto" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CryptoPayPanel_1.CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-card", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(lucide_react_1.CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), (0, jsx_runtime_1.jsxs)("div", { className: "ml-auto flex items-center gap-1", children: [(0, jsx_runtime_1.jsx)(VisaLogo, {}), (0, jsx_runtime_1.jsx)(MastercardLogo, {}), (0, jsx_runtime_1.jsx)(AmexLogo, {})] })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "card" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(FiatCheckoutPanel_1.FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop, kycEnabled: kycEnabled }) }) }, "card-panel")) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "anyspend-method-coinbase", children: [(0, jsx_runtime_1.jsxs)("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [(0, jsx_runtime_1.jsx)(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), (0, jsx_runtime_1.jsx)(CoinbaseLogo, {}), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), (0, jsx_runtime_1.jsx)(react_1.AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && ((0, jsx_runtime_1.jsx)(react_1.motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: (0, jsx_runtime_1.jsx)("div", { className: expandedPanelClass, children: (0, jsx_runtime_1.jsx)(CoinbaseCheckoutPanel_1.CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
|
|
64
62
|
}
|
|
@@ -10,7 +10,6 @@ const react_query_1 = require("@tanstack/react-query");
|
|
|
10
10
|
const react_2 = require("react");
|
|
11
11
|
const viem_1 = require("viem");
|
|
12
12
|
const chains_1 = require("viem/chains");
|
|
13
|
-
const wagmi_1 = require("wagmi");
|
|
14
13
|
const useKycStatus_1 = require("./useKycStatus");
|
|
15
14
|
const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId");
|
|
16
15
|
/**
|
|
@@ -20,7 +19,7 @@ const useValidatedClientReferenceId_1 = require("./useValidatedClientReferenceId
|
|
|
20
19
|
function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
21
20
|
// Get B3 context values
|
|
22
21
|
const { partnerId } = (0, react_1.useB3Config)();
|
|
23
|
-
const { address } = (0,
|
|
22
|
+
const { address, getHeaders: getWalletAuthHeaders } = (0, useKycStatus_1.useWalletAuthHeaders)();
|
|
24
23
|
// Get validated client reference ID from B3 context
|
|
25
24
|
const createValidatedClientReferenceId = (0, useValidatedClientReferenceId_1.useValidatedClientReferenceId)();
|
|
26
25
|
// Get fingerprint data
|
|
@@ -44,12 +43,11 @@ function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
|
44
43
|
const srcAmountOnRampInWei = (0, viem_1.parseUnits)(srcFiatAmount, constants_1.USDC_BASE.decimals);
|
|
45
44
|
// For card payments, include wallet auth headers so the backend can verify
|
|
46
45
|
// KYC by the signing wallet address (may differ from the B3 JWT address).
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
// KycGate pre-caches the headers in the "Continue to Verify" user-gesture.
|
|
46
|
+
// First try cached headers (from KycGate), then sign on-the-fly.
|
|
47
|
+
// The user is already clicking "Continue"/"Pay" so signing here is acceptable.
|
|
50
48
|
let kycWalletHeaders;
|
|
51
49
|
if (onramp.vendor === "stripe-web2" && address) {
|
|
52
|
-
kycWalletHeaders = (0, useKycStatus_1.getCachedWalletHeaders)(address);
|
|
50
|
+
kycWalletHeaders = (0, useKycStatus_1.getCachedWalletHeaders)(address) || (await getWalletAuthHeaders());
|
|
53
51
|
}
|
|
54
52
|
return await anyspend_1.anyspendService.createOrder({
|
|
55
53
|
recipientAddress: (0, utils_1.normalizeAddress)(recipientAddress),
|
|
@@ -24,10 +24,12 @@ interface KycVerifyResponse {
|
|
|
24
24
|
export declare function getCachedWalletHeaders(address: string): Record<string, string> | undefined;
|
|
25
25
|
/**
|
|
26
26
|
* Returns a function that builds the wallet-signature auth headers.
|
|
27
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
28
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
27
29
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
28
30
|
*/
|
|
29
31
|
export declare function useWalletAuthHeaders(): {
|
|
30
|
-
address:
|
|
32
|
+
address: string | undefined;
|
|
31
33
|
getHeaders: () => Promise<Record<string, string>>;
|
|
32
34
|
};
|
|
33
35
|
export declare function useKycStatus(enabled?: boolean): {
|
|
@@ -7,9 +7,9 @@ exports.useKycStatus = useKycStatus;
|
|
|
7
7
|
exports.useCreateKycInquiry = useCreateKycInquiry;
|
|
8
8
|
exports.useVerifyKyc = useVerifyKyc;
|
|
9
9
|
const constants_1 = require("../../../anyspend/constants");
|
|
10
|
+
const react_1 = require("../../../global-account/react");
|
|
10
11
|
const react_query_1 = require("@tanstack/react-query");
|
|
11
|
-
const
|
|
12
|
-
const wagmi_1 = require("wagmi");
|
|
12
|
+
const react_2 = require("react");
|
|
13
13
|
function buildWalletAuthMessage(walletAddress, timestamp) {
|
|
14
14
|
return `AnySpend wants to verify your identity for card payments.\n\nThis signature does not trigger a blockchain transaction or cost any gas.\n\nWallet: ${walletAddress.toLowerCase()}\nNonce: ${timestamp}`;
|
|
15
15
|
}
|
|
@@ -27,21 +27,25 @@ function getCachedWalletHeaders(address) {
|
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Returns a function that builds the wallet-signature auth headers.
|
|
30
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
31
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
30
32
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
31
33
|
*/
|
|
32
34
|
function useWalletAuthHeaders() {
|
|
33
|
-
const { address } = (0,
|
|
34
|
-
const
|
|
35
|
-
const getHeaders = (0,
|
|
35
|
+
const { address, wallet } = (0, react_1.useAccountWallet)();
|
|
36
|
+
const signMessage = wallet.signMessage;
|
|
37
|
+
const getHeaders = (0, react_2.useCallback)(async () => {
|
|
36
38
|
if (!address)
|
|
37
39
|
throw new Error("No wallet connected");
|
|
40
|
+
if (!signMessage)
|
|
41
|
+
throw new Error("Wallet does not support message signing");
|
|
38
42
|
const walletAddress = address.toLowerCase();
|
|
39
43
|
const cached = headerCache.get(walletAddress);
|
|
40
44
|
if (cached && Date.now() < cached.expiresAt)
|
|
41
45
|
return cached.headers;
|
|
42
46
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
43
47
|
const message = buildWalletAuthMessage(walletAddress, timestamp);
|
|
44
|
-
const signature = await
|
|
48
|
+
const signature = await signMessage({ message });
|
|
45
49
|
const headers = {
|
|
46
50
|
"X-Wallet-Address": walletAddress,
|
|
47
51
|
"X-Wallet-Signature": signature,
|
|
@@ -50,7 +54,7 @@ function useWalletAuthHeaders() {
|
|
|
50
54
|
// Cache for 4 minutes so repeated fetches don't re-prompt the user
|
|
51
55
|
headerCache.set(walletAddress, { headers, expiresAt: Date.now() + 4 * 60 * 1000 });
|
|
52
56
|
return headers;
|
|
53
|
-
}, [address,
|
|
57
|
+
}, [address, signMessage]);
|
|
54
58
|
return { address, getHeaders };
|
|
55
59
|
}
|
|
56
60
|
function useKycStatus(enabled = true) {
|
|
@@ -8,6 +8,7 @@ exports.InnerProvider = InnerProvider;
|
|
|
8
8
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
9
|
const react_query_1 = require("@tanstack/react-query");
|
|
10
10
|
const react_1 = require("thirdweb/react");
|
|
11
|
+
const react_2 = require("react");
|
|
11
12
|
const wagmi_1 = require("wagmi");
|
|
12
13
|
const createWagmiConfig_1 = require("../../utils/createWagmiConfig");
|
|
13
14
|
const AuthenticationProvider_1 = __importDefault(require("./AuthenticationProvider"));
|
|
@@ -25,6 +26,6 @@ function B3Provider({ theme = "light", children, accountOverride, environment, c
|
|
|
25
26
|
* Inner provider component for native
|
|
26
27
|
*/
|
|
27
28
|
function InnerProvider({ children, accountOverride, environment, defaultPermissions, theme = "light", clientType = "socket", partnerId, rpcUrls, }) {
|
|
28
|
-
const wagmiConfig = (0, createWagmiConfig_1.createWagmiConfig)({ partnerId, rpcUrls });
|
|
29
|
+
const wagmiConfig = (0, react_2.useMemo)(() => (0, createWagmiConfig_1.createWagmiConfig)({ partnerId, rpcUrls }), [partnerId, rpcUrls]);
|
|
29
30
|
return ((0, jsx_runtime_1.jsx)(wagmi_1.WagmiProvider, { config: wagmiConfig, children: (0, jsx_runtime_1.jsx)(react_query_1.QueryClientProvider, { client: queryClient, children: (0, jsx_runtime_1.jsx)(B3ConfigProvider_1.B3ConfigProvider, { accountOverride: accountOverride, environment: environment, automaticallySetFirstEoa: false, theme: theme, clientType: clientType, partnerId: partnerId, defaultPermissions: defaultPermissions, children: children }) }) }));
|
|
30
31
|
}
|
|
@@ -44,7 +44,7 @@ function useAuth() {
|
|
|
44
44
|
const useAutoConnectLoadingPrevious = (0, react_2.useRef)(false);
|
|
45
45
|
const referralCode = (0, useSearchParamsSSR_1.useSearchParam)("referralCode");
|
|
46
46
|
const { partnerId } = (0, react_1.useB3Config)();
|
|
47
|
-
const wagmiConfig = (0, createWagmiConfig_1.
|
|
47
|
+
const wagmiConfig = (0, react_2.useMemo)(() => (0, createWagmiConfig_1.createWagmiConfig)({ partnerId }), [partnerId]);
|
|
48
48
|
const { connect } = (0, wagmi_1.useConnect)();
|
|
49
49
|
const activeWagmiAccount = (0, wagmi_1.useAccount)();
|
|
50
50
|
const { switchAccount } = (0, wagmi_1.useSwitchAccount)();
|
|
@@ -126,9 +126,7 @@ function useAuth() {
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
syncWagmiFunc();
|
|
129
|
-
|
|
130
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
131
|
-
}, [partnerId, wallets]);
|
|
129
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
132
130
|
(0, react_2.useEffect)(() => {
|
|
133
131
|
syncWagmi();
|
|
134
132
|
}, [wallets, syncWagmi]);
|
|
@@ -54,7 +54,7 @@ function useAuthentication(partnerId, { skipAutoConnect = false } = {}) {
|
|
|
54
54
|
const { authenticate } = (0, useTWAuth_1.useTWAuth)();
|
|
55
55
|
const { user, setUser } = (0, useUserQuery_1.useUserQuery)();
|
|
56
56
|
const useAutoConnectLoadingPrevious = (0, react_2.useRef)(false);
|
|
57
|
-
const wagmiConfig = (0, createWagmiConfig_1.createWagmiConfig)({ partnerId });
|
|
57
|
+
const wagmiConfig = (0, react_2.useMemo)(() => (0, createWagmiConfig_1.createWagmiConfig)({ partnerId }), [partnerId]);
|
|
58
58
|
const { connect } = (0, wagmi_1.useConnect)();
|
|
59
59
|
const activeWagmiAccount = (0, wagmi_1.useAccount)();
|
|
60
60
|
const { switchAccount } = (0, wagmi_1.useSwitchAccount)();
|
|
@@ -105,9 +105,7 @@ function useAuthentication(partnerId, { skipAutoConnect = false } = {}) {
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
syncWagmiFunc();
|
|
108
|
-
|
|
109
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
110
|
-
}, [partnerId, wallets]);
|
|
108
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
111
109
|
(0, react_2.useEffect)(() => {
|
|
112
110
|
syncWagmi();
|
|
113
111
|
}, [wallets, syncWagmi]);
|
|
@@ -25,21 +25,3 @@ export declare function createWagmiConfig(options: CreateWagmiConfigOptions): im
|
|
|
25
25
|
}, {
|
|
26
26
|
"thirdweb:lastChainId": number;
|
|
27
27
|
}>)[]>;
|
|
28
|
-
/**
|
|
29
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
30
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
31
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
32
|
-
*/
|
|
33
|
-
export declare function getCachedWagmiConfig(options: CreateWagmiConfigOptions): import("wagmi").Config<readonly [import("viem").Chain, ...import("viem").Chain[]], {
|
|
34
|
-
[k: string]: import("viem").HttpTransport<undefined, false>;
|
|
35
|
-
}, (CreateConnectorFn | CreateConnectorFn<import("thirdweb/dist/types/adapters/eip1193").EIP1193Provider | undefined, {
|
|
36
|
-
connect<withCapabilities extends boolean = false>(parameters?: import("@thirdweb-dev/wagmi-adapter").ConnectionOptions<withCapabilities> | undefined): Promise<{
|
|
37
|
-
accounts: withCapabilities extends true ? readonly {
|
|
38
|
-
address: `0x${string}`;
|
|
39
|
-
capabilities: Record<string, unknown>;
|
|
40
|
-
}[] : readonly `0x${string}`[];
|
|
41
|
-
chainId: number;
|
|
42
|
-
}>;
|
|
43
|
-
}, {
|
|
44
|
-
"thirdweb:lastChainId": number;
|
|
45
|
-
}>)[]>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createWagmiConfig = createWagmiConfig;
|
|
4
|
-
exports.getCachedWagmiConfig = getCachedWagmiConfig;
|
|
5
4
|
const constants_1 = require("../../../shared/constants");
|
|
6
5
|
const supported_1 = require("../../../shared/constants/chains/supported");
|
|
7
6
|
const thirdweb_1 = require("../../../shared/utils/thirdweb");
|
|
@@ -31,19 +30,3 @@ function createWagmiConfig(options) {
|
|
|
31
30
|
connectors: finalConnectors,
|
|
32
31
|
});
|
|
33
32
|
}
|
|
34
|
-
/** Module-level cache — wagmi configs must not be recreated on every render. */
|
|
35
|
-
const wagmiConfigCache = new Map();
|
|
36
|
-
/**
|
|
37
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
38
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
39
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
40
|
-
*/
|
|
41
|
-
function getCachedWagmiConfig(options) {
|
|
42
|
-
const key = options.partnerId;
|
|
43
|
-
let config = wagmiConfigCache.get(key);
|
|
44
|
-
if (!config) {
|
|
45
|
-
config = createWagmiConfig(options);
|
|
46
|
-
wagmiConfigCache.set(key, config);
|
|
47
|
-
}
|
|
48
|
-
return config;
|
|
49
|
-
}
|
|
@@ -53,9 +53,7 @@ export function CheckoutPaymentPanel({ recipientAddress, destinationTokenAddress
|
|
|
53
53
|
if (activeOrderId) {
|
|
54
54
|
return (_jsx(CheckoutOrderStatus, { orderId: activeOrderId, themeColor: themeColor, returnUrl: returnUrl, returnLabel: returnLabel, onSuccess: onSuccess, onError: onError, onRetry: handleRetry, showPoints: showPoints, showOrderId: showOrderId, classes: classes }));
|
|
55
55
|
}
|
|
56
|
-
const accordionButtonClass = (active) => cn("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", active
|
|
57
|
-
|
|
58
|
-
: "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
|
|
59
|
-
const expandedPanelClass = cn("anyspend-payment-method-panel border-t border-gray-100 bg-white px-4 py-4 dark:border-neutral-800 dark:bg-neutral-900");
|
|
56
|
+
const accordionButtonClass = (active) => cn("anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors", !active && "hover:bg-gray-50 dark:hover:bg-neutral-800", classes?.paymentMethodButton);
|
|
57
|
+
const expandedPanelClass = cn("anyspend-payment-method-panel border-t border-gray-100 px-4 py-4 dark:border-neutral-800");
|
|
60
58
|
return (_jsxs("div", { className: cn("anyspend-payment-panel flex flex-col gap-5", classes?.paymentPanel), children: [_jsx("h2", { className: cn("anyspend-payment-title text-lg font-semibold text-gray-900 dark:text-gray-100", classes?.paymentTitle), children: "Payment" }), !isFormValid && (_jsx("p", { className: "text-sm text-amber-600 dark:text-amber-400", children: "Please complete the required fields above before proceeding to payment." })), _jsxs("div", { className: cn("anyspend-payment-methods divide-y divide-gray-200 overflow-hidden rounded-xl border border-gray-200 dark:divide-neutral-700 dark:border-neutral-700", !isFormValid && "pointer-events-none opacity-50", classes?.paymentMethodSelector), children: [_jsxs("div", { className: "anyspend-method-crypto", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "crypto" ? null : "crypto"), className: accordionButtonClass(paymentMethod === "crypto"), children: [_jsx(RadioCircle, { selected: paymentMethod === "crypto", themeColor: themeColor }), _jsx(Wallet, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Pay with crypto" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "crypto" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CryptoPayPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, buttonText: buttonText, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, senderAddress: senderAddress }) }) }, "crypto-panel")) })] }), _jsxs("div", { className: "anyspend-method-card", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "card" ? null : "card"), className: accordionButtonClass(paymentMethod === "card"), children: [_jsx(RadioCircle, { selected: paymentMethod === "card", themeColor: themeColor }), _jsx(CreditCard, { className: "h-5 w-5 text-gray-700 dark:text-gray-300" }), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Credit or debit card" }), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [_jsx(VisaLogo, {}), _jsx(MastercardLogo, {}), _jsx(AmexLogo, {})] })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "card" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(FiatCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes, feeOnTop: feeOnTop, kycEnabled: kycEnabled }) }) }, "card-panel")) })] }), _jsxs("div", { className: "anyspend-method-coinbase", children: [_jsxs("button", { onClick: () => setPaymentMethod(paymentMethod === "coinbase" ? null : "coinbase"), className: accordionButtonClass(paymentMethod === "coinbase"), children: [_jsx(RadioCircle, { selected: paymentMethod === "coinbase", themeColor: themeColor }), _jsx(CoinbaseLogo, {}), _jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: "Coinbase Pay" })] }), _jsx(AnimatePresence, { initial: false, children: paymentMethod === "coinbase" && (_jsx(motion.div, { initial: { height: 0, opacity: 0 }, animate: { height: "auto", opacity: 1 }, exit: { height: 0, opacity: 0 }, transition: { duration: 0.2, ease: "easeOut" }, style: { overflow: "hidden" }, children: _jsx("div", { className: expandedPanelClass, children: _jsx(CoinbaseCheckoutPanel, { recipientAddress: recipientAddress, destinationTokenAddress: destinationTokenAddress, destinationTokenChainId: destinationTokenChainId, totalAmount: totalAmount, themeColor: themeColor, onOrderCreated: handleOrderCreated, onError: onError, callbackMetadata: callbackMetadata, classes: classes }) }) }, "coinbase-panel")) })] })] })] }));
|
|
61
59
|
}
|
|
@@ -7,8 +7,7 @@ import { useMutation } from "@tanstack/react-query";
|
|
|
7
7
|
import { useMemo } from "react";
|
|
8
8
|
import { parseUnits } from "viem";
|
|
9
9
|
import { base } from "viem/chains";
|
|
10
|
-
import {
|
|
11
|
-
import { getCachedWalletHeaders } from "./useKycStatus.js";
|
|
10
|
+
import { getCachedWalletHeaders, useWalletAuthHeaders } from "./useKycStatus.js";
|
|
12
11
|
import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.js";
|
|
13
12
|
/**
|
|
14
13
|
* Hook for creating onramp orders in the Anyspend protocol
|
|
@@ -17,7 +16,7 @@ import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId.j
|
|
|
17
16
|
export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
18
17
|
// Get B3 context values
|
|
19
18
|
const { partnerId } = useB3Config();
|
|
20
|
-
const { address } =
|
|
19
|
+
const { address, getHeaders: getWalletAuthHeaders } = useWalletAuthHeaders();
|
|
21
20
|
// Get validated client reference ID from B3 context
|
|
22
21
|
const createValidatedClientReferenceId = useValidatedClientReferenceId();
|
|
23
22
|
// Get fingerprint data
|
|
@@ -41,12 +40,11 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError } = {}) {
|
|
|
41
40
|
const srcAmountOnRampInWei = parseUnits(srcFiatAmount, USDC_BASE.decimals);
|
|
42
41
|
// For card payments, include wallet auth headers so the backend can verify
|
|
43
42
|
// KYC by the signing wallet address (may differ from the B3 JWT address).
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
// KycGate pre-caches the headers in the "Continue to Verify" user-gesture.
|
|
43
|
+
// First try cached headers (from KycGate), then sign on-the-fly.
|
|
44
|
+
// The user is already clicking "Continue"/"Pay" so signing here is acceptable.
|
|
47
45
|
let kycWalletHeaders;
|
|
48
46
|
if (onramp.vendor === "stripe-web2" && address) {
|
|
49
|
-
kycWalletHeaders = getCachedWalletHeaders(address);
|
|
47
|
+
kycWalletHeaders = getCachedWalletHeaders(address) || (await getWalletAuthHeaders());
|
|
50
48
|
}
|
|
51
49
|
return await anyspendService.createOrder({
|
|
52
50
|
recipientAddress: normalizeAddress(recipientAddress),
|
|
@@ -24,10 +24,12 @@ interface KycVerifyResponse {
|
|
|
24
24
|
export declare function getCachedWalletHeaders(address: string): Record<string, string> | undefined;
|
|
25
25
|
/**
|
|
26
26
|
* Returns a function that builds the wallet-signature auth headers.
|
|
27
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
28
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
27
29
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
28
30
|
*/
|
|
29
31
|
export declare function useWalletAuthHeaders(): {
|
|
30
|
-
address:
|
|
32
|
+
address: string | undefined;
|
|
31
33
|
getHeaders: () => Promise<Record<string, string>>;
|
|
32
34
|
};
|
|
33
35
|
export declare function useKycStatus(enabled?: boolean): {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { ANYSPEND_MAINNET_BASE_URL } from "../../../anyspend/constants/index.js";
|
|
3
|
+
import { useAccountWallet } from "../../../global-account/react/index.js";
|
|
3
4
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
4
5
|
import { useCallback } from "react";
|
|
5
|
-
import { useAccount, useSignMessage } from "wagmi";
|
|
6
6
|
function buildWalletAuthMessage(walletAddress, timestamp) {
|
|
7
7
|
return `AnySpend wants to verify your identity for card payments.\n\nThis signature does not trigger a blockchain transaction or cost any gas.\n\nWallet: ${walletAddress.toLowerCase()}\nNonce: ${timestamp}`;
|
|
8
8
|
}
|
|
@@ -20,21 +20,25 @@ export function getCachedWalletHeaders(address) {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Returns a function that builds the wallet-signature auth headers.
|
|
23
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
24
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
23
25
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
24
26
|
*/
|
|
25
27
|
export function useWalletAuthHeaders() {
|
|
26
|
-
const { address } =
|
|
27
|
-
const
|
|
28
|
+
const { address, wallet } = useAccountWallet();
|
|
29
|
+
const signMessage = wallet.signMessage;
|
|
28
30
|
const getHeaders = useCallback(async () => {
|
|
29
31
|
if (!address)
|
|
30
32
|
throw new Error("No wallet connected");
|
|
33
|
+
if (!signMessage)
|
|
34
|
+
throw new Error("Wallet does not support message signing");
|
|
31
35
|
const walletAddress = address.toLowerCase();
|
|
32
36
|
const cached = headerCache.get(walletAddress);
|
|
33
37
|
if (cached && Date.now() < cached.expiresAt)
|
|
34
38
|
return cached.headers;
|
|
35
39
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
36
40
|
const message = buildWalletAuthMessage(walletAddress, timestamp);
|
|
37
|
-
const signature = await
|
|
41
|
+
const signature = await signMessage({ message });
|
|
38
42
|
const headers = {
|
|
39
43
|
"X-Wallet-Address": walletAddress,
|
|
40
44
|
"X-Wallet-Signature": signature,
|
|
@@ -43,7 +47,7 @@ export function useWalletAuthHeaders() {
|
|
|
43
47
|
// Cache for 4 minutes so repeated fetches don't re-prompt the user
|
|
44
48
|
headerCache.set(walletAddress, { headers, expiresAt: Date.now() + 4 * 60 * 1000 });
|
|
45
49
|
return headers;
|
|
46
|
-
}, [address,
|
|
50
|
+
}, [address, signMessage]);
|
|
47
51
|
return { address, getHeaders };
|
|
48
52
|
}
|
|
49
53
|
export function useKycStatus(enabled = true) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
3
3
|
import { ThirdwebProvider } from "thirdweb/react";
|
|
4
|
+
import { useMemo } from "react";
|
|
4
5
|
import { WagmiProvider } from "wagmi";
|
|
5
6
|
import { createWagmiConfig } from "../../utils/createWagmiConfig.js";
|
|
6
7
|
import AuthenticationProvider from "./AuthenticationProvider.js";
|
|
@@ -18,6 +19,6 @@ export function B3Provider({ theme = "light", children, accountOverride, environ
|
|
|
18
19
|
* Inner provider component for native
|
|
19
20
|
*/
|
|
20
21
|
export function InnerProvider({ children, accountOverride, environment, defaultPermissions, theme = "light", clientType = "socket", partnerId, rpcUrls, }) {
|
|
21
|
-
const wagmiConfig = createWagmiConfig({ partnerId, rpcUrls });
|
|
22
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId, rpcUrls }), [partnerId, rpcUrls]);
|
|
22
23
|
return (_jsx(WagmiProvider, { config: wagmiConfig, children: _jsx(QueryClientProvider, { client: queryClient, children: _jsx(B3ConfigProvider, { accountOverride: accountOverride, environment: environment, automaticallySetFirstEoa: false, theme: theme, clientType: clientType, partnerId: partnerId, defaultPermissions: defaultPermissions, children: children }) }) }));
|
|
23
24
|
}
|
|
@@ -5,13 +5,13 @@ import { ecosystemWalletId } from "../../../shared/constants/index.js";
|
|
|
5
5
|
import { debugB3React } from "../../../shared/utils/debug.js";
|
|
6
6
|
import { client } from "../../../shared/utils/thirdweb.js";
|
|
7
7
|
import { getConnectors } from "@wagmi/core";
|
|
8
|
-
import { useCallback, useContext, useEffect, useRef } from "react";
|
|
8
|
+
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
9
9
|
import { useActiveWallet, useAutoConnect, useConnectedWallets, useDisconnect, useSetActiveWallet, } from "thirdweb/react";
|
|
10
10
|
import { ecosystemWallet } from "thirdweb/wallets";
|
|
11
11
|
import { preAuthenticate } from "thirdweb/wallets/in-app";
|
|
12
12
|
import { useAccount, useConnect, useSwitchAccount } from "wagmi";
|
|
13
13
|
import { LocalSDKContext } from "../components/B3Provider/LocalSDKProvider.js";
|
|
14
|
-
import {
|
|
14
|
+
import { createWagmiConfig } from "../utils/createWagmiConfig.js";
|
|
15
15
|
import { useSearchParam } from "./useSearchParamsSSR.js";
|
|
16
16
|
import { useUserQuery } from "./useUserQuery.js";
|
|
17
17
|
const debug = debugB3React("useAuth");
|
|
@@ -38,7 +38,7 @@ export function useAuth() {
|
|
|
38
38
|
const useAutoConnectLoadingPrevious = useRef(false);
|
|
39
39
|
const referralCode = useSearchParam("referralCode");
|
|
40
40
|
const { partnerId } = useB3Config();
|
|
41
|
-
const wagmiConfig =
|
|
41
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId }), [partnerId]);
|
|
42
42
|
const { connect } = useConnect();
|
|
43
43
|
const activeWagmiAccount = useAccount();
|
|
44
44
|
const { switchAccount } = useSwitchAccount();
|
|
@@ -120,9 +120,7 @@ export function useAuth() {
|
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
syncWagmiFunc();
|
|
123
|
-
|
|
124
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
125
|
-
}, [partnerId, wallets]);
|
|
123
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
126
124
|
useEffect(() => {
|
|
127
125
|
syncWagmi();
|
|
128
126
|
}, [wallets, syncWagmi]);
|
|
@@ -5,7 +5,7 @@ import { ecosystemWalletId } from "../../../shared/constants/index.js";
|
|
|
5
5
|
import { debugB3React } from "../../../shared/utils/debug.js";
|
|
6
6
|
import { client } from "../../../shared/utils/thirdweb.js";
|
|
7
7
|
import { getConnectors } from "@wagmi/core";
|
|
8
|
-
import { useCallback, useContext, useEffect, useRef } from "react";
|
|
8
|
+
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
9
9
|
import { useActiveWallet, useAutoConnect, useConnectedWallets, useDisconnect, useSetActiveWallet, } from "thirdweb/react";
|
|
10
10
|
import { ecosystemWallet } from "thirdweb/wallets";
|
|
11
11
|
import { preAuthenticate } from "thirdweb/wallets/in-app";
|
|
@@ -48,7 +48,7 @@ export function useAuthentication(partnerId, { skipAutoConnect = false } = {}) {
|
|
|
48
48
|
const { authenticate } = useTWAuth();
|
|
49
49
|
const { user, setUser } = useUserQuery();
|
|
50
50
|
const useAutoConnectLoadingPrevious = useRef(false);
|
|
51
|
-
const wagmiConfig = createWagmiConfig({ partnerId });
|
|
51
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId }), [partnerId]);
|
|
52
52
|
const { connect } = useConnect();
|
|
53
53
|
const activeWagmiAccount = useAccount();
|
|
54
54
|
const { switchAccount } = useSwitchAccount();
|
|
@@ -99,9 +99,7 @@ export function useAuthentication(partnerId, { skipAutoConnect = false } = {}) {
|
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
101
|
syncWagmiFunc();
|
|
102
|
-
|
|
103
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
104
|
-
}, [partnerId, wallets]);
|
|
102
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
105
103
|
useEffect(() => {
|
|
106
104
|
syncWagmi();
|
|
107
105
|
}, [wallets, syncWagmi]);
|
|
@@ -25,21 +25,3 @@ export declare function createWagmiConfig(options: CreateWagmiConfigOptions): im
|
|
|
25
25
|
}, {
|
|
26
26
|
"thirdweb:lastChainId": number;
|
|
27
27
|
}>)[]>;
|
|
28
|
-
/**
|
|
29
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
30
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
31
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
32
|
-
*/
|
|
33
|
-
export declare function getCachedWagmiConfig(options: CreateWagmiConfigOptions): import("wagmi").Config<readonly [import("viem").Chain, ...import("viem").Chain[]], {
|
|
34
|
-
[k: string]: import("viem").HttpTransport<undefined, false>;
|
|
35
|
-
}, (CreateConnectorFn | CreateConnectorFn<import("thirdweb/dist/types/adapters/eip1193").EIP1193Provider | undefined, {
|
|
36
|
-
connect<withCapabilities extends boolean = false>(parameters?: import("@thirdweb-dev/wagmi-adapter").ConnectionOptions<withCapabilities> | undefined): Promise<{
|
|
37
|
-
accounts: withCapabilities extends true ? readonly {
|
|
38
|
-
address: `0x${string}`;
|
|
39
|
-
capabilities: Record<string, unknown>;
|
|
40
|
-
}[] : readonly `0x${string}`[];
|
|
41
|
-
chainId: number;
|
|
42
|
-
}>;
|
|
43
|
-
}, {
|
|
44
|
-
"thirdweb:lastChainId": number;
|
|
45
|
-
}>)[]>;
|
|
@@ -27,19 +27,3 @@ export function createWagmiConfig(options) {
|
|
|
27
27
|
connectors: finalConnectors,
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
/** Module-level cache — wagmi configs must not be recreated on every render. */
|
|
31
|
-
const wagmiConfigCache = new Map();
|
|
32
|
-
/**
|
|
33
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
34
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
35
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
36
|
-
*/
|
|
37
|
-
export function getCachedWagmiConfig(options) {
|
|
38
|
-
const key = options.partnerId;
|
|
39
|
-
let config = wagmiConfigCache.get(key);
|
|
40
|
-
if (!config) {
|
|
41
|
-
config = createWagmiConfig(options);
|
|
42
|
-
wagmiConfigCache.set(key, config);
|
|
43
|
-
}
|
|
44
|
-
return config;
|
|
45
|
-
}
|
|
@@ -24,10 +24,12 @@ interface KycVerifyResponse {
|
|
|
24
24
|
export declare function getCachedWalletHeaders(address: string): Record<string, string> | undefined;
|
|
25
25
|
/**
|
|
26
26
|
* Returns a function that builds the wallet-signature auth headers.
|
|
27
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
28
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
27
29
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
28
30
|
*/
|
|
29
31
|
export declare function useWalletAuthHeaders(): {
|
|
30
|
-
address:
|
|
32
|
+
address: string | undefined;
|
|
31
33
|
getHeaders: () => Promise<Record<string, string>>;
|
|
32
34
|
};
|
|
33
35
|
export declare function useKycStatus(enabled?: boolean): {
|
|
@@ -25,21 +25,3 @@ export declare function createWagmiConfig(options: CreateWagmiConfigOptions): im
|
|
|
25
25
|
}, {
|
|
26
26
|
"thirdweb:lastChainId": number;
|
|
27
27
|
}>)[]>;
|
|
28
|
-
/**
|
|
29
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
30
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
31
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
32
|
-
*/
|
|
33
|
-
export declare function getCachedWagmiConfig(options: CreateWagmiConfigOptions): import("wagmi").Config<readonly [import("viem").Chain, ...import("viem").Chain[]], {
|
|
34
|
-
[k: string]: import("viem").HttpTransport<undefined, false>;
|
|
35
|
-
}, (CreateConnectorFn | CreateConnectorFn<import("thirdweb/dist/types/adapters/eip1193").EIP1193Provider | undefined, {
|
|
36
|
-
connect<withCapabilities extends boolean = false>(parameters?: import("@thirdweb-dev/wagmi-adapter").ConnectionOptions<withCapabilities> | undefined): Promise<{
|
|
37
|
-
accounts: withCapabilities extends true ? readonly {
|
|
38
|
-
address: `0x${string}`;
|
|
39
|
-
capabilities: Record<string, unknown>;
|
|
40
|
-
}[] : readonly `0x${string}`[];
|
|
41
|
-
chainId: number;
|
|
42
|
-
}>;
|
|
43
|
-
}, {
|
|
44
|
-
"thirdweb:lastChainId": number;
|
|
45
|
-
}>)[]>;
|
package/package.json
CHANGED
|
@@ -186,14 +186,12 @@ export function CheckoutPaymentPanel({
|
|
|
186
186
|
const accordionButtonClass = (active: boolean) =>
|
|
187
187
|
cn(
|
|
188
188
|
"anyspend-payment-method-btn flex w-full items-center gap-3 px-4 py-4 text-left transition-colors",
|
|
189
|
-
active
|
|
190
|
-
? "bg-white dark:bg-neutral-900"
|
|
191
|
-
: "bg-white hover:bg-gray-50 dark:bg-neutral-900 dark:hover:bg-neutral-800",
|
|
189
|
+
!active && "hover:bg-gray-50 dark:hover:bg-neutral-800",
|
|
192
190
|
classes?.paymentMethodButton,
|
|
193
191
|
);
|
|
194
192
|
|
|
195
193
|
const expandedPanelClass = cn(
|
|
196
|
-
"anyspend-payment-method-panel border-t border-gray-100
|
|
194
|
+
"anyspend-payment-method-panel border-t border-gray-100 px-4 py-4 dark:border-neutral-800",
|
|
197
195
|
);
|
|
198
196
|
|
|
199
197
|
return (
|
|
@@ -11,9 +11,8 @@ import { useMemo } from "react";
|
|
|
11
11
|
|
|
12
12
|
import { parseUnits } from "viem";
|
|
13
13
|
import { base } from "viem/chains";
|
|
14
|
-
import { useAccount } from "wagmi";
|
|
15
14
|
import { CreateOrderParams } from "./useAnyspendCreateOrder";
|
|
16
|
-
import { getCachedWalletHeaders } from "./useKycStatus";
|
|
15
|
+
import { getCachedWalletHeaders, useWalletAuthHeaders } from "./useKycStatus";
|
|
17
16
|
import { useValidatedClientReferenceId } from "./useValidatedClientReferenceId";
|
|
18
17
|
|
|
19
18
|
export type OnrampOptions = {
|
|
@@ -42,7 +41,7 @@ export type UseAnyspendCreateOnrampOrderProps = {
|
|
|
42
41
|
export function useAnyspendCreateOnrampOrder({ onSuccess, onError }: UseAnyspendCreateOnrampOrderProps = {}) {
|
|
43
42
|
// Get B3 context values
|
|
44
43
|
const { partnerId } = useB3Config();
|
|
45
|
-
const { address } =
|
|
44
|
+
const { address, getHeaders: getWalletAuthHeaders } = useWalletAuthHeaders();
|
|
46
45
|
|
|
47
46
|
// Get validated client reference ID from B3 context
|
|
48
47
|
const createValidatedClientReferenceId = useValidatedClientReferenceId();
|
|
@@ -86,12 +85,11 @@ export function useAnyspendCreateOnrampOrder({ onSuccess, onError }: UseAnyspend
|
|
|
86
85
|
|
|
87
86
|
// For card payments, include wallet auth headers so the backend can verify
|
|
88
87
|
// KYC by the signing wallet address (may differ from the B3 JWT address).
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// KycGate pre-caches the headers in the "Continue to Verify" user-gesture.
|
|
88
|
+
// First try cached headers (from KycGate), then sign on-the-fly.
|
|
89
|
+
// The user is already clicking "Continue"/"Pay" so signing here is acceptable.
|
|
92
90
|
let kycWalletHeaders: Record<string, string> | undefined;
|
|
93
91
|
if (onramp.vendor === "stripe-web2" && address) {
|
|
94
|
-
kycWalletHeaders = getCachedWalletHeaders(address);
|
|
92
|
+
kycWalletHeaders = getCachedWalletHeaders(address) || (await getWalletAuthHeaders());
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
return await anyspendService.createOrder({
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { ANYSPEND_MAINNET_BASE_URL } from "@b3dotfun/sdk/anyspend/constants";
|
|
4
|
+
import { useAccountWallet } from "@b3dotfun/sdk/global-account/react";
|
|
4
5
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
5
6
|
import { useCallback } from "react";
|
|
6
|
-
import { useAccount, useSignMessage } from "wagmi";
|
|
7
7
|
|
|
8
8
|
export interface KycStatusResponse {
|
|
9
9
|
kycRequired: boolean;
|
|
@@ -46,14 +46,17 @@ export function getCachedWalletHeaders(address: string): Record<string, string>
|
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Returns a function that builds the wallet-signature auth headers.
|
|
49
|
+
* Uses useAccountWallet (thirdweb) instead of wagmi so signing works
|
|
50
|
+
* for all wallet types (social login, EOA, smart wallet, etc.).
|
|
49
51
|
* Caches signatures for 4 minutes (server allows 5-minute window).
|
|
50
52
|
*/
|
|
51
53
|
export function useWalletAuthHeaders() {
|
|
52
|
-
const { address } =
|
|
53
|
-
const
|
|
54
|
+
const { address, wallet } = useAccountWallet();
|
|
55
|
+
const signMessage = wallet.signMessage;
|
|
54
56
|
|
|
55
57
|
const getHeaders = useCallback(async (): Promise<Record<string, string>> => {
|
|
56
58
|
if (!address) throw new Error("No wallet connected");
|
|
59
|
+
if (!signMessage) throw new Error("Wallet does not support message signing");
|
|
57
60
|
const walletAddress = address.toLowerCase();
|
|
58
61
|
|
|
59
62
|
const cached = headerCache.get(walletAddress);
|
|
@@ -61,7 +64,7 @@ export function useWalletAuthHeaders() {
|
|
|
61
64
|
|
|
62
65
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
63
66
|
const message = buildWalletAuthMessage(walletAddress, timestamp);
|
|
64
|
-
const signature = await
|
|
67
|
+
const signature = await signMessage({ message });
|
|
65
68
|
|
|
66
69
|
const headers = {
|
|
67
70
|
"X-Wallet-Address": walletAddress,
|
|
@@ -71,7 +74,7 @@ export function useWalletAuthHeaders() {
|
|
|
71
74
|
// Cache for 4 minutes so repeated fetches don't re-prompt the user
|
|
72
75
|
headerCache.set(walletAddress, { headers, expiresAt: Date.now() + 4 * 60 * 1000 });
|
|
73
76
|
return headers;
|
|
74
|
-
}, [address,
|
|
77
|
+
}, [address, signMessage]);
|
|
75
78
|
|
|
76
79
|
return { address, getHeaders };
|
|
77
80
|
}
|
|
@@ -5,6 +5,7 @@ import { Account, Wallet } from "thirdweb/wallets";
|
|
|
5
5
|
|
|
6
6
|
import { ClientType } from "../../../client-manager";
|
|
7
7
|
|
|
8
|
+
import { useMemo } from "react";
|
|
8
9
|
import { WagmiProvider } from "wagmi";
|
|
9
10
|
import { createWagmiConfig } from "../../utils/createWagmiConfig";
|
|
10
11
|
import AuthenticationProvider from "./AuthenticationProvider";
|
|
@@ -82,7 +83,7 @@ export function InnerProvider({
|
|
|
82
83
|
partnerId: string;
|
|
83
84
|
rpcUrls?: Record<number, string>;
|
|
84
85
|
}) {
|
|
85
|
-
const wagmiConfig = createWagmiConfig({ partnerId, rpcUrls });
|
|
86
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId, rpcUrls }), [partnerId, rpcUrls]);
|
|
86
87
|
|
|
87
88
|
return (
|
|
88
89
|
<WagmiProvider config={wagmiConfig}>
|
|
@@ -6,7 +6,7 @@ import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
|
6
6
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
7
7
|
import { ConnectionOptions } from "@thirdweb-dev/wagmi-adapter";
|
|
8
8
|
import { getConnectors } from "@wagmi/core";
|
|
9
|
-
import { useCallback, useContext, useEffect, useRef } from "react";
|
|
9
|
+
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
10
10
|
import {
|
|
11
11
|
useActiveWallet,
|
|
12
12
|
useAutoConnect,
|
|
@@ -18,7 +18,7 @@ import { Wallet, ecosystemWallet } from "thirdweb/wallets";
|
|
|
18
18
|
import { preAuthenticate } from "thirdweb/wallets/in-app";
|
|
19
19
|
import { useAccount, useConnect, useSwitchAccount } from "wagmi";
|
|
20
20
|
import { LocalSDKContext } from "../components/B3Provider/LocalSDKProvider";
|
|
21
|
-
import {
|
|
21
|
+
import { createWagmiConfig } from "../utils/createWagmiConfig";
|
|
22
22
|
import { useSearchParam } from "./useSearchParamsSSR";
|
|
23
23
|
import { useUserQuery } from "./useUserQuery";
|
|
24
24
|
|
|
@@ -47,7 +47,7 @@ export function useAuth() {
|
|
|
47
47
|
const useAutoConnectLoadingPrevious = useRef(false);
|
|
48
48
|
const referralCode = useSearchParam("referralCode");
|
|
49
49
|
const { partnerId } = useB3Config();
|
|
50
|
-
const wagmiConfig =
|
|
50
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId }), [partnerId]);
|
|
51
51
|
const { connect } = useConnect();
|
|
52
52
|
const activeWagmiAccount = useAccount();
|
|
53
53
|
const { switchAccount } = useSwitchAccount();
|
|
@@ -136,9 +136,7 @@ export function useAuth() {
|
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
syncWagmiFunc();
|
|
139
|
-
|
|
140
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
141
|
-
}, [partnerId, wallets]);
|
|
139
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
142
140
|
|
|
143
141
|
useEffect(() => {
|
|
144
142
|
syncWagmi();
|
|
@@ -6,7 +6,7 @@ import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
|
6
6
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
7
7
|
import { ConnectionOptions } from "@thirdweb-dev/wagmi-adapter";
|
|
8
8
|
import { getConnectors } from "@wagmi/core";
|
|
9
|
-
import { useCallback, useContext, useEffect, useRef } from "react";
|
|
9
|
+
import { useCallback, useContext, useEffect, useMemo, useRef } from "react";
|
|
10
10
|
import {
|
|
11
11
|
useActiveWallet,
|
|
12
12
|
useAutoConnect,
|
|
@@ -57,7 +57,7 @@ export function useAuthentication(partnerId: string, { skipAutoConnect = false }
|
|
|
57
57
|
const { authenticate } = useTWAuth();
|
|
58
58
|
const { user, setUser } = useUserQuery();
|
|
59
59
|
const useAutoConnectLoadingPrevious = useRef(false);
|
|
60
|
-
const wagmiConfig = createWagmiConfig({ partnerId });
|
|
60
|
+
const wagmiConfig = useMemo(() => createWagmiConfig({ partnerId }), [partnerId]);
|
|
61
61
|
const { connect } = useConnect();
|
|
62
62
|
const activeWagmiAccount = useAccount();
|
|
63
63
|
const { switchAccount } = useSwitchAccount();
|
|
@@ -113,9 +113,7 @@ export function useAuthentication(partnerId: string, { skipAutoConnect = false }
|
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
115
|
syncWagmiFunc();
|
|
116
|
-
|
|
117
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
118
|
-
}, [partnerId, wallets]);
|
|
116
|
+
}, [wagmiConfig, wallets, connect, switchAccount]);
|
|
119
117
|
|
|
120
118
|
useEffect(() => {
|
|
121
119
|
syncWagmi();
|
|
@@ -38,21 +38,3 @@ export function createWagmiConfig(options: CreateWagmiConfigOptions) {
|
|
|
38
38
|
connectors: finalConnectors,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
|
|
42
|
-
/** Module-level cache — wagmi configs must not be recreated on every render. */
|
|
43
|
-
const wagmiConfigCache = new Map<string, ReturnType<typeof createWagmiConfig>>();
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Returns a cached wagmi config for the given partnerId.
|
|
47
|
-
* Use this instead of calling createWagmiConfig() directly inside React components or hooks
|
|
48
|
-
* to avoid registering duplicate EventEmitter listeners on every render.
|
|
49
|
-
*/
|
|
50
|
-
export function getCachedWagmiConfig(options: CreateWagmiConfigOptions) {
|
|
51
|
-
const key = options.partnerId;
|
|
52
|
-
let config = wagmiConfigCache.get(key);
|
|
53
|
-
if (!config) {
|
|
54
|
-
config = createWagmiConfig(options);
|
|
55
|
-
wagmiConfigCache.set(key, config);
|
|
56
|
-
}
|
|
57
|
-
return config;
|
|
58
|
-
}
|