// NAC XIC Presale — Purchase Logic Hook // Handles BSC USDT, ETH USDT purchase flows import { useState, useCallback } from "react"; import { Contract, parseUnits, formatUnits } from "ethers"; import { CONTRACTS, PRESALE_ABI, ERC20_ABI, PRESALE_CONFIG } from "@/lib/contracts"; import { WalletState } from "./useWallet"; export type PurchaseStep = | "idle" | "approving" | "approved" | "purchasing" | "success" | "error"; // All 6 steps are valid export interface PurchaseState { step: PurchaseStep; txHash: string | null; error: string | null; tokenAmount: number; } export function usePresale(wallet: WalletState, network: "BSC" | "ETH") { const [purchaseState, setPurchaseState] = useState({ step: "idle", txHash: null, error: null, tokenAmount: 0, }); const networkConfig = CONTRACTS[network]; const buyWithUSDT = useCallback( async (usdtAmount: number) => { if (!wallet.signer || !wallet.address) { setPurchaseState(s => ({ ...s, step: "error", error: "Please connect your wallet first." })); return; } const tokenAmount = usdtAmount / PRESALE_CONFIG.tokenPrice; setPurchaseState({ step: "approving", txHash: null, error: null, tokenAmount }); try { // USDT on BSC has 18 decimals, on ETH has 6 decimals const usdtDecimals = network === "ETH" ? 6 : 18; const usdtAmountWei = parseUnits(usdtAmount.toString(), usdtDecimals); // Step 1: Approve USDT spending const usdtContract = new Contract(networkConfig.usdt, ERC20_ABI, wallet.signer); const presaleAddress = networkConfig.presale; // Check current allowance const currentAllowance = await usdtContract.allowance(wallet.address, presaleAddress); if (currentAllowance < usdtAmountWei) { const approveTx = await usdtContract.approve(presaleAddress, usdtAmountWei); await approveTx.wait(); } setPurchaseState(s => ({ ...s, step: "approved" })); // Step 2: Buy tokens const presaleContract = new Contract(presaleAddress, PRESALE_ABI, wallet.signer); const buyTx = await presaleContract.buyTokensWithUSDT(usdtAmountWei); setPurchaseState(s => ({ ...s, step: "purchasing", txHash: buyTx.hash })); await buyTx.wait(); setPurchaseState(s => ({ ...s, step: "success" })); } catch (err: unknown) { const errMsg = (err as { reason?: string; message?: string }).reason || (err as Error).message || "Transaction failed"; setPurchaseState(s => ({ ...s, step: "error", error: errMsg })); } }, [wallet, network, networkConfig] ); const reset = useCallback(() => { setPurchaseState({ step: "idle", txHash: null, error: null, tokenAmount: 0 }); }, []); // Calculate token amount from USDT input const calcTokens = (usdtAmount: number): number => { return usdtAmount / PRESALE_CONFIG.tokenPrice; }; // Get user's USDT balance const getUsdtBalance = useCallback(async (): Promise => { if (!wallet.provider || !wallet.address) return 0; try { const usdtDecimals = network === "ETH" ? 6 : 18; const usdtContract = new Contract(networkConfig.usdt, ERC20_ABI, wallet.provider); const balance = await usdtContract.balanceOf(wallet.address); return parseFloat(formatUnits(balance, usdtDecimals)); } catch { return 0; } }, [wallet, network, networkConfig]); return { purchaseState, buyWithUSDT, reset, calcTokens, getUsdtBalance, }; }