Skip to main content

evm-token-decimals

Prevent silent decimal mismatch bugs across EVM chains. Covers runtime decimal lookup, chain-aware caching, bridged-token precision drift, and safe normalization for bots, dashboards, and DeFi tools. Use when handling token amounts across EVM chains, or when a balance, price, or transfer amount is off by orders of magnitude.

Ir a la instalación

Datos de origen

Repositorio
affaan-m/ECC
Última actividad en el origen
12 de agosto de 2026 a las 03:58
Idioma detectado de SKILL.md
inglés
Estrellas
264.820
Forks
39.570

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
evm-token-decimals
description
Prevent silent decimal mismatch bugs across EVM chains. Covers runtime decimal lookup, chain-aware caching, bridged-token precision drift, and safe normalization for bots, dashboards, and DeFi tools. Use when handling token amounts across EVM chains, or when a balance, price, or transfer amount is off by orders of magnitude.
metadata
{"version":"1.0.0","origin":"ECC direct-port adaptation"}
# EVM Token Decimals Silent decimal mismatches are one of the easiest ways to ship balances or USD values that are off by orders of magnitude without throwing an error. ## When to Use - Reading ERC-20 balances in Python, TypeScript, or Solidity - Calculating fiat values from on-chain balances - Comparing token amounts across multiple EVM chains - Handling bridged assets - Building portfolio trackers, bots, or aggregators ## How It Works Never assume stablecoins use the same decimals everywhere. Query `decimals()` at runtime, cache by `(chain_id, token_address)`, and use decimal-safe math for value calculations. ## Examples ### Query decimals at runtime ```python from decimal import Decimal from web3 import Web3 ERC20_ABI = [ {"name": "decimals", "type": "function", "inputs": [], "outputs": [{"type": "uint8"}], "stateMutability": "view"}, {"name": "balanceOf", "type": "function", "inputs": [{"name": "account", "type": "address"}], "outputs": [{"type": "uint256"}], "stateMutability": "view"}, ] def get_token_balance(w3: Web3, token_address: str, wallet: str) -> Decimal: contract = w3.eth.contract( address=Web3.to_checksum_address(token_address), abi=ERC20_ABI, ) decimals = contract.functions.decimals().call() raw = contract.functions.balanceOf(Web3.to_checksum_address(wallet)).call() return Decimal(raw) / Decimal(10 ** decimals) ``` Do not hardcode `1_000_000` because a symbol usually has 6 decimals somewhere else. ### Cache by chain and token ```python from functools import lru_cache @lru_cache(maxsize=512) def get_decimals(chain_id: int, token_address: str) -> int: w3 = get_web3_for_chain(chain_id) contract = w3.eth.contract( address=Web3.to_checksum_address(token_address), abi=ERC20_ABI, ) return contract.functions.decimals().call() ``` ### Handle odd tokens defensively ```python try: decimals = contract.functions.decimals().call() except Exception: logging.warning( "decimals() reverted on %s (chain %s), defaulting to 18", token_address, chain_id, ) decimals = 18 ``` Log the fallback and keep it visible. Old or non-standard tokens still exist. ### Normalize to 18-decimal WAD in Solidity ```solidity interface IERC20Metadata { function decimals() external view returns (uint8); } function normalizeToWad(address token, uint256 amount) internal view returns (uint256) { uint8 d = IERC20Metadata(token).decimals(); if (d == 18) return amount; if (d < 18) return amount * 10 ** (18 - d); return amount / 10 ** (d - 18); } ``` ### TypeScript with ethers ```typescript import { Contract, formatUnits } from 'ethers'; const ERC20_ABI = [ 'function decimals() view returns (uint8)', 'function balanceOf(address) view returns (uint256)', ]; async function getBalance(provider: any, tokenAddress: string, wallet: string): Promise<string> { const token = new Contract(tokenAddress, ERC20_ABI, provider); const [decimals, raw] = await Promise.all([ token.decimals(), token.balanceOf(wallet), ]); return formatUnits(raw, decimals); } ``` ### Quick on-chain check ```bash cast call <token_address> "decimals()(uint8)" --rpc-url <rpc> ``` ## Rules - Always query `decimals()` at runtime - Cache by chain plus token address, not symbol - Use `Decimal`, `BigInt`, or equivalent exact math, not float - Re-query decimals after bridging or wrapper changes - Normalize internal accounting consistently before comparison or pricing
Ver en GitHub