@b3dotfun/sdk 0.0.62 → 0.0.63-test.0-alpha.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/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +275 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +5 -4
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +288 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- package/dist/cjs/anyspend/react/components/AnySpendStakeUpsideExactIn.js +33 -0
- 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 +66 -147
- package/dist/cjs/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
- 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/components/index.d.ts +5 -1
- package/dist/cjs/anyspend/react/components/index.js +11 -3
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/cjs/anyspend/react/hooks/useAnyspendFlow.js +42 -19
- package/dist/cjs/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- package/dist/cjs/anyspend/react/hooks/useAnyspendQuote.js +1 -1
- 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/anyspend/types/api.d.ts +665 -3
- package/dist/cjs/anyspend/utils/orderPayload.js +4 -0
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +10 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/cjs/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
- 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/useAuthentication.d.ts +2 -2
- package/dist/cjs/global-account/react/hooks/useAuthentication.js +7 -2
- 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/cjs/global-account/react/stores/useModalStore.d.ts +31 -1
- 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/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +269 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +7 -6
- package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +285 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- package/dist/esm/anyspend/react/components/AnySpendStakeUpsideExactIn.js +30 -0
- 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 +67 -148
- package/dist/esm/anyspend/react/components/common/OrderDetailsCollapsible.js +2 -3
- 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/components/index.d.ts +5 -1
- package/dist/esm/anyspend/react/components/index.js +5 -1
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/esm/anyspend/react/hooks/useAnyspendFlow.js +42 -19
- package/dist/esm/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- package/dist/esm/anyspend/react/hooks/useAnyspendQuote.js +1 -1
- 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/anyspend/types/api.d.ts +665 -3
- package/dist/esm/anyspend/utils/orderPayload.js +4 -0
- package/dist/esm/global-account/react/components/B3DynamicModal.js +11 -2
- package/dist/esm/global-account/react/components/SignInWithB3/SignInWithB3Flow.js +3 -1
- package/dist/esm/global-account/react/components/SignInWithB3/steps/LoginStep.js +2 -2
- 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/useAuthentication.d.ts +2 -2
- package/dist/esm/global-account/react/hooks/useAuthentication.js +7 -2
- 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/esm/global-account/react/stores/useModalStore.d.ts +31 -1
- package/dist/types/anyspend/react/components/AnySpendCustomExactIn.d.ts +34 -0
- package/dist/types/anyspend/react/components/AnySpendStakeB3ExactIn.d.ts +9 -0
- package/dist/types/anyspend/react/components/AnySpendStakeUpsideExactIn.d.ts +11 -0
- 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/components/index.d.ts +5 -1
- package/dist/types/anyspend/react/hooks/useAnyspendFlow.d.ts +25 -3
- package/dist/types/anyspend/react/hooks/useAnyspendOrderHistory.d.ts +116 -0
- 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/anyspend/types/api.d.ts +665 -3
- package/dist/types/global-account/react/hooks/index.d.ts +2 -1
- package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
- 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/dist/types/global-account/react/stores/useModalStore.d.ts +31 -1
- 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/AnySpendCustomExactIn.tsx +595 -0
- package/src/anyspend/react/components/AnySpendStakeB3.tsx +8 -11
- package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +522 -0
- package/src/anyspend/react/components/AnySpendStakeUpsideExactIn.tsx +73 -0
- 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 +76 -190
- package/src/anyspend/react/components/common/OrderDetailsCollapsible.tsx +2 -3
- 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/components/index.ts +5 -1
- package/src/anyspend/react/hooks/useAnyspendFlow.ts +51 -18
- package/src/anyspend/react/hooks/useAnyspendQuote.ts +1 -1
- 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/anyspend/types/api.ts +669 -1
- package/src/anyspend/utils/orderPayload.ts +5 -1
- package/src/global-account/react/components/B3DynamicModal.tsx +11 -1
- package/src/global-account/react/components/SignInWithB3/SignInWithB3Flow.tsx +3 -1
- package/src/global-account/react/components/SignInWithB3/steps/LoginStep.tsx +2 -2
- package/src/global-account/react/hooks/index.ts +2 -1
- package/src/global-account/react/hooks/useAuthentication.ts +10 -2
- 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
- package/src/global-account/react/stores/useModalStore.ts +34 -0
|
@@ -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 {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useAccountWallet } from "../../../global-account/react/index.js";
|
|
2
|
+
import { useAccount } from "wagmi";
|
|
3
|
+
import { CryptoPaymentMethodType } from "../components/common/CryptoPaymentMethod.js";
|
|
4
|
+
/**
|
|
5
|
+
* Custom hook to determine which wallet to display and its address
|
|
6
|
+
* Handles logic for showing EOA wallet, wagmi wallet, or global wallet based on payment method
|
|
7
|
+
*/
|
|
8
|
+
export function useConnectedWalletDisplay(selectedCryptoPaymentMethod) {
|
|
9
|
+
const { connectedEOAWallet, connectedSmartWallet } = useAccountWallet();
|
|
10
|
+
const { address: wagmiAddress, isConnected: wagmiWalletIsConnected } = useAccount();
|
|
11
|
+
// Helper function to check if two addresses are the same
|
|
12
|
+
const isSameAddress = (addr1, addr2) => {
|
|
13
|
+
if (!addr1 || !addr2)
|
|
14
|
+
return false;
|
|
15
|
+
return addr1.toLowerCase() === addr2.toLowerCase();
|
|
16
|
+
};
|
|
17
|
+
// Check if connectedEOAWallet and wagmi wallet represent the same wallet
|
|
18
|
+
const connectedEOAAddress = connectedEOAWallet?.getAccount()?.address;
|
|
19
|
+
const isWalletDuplicated = isSameAddress(connectedEOAAddress, wagmiAddress);
|
|
20
|
+
// Determine which wallet to show (prefer connectedEOAWallet if both exist and are the same)
|
|
21
|
+
const shouldShowConnectedEOA = !!connectedEOAWallet;
|
|
22
|
+
const shouldShowWagmiWallet = wagmiWalletIsConnected && (!isWalletDuplicated || !connectedEOAWallet);
|
|
23
|
+
// Determine which address to use based on payment method
|
|
24
|
+
let walletAddress;
|
|
25
|
+
if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.GLOBAL_WALLET) {
|
|
26
|
+
walletAddress = connectedSmartWallet?.getAccount()?.address;
|
|
27
|
+
}
|
|
28
|
+
else if (selectedCryptoPaymentMethod === CryptoPaymentMethodType.CONNECT_WALLET) {
|
|
29
|
+
// Prefer connectedEOAWallet, fallback to wagmi wallet
|
|
30
|
+
walletAddress = connectedEOAAddress || wagmiAddress;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
// Default behavior: use connectedEOAWallet if available, otherwise wagmi
|
|
34
|
+
walletAddress = connectedEOAAddress || wagmiAddress;
|
|
35
|
+
}
|
|
36
|
+
// Suggest a payment method based on available wallets
|
|
37
|
+
// Priority: Connected EOA/Wagmi wallet > Global wallet > None
|
|
38
|
+
let suggestedPaymentMethod = CryptoPaymentMethodType.NONE;
|
|
39
|
+
if (connectedEOAAddress || wagmiAddress) {
|
|
40
|
+
// If there's a connected EOA or wagmi wallet, suggest CONNECT_WALLET
|
|
41
|
+
suggestedPaymentMethod = CryptoPaymentMethodType.CONNECT_WALLET;
|
|
42
|
+
}
|
|
43
|
+
else if (connectedSmartWallet?.getAccount()?.address) {
|
|
44
|
+
// If only global wallet is available, suggest that
|
|
45
|
+
suggestedPaymentMethod = CryptoPaymentMethodType.GLOBAL_WALLET;
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
walletAddress,
|
|
49
|
+
shouldShowConnectedEOA,
|
|
50
|
+
shouldShowWagmiWallet,
|
|
51
|
+
isWalletDuplicated,
|
|
52
|
+
suggestedPaymentMethod,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
interface UsePhantomTransferParams {
|
|
2
|
+
/** RPC endpoint URL for Solana network */
|
|
3
|
+
rpcEndpoint?: string;
|
|
4
|
+
}
|
|
5
|
+
interface PhantomTransferParams {
|
|
6
|
+
/** Amount in lamports (for SOL) or smallest token unit (for SPL tokens) */
|
|
7
|
+
amountLamports: string;
|
|
8
|
+
/** Token address (use "11111111111111111111111111111111" for native SOL) */
|
|
9
|
+
tokenAddress: string;
|
|
10
|
+
/** Recipient address */
|
|
11
|
+
recipientAddress: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Custom hook for handling Phantom wallet transfers on Solana.
|
|
15
|
+
* Supports both native SOL and SPL token transfers with automatic priority fee calculation.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const { initiateTransfer, isPhantomAvailable } = usePhantomTransfer();
|
|
20
|
+
*
|
|
21
|
+
* await initiateTransfer({
|
|
22
|
+
* amountLamports: "1000000000", // 1 SOL
|
|
23
|
+
* tokenAddress: "11111111111111111111111111111111",
|
|
24
|
+
* recipientAddress: "..."
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function usePhantomTransfer({ rpcEndpoint }?: UsePhantomTransferParams): {
|
|
29
|
+
/** Function to initiate a transfer */
|
|
30
|
+
initiateTransfer: ({ amountLamports, tokenAddress, recipientAddress }: PhantomTransferParams) => Promise<void>;
|
|
31
|
+
/** Whether Phantom wallet is available (installed) */
|
|
32
|
+
isPhantomAvailable: any;
|
|
33
|
+
/** Get the currently connected Phantom wallet address */
|
|
34
|
+
getConnectedAddress: () => any;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createAssociatedTokenAccountInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, } from "@solana/spl-token";
|
|
2
|
+
import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { toast } from "sonner";
|
|
5
|
+
/**
|
|
6
|
+
* Custom hook for handling Phantom wallet transfers on Solana.
|
|
7
|
+
* Supports both native SOL and SPL token transfers with automatic priority fee calculation.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const { initiateTransfer, isPhantomAvailable } = usePhantomTransfer();
|
|
12
|
+
*
|
|
13
|
+
* await initiateTransfer({
|
|
14
|
+
* amountLamports: "1000000000", // 1 SOL
|
|
15
|
+
* tokenAddress: "11111111111111111111111111111111",
|
|
16
|
+
* recipientAddress: "..."
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function usePhantomTransfer({ rpcEndpoint } = {}) {
|
|
21
|
+
// Default RPC endpoint
|
|
22
|
+
const defaultRpcEndpoint = "https://mainnet.helius-rpc.com/?api-key=efafd9b3-1807-4cf8-8aa4-3d984f56d8fb";
|
|
23
|
+
const effectiveRpcEndpoint = rpcEndpoint || defaultRpcEndpoint;
|
|
24
|
+
// Check for Phantom wallet availability
|
|
25
|
+
const isPhantomMobile = useMemo(() => navigator.userAgent.includes("Phantom"), []);
|
|
26
|
+
const isPhantomBrowser = useMemo(() => window.phantom?.solana?.isPhantom, []);
|
|
27
|
+
const isPhantomAvailable = isPhantomMobile || isPhantomBrowser;
|
|
28
|
+
/**
|
|
29
|
+
* Get the connected Phantom wallet address if available
|
|
30
|
+
*/
|
|
31
|
+
const getConnectedAddress = useCallback(() => {
|
|
32
|
+
const phantom = window.phantom?.solana;
|
|
33
|
+
if (phantom?.isConnected && phantom?.publicKey) {
|
|
34
|
+
return phantom.publicKey.toString();
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}, []);
|
|
38
|
+
/**
|
|
39
|
+
* Calculate optimal priority fee based on recent network activity
|
|
40
|
+
*/
|
|
41
|
+
const calculatePriorityFee = useCallback(async (connection, fromPubkey) => {
|
|
42
|
+
let priorityFee = 10000; // Default fallback (10,000 micro-lamports)
|
|
43
|
+
try {
|
|
44
|
+
const recentFees = await connection.getRecentPrioritizationFees({
|
|
45
|
+
lockedWritableAccounts: [fromPubkey],
|
|
46
|
+
});
|
|
47
|
+
if (recentFees && recentFees.length > 0) {
|
|
48
|
+
// Use 75th percentile of recent fees for good priority
|
|
49
|
+
const sortedFees = recentFees.map(fee => fee.prioritizationFee).sort((a, b) => a - b);
|
|
50
|
+
const percentile75Index = Math.floor(sortedFees.length * 0.75);
|
|
51
|
+
priorityFee = Math.max(sortedFees[percentile75Index] || 10000, 10000);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (feeError) {
|
|
55
|
+
console.warn("Failed to fetch recent priority fees, using default:", feeError);
|
|
56
|
+
}
|
|
57
|
+
return priorityFee;
|
|
58
|
+
}, []);
|
|
59
|
+
/**
|
|
60
|
+
* Create a native SOL transfer transaction with priority fees
|
|
61
|
+
*/
|
|
62
|
+
const createNativeTransferTransaction = useCallback(async (_connection, fromPubkey, toPubkey, amount, priorityFee) => {
|
|
63
|
+
const computeUnitLimit = 1000; // SOL transfer + compute budget instructions need ~600-800 CU
|
|
64
|
+
const computeUnitPrice = Math.min(priorityFee, 100000); // Cap at 100k micro-lamports for safety
|
|
65
|
+
const transaction = new Transaction()
|
|
66
|
+
.add(
|
|
67
|
+
// Set compute unit limit first (must come before other instructions)
|
|
68
|
+
ComputeBudgetProgram.setComputeUnitLimit({
|
|
69
|
+
units: computeUnitLimit,
|
|
70
|
+
}))
|
|
71
|
+
.add(
|
|
72
|
+
// Set priority fee
|
|
73
|
+
ComputeBudgetProgram.setComputeUnitPrice({
|
|
74
|
+
microLamports: computeUnitPrice,
|
|
75
|
+
}))
|
|
76
|
+
.add(
|
|
77
|
+
// Actual transfer instruction
|
|
78
|
+
SystemProgram.transfer({
|
|
79
|
+
fromPubkey,
|
|
80
|
+
toPubkey,
|
|
81
|
+
lamports: Number(amount),
|
|
82
|
+
}));
|
|
83
|
+
console.log(`Using priority fee: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU`);
|
|
84
|
+
return transaction;
|
|
85
|
+
}, []);
|
|
86
|
+
/**
|
|
87
|
+
* Create an SPL token transfer transaction with priority fees
|
|
88
|
+
*/
|
|
89
|
+
const createSPLTransferTransaction = useCallback(async (connection, fromPubkey, toPubkey, mintPubkey, amount, priorityFee) => {
|
|
90
|
+
// Get associated token accounts
|
|
91
|
+
const fromTokenAccount = getAssociatedTokenAddressSync(mintPubkey, fromPubkey);
|
|
92
|
+
const toTokenAccount = getAssociatedTokenAddressSync(mintPubkey, toPubkey);
|
|
93
|
+
// Check if destination token account exists
|
|
94
|
+
const toTokenAccountInfo = await connection.getAccountInfo(toTokenAccount);
|
|
95
|
+
const needsDestinationAccount = !toTokenAccountInfo;
|
|
96
|
+
// Get mint info to determine decimals
|
|
97
|
+
const mintInfo = await connection.getParsedAccountInfo(mintPubkey);
|
|
98
|
+
const decimals = mintInfo.value?.data?.parsed?.info?.decimals || 9;
|
|
99
|
+
// SPL transfers need more compute units than SOL transfers
|
|
100
|
+
// Add extra CU if we need to create destination account
|
|
101
|
+
const computeUnitLimit = needsDestinationAccount ? 40000 : 20000;
|
|
102
|
+
const computeUnitPrice = Math.min(priorityFee, 100000);
|
|
103
|
+
// Create transfer instruction
|
|
104
|
+
const transferInstruction = createTransferCheckedInstruction(fromTokenAccount, mintPubkey, toTokenAccount, fromPubkey, Number(amount), decimals);
|
|
105
|
+
const transaction = new Transaction()
|
|
106
|
+
.add(ComputeBudgetProgram.setComputeUnitLimit({
|
|
107
|
+
units: computeUnitLimit,
|
|
108
|
+
}))
|
|
109
|
+
.add(ComputeBudgetProgram.setComputeUnitPrice({
|
|
110
|
+
microLamports: computeUnitPrice,
|
|
111
|
+
}));
|
|
112
|
+
// Add create destination account instruction if needed
|
|
113
|
+
if (needsDestinationAccount) {
|
|
114
|
+
transaction.add(createAssociatedTokenAccountInstruction(fromPubkey, // payer
|
|
115
|
+
toTokenAccount, // ata
|
|
116
|
+
toPubkey, // owner
|
|
117
|
+
mintPubkey));
|
|
118
|
+
}
|
|
119
|
+
// Add the transfer instruction
|
|
120
|
+
transaction.add(transferInstruction);
|
|
121
|
+
console.log(`SPL Token transfer: ${computeUnitPrice} micro-lamports per CU, limit: ${computeUnitLimit} CU, creating destination: ${needsDestinationAccount}`);
|
|
122
|
+
return transaction;
|
|
123
|
+
}, []);
|
|
124
|
+
/**
|
|
125
|
+
* Initiate a Phantom wallet transfer for SOL or SPL tokens
|
|
126
|
+
*/
|
|
127
|
+
const initiateTransfer = useCallback(async ({ amountLamports, tokenAddress, recipientAddress }) => {
|
|
128
|
+
try {
|
|
129
|
+
// Step 1: Check if Phantom is installed
|
|
130
|
+
if (!isPhantomAvailable) {
|
|
131
|
+
toast.error("Phantom wallet not installed. Please install Phantom wallet to continue.");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Step 2: Ensure Phantom is connected/unlocked
|
|
135
|
+
const phantom = window.phantom?.solana;
|
|
136
|
+
if (!phantom) {
|
|
137
|
+
toast.error("Phantom wallet not accessible");
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Connect and unlock wallet if needed
|
|
141
|
+
let publicKey;
|
|
142
|
+
try {
|
|
143
|
+
const connection = await phantom.connect();
|
|
144
|
+
publicKey = connection.publicKey;
|
|
145
|
+
}
|
|
146
|
+
catch (connectError) {
|
|
147
|
+
toast.error("Failed to connect to Phantom wallet");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
// Step 3: Setup connection and public keys
|
|
151
|
+
const connection = new Connection(effectiveRpcEndpoint);
|
|
152
|
+
const fromPubkey = new PublicKey(publicKey.toString());
|
|
153
|
+
const toPubkey = new PublicKey(recipientAddress);
|
|
154
|
+
const amount = BigInt(amountLamports);
|
|
155
|
+
// Step 4: Calculate optimal priority fee
|
|
156
|
+
const priorityFee = await calculatePriorityFee(connection, fromPubkey);
|
|
157
|
+
// Step 5: Create transaction based on token type
|
|
158
|
+
let transaction;
|
|
159
|
+
if (tokenAddress === "11111111111111111111111111111111") {
|
|
160
|
+
// Native SOL transfer
|
|
161
|
+
transaction = await createNativeTransferTransaction(connection, fromPubkey, toPubkey, amount, priorityFee);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// SPL Token transfer
|
|
165
|
+
const mintPubkey = new PublicKey(tokenAddress);
|
|
166
|
+
transaction = await createSPLTransferTransaction(connection, fromPubkey, toPubkey, mintPubkey, amount, priorityFee);
|
|
167
|
+
}
|
|
168
|
+
// Step 6: Get latest blockhash and set fee payer
|
|
169
|
+
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
170
|
+
transaction.recentBlockhash = blockhash;
|
|
171
|
+
transaction.feePayer = fromPubkey;
|
|
172
|
+
// Step 7: Sign and send transaction
|
|
173
|
+
const signedTransaction = await phantom.signAndSendTransaction(transaction);
|
|
174
|
+
toast.success(`Transaction successful! Signature: ${signedTransaction.signature}`);
|
|
175
|
+
console.log("Transaction sent with priority fees. Signature:", signedTransaction.signature);
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error("Transfer error:", error);
|
|
179
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
180
|
+
if (errorMessage.includes("User rejected")) {
|
|
181
|
+
toast.error("Transaction was cancelled by user");
|
|
182
|
+
}
|
|
183
|
+
else if (errorMessage.includes("insufficient")) {
|
|
184
|
+
toast.error("Insufficient balance for this transaction");
|
|
185
|
+
}
|
|
186
|
+
else if (errorMessage.includes("blockhash not found")) {
|
|
187
|
+
toast.error("Network congestion detected. Please try again in a moment.");
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
toast.error(`Transfer failed: ${errorMessage}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}, [
|
|
194
|
+
isPhantomAvailable,
|
|
195
|
+
effectiveRpcEndpoint,
|
|
196
|
+
calculatePriorityFee,
|
|
197
|
+
createNativeTransferTransaction,
|
|
198
|
+
createSPLTransferTransaction,
|
|
199
|
+
]);
|
|
200
|
+
return {
|
|
201
|
+
/** Function to initiate a transfer */
|
|
202
|
+
initiateTransfer,
|
|
203
|
+
/** Whether Phantom wallet is available (installed) */
|
|
204
|
+
isPhantomAvailable,
|
|
205
|
+
/** Get the currently connected Phantom wallet address */
|
|
206
|
+
getConnectedAddress,
|
|
207
|
+
};
|
|
208
|
+
}
|