package tokens import ( "math" "math/big" "github.com/ethereum/go-ethereum/common" ) // Common token addresses on Arbitrum var ( WETH = common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1") USDC = common.HexToAddress("0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8") USDT = common.HexToAddress("0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9") WBTC = common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f") ARB = common.HexToAddress("0x912CE59144191C1204E64559FE8253a0e49E6548") DAI = common.HexToAddress("0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1") LINK = common.HexToAddress("0xf97f4df75117a78c1A5a0DBb814Af92458539FB4") UNI = common.HexToAddress("0xFa7F8980b0f1E64A2062791cc3b0871572f1F7f0") GMX = common.HexToAddress("0xfc5A1A6EB076a2C7aD06eD22C90d7E710E35ad0a") MAGIC = common.HexToAddress("0x539bdE0d7Dbd336b79148AA742883198BBF60342") ) // TokenDecimals maps token addresses to their decimal places var TokenDecimals = map[common.Address]uint8{ WETH: 18, USDC: 6, USDT: 6, WBTC: 8, ARB: 18, DAI: 18, LINK: 18, UNI: 18, GMX: 18, MAGIC: 18, } // GetTokenDecimals returns the decimal places for a token, defaulting to 18 if unknown func GetTokenDecimals(tokenAddress common.Address) uint8 { if decimals, ok := TokenDecimals[tokenAddress]; ok { return decimals } // Default to 18 decimals for unknown tokens (most ERC20 tokens use 18) return 18 } // ConvertToFloat converts a token amount to a float64 considering its decimals func ConvertToFloat(amount *big.Int, tokenAddress common.Address) float64 { if amount == nil { return 0 } decimals := GetTokenDecimals(tokenAddress) divisor := math.Pow(10, float64(decimals)) amountFloat := new(big.Float).SetInt(amount) divisorFloat := big.NewFloat(divisor) result := new(big.Float).Quo(amountFloat, divisorFloat) val, _ := result.Float64() return val } // ConvertToBigFloat converts a token amount to a *big.Float considering its decimals func ConvertToBigFloat(amount *big.Int, tokenAddress common.Address) *big.Float { if amount == nil { return big.NewFloat(0) } decimals := GetTokenDecimals(tokenAddress) divisor := math.Pow(10, float64(decimals)) amountFloat := new(big.Float).SetInt(amount) divisorFloat := big.NewFloat(divisor) return new(big.Float).Quo(amountFloat, divisorFloat) } // ConvertFromFloat converts a float64 to the token's smallest unit (*big.Int) func ConvertFromFloat(amount float64, tokenAddress common.Address) *big.Int { decimals := GetTokenDecimals(tokenAddress) multiplier := math.Pow(10, float64(decimals)) // Convert to smallest unit rawAmount := amount * multiplier // Convert to big.Int amountBig := new(big.Float).SetFloat64(rawAmount) result, _ := amountBig.Int(nil) return result } // NormalizeAmount converts an amount from one token's decimals to another's func NormalizeAmount(amount *big.Int, fromToken, toToken common.Address) *big.Int { if amount == nil { return big.NewInt(0) } fromDecimals := GetTokenDecimals(fromToken) toDecimals := GetTokenDecimals(toToken) if fromDecimals == toDecimals { return new(big.Int).Set(amount) } // Convert to float with proper decimals amountFloat := ConvertToBigFloat(amount, fromToken) // Convert back with target token decimals toMultiplier := math.Pow(10, float64(toDecimals)) multiplierBig := big.NewFloat(toMultiplier) result := new(big.Float).Mul(amountFloat, multiplierBig) resultInt, _ := result.Int(nil) return resultInt } // GetMinimumTradeAmount returns the minimum trade amount for a token // This helps avoid dust trades that aren't economically viable func GetMinimumTradeAmount(tokenAddress common.Address) *big.Int { // Note: decimals are handled in ConvertFromFloat // Minimum amounts in token units (not smallest units) var minAmount float64 switch tokenAddress { case USDC, USDT: minAmount = 10.0 // $10 minimum for stablecoins case WBTC: minAmount = 0.0001 // 0.0001 BTC minimum case WETH, ARB, UNI, LINK, GMX: minAmount = 0.01 // 0.01 token minimum default: minAmount = 0.01 // Default minimum } return ConvertFromFloat(minAmount, tokenAddress) } // FormatTokenAmount formats a token amount for display func FormatTokenAmount(amount *big.Int, tokenAddress common.Address, precision int) string { if amount == nil { return "0" } amountFloat := ConvertToBigFloat(amount, tokenAddress) return amountFloat.Text('f', precision) }