Overview

Single Token Representation is a key design in Cosmos EVM that removes the complexity and risks of traditional token wrapping. Instead of creating wrapped tokens with separate balances, Cosmos EVM provides multiple interfaces (native Cosmos operations, ERC20 contracts, and bank queries) all pointing to the same underlying token balance stored in the Cosmos SDK bank module. Core Principle: One token, multiple interfaces, one source of truth.

The Problem with Traditional Wrapping

Wrapping tokens requires extra steps and introduces risks: Example: Ethereum’s WETH contract:
contract WETH {
    mapping(address => uint256) private _balances;

    function deposit() external payable {
        _balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external {
        _balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }
}
Issues:
  • Liquidity fragmentation (ETH and WETH pools separate)
  • Smart contract risk (vulnerabilities or malicious upgrades)
  • User complexity (manual wrap/unwrap required)
  • Additional gas costs for wrap/unwrap operations

The Solution: Single Token Representation

Cosmos EVM stores token balances only once (in the bank module), providing:
  • Single Balance: All token amounts in the bank module
  • Multiple Interfaces: ERC20 precompiles, native operations, and queries access the same balance
  • Automatic Sync: All interfaces reflect the same state without conversion
  • No Wrapping Needed: Eliminates manual wrap/unwrap transactions

Technical Implementation

Precision Handling with x/precisebank

The key challenge is bridging Cosmos’s 6-decimal precision with EVM’s 18-decimal standard. The x/precisebank module solves this while maintaining single token representation.
Cosmos EVM uses the precisebank module to handle the precision difference:
  • Cosmos native: uatom with 6 decimals (1 ATOM = 10^6 uatom)
  • EVM native: aatom with 18 decimals (1 ATOM = 10^18 aatom)
  • Conversion: 1 uatom = 10^12 aatom
The precisebank module wraps the bank module to:
  1. Store integer balances (uatom) in x/bank
  2. Store fractional balances (remainder aatom) in x/precisebank
  3. Maintain the invariant: aatom_balance = uatom_balance * 10^12 + fractional_balance
// From x/precisebank implementation
// a(n) = b(n) * C + f(n)
// where:
// a(n) = aatom balance (18 decimals)
// b(n) = uatom balance in x/bank (6 decimals)
// f(n) = fractional balance in x/precisebank
// C = 10^12 (conversion factor)

WERC20 Precompile

The WERC20 precompile (source) extends the ERC20 precompile to provide deposit/withdraw functionality:
// WERC20 wraps ERC20 precompile with additional methods
type Precompile struct {
    *erc20.Precompile  // Inherits standard ERC20 functionality
}

// Key methods:
// - Deposit(): Convert native tokens to ERC20 representation
// - Withdraw(): Convert ERC20 representation back to native
// - All ERC20 standard methods (transfer, approve, etc.)
Important: Despite the “deposit” and “withdraw” terminology, no actual wrapping occurs. These methods simply change which interface you’re using to interact with the same underlying balance.

Real-World Example

const userAddress = "0x742d35cc6644c068532fddb11B4C36A58D6D3eAb";

// Query native bank balance (returns uatom units)
const bankBalance = await bankContract.balances(userAddress);
console.log(bankBalance); // [{denom: "uatom", amount: "100"}] = 100 uatom

// Query ERC20 balance (returns aatom units - 18 decimals)
const watomBalance = await watomContract.balanceOf(userAddress);
console.log(watomBalance); // "100000000000000" = 100 * 10^12 aatom

// Transfer 30 uatom worth via ERC20 interface
await watomContract.transfer(recipient, "30000000000000"); // 30 * 10^12 aatom

// Check updated balances - both show 70 uatom worth
console.log(await bankContract.balances(userAddress)); // 70 uatom
console.log(await watomContract.balanceOf(userAddress)); // 70 * 10^12 aatom

// Native Cosmos operations also reflect changes automatically
// cosmos tx bank send cosmos1... cosmos1... 20uatom
// After this, ERC20 balance would show 50 * 10^12 aatom

Benefits

  1. Unified Liquidity
    • No fragmented token pools
    • DeFi protocols use the same liquidity regardless of interface
  2. Reduced Smart Contract Risk
    • No wrapping contracts to exploit
    • Bank module is well-tested and secure
  3. Simplified User Experience
    • No manual wrapping/unwrapping
    • Seamless Cosmos and EVM integration
  4. Performance & Cost Efficiency
    • No extra gas for wrapping operations
    • Direct access to optimized bank module
  5. Developer Friendly
    • Use standard ERC20 interfaces without wrapping logic
    • Compatible with existing EVM tooling

Implementation Examples

Solidity Liquidity Pool Integration

contract LiquidityPool {
    IERC20 public token;

    function addLiquidity(uint256 amount) external {
        // Works with native token's ERC20 interface
        // No wrapping needed - operates on same underlying balance
        token.transferFrom(msg.sender, address(this), amount);
    }
}

Cross-Ecosystem Operations

async function demonstrateUnification() {
    // Native Cosmos transfer (6 decimals)
    await execCmd("cosmos tx bank send cosmos1abc... cosmos1def... 100uatom");

    // ERC20 operations (18 decimals)
    await watomContract.approve(dexContract.address, "50000000000000"); // 50 uatom worth
    await dexContract.swap(watomContract.address, "50000000000000");

    // Native staking (6 decimals)
    await execCmd("cosmos tx staking delegate cosmosvaloper1... 25uatom");

    // All operations affect the same underlying balance
}

Comparison

AspectTraditional WrappingSingle Token Representation
LiquidityFragmentedUnified
Smart Contract RiskHighMinimal (bank module only)
User ComplexityHigh (manual wrap/unwrap)Low (automatic)
Gas OverheadHighLow
State ManagementDual balancesSingle source of truth
Developer ExperienceMust handle wrappingStandard ERC20 interface
InteroperabilityManual bridgesNative cross-ecosystem
Decimal PrecisionFixed per tokenAutomatic conversion

Technical Details

Reserve Account

The precisebank module maintains a reserve account to ensure supply consistency:
// Reserve holds uatom equal to:
// - All fractional balances (< 10^12 aatom per account)
// - Plus remainder to maintain supply invariant
reserve_uatom = sum(all_fractional_balances) / 10^12 + remainder
This ensures: Total_aatom = Total_uatom * 10^12 - remainder where 0 ≤ remainder < 10^12

ERC20 Token Pairs

The x/erc20 module manages the registration and tracking of token pairs:
  • Native Cosmos denoms ↔ ERC20 contract addresses
  • Automatic registration for IBC tokens when enabled
  • Governance-controlled token pair registration
  • WERC20 Precompile: Implements single token representation for native tokens at specific addresses. See WERC20 docs.
  • ERC20 Module: Manages token pairs and automatic registration. See ERC20 module docs.
  • Bank Precompile: Provides direct access to native token balances. See Bank precompile docs.
  • PreciseBank Module: Handles decimal precision conversion transparently.

Future Implications

  • Cross-chain DeFi without liquidity fragmentation
  • Easier multi-chain onboarding without learning new token models
  • Native interoperability without complex bridges
  • Unified developer experience across blockchain ecosystems
  • Seamless IBC token integration with automatic ERC20 representation