对于已存在 ERC20 代币对的 pre-v0.4.x 链,这是一个必须执行的破坏性变更。如果忽略此迁移,现有 ERC20 代币将无法访问,余额查询会返回 0,并且所有操作都会失败。

影响评估

受影响的链

如果你的链存在以下任一情况,就需要执行这次迁移:
  • 已转换为 ERC20 的 IBC 代币
  • 带有 ERC20 表示形式的 Token Factory 代币
  • 存储中存在任何已有的 DynamicPrecompiles 或 NativePrecompiles

未迁移时的症状

  • 通过 EVM 查询时,ERC20 余额会显示为 0
  • totalSupply() 调用会返回 0
  • 通过 ERC20 接口进行的代币转账会失败
  • 原生 Cosmos 余额仍然存在,但无法通过 EVM 访问

存储变更

    实现方式

    快速开始

    将以下逻辑添加到你现有的升级处理器中:
    // 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"))
    }
    

    测试

    升级前验证

    # 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
    

    升级后验证

    # 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'
    

    集成测试

    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)
    }
    

    验证清单

    • 先在测试网执行迁移测试
    • 记录所有现有代币对
    • 在升级后验证 ERC20 余额
    • 测试代币转账是否正常工作
    • 确认 IBC 代币转换功能正常

    参考资料


    This is a mandatory breaking change for pre-v0.4.x chains with existing ERC20 token pairs.If neglected, existing ERC20 tokens will become inaccessible, return zero balances and fail all operations.

    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 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