Impact Assessment

Affected Chains

Your chain needs this migration if you have:
  • IBC tokens converted to ERC20
  • Token factory tokens with ERC20 representations
  • Any existing DynamicPrecompiles or NativePrecompiles in storage

Symptoms if Not Migrated

  • ERC20 balances will show as 0 when queried via EVM
  • totalSupply() calls return 0
  • Token transfers via ERC20 interface fail
  • Native Cosmos balances remain intact but inaccessible via EVM

Storage Changes

    Implementation

    Quick Start

    Add this essential migration logic to your existing upgrade handler:
    // In your upgrade handler
    store := ctx.KVStore(storeKeys[erc20types.StoreKey])
    const addressLength = 42 // "0x" + 40 hex characters
    
    // Migrate dynamic precompiles (IBC tokens, token factory)
    if oldData := store.Get([]byte("DynamicPrecompiles")); len(oldData) > 0 {
        for i := 0; i < len(oldData); i += addressLength {
            address := common.HexToAddress(string(oldData[i : i+addressLength]))
            erc20Keeper.SetDynamicPrecompile(ctx, address)
        }
        store.Delete([]byte("DynamicPrecompiles"))
    }
    
    // Migrate native precompiles
    if oldData := store.Get([]byte("NativePrecompiles")); len(oldData) > 0 {
        for i := 0; i < len(oldData); i += addressLength {
            address := common.HexToAddress(string(oldData[i : i+addressLength]))
            erc20Keeper.SetNativePrecompile(ctx, address)
        }
        store.Delete([]byte("NativePrecompiles"))
    }
    

    Testing

    Pre-Upgrade Verification

    # Query existing token pairs
    mantrachaind query erc20 token-pairs --output json | jq
    
    # Check ERC20 balances for a known address
    cast call $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url http://localhost:8545
    
    # Export state for backup
    mantrachaind export > pre-upgrade-state.json
    

    Post-Upgrade Verification

    # Verify precompiles are accessible
    cast call $TOKEN_ADDRESS "totalSupply()" --rpc-url http://localhost:8545
    
    # Check balance restoration
    cast call $TOKEN_ADDRESS "balanceOf(address)" $USER_ADDRESS --rpc-url http://localhost:8545
    
    # Test token transfer
    cast send $TOKEN_ADDRESS "transfer(address,uint256)" $RECIPIENT 1000 \
      --private-key $PRIVATE_KEY --rpc-url http://localhost:8545
    
    # Verify in exported state
    mantrachaind export | jq '.app_state.erc20.dynamic_precompiles'
    

    Integration Test

    tests/upgrade_test.go
    func TestERC20PrecompileMigration(t *testing.T) {
        // Setup test environment
        app, ctx := setupTestApp(t)
    
        // Create legacy storage entries
        store := ctx.KVStore(app.keys[erc20types.StoreKey])
    
        // Add test addresses in old format
        dynamicAddresses := []string{
            "0x6eC942095eCD4948d9C094337ABd59Dc3c521005",
            "0x1234567890123456789012345678901234567890",
        }
        dynamicData := ""
        for _, addr := range dynamicAddresses {
            dynamicData += addr
        }
        store.Set([]byte("DynamicPrecompiles"), []byte(dynamicData))
    
        // Run migration
        err := migrateERC20Precompiles(ctx, app.keys[erc20types.StoreKey], app.Erc20Keeper)
        require.NoError(t, err)
    
        // Verify migration
        migratedAddresses := app.Erc20Keeper.GetDynamicPrecompiles(ctx)
        require.Len(t, migratedAddresses, len(dynamicAddresses))
    
        // Verify old storage is cleaned
        oldData := store.Get([]byte("DynamicPrecompiles"))
        require.Nil(t, oldData)
    }
    

    Verification Checklist

    • Test migration on testnet first
    • Document all existing token pairs
    • Verify ERC20 balances post-upgrade
    • Test token transfers work
    • Confirm IBC token conversions function

    References