> ## Documentation Index
> Fetch the complete documentation index at: https://cosmos-docs-sync-security-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Migration: v0.5.0 to v0.6.0

## 0) Prep

* Create a branch: `git switch -c upgrade/evm-v0.6`.
* Ensure a clean build + tests green pre-upgrade.
* Snapshot your current params/genesis for comparison later.

***

## 1) Dependency bumps (go.mod)

* Bump `github.com/cosmos/evm` to v0.6.0 and run:

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
go mod tidy
```

***

## 2) App Wiring Changes

### IBC Transfer Module

v0.6.0 removes the custom IBC transfer keeper override and now uses the official IBC-Go transfer keeper directly. This means that **ERC20 conversions via Cosmos IBC transfer transactions are not possible**. These are now only handled in the ICS20 precompile, and any ERC20 transfer must be initiated through there.

**Changes required in `app.go`:**

1. **Update imports** - Replace custom transfer imports with official IBC-Go imports:

```diff theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
- import (
-     "github.com/cosmos/evm/x/ibc/transfer"
-     transferkeeper "github.com/cosmos/evm/x/ibc/transfer/keeper"
-     transferv2 "github.com/cosmos/evm/x/ibc/transfer/v2"
-     ibctransfer "github.com/cosmos/ibc-go/v11/modules/apps/transfer"
-     ibctransfertypes "github.com/cosmos/ibc-go/v11/modules/apps/transfer/types"
- )
+ import (
+     transfer "github.com/cosmos/ibc-go/v11/modules/apps/transfer"
+     transferkeeper "github.com/cosmos/ibc-go/v11/modules/apps/transfer/keeper"
+     ibctransfertypes "github.com/cosmos/ibc-go/v11/modules/apps/transfer/types"
+     transferv2 "github.com/cosmos/ibc-go/v11/modules/apps/transfer/v2"
+ )
```

2. **Update TransferKeeper initialization** - Remove ERC20 keeper parameter:

```diff theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
  app.TransferKeeper = transferkeeper.NewKeeper(
      appCodec,
      runtime.NewKVStoreService(keys[ibctransfertypes.StoreKey]),
+     nil, // ICS4Wrapper param
      app.IBCKeeper.ChannelKeeper,
      app.IBCKeeper.ChannelKeeper,
      app.MsgServiceRouter(),
      app.AccountKeeper,
      app.BankKeeper,
-     app.Erc20Keeper, // Remove: no longer passed to transfer keeper
      authAddr,
  )
```

3. **Update module registration** - Use official transfer module:

```diff theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
  app.BasicModuleManager = module.NewBasicManager(
      // ... other modules
-     ibctransfertypes.ModuleName: transfer.AppModuleBasic{AppModuleBasic: &ibctransfer.AppModuleBasic{}},
+     ibctransfertypes.ModuleName: transfer.AppModuleBasic{},
  )
```

4. **Update ICS20 precompile wiring** - Pass ERC20 keeper to ICS20 precompile:

```diff theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
  precompiletypes.DefaultStaticPrecompiles(
      *app.StakingKeeper,
      app.DistrKeeper,
      app.PreciseBankKeeper,
      &app.Erc20Keeper,
      &app.TransferKeeper,
      app.IBCKeeper.ChannelKeeper,
      app.GovKeeper,
      app.SlashingKeeper,
      appCodec,
  )
```

The ICS20 precompile now takes the ERC20 keeper as a parameter (instead of the transfer keeper receiving it). This allows the precompile to handle ERC20 conversions directly.

***

## 3) Breaking API Changes

### StateDB Requirements

v0.6.0 introduces significant changes to event tracking and state management. All EVM execution functions now require an explicit `stateDB` parameter and a `callFromPrecompile` flag to properly handle event management and state transitions.

**NOTE:** The only function calls affected are `CallEVM`, `CallEVMWithData`, `ApplyMessage`, and `ApplyMessageWithConfig`. These are typically used in common precompiles and logic that calls back into the EVM from the SDK. If your project does not use these functions, then no steps need to be taken for the upgrade.

#### Advanced Changes

The following functions have updated signatures:

##### `CallEVM`

**Before (v0.5.x):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k Keeper) CallEVM(
    ctx sdk.Context,
    abi abi.ABI,
    from, contract common.Address,
    commit bool,
    gasCap *big.Int,
    method string,
    args ...interface{},
) (*types.MsgEthereumTxResponse, error)
```

**After (v0.6.0):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k Keeper) CallEVM(
    ctx sdk.Context,
    stateDB *statedb.StateDB,
    abi abi.ABI,
    from, contract common.Address,
    commit bool,
    callFromPrecompile bool,
    gasCap *big.Int,
    method string,
    args ...interface{},
) (*types.MsgEthereumTxResponse, error)
```

##### `CallEVMWithData`

**Before (v0.5.x):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k Keeper) CallEVMWithData(
    ctx sdk.Context,
    from common.Address,
    contract *common.Address,
    data []byte,
    commit bool,
    gasCap *big.Int,
) (*types.MsgEthereumTxResponse, error)
```

