Files
pwap/shared/utils/auth.ts
T
pezkuwichain cd6da00ef8 fix: DEX improvements - Asset Hub balance fetching and Treasury admin support
- WalletContext: Use Asset Hub API for PEZ/wHEZ/wUSDT balance queries
- AddLiquidityModal: Handle native token balance via system.account
- auth.ts: Add Treasury accounts to admin list for pool creation and minting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 23:27:36 +03:00

120 lines
3.6 KiB
TypeScript

/**
* 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 = '5GgTgG9sRmPQAYU1RsTejZYnZRjwzKZKWD3awtuqjHioki45';
/**
* 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';
};