@b3dotfun/sdk 0.0.62 → 0.0.63-test.0
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/AnySpend.js +61 -23
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +3 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +5 -4
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +4 -4
- package/dist/cjs/anyspend/react/components/common/CryptoPaySection.js +4 -6
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/cjs/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
- package/dist/cjs/anyspend/react/components/common/OrderDetails.js +56 -145
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/cjs/anyspend/react/components/common/OrderTokenAmount.js +39 -15
- package/dist/cjs/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/cjs/anyspend/react/components/common/TokenBalance.js +1 -1
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +12 -11
- package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +56 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/cjs/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +73 -0
- package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/cjs/anyspend/react/hooks/useConnectedWalletDisplay.js +57 -0
- package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/cjs/anyspend/react/hooks/usePhantomTransfer.js +211 -0
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/cjs/global-account/react/hooks/index.d.ts +2 -1
- package/dist/cjs/global-account/react/hooks/index.js +5 -3
- package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/useSimBalance.js +6 -5
- package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/dist/cjs/global-account/react/hooks/useTokenBalanceDirect.js +62 -0
- package/dist/cjs/global-account/react/hooks/useTokenFromUrl.js +4 -3
- package/dist/esm/anyspend/react/components/AnySpend.js +62 -24
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +3 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +7 -6
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +4 -4
- package/dist/esm/anyspend/react/components/common/CryptoPaySection.js +5 -7
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +9 -17
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/esm/anyspend/react/components/common/CryptoReceiveSection.js +11 -1
- package/dist/esm/anyspend/react/components/common/OrderDetails.js +57 -146
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/esm/anyspend/react/components/common/OrderTokenAmount.js +40 -16
- package/dist/esm/anyspend/react/components/common/PaySection.js +1 -1
- package/dist/esm/anyspend/react/components/common/TokenBalance.js +2 -2
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +12 -11
- package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/esm/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.js +53 -0
- package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/esm/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.js +70 -0
- package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/esm/anyspend/react/hooks/useConnectedWalletDisplay.js +54 -0
- package/dist/esm/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/esm/anyspend/react/hooks/usePhantomTransfer.js +208 -0
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/esm/global-account/react/hooks/index.d.ts +2 -1
- package/dist/esm/global-account/react/hooks/index.js +2 -1
- package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/useSimBalance.js +6 -5
- package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/dist/esm/global-account/react/hooks/useTokenBalanceDirect.js +59 -0
- package/dist/esm/global-account/react/hooks/useTokenFromUrl.js +4 -3
- package/dist/types/anyspend/react/components/common/CryptoReceiveSection.d.ts +6 -1
- package/dist/types/anyspend/react/components/common/OrderTokenAmount.d.ts +2 -1
- package/dist/types/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.d.ts +26 -0
- package/dist/types/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.d.ts +10 -0
- package/dist/types/anyspend/react/hooks/useConnectedWalletDisplay.d.ts +14 -0
- package/dist/types/anyspend/react/hooks/usePhantomTransfer.d.ts +36 -0
- package/dist/types/global-account/react/hooks/index.d.ts +2 -1
- package/dist/types/global-account/react/hooks/useSimBalance.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useTokenBalanceDirect.d.ts +12 -0
- package/package.json +3 -2
- package/src/anyspend/react/components/AnySpend.tsx +73 -22
- package/src/anyspend/react/components/AnySpendCustom.tsx +4 -0
- package/src/anyspend/react/components/AnySpendStakeB3.tsx +8 -11
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +7 -3
- package/src/anyspend/react/components/common/CryptoPaySection.tsx +5 -7
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +9 -18
- package/src/anyspend/react/components/common/CryptoReceiveSection.tsx +22 -0
- package/src/anyspend/react/components/common/OrderDetails.tsx +66 -188
- package/src/anyspend/react/components/common/OrderTokenAmount.tsx +48 -17
- package/src/anyspend/react/components/common/PaySection.tsx +1 -0
- package/src/anyspend/react/components/common/TokenBalance.tsx +2 -2
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +13 -10
- package/src/anyspend/react/hooks/useAutoSelectCryptoPaymentMethod.ts +72 -0
- package/src/anyspend/react/hooks/useAutoSetActiveWalletFromWagmi.ts +80 -0
- package/src/anyspend/react/hooks/useConnectedWalletDisplay.ts +69 -0
- package/src/anyspend/react/hooks/usePhantomTransfer.ts +301 -0
- package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +3 -1
- package/src/global-account/react/hooks/index.ts +2 -1
- package/src/global-account/react/hooks/useSimBalance.ts +6 -5
- package/src/global-account/react/hooks/useTokenBalanceDirect.tsx +84 -0
- package/src/global-account/react/hooks/useTokenFromUrl.tsx +6 -5
|
@@ -6,18 +6,17 @@ import { useRouter, useSearchParams } from "../../../../shared/react/hooks/index
|
|
|
6
6
|
import { cn } from "../../../../shared/utils/index.js";
|
|
7
7
|
import centerTruncate from "../../../../shared/utils/centerTruncate.js";
|
|
8
8
|
import { formatTokenAmount } from "../../../../shared/utils/number.js";
|
|
9
|
-
import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
|
|
10
|
-
import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
|
|
11
9
|
import { WalletCoinbase, WalletMetamask, WalletPhantom, WalletTrust, WalletWalletConnect } from "@web3icons/react";
|
|
12
10
|
import { CheckIcon, ChevronRight, Copy, ExternalLink, Home, Loader2, RefreshCcw } from "lucide-react";
|
|
13
11
|
import { motion } from "motion/react";
|
|
14
12
|
import { QRCodeSVG } from "qrcode.react";
|
|
15
|
-
import { memo, useCallback, useEffect, useMemo, useState } from "react";
|
|
13
|
+
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
16
14
|
import TimeAgo from "react-timeago";
|
|
17
15
|
import { toast } from "sonner";
|
|
18
16
|
import { encodeFunctionData, erc20Abi } from "viem";
|
|
19
17
|
import { b3 } from "viem/chains";
|
|
20
18
|
import { useWaitForTransactionReceipt, useWalletClient } from "wagmi";
|
|
19
|
+
import { usePhantomTransfer } from "../../hooks/usePhantomTransfer.js";
|
|
21
20
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./Accordion.js";
|
|
22
21
|
import ConnectWalletPayment from "./ConnectWalletPayment.js";
|
|
23
22
|
import { CryptoPaymentMethodType } from "./CryptoPaymentMethod.js";
|
|
@@ -149,6 +148,10 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
|
|
|
149
148
|
const [showQRCode, setShowQRCode] = useState(false);
|
|
150
149
|
const { isLoading: txLoading, isSuccess: txSuccess } = useWaitForTransactionReceipt({ hash: txHash });
|
|
151
150
|
const { switchChainAndExecuteWithEOA, switchChainAndExecute, isSwitchingOrExecuting } = useUnifiedChainSwitchAndExecute();
|
|
151
|
+
// Track if auto-payment was attempted to avoid re-triggering
|
|
152
|
+
const autoPaymentAttempted = useRef(false);
|
|
153
|
+
// Track if component is ready for auto-payment (all data loaded)
|
|
154
|
+
const [isComponentReady, setIsComponentReady] = useState(false);
|
|
152
155
|
const roundedUpSrcAmount = useMemo(() => {
|
|
153
156
|
// Display the full transfer amount without rounding since users need to see the exact value they're transferring.
|
|
154
157
|
// Use 21 significant digits (max allowed by Intl.NumberFormat)
|
|
@@ -204,19 +207,25 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
|
|
|
204
207
|
setTxHash(txHash);
|
|
205
208
|
}
|
|
206
209
|
}, [order, switchChainAndExecuteWithEOA, switchChainAndExecute, depositDeficit, effectiveCryptoPaymentMethod]);
|
|
210
|
+
// Use Phantom transfer hook for Solana payments
|
|
211
|
+
const { initiateTransfer: initiatePhantomTransfer, getConnectedAddress: getPhantomAddress } = usePhantomTransfer();
|
|
207
212
|
// Main payment handler that triggers chain switch and payment
|
|
208
|
-
const handlePayment = async () => {
|
|
213
|
+
const handlePayment = useCallback(async () => {
|
|
209
214
|
console.log("Initiating payment process. Target chain:", order.srcChain, "Current chain:", walletClient?.chain?.id);
|
|
210
215
|
if (order.srcChain === RELAY_SOLANA_MAINNET_CHAIN_ID) {
|
|
211
216
|
// Use the existing depositDeficit calculation to determine amount to send
|
|
212
217
|
const amountToSend = depositDeficit > BigInt(0) ? depositDeficit.toString() : order.srcAmount;
|
|
213
|
-
await initiatePhantomTransfer(
|
|
218
|
+
await initiatePhantomTransfer({
|
|
219
|
+
amountLamports: amountToSend,
|
|
220
|
+
tokenAddress: order.srcTokenAddress,
|
|
221
|
+
recipientAddress: order.globalAddress,
|
|
222
|
+
});
|
|
214
223
|
}
|
|
215
224
|
else {
|
|
216
225
|
// Use unified payment process for both EOA and AA wallets
|
|
217
226
|
await handleUnifiedPaymentProcess();
|
|
218
227
|
}
|
|
219
|
-
};
|
|
228
|
+
}, [order, walletClient?.chain?.id, depositDeficit, handleUnifiedPaymentProcess, initiatePhantomTransfer]);
|
|
220
229
|
// When waitingForDeposit is true, we show a message to the user to wait for the deposit to be processed.
|
|
221
230
|
const setWaitingForDeposit = useCallback(() => {
|
|
222
231
|
if (disableUrlParamManagement)
|
|
@@ -254,16 +263,50 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
|
|
|
254
263
|
setTxHash(undefined);
|
|
255
264
|
}
|
|
256
265
|
}, [setWaitingForDeposit, txSuccess]);
|
|
257
|
-
const isPhantomMobile = useMemo(() => navigator.userAgent.includes("Phantom"), []);
|
|
258
|
-
const isPhantomBrowser = useMemo(() => window.phantom?.solana?.isPhantom, []);
|
|
259
266
|
// Get connected Phantom wallet address if available
|
|
260
|
-
const phantomWalletAddress = useMemo(() =>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
267
|
+
const phantomWalletAddress = useMemo(() => getPhantomAddress(), [getPhantomAddress]);
|
|
268
|
+
// Calculate status display before using it
|
|
269
|
+
const { text: statusText, status: statusDisplay } = getStatusDisplay(order);
|
|
270
|
+
// Memoize the payable state calculation to avoid recalculating on every render
|
|
271
|
+
const isPayableState = useMemo(() => {
|
|
272
|
+
const waitingForDeposit = new URLSearchParams(window.location.search).get("waitingForDeposit") === "true";
|
|
273
|
+
return (refundTxs.length === 0 &&
|
|
274
|
+
!executeTx &&
|
|
275
|
+
!(relayTxs.length > 0 && relayTxs.every(tx => tx.status === "success")) &&
|
|
276
|
+
!depositTxs?.length &&
|
|
277
|
+
!waitingForDeposit &&
|
|
278
|
+
statusDisplay === "processing" &&
|
|
279
|
+
!order.onrampMetadata &&
|
|
280
|
+
(effectiveCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ||
|
|
281
|
+
effectiveCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET));
|
|
282
|
+
}, [
|
|
283
|
+
refundTxs.length,
|
|
284
|
+
executeTx,
|
|
285
|
+
relayTxs,
|
|
286
|
+
depositTxs?.length,
|
|
287
|
+
statusDisplay,
|
|
288
|
+
order.onrampMetadata,
|
|
289
|
+
effectiveCryptoPaymentMethod,
|
|
290
|
+
]);
|
|
291
|
+
// Mark component as ready once all critical data is available
|
|
292
|
+
// This ensures we don't trigger payment before the component has fully initialized
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (!isComponentReady && srcToken && dstToken && statusDisplay) {
|
|
295
|
+
setIsComponentReady(true);
|
|
264
296
|
}
|
|
265
|
-
|
|
266
|
-
|
|
297
|
+
}, [isComponentReady, srcToken, dstToken, statusDisplay]);
|
|
298
|
+
// Auto-trigger payment when component is ready and order is in payable state
|
|
299
|
+
// This effect only runs when isPayableState or isComponentReady changes
|
|
300
|
+
useEffect(() => {
|
|
301
|
+
// Only trigger payment if:
|
|
302
|
+
// 1. We haven't attempted payment yet
|
|
303
|
+
// 2. Component is fully ready (all data loaded)
|
|
304
|
+
// 3. Order is in a payable state
|
|
305
|
+
if (!autoPaymentAttempted.current && isComponentReady && isPayableState) {
|
|
306
|
+
autoPaymentAttempted.current = true;
|
|
307
|
+
handlePayment();
|
|
308
|
+
}
|
|
309
|
+
}, [isPayableState, isComponentReady, handlePayment]);
|
|
267
310
|
if (!srcToken || !dstToken) {
|
|
268
311
|
return _jsx("div", { children: "Loading..." });
|
|
269
312
|
}
|
|
@@ -278,138 +321,6 @@ export const OrderDetails = memo(function OrderDetails({ mode = "modal", order,
|
|
|
278
321
|
const formattedActualDstAmount = actualDstAmount
|
|
279
322
|
? formatTokenAmount(BigInt(actualDstAmount), dstToken.decimals)
|
|
280
323
|
: undefined;
|
|
281
|
-
const { text: statusText, status: statusDisplay } = getStatusDisplay(order);
|
|
282
|
-
const initiatePhantomTransfer = async (amountLamports, tokenAddress, recipientAddress) => {
|
|
283
|
-
try {
|
|
284
|
-
if (!isPhantomBrowser && !isPhantomMobile) {
|
|
285
|
-
toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// Step 2: Ensure Phantom is connected/unlocked
|
|
289
|
-
const phantom = window.phantom?.solana;
|
|
290
|
-
if (!phantom) {
|
|
291
|
-
toast.error("Phantom wallet not accessible");
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
// Connect and unlock wallet if needed
|
|
295
|
-
let publicKey;
|
|
296
|
-
try {
|
|
297
|
-
const connection = await phantom.connect();
|
|
298
|
-
publicKey = connection.publicKey;
|
|
299
|
-
}
|
|
300
|
-
catch (connectError) {
|
|
301
|
-
toast.error("Failed to connect to Phantom wallet");
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
// Step 3: Create transaction with priority fees
|
|
305
|
-
const connection = new Connection("https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb");
|
|
306
|
-
const fromPubkey = new PublicKey(publicKey.toString());
|
|
307
|
-
const toPubkey = new PublicKey(recipientAddress);
|
|
308
|
-
const amount = BigInt(amountLamports);
|
|
309
|
-
// Step 4: Get recent priority fees to determine optimal pricing
|
|
310
|
-
let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
|
|
311
|
-
try {
|
|
312
|
-
const recentFees = await connection.getRecentPrioritizationFees({
|
|
313
|
-
lockedWritableAccounts: [fromPubkey],
|
|
314
|
-
});
|
|
315
|
-
if (recentFees && recentFees.length > 0) {
|
|
316
|
-
// Use 75th percentile of recent fees for good priority
|
|
317
|
-
const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
|
|
318
|
-
const percentile75Index = Math.floor(sortedFees.length * 0.75);
|
|
319
|
-
priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
catch (feeError) {
|
|
323
|
-
console.warn("Failed to fetch recent priority fees, using default:", feeError);
|
|
324
|
-
}
|
|
325
|
-
let transaction;
|
|
326
|
-
// Check if this is native SOL transfer
|
|
327
|
-
if (tokenAddress === "11111111111111111111111111111111") {
|
|
328
|
-
// Native SOL transfer with priority fees
|
|
329
|
-
const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
|
|
330
|
-
const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
|
|
331
|
-
transaction = new Transaction()
|
|
332
|
-
.add(
|
|
333
|
-
// Set compute unit limit first (must come before other instructions)
|
|
334
|
-
ComputeBudgetProgram.setComputeUnitLimit({
|
|
335
|
-
units: computeUnitLimit,
|
|
336
|
-
}))
|
|
337
|
-
.add(
|
|
338
|
-
// Set priority fee
|
|
339
|
-
ComputeBudgetProgram.setComputeUnitPrice({
|
|
340
|
-
microLamports: computeUnitPrice,
|
|
341
|
-
}))
|
|
342
|
-
.add(
|
|
343
|
-
// Actual transfer instruction
|
|
344
|
-
SystemProgram.transfer({
|
|
345
|
-
fromPubkey,
|
|
346
|
-
toPubkey,
|
|
347
|
-
lamports: Number(amount),
|
|
348
|
-
}));
|
|
349
|
-
console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
// SPL Token transfer with priority fees
|
|
353
|
-
const mintPubkey = new PublicKey(tokenAddress);
|
|
354
|
-
// Get associated token accounts
|
|
355
|
-
const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
|
|
356
|
-
const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
|
|
357
|
-
// Check if destination token account exists
|
|
358
|
-
const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
|
|
359
|
-
const needsDestinationAccount = !toTokenAccountInfo;
|
|
360
|
-
// Get mint info to determine decimals
|
|
361
|
-
const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
|
|
362
|
-
const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
|
|
363
|
-
// SPL transfers need more compute units than SOL transfers
|
|
364
|
-
// Add extra CU if we need to create destination account
|
|
365
|
-
const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
|
|
366
|
-
const computeUnitPrice = Math.min(priorityFee, 100000);
|
|
367
|
-
// Create transfer instruction
|
|
368
|
-
const transferInstruction = createTransferCheckedInstruction(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
|
|
369
|
-
transaction = new Transaction()
|
|
370
|
-
.add(ComputeBudgetProgram.setComputeUnitLimit({
|
|
371
|
-
units: computeUnitLimit,
|
|
372
|
-
}))
|
|
373
|
-
.add(ComputeBudgetProgram.setComputeUnitPrice({
|
|
374
|
-
microLamports: computeUnitPrice,
|
|
375
|
-
}));
|
|
376
|
-
// Add create destination account instruction if needed
|
|
377
|
-
if (needsDestinationAccount) {
|
|
378
|
-
transaction.add(createAssociatedTokenAccountInstruction(fromPubkey, // payer
|
|
379
|
-
toTokenAccount, // ata
|
|
380
|
-
toPubkey, // owner
|
|
381
|
-
mintPubkey));
|
|
382
|
-
}
|
|
383
|
-
// Add the transfer instruction
|
|
384
|
-
transaction.add(transferInstruction);
|
|
385
|
-
console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
|
|
386
|
-
}
|
|
387
|
-
// Step 5: Get latest blockhash and simulate transaction to verify
|
|
388
|
-
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
389
|
-
transaction.recentBlockhash = blockhash;
|
|
390
|
-
transaction.feePayer = fromPubkey;
|
|
391
|
-
// Step 6: Sign and send transaction with priority fees
|
|
392
|
-
const signedTransaction = await phantom.signAndSendTransaction(transaction);
|
|
393
|
-
toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
|
|
394
|
-
console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
console.error("Transfer error:", error);
|
|
398
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
399
|
-
if (errorMessage.includes("User rejected")) {
|
|
400
|
-
toast.error("Transaction was cancelled by user");
|
|
401
|
-
}
|
|
402
|
-
else if (errorMessage.includes("insufficient")) {
|
|
403
|
-
toast.error("Insufficient balance for this transaction");
|
|
404
|
-
}
|
|
405
|
-
else if (errorMessage.includes("blockhash not found")) {
|
|
406
|
-
toast.error("Network congestion detected. Please try again in a moment.");
|
|
407
|
-
}
|
|
408
|
-
else {
|
|
409
|
-
toast.error(`Transfer failed: ${errorMessage}`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
};
|
|
413
324
|
if (refundTxs.length > 0) {
|
|
414
325
|
return (_jsxs(_Fragment, { children: [_jsx(OrderStatus, { order: order, selectedCryptoPaymentMethod: effectiveCryptoPaymentMethod }), _jsx(OrderDetailsCollapsible, { order: order, dstToken: dstToken, tournament: tournament, nft: nft, recipientName: recipientName, formattedExpectedDstAmount: formattedExpectedDstAmount, points: points }), _jsx(Accordion, { type: "single", collapsible: true, className: "order-details-accordion w-full", children: _jsxs(AccordionItem, { value: "refund-details", className: "order-details-refund-item", children: [_jsx(AccordionTrigger, { className: "accordion-trigger", children: "Transaction Details" }), _jsx(AccordionContent, { className: "accordion-content pl-2", children: _jsxs("div", { className: "relative flex w-full flex-col gap-4", children: [_jsx("div", { className: "bg-as-surface-secondary absolute bottom-2 left-4 top-2 z-[5] w-2", children: _jsx(motion.div, { className: "bg-as-border-primary absolute left-[2px] top-0 z-10 w-[3px]", initial: { height: "0%" }, animate: { height: "100%" }, transition: { duration: 1.5, ease: "easeInOut" } }) }), depositTxs
|
|
415
326
|
? depositTxs.map(dTx => (_jsx(TransactionDetails, { title: order.onrampMetadata?.vendor === "stripe-web2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { components } from "../../../../anyspend/types/api";
|
|
2
|
-
export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, }: {
|
|
2
|
+
export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect, canEditAmount, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, }: {
|
|
3
3
|
disabled?: boolean;
|
|
4
4
|
inputValue: string;
|
|
5
5
|
onChangeInput: (value: string) => void;
|
|
@@ -18,4 +18,5 @@ export declare function OrderTokenAmount({ disabled, inputValue, onChangeInput,
|
|
|
18
18
|
onTokenSelect?: (token: components["schemas"]["Token"], event: {
|
|
19
19
|
preventDefault: () => void;
|
|
20
20
|
}) => void;
|
|
21
|
+
walletAddress?: string | undefined;
|
|
21
22
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -3,27 +3,55 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { ChevronsUpDown } from "lucide-react";
|
|
4
4
|
import { useEffect, useRef } from "react";
|
|
5
5
|
import { NumericFormat } from "react-number-format";
|
|
6
|
+
import { formatUnits } from "viem";
|
|
6
7
|
import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID } from "../../../../anyspend/index.js";
|
|
7
|
-
import {
|
|
8
|
+
import { getNativeRequired } from "../../../../anyspend/utils/chain.js";
|
|
9
|
+
import { isNativeToken } from "../../../../anyspend/utils/token.js";
|
|
10
|
+
import { Button, useTokenBalance } from "../../../../global-account/react/index.js";
|
|
8
11
|
import { cn } from "../../../../shared/utils/index.js";
|
|
9
12
|
import { TokenSelector } from "@relayprotocol/relay-kit-ui";
|
|
10
13
|
import { ChainTokenIcon } from "./ChainTokenIcon.js";
|
|
11
|
-
export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, }) {
|
|
14
|
+
export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context, address, chainId, setChainId, token, setToken, hideTokenSelect = false, canEditAmount = true, className, innerClassName, amountClassName, tokenSelectClassName, onTokenSelect, walletAddress, }) {
|
|
12
15
|
// Track previous token to detect changes
|
|
13
16
|
const prevTokenRef = useRef(token.address);
|
|
17
|
+
// Track if initial balance has been set
|
|
18
|
+
const initialBalanceSetRef = useRef(false);
|
|
19
|
+
// Only get token balance when context is "from" (for setting max amount)
|
|
20
|
+
const { rawBalance } = useTokenBalance({
|
|
21
|
+
token,
|
|
22
|
+
address: context === "from" && walletAddress ? walletAddress : undefined,
|
|
23
|
+
});
|
|
24
|
+
// Reset balance ref when token address or chain changes
|
|
14
25
|
useEffect(() => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
initialBalanceSetRef.current = false;
|
|
27
|
+
}, [token.address, token.chainId]);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
// Only handle "from" context
|
|
30
|
+
if (context !== "from")
|
|
31
|
+
return;
|
|
32
|
+
// Check if token changed or if this is the initial load with balance
|
|
33
|
+
const isTokenChanged = prevTokenRef.current !== token.address;
|
|
34
|
+
const isInitialLoad = !initialBalanceSetRef.current && rawBalance;
|
|
35
|
+
if ((isTokenChanged || isInitialLoad) && rawBalance) {
|
|
36
|
+
console.log(`Setting max balance - Token: ${token.address}, Changed: ${isTokenChanged}, Initial: ${isInitialLoad}`);
|
|
37
|
+
// Calculate max amount with gas reserve for native tokens
|
|
38
|
+
let maxAmount;
|
|
39
|
+
if (isNativeToken(token.address)) {
|
|
40
|
+
const gasReserve = getNativeRequired(token.chainId);
|
|
41
|
+
// Ensure we don't go negative
|
|
42
|
+
maxAmount = rawBalance > gasReserve ? rawBalance - gasReserve : BigInt(0);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
// For ERC20 tokens, use full balance
|
|
46
|
+
maxAmount = rawBalance;
|
|
22
47
|
}
|
|
23
|
-
//
|
|
48
|
+
// Set the max amount as input value
|
|
49
|
+
onChangeInput(formatUnits(maxAmount, token.decimals));
|
|
50
|
+
// Update refs
|
|
24
51
|
prevTokenRef.current = token.address;
|
|
52
|
+
initialBalanceSetRef.current = true;
|
|
25
53
|
}
|
|
26
|
-
}, [token.address, chainId, context, onChangeInput]);
|
|
54
|
+
}, [token.address, token.chainId, token.decimals, chainId, context, onChangeInput, rawBalance]);
|
|
27
55
|
const handleTokenSelect = (newToken) => {
|
|
28
56
|
const token = {
|
|
29
57
|
address: newToken.address,
|
|
@@ -50,12 +78,8 @@ export function OrderTokenAmount({ disabled, inputValue, onChangeInput, context,
|
|
|
50
78
|
prevTokenRef.current = "changing"; // Temporary value to force effect
|
|
51
79
|
// Set the chain ID first
|
|
52
80
|
setChainId(newToken.chainId);
|
|
53
|
-
// Then set the new token
|
|
81
|
+
// Then set the new token - the useEffect will handle setting the max balance
|
|
54
82
|
setToken(token);
|
|
55
|
-
// If this is the source token, reset the amount immediately
|
|
56
|
-
if (context === "from") {
|
|
57
|
-
onChangeInput("0.01");
|
|
58
|
-
}
|
|
59
83
|
};
|
|
60
84
|
return (_jsx("div", { className: cn("border-as-stroke flex w-full flex-col gap-2 rounded-xl", className), children: _jsxs("div", { className: cn("flex items-center justify-between gap-3", innerClassName), children: [!canEditAmount ? (_jsx("h2", { className: cn("text-3xl font-medium text-white", amountClassName), children: inputValue || "--" })) : (_jsx(NumericFormat, { decimalSeparator: ".", allowedDecimalSeparators: [","], thousandSeparator: true, inputMode: "decimal", autoComplete: "off", autoCorrect: "off", type: "text", placeholder: "0.00", minLength: 1, maxLength: 30, spellCheck: "false", className: cn("placeholder:text-as-primary/70 disabled:text-as-primary/70 text-as-primary w-full bg-transparent text-4xl font-semibold leading-[42px] outline-none sm:text-[30px]", amountClassName), pattern: "^[0-9]*[.,]?[0-9]*$", disabled: disabled, value: inputValue, allowNegative: false, onChange: e => onChangeInput(e.currentTarget.value) }, `input-${token.address}-${chainId}`)), !hideTokenSelect && (_jsx(TokenSelector, { address: address, chainIdsFilter: Object.values(ALL_CHAINS).map(chain => chain.id), context: context, fromChainWalletVMSupported: true, isValidAddress: true, lockedChainIds: Object.values(ALL_CHAINS).map(chain => chain.id), multiWalletSupportEnabled: true, onAnalyticEvent: undefined, popularChainIds: [1, 8453, RELAY_SOLANA_MAINNET_CHAIN_ID], setToken: handleTokenSelect, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs(Button, { variant: "outline", role: "combobox", className: cn("token-selector-button bg-b3-react-background border-as-stroke flex h-auto w-fit shrink-0 items-center justify-center gap-2 rounded-xl border-2 px-2 py-1 pr-2 text-center", tokenSelectClassName), children: [token.metadata.logoURI ? (_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[chainId].logoUrl, tokenUrl: token.metadata.logoURI, className: "h-8 min-h-8 w-8 min-w-8" })) : (_jsx("div", { className: "h-8 w-8 rounded-full bg-gray-700" })), _jsxs("div", { className: "flex flex-col items-start gap-0", children: [_jsx("div", { className: "text-as-primary font-semibold", children: token.symbol }), _jsx("div", { className: "text-as-primary/70 text-xs", children: ALL_CHAINS[chainId].name })] }), _jsx(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-70" })] }) }, `selector-${context}-${token.address}-${chainId}`))] }) }, `${context}-${token.address}-${chainId}`));
|
|
61
85
|
}
|
|
@@ -39,7 +39,7 @@ export function CryptoPaySection({ selectedSrcChainId, setSelectedSrcChainId, se
|
|
|
39
39
|
useEffect(() => {
|
|
40
40
|
appliedSrcMetadataRef.current = false;
|
|
41
41
|
}, [selectedSrcToken.address, selectedSrcToken.chainId]);
|
|
42
|
-
return (_jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, className: "pay-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-7 items-center text-sm", children: "Pay" }), _jsx("button", { className: "text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none", onClick: onSelectCryptoPaymentMethod, children: selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (_jsxs(_Fragment, { children: [isConnected ? (_jsx("div", { className: "flex items-center gap-1", children: connectedName ? formatUsername(connectedName) : shortenAddress(connectedAddress || "") })) : ("Connect wallet"), _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ? (_jsxs(_Fragment, { children: ["Global Account", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (_jsxs(_Fragment, { children: ["Transfer crypto", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : (_jsxs(_Fragment, { children: ["Select payment method", _jsx(ChevronRight, { className: "h-4 w-4" })] })) })] }), _jsx(OrderTokenAmount, { address: connectedAddress, context: "from", inputValue: srcAmount, onChangeInput: value => {
|
|
42
|
+
return (_jsxs(motion.div, { initial: { opacity: 0, y: 20, filter: "blur(10px)" }, animate: { opacity: 1, y: 0, filter: "blur(0px)" }, transition: { duration: 0.3, delay: 0, ease: "easeInOut" }, className: "pay-section bg-as-surface-secondary border-as-border-secondary relative flex w-full flex-col gap-2 rounded-2xl border p-4 sm:p-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-7 items-center text-sm", children: "Pay" }), _jsx("button", { className: "text-as-tertiarry flex h-7 items-center gap-2 text-sm transition-colors focus:!outline-none", onClick: onSelectCryptoPaymentMethod, children: selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET ? (_jsxs(_Fragment, { children: [isConnected ? (_jsx("div", { className: "flex items-center gap-1", children: connectedName ? formatUsername(connectedName) : shortenAddress(connectedAddress || "") })) : ("Connect wallet"), _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET ? (_jsxs(_Fragment, { children: ["Global Account", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : selectedCryptoPaymentMethod === CryptoPaymentMethodType.TRANSFER_CRYPTO ? (_jsxs(_Fragment, { children: ["Transfer crypto", _jsx(ChevronRight, { className: "h-4 w-4" })] })) : (_jsxs(_Fragment, { children: ["Select payment method", _jsx(ChevronRight, { className: "h-4 w-4" })] })) })] }), _jsx(OrderTokenAmount, { address: connectedAddress, walletAddress: connectedAddress, context: "from", inputValue: srcAmount, onChangeInput: value => {
|
|
43
43
|
setIsSrcInputDirty(true);
|
|
44
44
|
setSrcAmount(value);
|
|
45
45
|
}, chainId: selectedSrcChainId, setChainId: setSelectedSrcChainId, token: selectedSrcToken, setToken: setSelectedSrcToken }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("div", { className: "text-as-primary/50 flex h-5 items-center text-sm", children: formatDisplayNumber(anyspendQuote?.data?.currencyIn?.amountUsd, {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { getNativeRequired } from "../../../../anyspend/utils/chain.js";
|
|
3
3
|
import { isNativeToken } from "../../../../anyspend/utils/token.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useTokenBalanceDirect } from "../../../../global-account/react/index.js";
|
|
5
5
|
import { formatUnits } from "viem";
|
|
6
6
|
export function TokenBalance({ token, walletAddress, onChangeInput, }) {
|
|
7
|
-
const { rawBalance, formattedBalance, isLoading } =
|
|
7
|
+
const { rawBalance, formattedBalance, isLoading } = useTokenBalanceDirect({
|
|
8
8
|
token,
|
|
9
9
|
address: walletAddress,
|
|
10
10
|
});
|
|
@@ -10,6 +10,8 @@ import { base, mainnet } from "viem/chains";
|
|
|
10
10
|
import { useAccount } from "wagmi";
|
|
11
11
|
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod.js";
|
|
12
12
|
import { FiatPaymentMethod } from "../components/common/FiatPaymentMethod.js";
|
|
13
|
+
import { useAutoSelectCryptoPaymentMethod } from "./useAutoSelectCryptoPaymentMethod.js";
|
|
14
|
+
import { useAutoSetActiveWalletFromWagmi } from "./useAutoSetActiveWalletFromWagmi.js";
|
|
13
15
|
export var PanelView;
|
|
14
16
|
(function (PanelView) {
|
|
15
17
|
PanelView[PanelView["MAIN"] = 0] = "MAIN";
|
|
@@ -45,6 +47,8 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
|
|
|
45
47
|
const [selectedRecipientAddress, setSelectedRecipientAddress] = useState(recipientAddress);
|
|
46
48
|
const recipientProfile = useProfile({ address: selectedRecipientAddress, fresh: true });
|
|
47
49
|
const recipientName = recipientProfile.data?.name;
|
|
50
|
+
// Auto-set active wallet from wagmi
|
|
51
|
+
useAutoSetActiveWalletFromWagmi();
|
|
48
52
|
// Set default recipient address when wallet changes
|
|
49
53
|
useEffect(() => {
|
|
50
54
|
if (!selectedRecipientAddress && globalAddress) {
|
|
@@ -68,17 +72,14 @@ export function useAnyspendFlow({ paymentType = "crypto", recipientAddress, load
|
|
|
68
72
|
return false;
|
|
69
73
|
}
|
|
70
74
|
}, [rawBalance, srcAmount, selectedSrcToken.decimals, isBalanceLoading, paymentType]);
|
|
71
|
-
// Auto-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}, [paymentType, hasEnoughBalance, isBalanceLoading]);
|
|
75
|
+
// Auto-select crypto payment method based on available wallets and balance
|
|
76
|
+
useAutoSelectCryptoPaymentMethod({
|
|
77
|
+
paymentType,
|
|
78
|
+
selectedCryptoPaymentMethod,
|
|
79
|
+
setSelectedCryptoPaymentMethod,
|
|
80
|
+
hasEnoughBalance,
|
|
81
|
+
isBalanceLoading,
|
|
82
|
+
});
|
|
82
83
|
// Fetch specific token when sourceTokenAddress and sourceTokenChainId are provided
|
|
83
84
|
useEffect(() => {
|
|
84
85
|
const fetchSourceToken = async () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
|
|
2
|
+
interface UseAutoSelectCryptoPaymentMethodParams {
|
|
3
|
+
/** Current payment type (crypto or fiat) */
|
|
4
|
+
paymentType?: "crypto" | "fiat";
|
|
5
|
+
/** Currently selected payment method */
|
|
6
|
+
selectedCryptoPaymentMethod: CryptoPaymentMethodType;
|
|
7
|
+
/** Function to update the selected payment method */
|
|
8
|
+
setSelectedCryptoPaymentMethod: (method: CryptoPaymentMethodType) => void;
|
|
9
|
+
/** Whether user has enough balance to pay */
|
|
10
|
+
hasEnoughBalance: boolean;
|
|
11
|
+
/** Whether balance is still loading */
|
|
12
|
+
isBalanceLoading: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Custom hook to automatically select appropriate crypto payment method
|
|
16
|
+
* based on available wallets and balance.
|
|
17
|
+
*
|
|
18
|
+
* Auto-selection logic:
|
|
19
|
+
* - Only auto-selects when payment method is NONE (doesn't override user choices)
|
|
20
|
+
* - If EOA/Wagmi wallet connected + has balance → CONNECT_WALLET
|
|
21
|
+
* - If EOA/Wagmi wallet connected + insufficient balance → TRANSFER_CRYPTO
|
|
22
|
+
* - If only Global wallet available → GLOBAL_WALLET
|
|
23
|
+
* - If no wallets → remains NONE
|
|
24
|
+
*/
|
|
25
|
+
export declare function useAutoSelectCryptoPaymentMethod({ paymentType, selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod, hasEnoughBalance, isBalanceLoading, }: UseAutoSelectCryptoPaymentMethodParams): void;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod.js";
|
|
3
|
+
import { useConnectedWalletDisplay } from "./useConnectedWalletDisplay.js";
|
|
4
|
+
/**
|
|
5
|
+
* Custom hook to automatically select appropriate crypto payment method
|
|
6
|
+
* based on available wallets and balance.
|
|
7
|
+
*
|
|
8
|
+
* Auto-selection logic:
|
|
9
|
+
* - Only auto-selects when payment method is NONE (doesn't override user choices)
|
|
10
|
+
* - If EOA/Wagmi wallet connected + has balance → CONNECT_WALLET
|
|
11
|
+
* - If EOA/Wagmi wallet connected + insufficient balance → TRANSFER_CRYPTO
|
|
12
|
+
* - If only Global wallet available → GLOBAL_WALLET
|
|
13
|
+
* - If no wallets → remains NONE
|
|
14
|
+
*/
|
|
15
|
+
export function useAutoSelectCryptoPaymentMethod({ paymentType = "crypto", selectedCryptoPaymentMethod, setSelectedCryptoPaymentMethod, hasEnoughBalance, isBalanceLoading, }) {
|
|
16
|
+
// Get suggested payment method based on available wallets
|
|
17
|
+
const { suggestedPaymentMethod } = useConnectedWalletDisplay(selectedCryptoPaymentMethod);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
// Only auto-select when on crypto payment type and payment method is NONE
|
|
20
|
+
if (paymentType !== "crypto" || selectedCryptoPaymentMethod !== CryptoPaymentMethodType.NONE) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// If we have a suggested payment method (wallet is connected), use it
|
|
24
|
+
if (suggestedPaymentMethod !== CryptoPaymentMethodType.NONE) {
|
|
25
|
+
// If we have balance info and enough balance, use CONNECT_WALLET
|
|
26
|
+
// Otherwise, default to TRANSFER_CRYPTO if balance is insufficient
|
|
27
|
+
if (!isBalanceLoading) {
|
|
28
|
+
if (hasEnoughBalance && suggestedPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
|
|
29
|
+
setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.CONNECT_WALLET);
|
|
30
|
+
}
|
|
31
|
+
else if (!hasEnoughBalance && suggestedPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
|
|
32
|
+
// Wallet connected but insufficient balance - suggest transfer
|
|
33
|
+
setSelectedCryptoPaymentMethod(CryptoPaymentMethodType.TRANSFER_CRYPTO);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Use suggested method (e.g., GLOBAL_WALLET)
|
|
37
|
+
setSelectedCryptoPaymentMethod(suggestedPaymentMethod);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Balance still loading, use suggested method
|
|
42
|
+
setSelectedCryptoPaymentMethod(suggestedPaymentMethod);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}, [
|
|
46
|
+
paymentType,
|
|
47
|
+
selectedCryptoPaymentMethod,
|
|
48
|
+
suggestedPaymentMethod,
|
|
49
|
+
hasEnoughBalance,
|
|
50
|
+
isBalanceLoading,
|
|
51
|
+
setSelectedCryptoPaymentMethod,
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook that automatically sets the active thirdweb wallet when a wagmi wallet connects.
|
|
3
|
+
*
|
|
4
|
+
* This is useful for syncing wagmi wallet connections with thirdweb's wallet system,
|
|
5
|
+
* ensuring that when users connect via wagmi, the active wallet is properly set.
|
|
6
|
+
*
|
|
7
|
+
* Place this hook in components that stay mounted throughout the user flow
|
|
8
|
+
* (not in components that unmount during navigation).
|
|
9
|
+
*/
|
|
10
|
+
export declare function useAutoSetActiveWalletFromWagmi(): void;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { client } from "../../../shared/utils/thirdweb.js";
|
|
2
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
3
|
+
import { useSetActiveWallet } from "thirdweb/react";
|
|
4
|
+
import { createWallet } from "thirdweb/wallets";
|
|
5
|
+
import { useAccount } from "wagmi";
|
|
6
|
+
/**
|
|
7
|
+
* Hook that automatically sets the active thirdweb wallet when a wagmi wallet connects.
|
|
8
|
+
*
|
|
9
|
+
* This is useful for syncing wagmi wallet connections with thirdweb's wallet system,
|
|
10
|
+
* ensuring that when users connect via wagmi, the active wallet is properly set.
|
|
11
|
+
*
|
|
12
|
+
* Place this hook in components that stay mounted throughout the user flow
|
|
13
|
+
* (not in components that unmount during navigation).
|
|
14
|
+
*/
|
|
15
|
+
export function useAutoSetActiveWalletFromWagmi() {
|
|
16
|
+
const { address: wagmiAddress, connector: wagmiConnector } = useAccount();
|
|
17
|
+
const setActiveWallet = useSetActiveWallet();
|
|
18
|
+
const prevWagmiAddress = useRef(undefined);
|
|
19
|
+
// Map wagmi connector names to thirdweb wallet IDs
|
|
20
|
+
const getThirdwebWalletId = useCallback((connectorName) => {
|
|
21
|
+
const walletMap = {
|
|
22
|
+
MetaMask: "io.metamask",
|
|
23
|
+
"Coinbase Wallet": "com.coinbase.wallet",
|
|
24
|
+
Rainbow: "me.rainbow",
|
|
25
|
+
WalletConnect: "walletConnect",
|
|
26
|
+
Phantom: "app.phantom",
|
|
27
|
+
};
|
|
28
|
+
return walletMap[connectorName] || null;
|
|
29
|
+
}, []);
|
|
30
|
+
// Create thirdweb wallet from wagmi connector
|
|
31
|
+
const createThirdwebWalletFromConnector = useCallback(async (connectorName) => {
|
|
32
|
+
const walletId = getThirdwebWalletId(connectorName);
|
|
33
|
+
if (!walletId) {
|
|
34
|
+
console.warn(`No thirdweb wallet ID found for connector: ${connectorName}`);
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const thirdwebWallet = createWallet(walletId);
|
|
39
|
+
await thirdwebWallet.connect({ client });
|
|
40
|
+
return thirdwebWallet;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error(`Failed to create thirdweb wallet for ${connectorName}:`, error);
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}, [getThirdwebWalletId]);
|
|
47
|
+
// Listen for wagmi wallet connections and automatically set active wallet
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const isNewConnection = wagmiAddress && wagmiAddress !== prevWagmiAddress.current;
|
|
50
|
+
if (isNewConnection && wagmiConnector?.name) {
|
|
51
|
+
prevWagmiAddress.current = wagmiAddress;
|
|
52
|
+
const setupThirdwebWallet = async () => {
|
|
53
|
+
try {
|
|
54
|
+
const thirdwebWallet = await createThirdwebWalletFromConnector(wagmiConnector.name);
|
|
55
|
+
if (thirdwebWallet) {
|
|
56
|
+
setActiveWallet(thirdwebWallet);
|
|
57
|
+
console.log(`Auto-set active wallet for ${wagmiConnector.name}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error("Failed to auto-set active wallet:", error);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
setupThirdwebWallet();
|
|
65
|
+
}
|
|
66
|
+
if (!wagmiAddress) {
|
|
67
|
+
prevWagmiAddress.current = undefined;
|
|
68
|
+
}
|
|
69
|
+
}, [wagmiAddress, wagmiConnector?.name, setActiveWallet, createThirdwebWalletFromConnector]);
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod";
|
|
2
|
+
interface UseConnectedWalletDisplayResult {
|
|
3
|
+
walletAddress: string | undefined;
|
|
4
|
+
shouldShowConnectedEOA: boolean;
|
|
5
|
+
shouldShowWagmiWallet: boolean;
|
|
6
|
+
isWalletDuplicated: boolean;
|
|
7
|
+
suggestedPaymentMethod: CryptoPaymentMethodType;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Custom hook to determine which wallet to display and its address
|
|
11
|
+
* Handles logic for showing EOA wallet, wagmi wallet, or global wallet based on payment method
|
|
12
|
+
*/
|
|
13
|
+
export declare function useConnectedWalletDisplay(selectedCryptoPaymentMethod?: CryptoPaymentMethodType): UseConnectedWalletDisplayResult;
|
|
14
|
+
export {};
|