**After (v0.6.0):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k Keeper) CallEVMWithData(
    ctx sdk.Context,
    stateDB *statedb.StateDB,
    from common.Address,
    contract *common.Address,
    data []byte,
    commit bool,
    callFromPrecompile bool,
    gasCap *big.Int,
) (*types.MsgEthereumTxResponse, error)
```

##### `ApplyMessage`

**Before (v0.5.x):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k *Keeper) ApplyMessage(
    ctx sdk.Context,
    msg core.Message,
    tracer *tracing.Hooks,
    commit bool,
    internal bool,
) (*types.MsgEthereumTxResponse, error)
```

**After (v0.6.0):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k *Keeper) ApplyMessage(
    ctx sdk.Context,
    stateDB *statedb.StateDB,
    msg core.Message,
    tracer *tracing.Hooks,
    commit bool,
    callFromPrecompile bool,
    internal bool,
) (*types.MsgEthereumTxResponse, error)
```

##### `ApplyMessageWithConfig`

**Before (v0.5.x):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k *Keeper) ApplyMessageWithConfig(
    ctx sdk.Context,
    msg core.Message,
    tracer *tracing.Hooks,
    commit bool,
    cfg *statedb.EVMConfig,
    txConfig statedb.TxConfig,
    internal bool,
    overrides *rpctypes.StateOverride,
) (*types.MsgEthereumTxResponse, error)
```

**After (v0.6.0):**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
func (k *Keeper) ApplyMessageWithConfig(
    ctx sdk.Context,
    stateDB *statedb.StateDB,
    msg core.Message,
    tracer *tracing.Hooks,
    commit bool,
    callFromPrecompile bool,
    cfg *statedb.EVMConfig,
    txConfig statedb.TxConfig,
    internal bool,
    overrides *rpctypes.StateOverride,
) (*types.MsgEthereumTxResponse, error)
```

### Migration Steps

#### For Non-Precompile Contexts

If you're calling EVM functions from **outside** a precompile (e.g., from a module keeper, message server, or query handler):

1. Create a new `stateDB` before calling EVM functions
2. Pass `false` for the `callFromPrecompile` parameter

**Example:**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
import (
    "github.com/cosmos/evm/x/vm/statedb"
)

// Before (v0.5.x)
res, err := k.evmKeeper.CallEVM(
    ctx,
    abi,
    from,
    contract,
    false, // commit
    nil,   // gasCap
    "balanceOf",
    account,
)

// After (v0.6.0)
stateDB := statedb.New(ctx, k.evmKeeper, statedb.NewEmptyTxConfig())
res, err := k.evmKeeper.CallEVM(
    ctx,
    stateDB,
    abi,
    from,
    contract,
    false, // commit
    false, // callFromPrecompile
    nil,   // gasCap
    "balanceOf",
    account,
)
```

#### For Precompile Contexts

If you're calling EVM functions from **within** a precompile:

1. **Reuse the existing `stateDB`** from your precompile context (do not create a new one)
2. Pass `true` for the `callFromPrecompile` parameter
3. The existing `stateDB` is typically available as a parameter in your precompile function

**Example:**

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// In your precompile's Run() method, you'll have access to stateDB
func (p *MyPrecompile) Run(
    evm *vm.EVM,
    contract *vm.Contract,
    readOnly bool,
) ([]byte, error) {
    stateDB := evm.StateDB.(*statedb.StateDB)

    // Use the existing stateDB and set callFromPrecompile=true
    res, err := p.evmKeeper.CallEVM(
        ctx,
        stateDB,        // reuse existing stateDB
        abi,
        from,
        contract,
        true,           // commit (will flush to cache context)
        true,           // callFromPrecompile
        nil,            // gasCap
        "transfer",
        recipient,
        amount,
    )
}
```

### Important Notes

* **Never pass `nil` for `stateDB`**: This will return `ErrNilStateDB` error
* **Commit behavior in precompiles**: When `commit=true` and `callFromPrecompile=true`, the state changes are flushed to the cache context rather than fully committed. This prevents collapsing the cache stack in nested call scenarios.

### EVMKeeper Interface Changes

If you implement or mock the `EVMKeeper` interface, update your implementation:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type EVMKeeper interface {
    // Updated signatures
    ApplyMessage(
        ctx sdk.Context,
        stateDB *statedb.StateDB,
        msg core.Message,
        tracer *tracing.Hooks,
        commit, callFromPrecompile, internal bool,
    ) (*evmtypes.MsgEthereumTxResponse, error)

    CallEVM(
        ctx sdk.Context,
        stateDB *statedb.StateDB,
        abi abi.ABI,
        from, contract common.Address,
        commit, callFromPrecompile bool,
        gasCap *big.Int,
        method string,
        args ...interface{},
    ) (*evmtypes.MsgEthereumTxResponse, error)

    CallEVMWithData(
        ctx sdk.Context,
        stateDB *statedb.StateDB,
        from common.Address,
        contract *common.Address,
        data []byte,
        commit bool,
        callFromPrecompile bool,
        gasCap *big.Int,
    ) (*evmtypes.MsgEthereumTxResponse, error)

    // ... other methods
}
```

