/** * Authentication and Authorization Utilities * Security-critical: Founder wallet detection and permissions */ // DEPRECATED: Hardcoded founder address (kept for fallback only) // Modern approach: Fetch sudo key dynamically from blockchain export const FOUNDER_ADDRESS_FALLBACK = '5CyuFfbF95rzBxru7c9yEsX4XmQXUxpLUcbj9RLg9K1cGiiF'; /** * Treasury and admin accounts that have elevated permissions * These accounts can perform admin operations like creating pools, minting tokens, etc. */ export const TREASURY_ADDRESSES = [ '5EhCpn82QtdU53MF6PoNFrKHgSrsfcAxFTMwrn3JYf9dioQw', // Treasury 1 - wUSDT issuer/admin ] as const; /** * Check if given address is the sudo account (admin/founder) or a treasury account * SECURITY: Only allows admin access when connected to blockchain with valid sudo key, * or if the address is a known treasury account * @param address - Substrate address to check * @param sudoKey - Sudo key fetched from blockchain (REQUIRED for admin access) * @returns true if address matches sudo key from blockchain or is a treasury account */ export const isFounderWallet = ( address: string | null | undefined, sudoKey?: string | null ): boolean => { if (!address) return false; // Check if address is a known treasury account if (TREASURY_ADDRESSES.includes(address as typeof TREASURY_ADDRESSES[number])) { return true; } // SECURITY FIX: ONLY use dynamic sudo key from blockchain // No fallback to hardcoded address - admin access requires active blockchain connection if (!sudoKey || sudoKey === '') { return false; // No blockchain connection = no admin access } return address === sudoKey; }; /** * Validate substrate address format * @param address - Address to validate * @returns true if address is valid format */ export const isValidSubstrateAddress = (address: string): boolean => { // Substrate addresses start with 5 and are 47-48 characters return /^5[a-zA-Z0-9]{46,47}$/.test(address); }; /** * Permission levels for DEX operations */ export enum DexPermission { // Anyone can perform these VIEW_POOLS = 'view_pools', ADD_LIQUIDITY = 'add_liquidity', REMOVE_LIQUIDITY = 'remove_liquidity', SWAP = 'swap', // Only founder can perform these CREATE_POOL = 'create_pool', SET_FEE_RATE = 'set_fee_rate', PAUSE_POOL = 'pause_pool', WHITELIST_TOKEN = 'whitelist_token', } /** * Check if user has permission for a specific DEX operation * @param address - User's wallet address * @param permission - Required permission level * @param sudoKey - Sudo key fetched from blockchain (if available) * @returns true if user has permission */ export const hasPermission = ( address: string | null | undefined, permission: DexPermission, sudoKey?: string | null ): boolean => { if (!address || !isValidSubstrateAddress(address)) { return false; } const founderOnly = [ DexPermission.CREATE_POOL, DexPermission.SET_FEE_RATE, DexPermission.PAUSE_POOL, DexPermission.WHITELIST_TOKEN, ]; // Founder-only operations if (founderOnly.includes(permission)) { return isFounderWallet(address, sudoKey); } // Everyone can view and trade return true; }; /** * Get user role string for display * @param address - User's wallet address * @param sudoKey - Sudo key fetched from blockchain (if available) * @returns Human-readable role */ export const getUserRole = ( address: string | null | undefined, sudoKey?: string | null ): string => { if (!address) return 'Guest'; if (address === sudoKey) return 'Sudo (Admin)'; if (TREASURY_ADDRESSES.includes(address as typeof TREASURY_ADDRESSES[number])) return 'Treasury (Admin)'; return 'User'; };