Precision Handling
Understanding decimal precision bridging between Ontomir and EVM
The Precision Challenge
Ontomir SDK and Ethereum use different decimal precisions for their native tokens:
Ontomir SDK: 6 decimals (1 ATOM = 10^6 test)
Ethereum: 18 decimals (1 ETH = 10^18 wei)
This 12-decimal difference creates challenges when bridging the two ecosystems. Simply scaling values would lose precision or create rounding errors.
Mathematical Foundation
Balance Representation
Any account balance can be decomposed into integer and fractional components:
Total Balance = Integer Part × Conversion Factor + Fractional PartFor an account with balance a(n) in smallest units (18 decimals):
a(n) = b(n) × C + f(n)Where:
a(n)= Total balance in 18-decimal units (atest)b(n)= Integer balance in 6-decimal units (test)f(n)= Fractional balance (remainder)C= Conversion factor (10^12)Constraint:
0 ≤ f(n) < C
Example Decomposition
Consider a balance of 1,234,567,890,123,456,789 atest:
1,234,567,890,123,456,789 = 1,234,567 × 10^12 + 890,123,456,789
Where:
- b(n) = 1,234,567 test (stored in bank)
- f(n) = 890,123,456,789 atest (stored in precisebank)
- Total = 1.234567890123456789 ATOMThe Reserve Account
To maintain supply consistency, a reserve account holds backing for all fractional balances:
Reserve Equation
Reserve Balance × C = Sum of All Fractional Balances + RemainderFormally:
b(R) × C = Σf(n) + rWhere:
b(R)= Reserve balance in testΣf(n)= Sum of all account fractional balancesr= Remainder (fractional amount not in circulation)Constraint:
0 ≤ r < C
Supply Invariant
This ensures the fundamental invariant:
Total_atest = Total_test × 10^12 - remainderThe remainder represents sub-test amounts that exist in the system but aren't assigned to any specific account.
Operation Algorithms
Transfer Algorithm
Transferring between accounts requires careful handling of carries and borrows:
From account 1 to account 2, amount a:
Calculate new fractional balances:
f'(1) = (f(1) - a) mod C f'(2) = (f(2) + a) mod CHandle integer updates with carry/borrow:
b'(1) = b(1) - ⌊a/C⌋ - (f'(1) > f(1) ? 1 : 0) b'(2) = b(2) + ⌊a/C⌋ + (f'(2) < f(2) ? 1 : 0)Update reserve based on carry conditions:
Both carry/borrow: No reserve change
Only sender borrows: Reserve decreases by 1
Only receiver carries: Reserve increases by 1
Mint Algorithm
Creating new tokens while maintaining backing:
Update account:
f'(n) = (f(n) + a) mod C b'(n) = b(n) + ⌊a/C⌋ + (f'(n) < f(n) ? 1 : 0)Update remainder:
r' = (r - a) mod CAdjust reserve for consistency
Burn Algorithm
Removing tokens from circulation:
Update account:
f'(n) = (f(n) - a) mod C b'(n) = b(n) - ⌊a/C⌋ - (f'(n) > f(n) ? 1 : 0)Update remainder:
r' = (r + a) mod CAdjust reserve for consistency
Proof: Remainder Unchanged in Transfers
A critical property is that transfers don't change the global remainder:
Proof:
Start with transfer affecting fractional balances
Take modulo C of the balance equation
Since reserve changes are multiples of C, they vanish mod C
Fractional changes sum to zero (amount subtracted equals amount added)
Therefore:
r' = r
This elegant property means transfers are purely redistributive - they don't create or destroy fractional amounts.
Precision Hierarchy
The system manages three precision levels:
ATOM
10^0
0
Human display
test
10^-6
6
Ontomir native
atest
10^-18
18
EVM native
Conversion Examples
1 ATOM = 1,000,000 test = 1,000,000,000,000,000,000 atest
0.000001 ATOM = 1 test = 1,000,000,000,000 atest
0.000000000000000001 ATOM = 0.000000000001 test = 1 atestEdge Cases and Limits
Minimum Transferable Amount
In Ontomir: 1 test (0.000001 ATOM)
In EVM: 1 atest (0.000000000000000001 ATOM)
Maximum Precision
Ontomir operations: Limited to test granularity
EVM operations: Full atest precision
Cross-system: Automatic precision handling
Dust Amounts
Amounts smaller than 1 test but larger than 0 atest:
Tracked in fractional balances
Accumulate until reaching 1 test
Never lost or rounded away
Implementation Strategy
Storage Optimization
Rather than storing 18-decimal balances directly:
Store 6-decimal amounts in existing bank module
Store only the fractional remainder separately
Reconstruct full precision on demand
This approach:
Maintains backward compatibility
Minimizes storage overhead
Preserves full precision
Query Performance
When querying balances:
fullBalance = bankBalance * 10^12 + fractionalBalanceThis single multiplication and addition reconstructs the full 18-decimal balance efficiently.
Why This Matters
For Users
No precision loss: Every atest is accounted for
Seamless experience: Automatic handling across systems
Fair transactions: No rounding advantages or disadvantages
For Developers
Standard interfaces: Use familiar decimals for each system
Automatic conversion: No manual precision management
Predictable behavior: Mathematical guarantees on operations
For the Ecosystem
True interoperability: Native precision for both ecosystems
Future proof: Extensible to other precision requirements
Efficient design: Minimal overhead for maximum capability
Comparison with Alternatives
Simple Scaling
// Naive approach - loses precision
evmAmount = OntomirAmount * 10^12Problems: Loses sub-test amounts, rounding errors accumulate
Fixed-Point Arithmetic
// Complex fixed-point math
amount = FixedPoint{mantissa: 123456, exponent: -18}Problems: Complex implementation, performance overhead, compatibility issues
Separate Balances
// Maintain two separate balance systems
OntomirBalance: 1000000 test
evmBalance: 1000000000000000000 atestProblems: Synchronization issues, double accounting, complexity
PreciseBank Solution
// Elegant decomposition
totalBalance = integerPart * 10^12 + fractionalPartAdvantages: Simple, efficient, precise, compatible
Future Extensions
Multi-Precision Support
The mathematical framework extends to any precision:
8 decimals for certain tokens
27 decimals for high-precision applications
Variable precision based on token type
Cross-Chain Precision
Standardized precision handling for:
IBC transfers with different precisions
Bridge protocols with varying decimals
Universal precision abstraction layer