***

## 4) ERC20 Keeper Interface Changes

The `ERC20Keeper` interface has new methods:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
type ERC20Keeper interface {
    // ... existing methods

    // New methods in v0.6.0
    IsERC20Enabled(ctx sdk.Context) bool
    GetTokenPairID(ctx sdk.Context, token string) []byte
    ConvertERC20IntoCoinsForNativeToken(
        ctx sdk.Context,
        stateDB *statedb.StateDB,
        contract ethcommon.Address,
        amount math.Int,
        receiver sdk.AccAddress,
        sender ethcommon.Address,
        commit bool,
        callFromPrecompile bool,
    ) (*erc20types.MsgConvertERC20Response, error)
}
```

If you implement this interface, add these methods to your implementation.

***

## 5) Error Handling

A new error type has been added:

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
var ErrNilStateDB = errorsmod.Register(ModuleName, codeErrNilStateDB, "stateDB cannot be nil")
```

This error is returned when `nil` is passed as the `stateDB` parameter to EVM functions.

***

## 6) Build & Tests

```bash theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
go build ./...
go test ./...
```

### Testing Checklist

After migration, verify:

* [ ] All EVM calls pass a valid `stateDB`
* [ ] Non-precompile calls use `callFromPrecompile=false`
* [ ] Precompile calls reuse existing `stateDB` and use `callFromPrecompile=true`
* [ ] Event emission works correctly in both success and revert scenarios
* [ ] State changes are properly committed or reverted

***

## Common Migration Examples

### Example 1: Module Keeper Query

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Before (v0.5.x)
func (k Keeper) QueryBalance(ctx sdk.Context, addr common.Address) (*big.Int, error) {
    res, err := k.evmKeeper.CallEVM(
        ctx, erc20ABI, moduleAddr, contract, false, nil, "balanceOf", addr,
    )
    // ...
}

// After (v0.6.0)
func (k Keeper) QueryBalance(ctx sdk.Context, addr common.Address) (*big.Int, error) {
    stateDB := statedb.New(ctx, k.evmKeeper, statedb.NewEmptyTxConfig())
    res, err := k.evmKeeper.CallEVM(
        ctx, stateDB, erc20ABI, moduleAddr, contract, false, false, nil, "balanceOf", addr,
    )
    // ...
}
```

### Example 2: Message Server Transaction

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Before (v0.5.x)
func (ms msgServer) ConvertCoin(
	    goCtx context.Context,
		msg *types.MsgConvertCoin
    ) (*types.MsgConvertCoinResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)
    // ...
    res, err := ms.evmKeeper.CallEVMWithData(
        ctx, moduleAddr, &contract, data, true, nil,
    )
    // ...
}

// After (v0.6.0)
func (ms msgServer) ConvertCoin(
        goCtx context.Context,
        msg *types.MsgConvertCoin
    ) (*types.MsgConvertCoinResponse, error) {
    ctx := sdk.UnwrapSDKContext(goCtx)
    // ...
    stateDB := statedb.New(ctx, ms.evmKeeper, statedb.NewEmptyTxConfig())
    res, err := ms.evmKeeper.CallEVMWithData(
        ctx, stateDB, moduleAddr, &contract, data, true, false, nil,
    )
    // ...
}
```

### Example 3: Precompile Internal Call

```go theme={"theme":{"light":"github-light-high-contrast","dark":"github-dark-high-contrast"}}
// Before (v0.5.x)
func (p *StakingPrecompile) delegate(
    ctx sdk.Context,
    evm *vm.EVM,
    // ...
) ([]byte, error) {
    // ... delegate logic ...
    res, err := p.evmKeeper.CallEVM(
        ctx, delegationABI, from, contract, true, nil, "afterDelegate",
    )
    // ...
}

// After (v0.6.0)
func (p *StakingPrecompile) delegate(
    ctx sdk.Context,
    evm *vm.EVM,
    stateDB *statedb.StateDB, // typically passed from Run()
    // ...
) ([]byte, error) {
    // ... delegate logic ...
    res, err := p.evmKeeper.CallEVM(
        ctx, stateDB, delegationABI, from, contract, true, true, nil, "afterDelegate",
    )
    // ...
}
```
