Evm decimal validation
Skill widnyana/eyay-toolkits/plugins/evm-decimal-validation/skills/evm-decimal-validation
Agents don’t behave, they swarm. Skills and plugins slot in mid flight, and somehow the whole thing is a mess
npx -y skills add widnyana/eyay-toolkits --skill evm-decimal-validationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Validate and configure token decimals for EVM-compatible blockchain deployments. Use when working with ERC20 tokens, multi-chain deployments, token amount conversions, or when decimals might vary across chains.
SKILL.md
4.8 KB, as published. Nobody here has run it
EVM Token Decimal Validation
Ensure token decimals are correctly configured for each blockchain deployment to prevent amount calculation errors.
Quick Start
- Check current configuration - Review network configs for decimal fields
- Validate against contracts - Query on-chain decimals() for each deployment
- Update configuration - Add per-chain decimals to network config
- Test conversions - Verify FromWei/ToWei work correctly
Token Decimal Standards
Common Decimal Values
| Decimals | Use Case | Examples |
|---|---|---|
| 18 | Standard ERC20 (most tokens) | WETH, DAI, USDC (on some chains) |
| 6 | Stablecoins | USDC, USDT (on Ethereum) |
| 8 | Bitcoin-wrapped | WBTC |
| 2 | Fiat-backed (cents) | IDRX, some CBDCs |
| 0 | Raw integers | Some wrapped tokens |
Chain-Specific Variations
The same token contract deployed on different chains may use different decimals:
Token A on Ethereum: 18 decimals
Token A on Polygon: 0 decimals (different deployment)
Token A on BNB Chain: 0 decimals (different deployment)
Validation Checklist
Step 1: Audit Current Configuration
# Find hardcoded decimals
grep -rn "18)" --include="*.go" | grep -i "decimal\|wei\|amount"
# Check for decimals field in network config
grep -rn "Decimals" blockchain/networks.go
Step 2: Query On-Chain Values
// Get decimals from contract
decimals, err := contract.Decimals(nil)
if err != nil {
return fmt.Errorf("failed to get decimals: %w", err)
}
Step 3: Update Network Config
type NetworkConfig struct {
// ... existing fields ...
Decimals uint8 // Token decimals for this deployment
}
// In SupportedNetworks
BaseMainnet: {
// ...
Decimals: 2, // From on-chain query
},
Step 4: Add Helper Function
// GetDecimals returns token decimals for a chain ID.
// Always provide a sensible fallback.
func GetDecimals(chainID uint64) uint8 {
config, _, exists := GetNetworkConfigByChainID(chainID)
if !exists {
return 2 // Fallback to majority value
}
return config.Decimals
}
Step 5: Replace Hardcoded Values
// Before
return FromWei(balance, 18), nil
// After
return FromWei(balance, int32(GetDecimals(chainID))), nil
Common Pitfalls
Pitfall 1: Assuming All Chains Use Same Decimals
// WRONG: Hardcoded decimals
amount := FromWei(balance, 18)
// CORRECT: Per-chain decimals
amount := FromWei(balance, int32(GetDecimals(chainID)))
Pitfall 2: Missing Fallback Value
// WRONG: No fallback, potential nil pointer
return config.Decimals, nil
// CORRECT: Sensible fallback
if !exists {
return 2 // Majority value or most common
}
return config.Decimals
Pitfall 3: Not Validating Config Against Chain
// Add startup validation
func ValidateDecimals() error {
for name, config := range SupportedNetworks {
onChainDecimals, err := queryOnChain(config)
if err != nil {
continue // Skip unreachable chains
}
if config.Decimals != onChainDecimals {
return fmt.Errorf("%s: config=%d, on-chain=%d",
name, config.Decimals, onChainDecimals)
}
}
return nil
}
Test Patterns
func TestGetDecimals(t *testing.T) {
testCases := map[uint64]uint8{
8453: 2, // Base
137: 0, // Polygon
56: 0, // BSC
}
for chainID, expected := range testCases {
got := GetDecimals(chainID)
if got != expected {
t.Errorf("Chain %d: expected %d, got %d", chainID, expected, got)
}
}
}
func TestGetDecimalsFallback(t *testing.T) {
got := GetDecimals(999999) // Unknown chain
if got != 2 {
t.Errorf("Unknown chain should return fallback, got %d", got)
}
}
Integration Points
When adding decimal validation, check these areas:
| Area | What to Check |
|---|---|
| Balance queries | BalanceOf() uses per-chain decimals |
| Total supply | TotalSupply() uses per-chain decimals |
| Transfers | ParseTokenAmount() receives correct decimals |
| Bridge operations | Both source and dest chains considered |
| Fee calculations | Platform fees use correct decimals |
| API responses | Human-readable amounts properly converted |
Token Decimals by Chain
See decimals-by-chain.md for a reference guide of common tokens and their decimals across different EVM chains.
Sources
- ERC20 Token Standard: https://eips.ethereum.org/EIPS/eip-20
- OpenZeppelin ERC20: https://docs.openzeppelin.com/contracts/4.x/erc20