PreciseBank Module Reference
The PreciseBank module (x/precisebank) extends the precision of the standard Cosmos SDK bank module from 6 decimals to 18 decimals, enabling full EVM compatibility while maintaining Cosmos coin integrity. This module is required for chains using non-18-decimal native tokens.
Big thanks to the Kava team for their valuable contributions to this module.
Module Overview
Purpose: Bridge the decimal precision gap between Cosmos (typically 6 decimals) and EVM (18 decimals) Key Functionality:- Extends token precision without changing the base denomination
- Tracks fractional balances (sub-atomic units) separate from integer balances
- Maintains 1:1 backing between fractional and integer units
- Transparent to users - balances appear as expected in both environments
- Wraps
x/bankto provide 18-decimal precision forx/vm
When Do You Need PreciseBank?
✅ You NEED PreciseBank if:
Your native token has 6 decimals (or any non-18 decimal count):- Base denom:
ustake,utoken,uatom(micro prefix = 10^6) - Display denom:
stake,token,atom - Example: 1 STAKE = 1,000,000 ustake = 10^6 smallest units
❌ You DON’T NEED PreciseBank if:
Your native token has 18 decimals:- Base denom:
atest,atoken(atto prefix = 10^18) - Display denom:
test,token - Example: 1 TEST = 1,000,000,000,000,000,000 atest = 10^18 smallest units
Mathematical Foundation
The Precision Problem
Cosmos Standard: 6 decimal placesPreciseBank Solution
PreciseBank subdivides eachuatom into 10^12 sub-atomic units called aatom:
aatom is fully backed by uatom in x/bank. You cannot have fractional aatom without corresponding integer uatom reserves.
Balance Representation
For any accountn, the total balance in sub-atomic units a(n) is:
Where:
a(n)= Total aatom balance (18-decimal representation)b(n)= Integer uatom balance (stored in x/bank)f(n)= Fractional balance (stored in x/precisebank)C= Conversion factor = 10^12
Module Integration
Adding to Your Chain
PreciseBank requires integration inapp/app.go:
1. Import the module:
Configuration
Genesis Configuration
PreciseBank has minimal genesis configuration - it primarily tracks state, not parameters. File Location:~/.evmd/config/genesis.json under app_state.precisebank
Structure:
Required VM Module Configuration
When using PreciseBank, you MUST configureextended_denom_options in the VM module:
native_denom: 6-decimal Cosmos denom (ustake)extended_denom: 18-decimal EVM denom (astake)- Conversion: 1 ustake = 10^12 astake
uprefix (micro, 10^6) →aprefix (atto, 10^18):ustake→astake- Other prefixes → add
evmprefix:stake→evmstake
State
fractional_balances
What It Stores: The fractional (sub-atomic) portion of each account’s balance that cannot be represented as whole integer units. Type: Array ofFractionalBalance objects
Structure (fractional_balance.go:43-48):
- Amount must be positive (
amount > 0) - Amount must be less than conversion factor (
amount < 10^12) - Address must be valid Bech32
remainder
What It Stores: A module-level reserve balance that backs all fractional units in circulation. Type: Integer (sdkmath.Int) Purpose: Maintains invariant that total fractional balances equal the module reserve Invariant: Where:remainder= Module reserve in fractional units- = Sum of all account fractional balances
Operations
Transfer
When transferring fractional amounts, PreciseBank handles the complexity automatically: Example Transfer: Alice sends 1.5 ustake + 500 billion aatom to BobMint
Operation: Create new fractional units- Split amount into integer and fractional parts
- Mint integer part via x/bank
- Update fractional balance in x/precisebank
- Update remainder to maintain backing invariant
Burn
Operation: Destroy fractional units- Split amount into integer and fractional parts
- Burn integer part via x/bank
- Update fractional balance in x/precisebank
- Update remainder to maintain backing invariant
Keeper Interface
PreciseBank implements the fullBankKeeper interface, making it a drop-in replacement:
Source: keeper.go:16
SendCoins(ctx, from, to, coins)- Transfer with fractional precisionMintCoins(ctx, module, coins)- Create new fractional unitsBurnCoins(ctx, module, coins)- Destroy fractional unitsGetBalance(ctx, addr, denom)- Get extended balance (integer + fractional)SpendableCoins(ctx, addr)- Get spendable balances with fractional precision
GetSupply()- Total supplyIterateTotalSupply()- Supply iteration
Queries
gRPC Queries
Query Fractional Balance:EVM Integration
In Solidity Contracts
From the EVM perspective, users interact with the extended denomination:transfer(recipient, 1.5e18 astake)- PreciseBank: Transfers 1 ustake via x/bank + 500,000,000,000 aatom fractional
- User sees seamless 18-decimal precision
Events
PreciseBank emits events for fractional balance changes:SendCoins Event
MintCoins Event
BurnCoins Event
Common Issues and Solutions
Issue: “Fractional amount exceeds conversion factor”
Symptom: Transaction fails with fractional validation error Cause: Fractional balance >= 10^12 (should have been converted to integer unit) Solution: This indicates a bug in the keeper logic. Report to Cosmos EVM team.Issue: Balances Don’t Match Between Cosmos/EVM
Symptom: User sees different balance in Cosmos vs MetaMask Cause:- PreciseBank not integrated correctly in app.go
- VM module not using PreciseBankKeeper
- Missing
extended_denom_optionsconfiguration
Issue: Chain Won’t Start After Adding PreciseBank
Symptom: Genesis validation fails Cause: Missingextended_denom_options in VM params
Solution: Add to genesis.json:
Issue: Total Supply Mismatch
Symptom: Sum of balances doesn’t equal total supply Cause: Remainder not properly maintained Solution: Query remainder and verify:Testing and Verification
Verify Integration
1. Check module is loaded:Performance Considerations
Storage: Fractional balances add one storage entry per account with non-zero fractional amount Gas Cost: Fractional operations add minimal gas overhead (~5-10% more than standard bank operations) Scaling: Module has been tested with millions of accounts, no performance degradation Optimization: Fractional balances are only created when needed. Transfers of exact integer amounts don’t create fractional entries.Related Documentation
- Building Your Chain Guide - Main configuration walkthrough
- VM Module - extended_denom_options setup
- ERC20 Module - Token pair configuration
Source Code References
- Module Implementation: x/precisebank
- README (Math Background): x/precisebank/README.md
- Keeper: keeper/keeper.go
- Fractional Balance Logic: types/fractional_balance.go
- Send Operations: keeper/send.go
- Mint Operations: keeper/mint.go
- Burn Operations: keeper/burn.go
- Remainder Management: keeper/remainder_amount.go
- Storage Keys: types/keys.go