PreciseBank
Extended precision wrapper for x/bank enabling 18 decimal support
The x/precisebank module from Ontomir evm extends the standard x/bank module from 6 to 18 decimal precision for EVM compatibility.
For conceptual understanding of precision handling and mathematical proofs, see [Precision Handling](/docs/evm/next/documentation/concepts/precision-handling).
Overview
The module acts as a wrapper around x/bank, providing:
18 decimal precision for EVM (10^18 sub-atomic units)
Backward compatibility with 6 decimal Ontomir operations
Transparent conversion between precision levels
Fractional balance tracking for sub-test amounts
Developed with contributions from the [Kava](https://www.kava.io/) team.
State
The module maintains fractional balances and remainder :
FractionalBalance
0x01 + address
math.Int
Account fractional balance (0 to 10^12-1)
Remainder
0x02
math.Int
Uncirculated fractional amount
Balance Representation
Full balance calculation:
atest_balance = test_balance × 10^12 + fractional_balanceWhere:
test_balance: Stored in x/bank (6 decimals)fractional_balance: Stored in x/precisebank (0 to 10^12-1)atest_balance: Full 18-decimal precision
Keeper Interface
The module provides a bank-compatible keeper :
type Keeper interface {
// Query methods
GetBalance(ctx, addr, denom) sdk.Coin
SpendableCoins(ctx, addr) sdk.Coins
// Transfer methods
SendCoins(ctx, from, to, amt) error
SendCoinsFromModuleToAccount(ctx, module, to, amt) error
SendCoinsFromAccountToModule(ctx, from, module, amt) error
// Mint/Burn methods
MintCoins(ctx, module, amt) error
BurnCoins(ctx, module, amt) error
}Extended Coin Support
Automatic handling of "atest" denomination:
Converts between test and atest transparently
Maintains fractional balances for sub-test amounts
Ensures consistency between x/bank and x/precisebank
Operations
Transfer
Handles both integer and fractional components:
// SendCoins automatically handles precision
keeper.SendCoins(ctx, from, to, sdk.NewCoins(
sdk.NewCoin("atest", sdk.NewInt(1500000000000)), // 0.0015 test
))Algorithm:
Subtract from sender (update b(sender) and f(sender))
Add to receiver (update b(receiver) and f(receiver))
Update reserve based on carry/borrow
Remainder unchanged (mathematical guarantee)
Mint
Creates new tokens with proper backing:
// MintCoins with extended precision
keeper.MintCoins(ctx, moduleName, sdk.NewCoins(
sdk.NewCoin("atest", sdk.NewInt(1000000000000000000)), // 1 test
))Algorithm:
Add to account (update b(account) and f(account))
Decrease remainder (tokens enter circulation)
Update reserve for consistency
Burn
Removes tokens from circulation:
// BurnCoins with extended precision
keeper.BurnCoins(ctx, moduleName, sdk.NewCoins(
sdk.NewCoin("atest", sdk.NewInt(500000000000)), // 0.0005 test
))Algorithm:
Subtract from account (update b(account) and f(account))
Increase remainder (tokens leave circulation)
Update reserve for consistency
Events
Standard bank events with extended precision amounts:
Transfer Events
transfer
sender, recipient, amount
Full atest amount
coin_spent
spender, amount
Extended precision
coin_received
receiver, amount
Extended precision
Mint/Burn Events
coinbase
minter, amount
Minted with 18 decimals
burn
burner, amount
Burned with 18 decimals
Queries
gRPC
service Query {
// Get total of all fractional balances
rpc TotalFractionalBalances(QueryTotalFractionalBalancesRequest)
returns (QueryTotalFractionalBalancesResponse);
// Get current remainder amount
rpc Remainder(QueryRemainderRequest)
returns (QueryRemainderResponse);
// Get fractional balance for an account
rpc FractionalBalance(QueryFractionalBalanceRequest)
returns (QueryFractionalBalanceResponse);
}CLI
```bash Query-Total # Query total fractional balances evmd query precisebank total-fractional-balances
Example output:
total: "2000000000000atest"
```bash Query-Remainder
# Query remainder amount
evmd query precisebank remainder
# Example output:
# remainder: "100atest"# Query account fractional balance
evmd query precisebank fractional-balance Ontomir1...
# Example output:
# fractional_balance: "10000atest"Integration
For EVM Module
Replace bank keeper with precisebank keeper in app.go:
app.EvmKeeper = evmkeeper.NewKeeper(
app.PrecisebankKeeper, // Instead of app.BankKeeper
// ... other parameters
)For Other Modules
Query extended balances through standard interface:
// Automatically handles atest denomination
balance := keeper.GetBalance(ctx, addr, "atest")
// Transfer with 18 decimal precision
err := keeper.SendCoins(ctx, from, to,
sdk.NewCoins(sdk.NewCoin("atest", amount)))Reserve Account
The reserve account maintains backing for fractional balances:
Reserve_test × 10^12 = Σ(all fractional balances) + remainderMonitoring Reserve
# Check reserve account balance
evmd query bank balances Ontomir1m3h30wlvsf8llruxtpukdvsy0km2kum8g38c8q
# Verify invariant
evmd query precisebank total-fractional-balances
evmd query precisebank remainderInvariants
Critical invariants maintained by the module:
Supply
Total_atest = Total_test × 10^12 - remainder
Total supply consistency
Fractional Range
0 ≤ f(n) < 10^12
Valid fractional bounds
Reserve Backing
Reserve × 10^12 = Σf(n) + r
Full backing guarantee
Conservation
Δ(Total_atest) = Δ(Total_test × 10^12)
No creation/destruction
Best Practices
Chain Integration
Reserve Monitoring
Track reserve balance for validation
Set up alerts for invariant violations
Regular audits of fractional sums
Migration Path
// Deploy in passive mode app.PrecisebankKeeper = precisebankkeeper.NewKeeper( app.BankKeeper, // Fractional balances start at zero )Testing
// Verify fractional operations suite.Require().Equal( expectedFractional, keeper.GetFractionalBalance(ctx, addr), )
dApp Development
Balance Queries
// Query in atest (18 decimals) const balance = await queryClient.precisebank.fractionalBalance({ address: "Ontomir1..." });Precision Handling
// Convert between precisions const testAmount = atestAmount / BigInt(10**12); const atestAmount = testAmount * BigInt(10**12);
Security Considerations
Overflow Protection
All arithmetic uses checked math
Fractional values bounded to [0, 10^12)
Integer overflow impossible by design
Atomicity
Balance updates are atomic
Reserve adjustments in same transaction
No intermediate states visible
Precision Guarantees
No precision loss during operations
All fractional amounts preserved
Rounding only at display layer
Performance
Storage Impact
Additional O(n) storage for accounts with fractional balances
Most accounts have zero fractional balance (no storage)
Reserve account: single additional balance
Computation
Constant time operations for all transfers
Single addition/multiplication for balance queries
~10% gas overhead for fractional updates
Optimization
Lazy initialization (fractional balances start at zero)
Sparse storage (only non-zero fractions stored)
Batch operations maintain efficiency
Troubleshooting
Common Issues
"fractional overflow"
Fractional > 10^12
Check calculation logic
"insufficient balance"
Including fractional
Verify full atest balance
"invariant violation"
Supply mismatch
Audit reserve and remainder
Validation Commands
# Verify module invariants
evmd query precisebank total-fractional-balances
evmd query precisebank remainder
# Check specific account
evmd query bank balances Ontomir1... --denom test
evmd query precisebank fractional-balance Ontomir1...最后更新于
