import { ethers } from "ethers"; import { G$Decimals, SupportedChains } from "../constants"; export const BRIDGE_CONSTANTS = { DEFAULT_CHAIN_ID: 42220, DEBOUNCE_DELAY: 300, POLLING_INTERVAL: 5000, BRIDGE_ID_TOPIC_INDEX: 6, PRODUCTION_GDOLLAR_ADDRESS: "0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A", GDOLLAR_ADDRESSES: { [SupportedChains.FUSE]: "0x495d133B938596C9984d462F007B676bDc57eCEC", [SupportedChains.CELO]: "0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A", [SupportedChains.MAINNET]: "0x67c5870b4a41d4ebef24d2456547a03f1f3e094b", [SupportedChains.XDC]: "0xEC2136843a983885AebF2feB3931F73A8eBEe50c" } } as const; export const VALIDATION_REASONS = { MIN_AMOUNT: "minAmount", MAX_AMOUNT: "maxAmount", CANNOT_BRIDGE: "cannotBridge", ERROR: "error", INSUFFICIENT_BALANCE: "insufficientBalance", INVALID_CHAIN: "invalidChain" } as const; export const ERROR_MESSAGES = { BRIDGE_LIMITS_ERROR: "Something went wrong while determining transaction limits. Please try again later.", BRIDGE_ELIGIBILITY_ERROR: "Unable to verify bridge eligibility. Please try again later.", BRIDGE_SERVICE_UNAVAILABLE: "Bridge service is currently unavailable. Please try again later.", BRIDGE_FEES_ERROR: "Failed to fetch bridge fees. Please try again later.", BRIDGE_FEES_UNAVAILABLE: "Bridge fees not available for this route", TRANSACTION_LIMITS_UNAVAILABLE: "Transaction limits unavailable", UNEXPECTED_ERROR: "An unexpected error occurred. Please try again later." } as const; /** * Chain name mappings for better readability */ export const CHAIN_NAMES = { FUSE: "fuse", CELO: "celo", MAINNET: "mainnet", XDC: "xdc" } as const; /** * Bridge provider types */ export const BRIDGE_PROVIDERS = { LAYERZERO: "layerzero", AXELAR: "axelar" } as const; // Types for better type safety export type ChainName = "fuse" | "celo" | "mainnet" | "xdc"; export type BridgeProvider = "axelar" | "layerzero"; export type ValidationReason = | "minAmount" | "maxAmount" | "cannotBridge" | "error" | "insufficientBalance" | "invalidChain"; export const VALIDATION_ERROR_MESSAGES: Record = { minAmount: "Minimum amount is 1000 G$", maxAmount: "Amount exceeds maximum limit", cannotBridge: "Bridge not available for this amount", error: "Invalid amount", insufficientBalance: "Insufficient balance", invalidChain: "Invalid chain selection" } as const; export const CHAIN_MAPPING: Record = { [SupportedChains.FUSE]: "fuse", [SupportedChains.CELO]: "celo", [SupportedChains.MAINNET]: "mainnet", [SupportedChains.XDC]: "xdc" } as const; export const REVERSE_CHAIN_MAPPING: Record = { fuse: SupportedChains.FUSE, celo: SupportedChains.CELO, mainnet: SupportedChains.MAINNET, xdc: SupportedChains.XDC } as const; export const NATIVE_TOKEN_SYMBOLS: Record = { fuse: "FUSE", celo: "CELO", mainnet: "ETH", xdc: "XDC" } as const; export const FEE_ROUTES: Record> = { [BRIDGE_PROVIDERS.AXELAR]: { CELO_MAINNET: "AXL_CELO_TO_ETH", MAINNET_CELO: "AXL_ETH_TO_CELO" }, [BRIDGE_PROVIDERS.LAYERZERO]: { CELO_FUSE: "LZ_CELO_TO_FUSE", FUSE_CELO: "LZ_FUSE_TO_CELO", CELO_MAINNET: "LZ_CELO_TO_ETH", MAINNET_CELO: "LZ_ETH_TO_CELO", FUSE_MAINNET: "LZ_FUSE_TO_ETH", MAINNET_FUSE: "LZ_ETH_TO_FUSE", XDC_FUSE: "LZ_XDC_TO_FUSE", FUSE_XDC: "LZ_FUSE_TO_XDC", XDC_CELO: "LZ_XDC_TO_CELO", CELO_XDC: "LZ_CELO_TO_XDC", XDC_MAINNET: "LZ_XDC_TO_ETH", MAINNET_XDC: "LZ_ETH_TO_XDC" } } as const; const CONTRACT_DECIMALS = 18; export const normalizeAmountTo18 = (amount: ethers.BigNumber, sourceChainId: number): ethers.BigNumber => { const decimals = G$Decimals["G$"][sourceChainId as SupportedChains] ?? CONTRACT_DECIMALS; if (decimals < CONTRACT_DECIMALS) { return amount.mul(ethers.BigNumber.from(10).pow(CONTRACT_DECIMALS - decimals)); } if (decimals > CONTRACT_DECIMALS) { return amount.div(ethers.BigNumber.from(10).pow(decimals - CONTRACT_DECIMALS)); } return amount; }; export const resetStates = (setLoading: (loading: boolean) => void, setError: (error: string | null) => void) => { setLoading(true); setError(null); }; export const createEmptyBridgeFees = () => ({ nativeFee: null as ethers.BigNumber | null, zroFee: null as ethers.BigNumber | null }); export const handleError = ( error: any, operation: string, setError: (error: string) => void, setState?: (value: any) => void, fallbackValue?: any ) => { console.error(`Error during ${operation}:`, error); setError(error?.message || `Error during ${operation}`); if (setState && fallbackValue !== undefined) { setState(fallbackValue); } }; export const getChainName = (chainId: number): ChainName => { return CHAIN_MAPPING[chainId] || "mainnet"; }; export const getChainId = (chainName: string): number => { const normalizedName = chainName.toLowerCase() as ChainName; return REVERSE_CHAIN_MAPPING[normalizedName] || SupportedChains.MAINNET; }; export const getTargetChainId = (chainName: string): number => { return getChainId(chainName); }; export const getSourceChainId = (chainName: string): number => { return getChainId(chainName); }; export const isSupportedChain = (chainId: number): boolean => { return Object.values(SupportedChains).includes(chainId); }; export const getFeeString = ( fees: any, provider: BridgeProvider, sourceChain: string, targetChain: string ): string | null => { const route = `${sourceChain.toUpperCase()}_${targetChain.toUpperCase()}`; const feeKey = FEE_ROUTES[provider]?.[route]; // Handle case mismatch between provider key and API response const providerKey = provider.toUpperCase(); return feeKey ? fees[providerKey]?.[feeKey] : null; }; export const getNativeTokenSymbol = (chainName: string): string => { const normalizedName = chainName.toLowerCase() as ChainName; return NATIVE_TOKEN_SYMBOLS[normalizedName] || chainName.toUpperCase(); }; export const parseFeeToWei = (feeString: string): ethers.BigNumber => { const feeValue = parseFloat(feeString.split(" ")[0]); return ethers.BigNumber.from(ethers.utils.parseEther(feeValue.toString())); }; export const calculateBridgeFees = ( fees: any, provider: BridgeProvider, sourceChain: string, targetChain: string ): { nativeFee: ethers.BigNumber | null; zroFee: ethers.BigNumber | null } => { const feeString = getFeeString(fees, provider, sourceChain, targetChain); if (!feeString || typeof feeString !== "string") { return { nativeFee: null, zroFee: null }; } try { const nativeFee = parseFeeToWei(feeString); return { nativeFee, zroFee: ethers.BigNumber.from(0) }; } catch (error) { console.error("Error parsing fee:", error); return { nativeFee: null, zroFee: null }; } };