nac-presale/client/src/lib/contracts.ts

233 lines
7.7 KiB
TypeScript

// NAC XIC Token Presale — Contract Configuration
// Design: Dark Cyberpunk / Quantum Finance
// Colors: Amber Gold #f0b429, Quantum Blue #00d4ff, Deep Black #0a0a0f
// ============================================================
// CONTRACT ADDRESSES
// ============================================================
export const CONTRACTS = {
// BSC Mainnet (Chain ID: 56)
BSC: {
chainId: 56,
chainName: "BNB Smart Chain",
rpcUrl: "https://bsc-dataseed1.binance.org/",
explorerUrl: "https://bscscan.com",
nativeCurrency: { name: "BNB", symbol: "BNB", decimals: 18 },
presale: "0x5953c025dA734e710886916F2d739A3A78f8bbc4", // XICPresale v2 — 购买即时发放
token: "0x59FF34dD59680a7125782b1f6df2A86ed46F5A24",
usdt: "0x55d398326f99059fF775485246999027B3197955",
},
// Ethereum Mainnet (Chain ID: 1)
ETH: {
chainId: 1,
chainName: "Ethereum",
rpcUrl: "https://eth.llamarpc.com",
explorerUrl: "https://etherscan.io",
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
presale: "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
token: "", // XIC not yet on ETH
usdt: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
},
// TRON (TRC20) — Manual transfer
TRON: {
chainId: 0, // Not EVM
chainName: "TRON",
explorerUrl: "https://tronscan.org",
presale: "", // TRC20 uses manual transfer
token: "",
usdt: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
// Receiving wallet for TRC20 USDT
receivingWallet: "TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp",
},
} as const;
// ============================================================
// PRESALE PARAMETERS
// ============================================================
export const PRESALE_CONFIG = {
tokenPrice: 0.02, // $0.02 per XIC
tokenSymbol: "XIC",
tokenName: "New AssetChain Token",
tokenDecimals: 18,
minPurchaseUSDT: 0, // No minimum purchase limit
maxPurchaseUSDT: 50000, // Max $50,000 USDT per purchase
totalSupply: 100_000_000_000, // 100 billion XIC
presaleAllocation: 2_500_000_000, // 2.5 billion for presale (25亿)
// TRC20 memo format
trc20Memo: "XIC_PRESALE",
};
// ============================================================
// PRESALE CONTRACT ABI (BSC & ETH — same interface)
// ============================================================
export const PRESALE_ABI = [
// Read functions
{
"inputs": [],
"name": "tokenPrice",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalTokensSold",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalRaised",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "presaleActive",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "hardCap",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "user", "type": "address" }],
"name": "userPurchases",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
// Write functions
{
"inputs": [{ "internalType": "uint256", "name": "usdtAmount", "type": "uint256" }],
"name": "buyTokensWithUSDT",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "buyTokens",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
// Events
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "buyer", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "usdtAmount", "type": "uint256" },
{ "indexed": false, "internalType": "uint256", "name": "tokenAmount", "type": "uint256" }
],
"name": "TokensPurchased",
"type": "event"
}
] as const;
// ============================================================
// ERC20 USDT ABI (minimal — approve + allowance + balanceOf)
// ============================================================
export const ERC20_ABI = [
{
"inputs": [
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" }
],
"name": "approve",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" }
],
"name": "allowance",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
"name": "balanceOf",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
"stateMutability": "view",
"type": "function"
}
] as const;
// ============================================================
// NETWORK SWITCH HELPER
// ============================================================
export async function switchToNetwork(chainId: number): Promise<void> {
if (!window.ethereum) throw new Error("No wallet detected");
const hexChainId = "0x" + chainId.toString(16);
try {
await window.ethereum.request({
method: "wallet_switchEthereumChain",
params: [{ chainId: hexChainId }],
});
} catch (err: unknown) {
// Chain not added yet — add it
if ((err as { code?: number }).code === 4902) {
const network = Object.values(CONTRACTS).find(n => n.chainId === chainId);
if (!network || !("rpcUrl" in network)) throw new Error("Unknown network");
await window.ethereum.request({
method: "wallet_addEthereumChain",
params: [{
chainId: hexChainId,
chainName: network.chainName,
rpcUrls: [(network as { rpcUrl: string }).rpcUrl],
nativeCurrency: (network as { nativeCurrency: { name: string; symbol: string; decimals: number } }).nativeCurrency,
blockExplorerUrls: [network.explorerUrl],
}],
});
} else {
throw err;
}
}
}
// ============================================================
// FORMAT HELPERS
// ============================================================
export function formatNumber(n: number, decimals = 2): string {
if (n >= 1_000_000_000) return (n / 1_000_000_000).toFixed(decimals) + "B";
if (n >= 1_000_000) return (n / 1_000_000).toFixed(decimals) + "M";
if (n >= 1_000) return (n / 1_000).toFixed(decimals) + "K";
return n.toFixed(decimals);
}
export function shortenAddress(addr: string): string {
if (!addr) return "";
return addr.slice(0, 6) + "..." + addr.slice(-4);
}
// Declare window.ethereum for TypeScript
declare global {
interface Window {
ethereum?: {
request: (args: { method: string; params?: unknown[] }) => Promise<unknown>;
on: (event: string, handler: (...args: unknown[]) => void) => void;
removeListener: (event: string, handler: (...args: unknown[]) => void) => void;
isMetaMask?: boolean;
};
}
}