diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index d2053943bb54703f041a6fb1dcb87caa510ed72c..6c545cff39bd06f0e83046432123b1a0eef73ac9 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -66,9 +66,9 @@ var (
 // ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
 // DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
 type SimulatedBackend struct {
-	m         *stages.MockSentry
-	getHeader func(hash common.Hash, number uint64) *types.Header
-	checkTEVM func(common.Hash) (bool, error)
+	m               *stages.MockSentry
+	getHeader       func(hash common.Hash, number uint64) *types.Header
+	contractHasTEVM func(common.Hash) (bool, error)
 
 	mu              sync.Mutex
 	prependBlock    *types.Block
@@ -105,7 +105,7 @@ func NewSimulatedBackendWithConfig(alloc core.GenesisAlloc, config *params.Chain
 			return h
 		},
 	}
-	backend.checkTEVM = ethdb.GetCheckTEVM(olddb.NewObjectDatabase(m.DB))
+	backend.contractHasTEVM = ethdb.GetHasTEVM(olddb.NewObjectDatabase(m.DB))
 	backend.events = filters.NewEventSystem(&filterBackend{m.DB, backend})
 	backend.emptyPendingBlock()
 	return backend
@@ -654,7 +654,7 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
 	msg := callMsg{call}
 
 	txContext := core.NewEVMTxContext(msg)
-	evmContext := core.NewEVMBlockContext(block.Header(), b.getHeader, b.m.Engine, nil, b.checkTEVM)
+	evmContext := core.NewEVMBlockContext(block.Header(), b.getHeader, b.m.Engine, nil, b.contractHasTEVM)
 	// Create a new environment which holds all relevant information
 	// about the transaction and calling mechanisms.
 	vmEnv := vm.NewEVM(evmContext, txContext, statedb, b.m.ChainConfig, vm.Config{})
@@ -687,7 +687,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx types.Transac
 		&b.pendingHeader.Coinbase, b.gasPool,
 		b.pendingState, state.NewNoopWriter(),
 		b.pendingHeader, tx,
-		&b.pendingHeader.GasUsed, vm.Config{}, b.checkTEVM); err != nil {
+		&b.pendingHeader.GasUsed, vm.Config{}, b.contractHasTEVM); err != nil {
 		return err
 	}
 	//fmt.Printf("==== Start producing block %d\n", (b.prependBlock.NumberU64() + 1))
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index 5d2cc74e0b115a1efedc2cc6020c264859ca6217..0a9d075b4abe381a4aecc6f27745d5728f1eed48 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -127,15 +127,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
 	)
 	gaspool.AddGas(pre.Env.GasLimit)
 	vmContext := vm.BlockContext{
-		CanTransfer: core.CanTransfer,
-		Transfer:    core.Transfer,
-		Coinbase:    pre.Env.Coinbase,
-		BlockNumber: pre.Env.Number,
-		CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
-		Time:        pre.Env.Timestamp,
-		Difficulty:  pre.Env.Difficulty,
-		GasLimit:    pre.Env.GasLimit,
-		GetHash:     getHash,
+		CanTransfer:     core.CanTransfer,
+		Transfer:        core.Transfer,
+		Coinbase:        pre.Env.Coinbase,
+		BlockNumber:     pre.Env.Number,
+		ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
+		Time:            pre.Env.Timestamp,
+		Difficulty:      pre.Env.Difficulty,
+		GasLimit:        pre.Env.GasLimit,
+		GetHash:         getHash,
 	}
 	// If currentBaseFee is defined, add it to the vmContext.
 	if pre.Env.BaseFee != nil {
diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go
index 77a21acf81c6f0c29d08e0edfa53448d63f5da0e..954b73c29698ea2a351f56d4edca7403f62329e0 100644
--- a/cmd/hack/hack.go
+++ b/cmd/hack/hack.go
@@ -2187,7 +2187,8 @@ func scanReceipts(chaindata string, block uint64) error {
 		intraBlockState := state.New(dbstate)
 
 		getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
-		receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
+		contractHasTEVM := ethdb.GetHasTEVM(tx)
+		receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, contractHasTEVM, block, vmConfig)
 		if err1 != nil {
 			return err1
 		}
@@ -2219,7 +2220,7 @@ func scanReceipts(chaindata string, block uint64) error {
 }
 
 func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
-	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
+	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, contractHasTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
 	header := block.Header()
 	vmConfig.TraceJumpDest = true
 	engine := ethash.NewFullFaker()
@@ -2232,7 +2233,7 @@ func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWrit
 	rules := chainConfig.Rules(block.NumberU64())
 	for i, tx := range block.Transactions() {
 		ibs.Prepare(tx.Hash(), block.Hash(), i)
-		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, checkTEVM)
+		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, contractHasTEVM)
 		if err != nil {
 			return nil, fmt.Errorf("could not apply tx %d [%x] failed: %v", i, tx.Hash(), err)
 		}
diff --git a/cmd/rpcdaemon/commands/debug_api.go b/cmd/rpcdaemon/commands/debug_api.go
index 6eb4c56c9f0bfc76acc4b137ee68050d97a70bc9..8902cd4b2d83b5693c7274a5c02a2b3c3a230153 100644
--- a/cmd/rpcdaemon/commands/debug_api.go
+++ b/cmd/rpcdaemon/commands/debug_api.go
@@ -72,8 +72,8 @@ func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash co
 	getHeader := func(hash common.Hash, number uint64) *types.Header {
 		return rawdb.ReadHeader(tx, hash, number)
 	}
-	checkTEVM := ethdb.GetCheckTEVM(tx)
-	_, _, _, _, stateReader, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, checkTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
+	_, _, _, _, stateReader, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, contractHasTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
 	if err != nil {
 		return StorageRangeResult{}, err
 	}
@@ -235,8 +235,8 @@ func (api *PrivateDebugAPIImpl) AccountAt(ctx context.Context, blockHash common.
 	getHeader := func(hash common.Hash, number uint64) *types.Header {
 		return rawdb.ReadHeader(tx, hash, number)
 	}
-	checkTEVM := ethdb.GetCheckTEVM(tx)
-	_, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, checkTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
+	_, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, contractHasTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/rpcdaemon/commands/eth_block.go b/cmd/rpcdaemon/commands/eth_block.go
index c126520956b8348c6c978b07f7c55fe85d5c2362..092bff555621f54d4785e87866410e514936bab7 100644
--- a/cmd/rpcdaemon/commands/eth_block.go
+++ b/cmd/rpcdaemon/commands/eth_block.go
@@ -14,6 +14,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/core/vm"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/rpc"
 	"github.com/ledgerwatch/erigon/turbo/adapter/ethapi"
 	"github.com/ledgerwatch/erigon/turbo/rpchelper"
@@ -91,7 +92,7 @@ func (api *APIImpl) CallBundle(ctx context.Context, txHashes []common.Hash, stat
 		return nil, err
 	}
 
-	blockCtx, txCtx := transactions.GetEvmContext(firstMsg, header, stateBlockNumberOrHash.RequireCanonical, tx)
+	blockCtx, txCtx := transactions.GetEvmContext(firstMsg, header, stateBlockNumberOrHash.RequireCanonical, tx, ethdb.GetHasTEVM(tx))
 	evm := vm.NewEVM(blockCtx, txCtx, st, chainConfig, vm.Config{Debug: false})
 
 	timeoutMilliSeconds := int64(5000)
diff --git a/cmd/rpcdaemon/commands/eth_call.go b/cmd/rpcdaemon/commands/eth_call.go
index 03e9608dbb68e6142d27faa68a2f15ad1936102b..32e7a66cf215f2fd1ada5a1c4ac5b93a8f259ac6 100644
--- a/cmd/rpcdaemon/commands/eth_call.go
+++ b/cmd/rpcdaemon/commands/eth_call.go
@@ -14,6 +14,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/core/vm"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/internal/ethapi"
 	"github.com/ledgerwatch/erigon/params"
 	"github.com/ledgerwatch/erigon/rpc"
@@ -38,7 +39,9 @@ func (api *APIImpl) Call(ctx context.Context, args ethapi.CallArgs, blockNrOrHas
 		args.Gas = (*hexutil.Uint64)(&api.GasCap)
 	}
 
-	result, err := transactions.DoCall(ctx, args, tx, blockNrOrHash, overrides, api.GasCap, chainConfig, api.filters)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
+
+	result, err := transactions.DoCall(ctx, args, tx, blockNrOrHash, overrides, api.GasCap, chainConfig, api.filters, contractHasTEVM)
 	if err != nil {
 		return nil, err
 	}
@@ -163,11 +166,13 @@ func (api *APIImpl) EstimateGas(ctx context.Context, args ethapi.CallArgs, block
 		return 0, err
 	}
 
+	contractHasTEVM := ethdb.GetHasTEVM(dbtx)
+
 	// Create a helper to check if a gas allowance results in an executable transaction
 	executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
 		args.Gas = (*hexutil.Uint64)(&gas)
 
-		result, err := transactions.DoCall(ctx, args, dbtx, rpc.BlockNumberOrHash{BlockNumber: &lastBlockNum}, nil, api.GasCap, chainConfig, api.filters)
+		result, err := transactions.DoCall(ctx, args, dbtx, rpc.BlockNumberOrHash{BlockNumber: &lastBlockNum}, nil, api.GasCap, chainConfig, api.filters, contractHasTEVM)
 		if err != nil {
 			if errors.Is(err, core.ErrIntrinsicGas) {
 				// Special case, raise gas limit
diff --git a/cmd/rpcdaemon/commands/eth_receipts.go b/cmd/rpcdaemon/commands/eth_receipts.go
index edfd25be0b7c2f91b0585d56227759efccf7757b..8e3991befa8e2e55caf0bf61758c43b1c5bd330c 100644
--- a/cmd/rpcdaemon/commands/eth_receipts.go
+++ b/cmd/rpcdaemon/commands/eth_receipts.go
@@ -37,8 +37,8 @@ func getReceipts(ctx context.Context, tx kv.Tx, chainConfig *params.ChainConfig,
 	getHeader := func(hash common.Hash, number uint64) *types.Header {
 		return rawdb.ReadHeader(tx, hash, number)
 	}
-	checkTEVM := ethdb.GetCheckTEVM(tx)
-	_, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, checkTEVM, ethash.NewFaker(), tx, block.Hash(), 0)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
+	_, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, contractHasTEVM, ethash.NewFaker(), tx, block.Hash(), 0)
 	if err != nil {
 		return nil, err
 	}
@@ -48,7 +48,7 @@ func getReceipts(ctx context.Context, tx kv.Tx, chainConfig *params.ChainConfig,
 	var usedGas = new(uint64)
 	for i, txn := range block.Transactions() {
 		ibs.Prepare(txn.Hash(), block.Hash(), i)
-		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, ethash.NewFaker(), nil, gp, ibs, state.NewNoopWriter(), block.Header(), txn, usedGas, vm.Config{}, checkTEVM)
+		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, ethash.NewFaker(), nil, gp, ibs, state.NewNoopWriter(), block.Header(), txn, usedGas, vm.Config{}, contractHasTEVM)
 		if err != nil {
 			return nil, err
 		}
diff --git a/cmd/rpcdaemon/commands/trace_adhoc.go b/cmd/rpcdaemon/commands/trace_adhoc.go
index b652e150cf1d065d4cbd7937f20828ef4dc2a605..70cba2bb915f649fb2d6b8feb7d7829bea1e6b4c 100644
--- a/cmd/rpcdaemon/commands/trace_adhoc.go
+++ b/cmd/rpcdaemon/commands/trace_adhoc.go
@@ -22,6 +22,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/types/accounts"
 	"github.com/ledgerwatch/erigon/core/vm"
 	"github.com/ledgerwatch/erigon/core/vm/stack"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/rpc"
 	"github.com/ledgerwatch/erigon/turbo/rpchelper"
 	"github.com/ledgerwatch/erigon/turbo/shards"
@@ -902,7 +903,7 @@ func (api *TraceAPIImpl) Call(ctx context.Context, args TraceCallParam, traceTyp
 		return nil, err
 	}
 
-	blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, tx)
+	blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, tx, ethdb.GetHasTEVM(tx))
 	blockCtx.GasLimit = math.MaxUint64
 	blockCtx.MaxGasLimit = true
 
@@ -1107,7 +1108,7 @@ func (api *TraceAPIImpl) doCallMany(ctx context.Context, dbtx kv.Tx, msgs []type
 		}
 
 		// Get a new instance of the EVM.
-		blockCtx, txCtx := transactions.GetEvmContext(msg, header, parentNrOrHash.RequireCanonical, dbtx)
+		blockCtx, txCtx := transactions.GetEvmContext(msg, header, parentNrOrHash.RequireCanonical, dbtx, ethdb.GetHasTEVM(dbtx))
 		if useParent {
 			blockCtx.GasLimit = math.MaxUint64
 			blockCtx.MaxGasLimit = true
diff --git a/cmd/rpcdaemon/commands/tracing.go b/cmd/rpcdaemon/commands/tracing.go
index 87f4f928aaeaedd8254b9651a4914e89f26562a0..d9dce5a2c3ea62e70d3a8b02ee6abc22c3046be6 100644
--- a/cmd/rpcdaemon/commands/tracing.go
+++ b/cmd/rpcdaemon/commands/tracing.go
@@ -12,6 +12,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/eth/tracers"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/internal/ethapi"
 	"github.com/ledgerwatch/erigon/rpc"
 	"github.com/ledgerwatch/erigon/turbo/rpchelper"
@@ -53,7 +54,8 @@ func (api *PrivateDebugAPIImpl) TraceTransaction(ctx context.Context, hash commo
 	getHeader := func(hash common.Hash, number uint64) *types.Header {
 		return rawdb.ReadHeader(tx, hash, number)
 	}
-	msg, blockCtx, txCtx, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, nil /* checkTEVM */, ethash.NewFaker(), tx, blockHash, txIndex)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
+	msg, blockCtx, txCtx, ibs, _, err := transactions.ComputeTxEnv(ctx, block, chainConfig, getHeader, contractHasTEVM, ethash.NewFaker(), tx, blockHash, txIndex)
 	if err != nil {
 		stream.WriteNil()
 		return err
@@ -106,7 +108,8 @@ func (api *PrivateDebugAPIImpl) TraceCall(ctx context.Context, args ethapi.CallA
 	if err != nil {
 		return err
 	}
-	blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, dbtx)
+
+	blockCtx, txCtx := transactions.GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, dbtx, ethdb.GetHasTEVM(dbtx))
 	// Trace the transaction and return
 	return transactions.TraceTx(ctx, msg, blockCtx, txCtx, ibs, config, chainConfig, stream)
 }
diff --git a/cmd/snapshots/debug/debug_test.go b/cmd/snapshots/debug/debug_test.go
index cb5e510e49dfdfb28b833d2ed7f644825b430c9d..a5ea362db2461e81f0f5f20579c8377eae1521c1 100644
--- a/cmd/snapshots/debug/debug_test.go
+++ b/cmd/snapshots/debug/debug_test.go
@@ -121,9 +121,9 @@ func TestMatreshkaStream(t *testing.T) {
 			t.Fatal(err, currentBlock)
 		}
 
-		checkTEVM := ethdb.GetCheckTEVM(tx)
+		contractHasTEVM := ethdb.GetHasTEVM(tx)
 
-		_, err = core.ExecuteBlockEphemerally(chainConfig, &vm.Config{NoReceipts: true}, getHeader, ethash.NewFaker(), block, stateReaderWriter, stateReaderWriter, nil, nil, checkTEVM)
+		_, err = core.ExecuteBlockEphemerally(chainConfig, &vm.Config{NoReceipts: true}, getHeader, ethash.NewFaker(), block, stateReaderWriter, stateReaderWriter, nil, nil, contractHasTEVM)
 		if err != nil {
 			t.Fatal(err, currentBlock)
 		}
diff --git a/cmd/state/commands/check_change_sets.go b/cmd/state/commands/check_change_sets.go
index c2cd22839f1d0c20584b3dbbd4dfb6edab4410dc..acbaf1a50e6a5b7daa5d16f4859a289c289b45b6 100644
--- a/cmd/state/commands/check_change_sets.go
+++ b/cmd/state/commands/check_change_sets.go
@@ -21,6 +21,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/core/vm"
 	"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/log/v3"
 	"github.com/spf13/cobra"
 )
@@ -138,7 +139,8 @@ func CheckChangeSets(genesis *core.Genesis, logger log.Logger, blockNum uint64,
 		}
 
 		getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(rwtx, hash, number) }
-		receipts, err1 := runBlock(intraBlockState, noOpWriter, blockWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
+		contractHasTEVM := ethdb.GetHasTEVM(rwtx)
+		receipts, err1 := runBlock(intraBlockState, noOpWriter, blockWriter, chainConfig, getHeader, contractHasTEVM, block, vmConfig)
 		if err1 != nil {
 			return err1
 		}
diff --git a/cmd/state/commands/opcode_tracer.go b/cmd/state/commands/opcode_tracer.go
index ed0fec840cc0fa9a5f15c171a6a19f1eb24922e0..379140c96d6af2cfa80f4ec294cca7c7172a3a8f 100644
--- a/cmd/state/commands/opcode_tracer.go
+++ b/cmd/state/commands/opcode_tracer.go
@@ -25,6 +25,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/core/vm"
 	"github.com/ledgerwatch/erigon/core/vm/stack"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/params"
 	"github.com/ledgerwatch/log/v3"
 	"github.com/spf13/cobra"
@@ -549,7 +550,8 @@ func OpcodeTracer(genesis *core.Genesis, blockNum uint64, chaindata string, numB
 		intraBlockState.SetTracer(ot)
 
 		getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(historyTx, hash, number) }
-		receipts, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
+		contractHasTEVM := ethdb.GetHasTEVM(historyTx)
+		receipts, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, contractHasTEVM, block, vmConfig)
 		if err1 != nil {
 			return err1
 		}
@@ -664,7 +666,7 @@ func check(e error) {
 }
 
 func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
-	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
+	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, contractHasTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
 	header := block.Header()
 	vmConfig.TraceJumpDest = true
 	engine := ethash.NewFullFaker()
@@ -677,7 +679,7 @@ func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWrit
 	rules := chainConfig.Rules(block.NumberU64())
 	for i, tx := range block.Transactions() {
 		ibs.Prepare(tx.Hash(), block.Hash(), i)
-		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, checkTEVM)
+		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, contractHasTEVM)
 		if err != nil {
 			return nil, fmt.Errorf("could not apply tx %d [%x] failed: %v", i, tx.Hash(), err)
 		}
diff --git a/core/blockchain.go b/core/blockchain.go
index 133d2ad3dbcaf238e3e042475c41e3af166f793b..b8f99ad8550e2e5bf51566fe62a21ac3df925983 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -97,7 +97,7 @@ func ExecuteBlockEphemerally(
 	stateWriter state.WriterWithChangeSets,
 	epochReader consensus.EpochReader,
 	chainReader consensus.ChainHeaderReader,
-	checkTEVM func(codeHash common.Hash) (bool, error),
+	contractHasTEVM func(codeHash common.Hash) (bool, error),
 ) (types.Receipts, error) {
 	defer blockExecutionTimer.UpdateDuration(time.Now())
 	block.Uncles()
@@ -127,7 +127,7 @@ func ExecuteBlockEphemerally(
 			writeTrace = true
 		}
 
-		receipt, _, err := ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, checkTEVM)
+		receipt, _, err := ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, contractHasTEVM)
 		if writeTrace {
 			w, err1 := os.Create(fmt.Sprintf("txtrace_%x.txt", tx.Hash()))
 			if err1 != nil {
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 9cfac15cfd30e29dde6cfa9eb7c2867befc41eaa..1d722a5e2b9a4f561abd32c550b0f87ea5c11bcc 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -109,8 +109,8 @@ func (b *BlockGen) AddTxWithChain(getHeader func(hash common.Hash, number uint64
 		b.SetCoinbase(common.Address{})
 	}
 	b.ibs.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
-	checkTEVM := func(_ common.Hash) (bool, error) { return false, nil }
-	receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, checkTEVM)
+	contractHasTEVM := func(_ common.Hash) (bool, error) { return false, nil }
+	receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM)
 	if err != nil {
 		panic(err)
 	}
@@ -123,8 +123,8 @@ func (b *BlockGen) AddFailedTxWithChain(getHeader func(hash common.Hash, number
 		b.SetCoinbase(common.Address{})
 	}
 	b.ibs.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
-	checkTEVM := func(common.Hash) (bool, error) { return false, nil }
-	receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, checkTEVM)
+	contractHasTEVM := func(common.Hash) (bool, error) { return false, nil }
+	receipt, _, err := ApplyTransaction(b.config, getHeader, engine, &b.header.Coinbase, b.gasPool, b.ibs, state.NewNoopWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, contractHasTEVM)
 	_ = err // accept failed transactions
 	b.txs = append(b.txs, tx)
 	b.receipts = append(b.receipts, receipt)
diff --git a/core/evm.go b/core/evm.go
index b0e847ab094d4dd6a8a007b3b4eb43ef9173604a..716f037e57042b625b39f0432f39363a7bc6cbdd 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -28,7 +28,7 @@ import (
 )
 
 // NewEVMBlockContext creates a new context for use in the EVM.
-func NewEVMBlockContext(header *types.Header, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, checkTEVM func(contractHash common.Hash) (bool, error)) vm.BlockContext {
+func NewEVMBlockContext(header *types.Header, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, contractHasTEVM func(contractHash common.Hash) (bool, error)) vm.BlockContext {
 	// If we don't have an explicit author (i.e. not mining), extract from the header
 	var beneficiary common.Address
 	if author == nil {
@@ -43,23 +43,22 @@ func NewEVMBlockContext(header *types.Header, getHeader func(hash common.Hash, n
 			panic(fmt.Errorf("header.BaseFee higher than 2^256-1"))
 		}
 	}
-
-	if checkTEVM == nil {
-		checkTEVM = func(_ common.Hash) (bool, error) {
+	if contractHasTEVM == nil {
+		contractHasTEVM = func(_ common.Hash) (bool, error) {
 			return false, nil
 		}
 	}
 	return vm.BlockContext{
-		CanTransfer: CanTransfer,
-		Transfer:    Transfer,
-		GetHash:     GetHashFn(header, getHeader),
-		Coinbase:    beneficiary,
-		BlockNumber: header.Number.Uint64(),
-		Time:        header.Time,
-		Difficulty:  new(big.Int).Set(header.Difficulty),
-		BaseFee:     &baseFee,
-		GasLimit:    header.GasLimit,
-		CheckTEVM:   checkTEVM,
+		CanTransfer:     CanTransfer,
+		Transfer:        Transfer,
+		GetHash:         GetHashFn(header, getHeader),
+		Coinbase:        beneficiary,
+		BlockNumber:     header.Number.Uint64(),
+		Time:            header.Time,
+		Difficulty:      new(big.Int).Set(header.Difficulty),
+		BaseFee:         &baseFee,
+		GasLimit:        header.GasLimit,
+		ContractHasTEVM: contractHasTEVM,
 	}
 }
 
diff --git a/core/state_processor.go b/core/state_processor.go
index 29cbe3c777907f8a58ab03bb383d57254223c775..0a2fdca0a52226e011baad612476363a9be71a52 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -139,9 +139,9 @@ func applyTransaction(config *params.ChainConfig, gp *GasPool, statedb *state.In
 // and uses the input parameters for its environment. It returns the receipt
 // for the transaction, gas used and an error if the transaction failed,
 // indicating the block was invalid.
-func ApplyTransaction(config *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, gp *GasPool, ibs *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, cfg vm.Config, checkTEVM func(contractHash common.Hash) (bool, error)) (*types.Receipt, []byte, error) {
+func ApplyTransaction(config *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, engine consensus.Engine, author *common.Address, gp *GasPool, ibs *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx types.Transaction, usedGas *uint64, cfg vm.Config, contractHasTEVM func(contractHash common.Hash) (bool, error)) (*types.Receipt, []byte, error) {
 	// Create a new context to be used in the EVM environment
-	blockContext := NewEVMBlockContext(header, getHeader, engine, author, checkTEVM)
+	blockContext := NewEVMBlockContext(header, getHeader, engine, author, contractHasTEVM)
 	vmenv := vm.NewEVM(blockContext, vm.TxContext{}, ibs, config, cfg)
 	// Add addresses to access list if applicable
 	// about the transaction and calling mechanisms.
diff --git a/core/vm/evm.go b/core/vm/evm.go
index ffaa046b221647be81a694802256a681f0c32146..7f1893001743b0e0e60092231b61f8ad8317da5e 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -23,7 +23,6 @@ import (
 	"time"
 
 	"github.com/holiman/uint256"
-
 	"github.com/ledgerwatch/erigon/common"
 	"github.com/ledgerwatch/erigon/common/u256"
 	"github.com/ledgerwatch/erigon/crypto"
@@ -62,10 +61,21 @@ func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
 
 // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
 func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
+	callback, err := selectInterpreter(evm, contract)
+	if err != nil {
+		return nil, err
+	}
+
+	defer callback()
+
+	return evm.interpreter.Run(contract, input, readOnly)
+}
+
+func selectInterpreter(evm *EVM, contract *Contract) (func(), error) {
 	interpreter := evm.interpreter
-	defer func() {
+	callback := func() {
 		evm.interpreter = interpreter
-	}()
+	}
 
 	switch contract.vmType {
 	case EVMType:
@@ -76,7 +86,7 @@ func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, err
 		return nil, errors.New("no compatible interpreter")
 	}
 
-	return evm.interpreter.Run(contract, input, readOnly)
+	return callback, nil
 }
 
 // BlockContext provides the EVM with auxiliary information. Once provided
@@ -89,8 +99,8 @@ type BlockContext struct {
 	Transfer TransferFunc
 	// GetHash returns the hash corresponding to n
 	GetHash GetHashFunc
-	// checkTEVM returns true if the contract has TEVM code
-	CheckTEVM func(codeHash common.Hash) (bool, error)
+	// ContractHasTEVM returns true if the contract has TEVM code
+	ContractHasTEVM func(codeHash common.Hash) (bool, error)
 
 	// Block information
 	Coinbase    common.Address // Provides information for COINBASE
@@ -161,9 +171,10 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, state IntraBlockState, chain
 		ChainRules:      chainConfig.Rules(blockCtx.BlockNumber),
 	}
 
+	evmInterp := NewEVMInterpreter(evm, vmConfig)
 	evm.interpreters = []Interpreter{
-		EVMType:  NewEVMInterpreter(evm, vmConfig),
-		TEVMType: NewTEVMInterpreter(evm, vmConfig),
+		EVMType:  evmInterp,
+		TEVMType: NewTEVMInterpreterByVM(evmInterp.VM),
 	}
 	evm.interpreter = evm.interpreters[EVMType]
 
@@ -249,11 +260,11 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 			// The depth-check is already done, and precompiles handled above
 			codehash := evm.IntraBlockState.GetCodeHash(addrCopy)
 
-			var isTEVM bool
-			isTEVM, err = evm.Context.CheckTEVM(codehash)
+			var contractHasTEVM bool
+			contractHasTEVM, err = evm.Context.ContractHasTEVM(codehash)
 
 			if err == nil {
-				contract := NewContract(caller, AccountRef(addrCopy), value, gas, evm.Config.SkipAnalysis, isTEVM)
+				contract := NewContract(caller, AccountRef(addrCopy), value, gas, evm.Config.SkipAnalysis, contractHasTEVM)
 				contract.SetCallCode(&addrCopy, codehash, code)
 				ret, err = run(evm, contract, input, false)
 				gas = contract.Gas
@@ -323,7 +334,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
 		var isTEVM bool
 
 		codeHash := evm.IntraBlockState.GetCodeHash(addrCopy)
-		isTEVM, err = evm.Context.CheckTEVM(codeHash)
+		isTEVM, err = evm.Context.ContractHasTEVM(codeHash)
 
 		if err == nil {
 			contract := NewContract(caller, AccountRef(caller.Address()), value, gas, evm.Config.SkipAnalysis, isTEVM)
@@ -376,7 +387,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 		// Initialise a new contract and make initialise the delegate values
 		var isTEVM bool
 		codeHash := evm.IntraBlockState.GetCodeHash(addrCopy)
-		isTEVM, err = evm.Context.CheckTEVM(codeHash)
+		isTEVM, err = evm.Context.ContractHasTEVM(codeHash)
 
 		if err == nil {
 			contract := NewContract(caller, AccountRef(caller.Address()), nil, gas, evm.Config.SkipAnalysis, isTEVM).AsDelegate()
@@ -442,7 +453,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
 		// The contract is a scoped environment for this execution context only.
 		var isTEVM bool
 		codeHash := evm.IntraBlockState.GetCodeHash(addrCopy)
-		isTEVM, err = evm.Context.CheckTEVM(codeHash)
+		isTEVM, err = evm.Context.ContractHasTEVM(codeHash)
 
 		if err == nil {
 			contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas, evm.Config.SkipAnalysis, isTEVM)
diff --git a/core/vm/evm_test.go b/core/vm/evm_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..9e02dd1784d6f56686245706051cc06cc666c1c0
--- /dev/null
+++ b/core/vm/evm_test.go
@@ -0,0 +1,446 @@
+package vm
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/ledgerwatch/erigon/common"
+	"github.com/ledgerwatch/erigon/params"
+
+	"github.com/holiman/uint256"
+	"pgregory.net/rapid"
+)
+
+func TestInterpreterReadonly(t *testing.T) {
+	rapid.Check(t, func(t *rapid.T) {
+		env := NewEVM(BlockContext{
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
+		}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{EnableTEMV: true})
+
+		isEVMSliceTest := rapid.SliceOfN(rapid.Bool(), 1, -1).Draw(t, "tevm").([]bool)
+		readOnlySliceTest := rapid.SliceOfN(rapid.Bool(), len(isEVMSliceTest), len(isEVMSliceTest)).Draw(t, "readonly").([]bool)
+
+		isEVMCalled := make([]bool, len(isEVMSliceTest))
+		readOnlies := make([]*readOnlyState, len(readOnlySliceTest))
+		currentIdx := new(int)
+		*currentIdx = -1
+
+		evmInterpreter := &testVM{
+			readonlyGetSetter: env.interpreters[EVMType].(*EVMInterpreter),
+			isTEMV:            false,
+
+			recordedReadOnlies:  &readOnlies,
+			recordedIsEVMCalled: &isEVMCalled,
+
+			env:               env,
+			isEVMSliceTest:    isEVMSliceTest,
+			readOnlySliceTest: readOnlySliceTest,
+			currentIdx:        currentIdx,
+		}
+		tevmInterpreter := &testVM{
+			readonlyGetSetter: env.interpreters[TEVMType].(*TEVMInterpreter),
+			isTEMV:            true,
+
+			recordedReadOnlies:  &readOnlies,
+			recordedIsEVMCalled: &isEVMCalled,
+
+			env:               env,
+			isEVMSliceTest:    isEVMSliceTest,
+			readOnlySliceTest: readOnlySliceTest,
+			currentIdx:        currentIdx,
+		}
+
+		env.interpreters[EVMType] = evmInterpreter
+		env.interpreters[TEVMType] = tevmInterpreter
+
+		dummyContract := NewContract(
+			&dummyContractRef{},
+			&dummyContractRef{},
+			new(uint256.Int),
+			0,
+			false,
+			false,
+		)
+
+		newTestSequential(env, currentIdx, readOnlySliceTest, isEVMSliceTest).Run(dummyContract, nil, false)
+
+		var gotReadonly bool
+		var firstReadOnly int
+
+		// properties-invariants
+
+		if len(readOnlies) != len(readOnlySliceTest) {
+			t.Fatalf("expected static calls the same stack length as generated. got %d, expected %d", len(readOnlies), len(readOnlySliceTest))
+		}
+
+		if len(isEVMCalled) != len(isEVMSliceTest) {
+			t.Fatalf("expected VM calls the same stack length as generated. got %d, expected %d", len(isEVMCalled), len(isEVMSliceTest))
+		}
+
+		if *currentIdx != len(readOnlies) {
+			t.Fatalf("expected VM calls the same amount of calls as generated calls. got %d, expected %d", *currentIdx, len(readOnlies))
+		}
+
+		for i, readOnly := range readOnlies {
+			if isEVMCalled[i] != isEVMSliceTest[i] {
+				t.Fatalf("wrong VM was called in %d index, got EVM %t, expected EVM %t",
+					i, isEVMCalled[i], isEVMSliceTest[i])
+			}
+
+			if readOnly.outer != readOnlySliceTest[i] {
+				t.Fatalf("outer readOnly appeared in %d index, got readOnly %t, expected %t",
+					i, readOnly.outer, readOnlySliceTest[i])
+			}
+
+			if i > 0 {
+				if readOnly.before != readOnlies[i-1].in {
+					t.Fatalf("before readOnly appeared in %d index, got readOnly %t, expected %t",
+						i, readOnly.before, readOnlies[i-1].in)
+				}
+			}
+
+			if readOnly.in && !gotReadonly {
+				gotReadonly = true
+				firstReadOnly = i
+			}
+
+			if gotReadonly {
+				if !readOnly.in {
+					t.Fatalf("readOnly appeared in %d index, got non-readOnly in %d: %v",
+						firstReadOnly, i, trace(isEVMCalled, readOnlies))
+				}
+
+				switch {
+				case i < firstReadOnly:
+					if readOnly.after != false {
+						t.Fatalf("after readOnly appeared in %d index(first readonly %d, case <firstReadOnly), got readOnly %t, expected %t",
+							i, firstReadOnly, readOnly.after, false)
+					}
+				case i == firstReadOnly:
+					if readOnly.after != false {
+						t.Fatalf("after readOnly appeared in %d index(first readonly %d, case ==firstReadOnly), got readOnly %t, expected %t",
+							i, firstReadOnly, readOnly.after, false)
+					}
+				case i > firstReadOnly:
+					if readOnly.after != true {
+						t.Fatalf("after readOnly appeared in %d index(first readonly %d, case >firstReadOnly), got readOnly %t, expected %t",
+							i, firstReadOnly, readOnly.after, true)
+					}
+				}
+			} else {
+				if readOnly.after != false {
+					t.Fatalf("after readOnly didn't appear. %d index, got readOnly %t, expected %t",
+						i, readOnly.after, false)
+				}
+			}
+		}
+	})
+}
+
+func TestReadonlyBasicCases(t *testing.T) {
+	cases := []struct {
+		testName          string
+		readonlySliceTest []bool
+
+		expectedReadonlySlice []readOnlyState
+	}{
+		{
+			"simple non-readonly",
+			[]bool{false},
+
+			[]readOnlyState{
+				{
+					false,
+					false,
+					false,
+					false,
+				},
+			},
+		},
+		{
+			"simple readonly",
+			[]bool{true},
+
+			[]readOnlyState{
+				{
+					true,
+					true,
+					true,
+					false,
+				},
+			},
+		},
+
+		{
+			"2 calls non-readonly",
+			[]bool{false, false},
+
+			[]readOnlyState{
+				{
+					false,
+					false,
+					false,
+					false,
+				},
+				{
+					false,
+					false,
+					false,
+					false,
+				},
+			},
+		},
+
+		{
+			"2 calls true,false",
+			[]bool{true, false},
+
+			[]readOnlyState{
+				{
+					true,
+					true,
+					true,
+					false,
+				},
+				{
+					true,
+					true,
+					true,
+					true,
+				},
+			},
+		},
+		{
+			"2 calls false,true",
+			[]bool{false, true},
+
+			[]readOnlyState{
+				{
+					false,
+					false,
+					false,
+					false,
+				},
+				{
+					true,
+					true,
+					true,
+					false,
+				},
+			},
+		},
+		{
+			"2 calls readonly",
+			[]bool{true, true},
+
+			[]readOnlyState{
+				{
+					true,
+					true,
+					true,
+					true,
+				},
+				{
+					true,
+					true,
+					true,
+					true,
+				},
+			},
+		},
+	}
+
+	type evmsParamsTest struct {
+		emvs   []bool
+		suffix string
+	}
+
+	evmsTest := make([]evmsParamsTest, len(cases))
+
+	// fill all possible isEVM combinations
+	for i, testCase := range cases {
+		isEVMSliceTest := make([]bool, len(testCase.readonlySliceTest))
+
+		copy(isEVMSliceTest, testCase.readonlySliceTest)
+		evmsTest[i].emvs = isEVMSliceTest
+
+		suffix := "-isEVMSliceTest"
+		for _, evmParam := range testCase.readonlySliceTest {
+			if evmParam {
+				suffix += "-true"
+			} else {
+				suffix += "-false"
+			}
+		}
+
+		evmsTest[i].suffix = suffix
+	}
+
+	for _, testCase := range cases {
+		for _, evmsParams := range evmsTest {
+
+			testcase := testCase
+			evmsTestcase := evmsParams
+
+			if len(testcase.readonlySliceTest) != len(evmsTestcase.emvs) {
+				continue
+			}
+
+			t.Run(testcase.testName+evmsTestcase.suffix, func(t *testing.T) {
+				readonlySliceTest := testcase.readonlySliceTest
+
+				env := NewEVM(BlockContext{
+					ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
+				}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{EnableTEMV: true})
+
+				readonliesGot := make([]*readOnlyState, len(testcase.readonlySliceTest))
+				isEVMGot := make([]bool, len(evmsTestcase.emvs))
+
+				currentIdx := new(int)
+				*currentIdx = -1
+
+				evmInterpreter := &testVM{
+					readonlyGetSetter: env.interpreters[EVMType].(*EVMInterpreter),
+					isTEMV:            false,
+
+					recordedReadOnlies:  &readonliesGot,
+					recordedIsEVMCalled: &isEVMGot,
+
+					env:               env,
+					isEVMSliceTest:    evmsTestcase.emvs,
+					readOnlySliceTest: testcase.readonlySliceTest,
+					currentIdx:        currentIdx,
+				}
+				tevmInterpreter := &testVM{
+					readonlyGetSetter: env.interpreters[TEVMType].(*TEVMInterpreter),
+					isTEMV:            true,
+
+					recordedReadOnlies:  &readonliesGot,
+					recordedIsEVMCalled: &isEVMGot,
+
+					env:               env,
+					isEVMSliceTest:    evmsTestcase.emvs,
+					readOnlySliceTest: testcase.readonlySliceTest,
+					currentIdx:        currentIdx,
+				}
+
+				env.interpreters[EVMType] = evmInterpreter
+				env.interpreters[TEVMType] = tevmInterpreter
+
+				dummyContract := NewContract(
+					&dummyContractRef{},
+					&dummyContractRef{},
+					new(uint256.Int),
+					0,
+					false,
+					false,
+				)
+
+				newTestSequential(env, currentIdx, readonlySliceTest, evmsTestcase.emvs).Run(dummyContract, nil, false)
+
+				if len(readonliesGot) != len(readonlySliceTest) {
+					t.Fatalf("expected static calls the same stack length as generated. got %d, expected %d - %v", len(readonliesGot), len(readonlySliceTest), readonlySliceTest)
+				}
+
+				if len(isEVMGot) != len(evmsTestcase.emvs) {
+					t.Fatalf("expected VM calls the same stack length as generated. got %d, expected %d - %v, readonly %v", len(isEVMGot), len(evmsTestcase.emvs), evmsTestcase.emvs, readonlySliceTest)
+				}
+
+				if *currentIdx != len(readonlySliceTest) {
+					t.Fatalf("expected VM calls the same amount of calls as generated calls. got %d, expected %d", *currentIdx, len(readonlySliceTest))
+				}
+
+				var gotReadonly bool
+				var firstReadOnly int
+
+				for callIndex, readOnly := range readonliesGot {
+					if isEVMGot[callIndex] != evmsTestcase.emvs[callIndex] {
+						t.Fatalf("wrong VM was called in %d index, got EVM %t, expected EVM %t. Test EVMs %v; test readonly %v",
+							callIndex, isEVMGot[callIndex], evmsTestcase.emvs[callIndex], evmsTestcase.emvs, readonlySliceTest)
+					}
+
+					if readOnly.outer != readonlySliceTest[callIndex] {
+						t.Fatalf("outer readOnly appeared in %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+							callIndex, readOnly.outer, readonlySliceTest[callIndex], evmsTestcase.emvs, readonlySliceTest)
+					}
+
+					if callIndex > 0 {
+						if readOnly.before != readonliesGot[callIndex-1].in {
+							t.Fatalf("before readOnly appeared in %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+								callIndex, readOnly.before, readonliesGot[callIndex-1].in, evmsTestcase.emvs, readonlySliceTest)
+						}
+					}
+
+					if readOnly.in && !gotReadonly {
+						gotReadonly = true
+						firstReadOnly = callIndex
+					}
+
+					if gotReadonly {
+						if !readOnly.in {
+							t.Fatalf("readOnly appeared in %d index, got non-readOnly in %d: %v. Test EVMs %v; test readonly %v",
+								firstReadOnly, callIndex, trace(isEVMGot, readonliesGot), evmsTestcase.emvs, readonlySliceTest)
+						}
+
+						switch {
+						case callIndex < firstReadOnly:
+							if readOnly.after != false {
+								t.Fatalf("after readOnly appeared in %d index(first readonly %d, case <firstReadOnly), got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+									callIndex, firstReadOnly, readOnly.after, false, evmsTestcase.emvs, readonlySliceTest)
+							}
+						case callIndex == firstReadOnly:
+							if readOnly.after != false {
+								t.Fatalf("after readOnly appeared in %d index(first readonly %d, case ==firstReadOnly), got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+									callIndex, firstReadOnly, readOnly.after, false, evmsTestcase.emvs, readonlySliceTest)
+							}
+						case callIndex > firstReadOnly:
+							if readOnly.after != true {
+								t.Fatalf("after readOnly appeared in %d index(first readonly %d, case >firstReadOnly), got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+									callIndex, firstReadOnly, readOnly.after, true, evmsTestcase.emvs, readonlySliceTest)
+							}
+						}
+					} else {
+						if readOnly.after != false {
+							t.Fatalf("after readOnly didn't appear. %d index, got readOnly %t, expected %t. Test EVMs %v; test readonly %v",
+								callIndex, readOnly.after, false, evmsTestcase.emvs, readonlySliceTest)
+						}
+					}
+				}
+			})
+		}
+	}
+}
+
+type testSequential struct {
+	env         *EVM
+	currentIdx  *int
+	readOnlys   []bool
+	isEVMCalled []bool
+}
+
+func newTestSequential(env *EVM, currentIdx *int, readonlies []bool, isEVMCalled []bool) *testSequential {
+	return &testSequential{env, currentIdx, readonlies, isEVMCalled}
+}
+
+func (st *testSequential) Run(_ *Contract, _ []byte, _ bool) ([]byte, error) {
+	*st.currentIdx++
+
+	nextContract := NewContract(
+		&dummyContractRef{},
+		&dummyContractRef{},
+		new(uint256.Int),
+		0,
+		false,
+		!st.isEVMCalled[*st.currentIdx],
+	)
+
+	return run(st.env, nextContract, nil, st.readOnlys[*st.currentIdx])
+}
+
+func trace(isEVMSlice []bool, readOnlySlice []*readOnlyState) string {
+	res := "trace:\n"
+	for i := 0; i < len(isEVMSlice); i++ {
+		res += fmt.Sprintf("%d: EVM %t, readonly %t\n", i, isEVMSlice[i], readOnlySlice[i].in)
+	}
+	return res
+}
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index dd26a0b832eaca5a83a7b0daeb0a72f2d1ac0dc0..d02fe88c63762f47598d64fc738fe4a877a2965d 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -96,9 +96,9 @@ func TestEIP2200(t *testing.T) {
 
 			_ = s.CommitBlock(params.AllEthashProtocolChanges.Rules(0), state.NewPlainStateWriter(tx, tx, 0))
 			vmctx := BlockContext{
-				CanTransfer: func(IntraBlockState, common.Address, *uint256.Int) bool { return true },
-				Transfer:    func(IntraBlockState, common.Address, common.Address, *uint256.Int, bool) {},
-				CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
+				CanTransfer:     func(IntraBlockState, common.Address, *uint256.Int) bool { return true },
+				Transfer:        func(IntraBlockState, common.Address, common.Address, *uint256.Int, bool) {},
+				ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 			}
 			vmenv := NewEVM(vmctx, TxContext{}, s, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
 
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 33718098973b3dd24f5b1c3ef55a1e595fbb9d2f..a10f3a41b28d15e46fb75a06112f53867763b47a 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -96,7 +96,7 @@ func init() {
 func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		pc             = uint64(0)
@@ -197,7 +197,7 @@ func TestSAR(t *testing.T) {
 func TestAddMod(t *testing.T) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		evmInterpreter = NewEVMInterpreter(env, env.Config)
@@ -286,7 +286,7 @@ func TestJsonTestcases(t *testing.T) {
 func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		evmInterpreter = NewEVMInterpreter(env, env.Config)
@@ -522,7 +522,7 @@ func BenchmarkOpIsZero(b *testing.B) {
 func TestOpMstore(t *testing.T) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		mem            = NewMemory()
@@ -548,7 +548,7 @@ func TestOpMstore(t *testing.T) {
 func BenchmarkOpMstore(bench *testing.B) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		mem            = NewMemory()
@@ -571,7 +571,7 @@ func BenchmarkOpMstore(bench *testing.B) {
 func BenchmarkOpSHA3(bench *testing.B) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, nil, params.TestChainConfig, Config{})
 		stack          = stack.New()
 		mem            = NewMemory()
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 78b3e03587002498ca6673173ce618b07abf96cd..97a3906166bd4618c66d70b2431ccc528c5d61f8 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -69,11 +69,16 @@ type keccakState interface {
 
 // EVMInterpreter represents an EVM interpreter
 type EVMInterpreter struct {
+	*VM
+	jt *JumpTable // EVM instruction table
+}
+
+//structcheck doesn't see embedding
+//nolint:structcheck
+type VM struct {
 	evm *EVM
 	cfg Config
 
-	jt *JumpTable // EVM instruction table
-
 	hasher    keccakState // Keccak256 hasher instance shared across opcodes
 	hasherBuf common.Hash // Keccak256 hasher result array shared across opcodes
 
@@ -115,9 +120,49 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
 	}
 
 	return &EVMInterpreter{
-		evm: evm,
-		cfg: cfg,
-		jt:  jt,
+		VM: &VM{
+			evm: evm,
+			cfg: cfg,
+		},
+		jt: jt,
+	}
+}
+
+func NewEVMInterpreterByVM(vm *VM) *EVMInterpreter {
+	var jt *JumpTable
+	switch {
+	case vm.evm.ChainRules.IsLondon:
+		jt = &londonInstructionSet
+	case vm.evm.ChainRules.IsBerlin:
+		jt = &berlinInstructionSet
+	case vm.evm.ChainRules.IsIstanbul:
+		jt = &istanbulInstructionSet
+	case vm.evm.ChainRules.IsConstantinople:
+		jt = &constantinopleInstructionSet
+	case vm.evm.ChainRules.IsByzantium:
+		jt = &byzantiumInstructionSet
+	case vm.evm.ChainRules.IsEIP158:
+		jt = &spuriousDragonInstructionSet
+	case vm.evm.ChainRules.IsEIP150:
+		jt = &tangerineWhistleInstructionSet
+	case vm.evm.ChainRules.IsHomestead:
+		jt = &homesteadInstructionSet
+	default:
+		jt = &frontierInstructionSet
+	}
+	if len(vm.cfg.ExtraEips) > 0 {
+		for i, eip := range vm.cfg.ExtraEips {
+			if err := EnableEIP(eip, jt); err != nil {
+				// Disable it, so caller can check if it's activated or not
+				vm.cfg.ExtraEips = append(vm.cfg.ExtraEips[:i], vm.cfg.ExtraEips[i+1:]...)
+				log.Error("EIP activation failed", "eip", eip, "error", err)
+			}
+		}
+	}
+
+	return &EVMInterpreter{
+		VM: vm,
+		jt: jt,
 	}
 }
 
@@ -134,10 +179,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 
 	// Make sure the readOnly is only set if we aren't in readOnly yet.
 	// This makes also sure that the readOnly flag isn't removed for child calls.
-	if readOnly && !in.readOnly {
-		in.readOnly = true
-		defer func() { in.readOnly = false }()
-	}
+	callback := in.setReadonly(readOnly)
+	defer func() {
+		callback()
+	}()
 
 	// Reset the previous call's return data. It's unimportant to preserve the old buffer
 	// as every returning call will return new data anyway.
@@ -290,3 +335,17 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 	}
 	return nil, nil
 }
+
+func (vm *VM) setReadonly(outerReadonly bool) func() {
+	if outerReadonly && !vm.readOnly {
+		vm.readOnly = true
+		return func() {
+			vm.readOnly = false
+		}
+	}
+	return func() {}
+}
+
+func (vm *VM) getReadonly() bool {
+	return vm.readOnly
+}
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index f47c8a92d26e979f7a4cc8a259022988feb488ea..69cb44bde67c0240d6e5ad264454008c180bdead 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -23,38 +23,14 @@ import (
 	"github.com/holiman/uint256"
 
 	"github.com/ledgerwatch/erigon/common"
-	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/vm/stack"
 	"github.com/ledgerwatch/erigon/params"
 )
 
-type dummyContractRef struct {
-	calledForEach bool
-}
-
-func (dummyContractRef) ReturnGas(*big.Int)          {}
-func (dummyContractRef) Address() common.Address     { return common.Address{} }
-func (dummyContractRef) Value() *big.Int             { return new(big.Int) }
-func (dummyContractRef) SetCode(common.Hash, []byte) {}
-func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
-	d.calledForEach = true
-}
-func (d *dummyContractRef) SubBalance(amount *big.Int) {}
-func (d *dummyContractRef) AddBalance(amount *big.Int) {}
-func (d *dummyContractRef) SetBalance(*big.Int)        {}
-func (d *dummyContractRef) SetNonce(uint64)            {}
-func (d *dummyContractRef) Balance() *big.Int          { return new(big.Int) }
-
-type dummyStatedb struct {
-	state.IntraBlockState
-}
-
-func (*dummyStatedb) GetRefund() uint64 { return 1337 }
-
 func TestStoreCapture(t *testing.T) {
 	var (
 		env = NewEVM(BlockContext{
-			CheckTEVM: func(common.Hash) (bool, error) { return false, nil },
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, TxContext{}, &dummyStatedb{}, params.TestChainConfig, Config{})
 		logger   = NewStructLogger(nil)
 		mem      = NewMemory()
diff --git a/core/vm/mock_vm.go b/core/vm/mock_vm.go
new file mode 100644
index 0000000000000000000000000000000000000000..6228a307ec45e9d0dca65dc9b3ce68d6a82aa17a
--- /dev/null
+++ b/core/vm/mock_vm.go
@@ -0,0 +1,99 @@
+package vm
+
+import (
+	"fmt"
+	"math/big"
+
+	"github.com/holiman/uint256"
+
+	"github.com/ledgerwatch/erigon/common"
+	"github.com/ledgerwatch/erigon/core/state"
+)
+
+type readonlyGetSetter interface {
+	setReadonly(outerReadonly bool) func()
+	getReadonly() bool
+}
+
+type testVM struct {
+	readonlyGetSetter
+	isTEMV bool
+
+	recordedReadOnlies  *[]*readOnlyState
+	recordedIsEVMCalled *[]bool
+
+	env               *EVM
+	isEVMSliceTest    []bool
+	readOnlySliceTest []bool
+	currentIdx        *int
+}
+
+func (evm *testVM) Run(_ *Contract, _ []byte, readOnly bool) (ret []byte, err error) {
+	currentReadOnly := new(readOnlyState)
+
+	currentReadOnly.outer = readOnly
+	currentReadOnly.before = evm.getReadonly()
+
+	currentIndex := *evm.currentIdx
+
+	callback := evm.setReadonly(readOnly)
+	defer func() {
+		callback()
+		currentReadOnly.after = evm.getReadonly()
+	}()
+
+	currentReadOnly.in = evm.getReadonly()
+
+	(*evm.recordedReadOnlies)[currentIndex] = currentReadOnly
+	(*evm.recordedIsEVMCalled)[currentIndex] = !evm.isTEMV
+
+	*evm.currentIdx++
+
+	if *evm.currentIdx < len(evm.readOnlySliceTest) {
+		res, err := run(evm.env, NewContract(
+			&dummyContractRef{},
+			&dummyContractRef{},
+			new(uint256.Int),
+			0,
+			false,
+			!evm.isEVMSliceTest[*evm.currentIdx],
+		), nil, evm.readOnlySliceTest[*evm.currentIdx])
+		return res, err
+	}
+
+	return
+}
+
+type readOnlyState struct {
+	outer  bool
+	before bool
+	in     bool
+	after  bool
+}
+
+func (r *readOnlyState) String() string {
+	return fmt.Sprintf("READONLY Status: outer %t; before %t; in %t; after %t", r.outer, r.before, r.in, r.after)
+}
+
+type dummyContractRef struct {
+	calledForEach bool
+}
+
+func (dummyContractRef) ReturnGas(*big.Int)          {}
+func (dummyContractRef) Address() common.Address     { return common.Address{} }
+func (dummyContractRef) Value() *big.Int             { return new(big.Int) }
+func (dummyContractRef) SetCode(common.Hash, []byte) {}
+func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
+	d.calledForEach = true
+}
+func (d *dummyContractRef) SubBalance(amount *big.Int) {}
+func (d *dummyContractRef) AddBalance(amount *big.Int) {}
+func (d *dummyContractRef) SetBalance(*big.Int)        {}
+func (d *dummyContractRef) SetNonce(uint64)            {}
+func (d *dummyContractRef) Balance() *big.Int          { return new(big.Int) }
+
+type dummyStatedb struct {
+	state.IntraBlockState
+}
+
+func (*dummyStatedb) GetRefund() uint64 { return 1337 }
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index fdd2fb31d2996e2209b50e70f887c40e5b647a0e..bf514f74e0395f9cf49e3afd43e6b46e0d449b59 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -19,6 +19,7 @@ package runtime
 import (
 	"github.com/ledgerwatch/erigon/core"
 	"github.com/ledgerwatch/erigon/core/vm"
+	"github.com/ledgerwatch/erigon/ethdb"
 )
 
 func NewEnv(cfg *Config) *vm.EVM {
@@ -26,17 +27,18 @@ func NewEnv(cfg *Config) *vm.EVM {
 		Origin:   cfg.Origin,
 		GasPrice: cfg.GasPrice,
 	}
+
 	blockContext := vm.BlockContext{
-		CanTransfer: core.CanTransfer,
-		Transfer:    core.Transfer,
-		GetHash:     cfg.GetHashFn,
-		CheckTEVM:   cfg.CheckTEVM,
-		Coinbase:    cfg.Coinbase,
-		BlockNumber: cfg.BlockNumber.Uint64(),
-		Time:        cfg.Time.Uint64(),
-		Difficulty:  cfg.Difficulty,
-		GasLimit:    cfg.GasLimit,
-		BaseFee:     cfg.BaseFee,
+		CanTransfer:     core.CanTransfer,
+		Transfer:        core.Transfer,
+		GetHash:         cfg.GetHashFn,
+		ContractHasTEVM: ethdb.GetHasTEVM(cfg.kv),
+		Coinbase:        cfg.Coinbase,
+		BlockNumber:     cfg.BlockNumber.Uint64(),
+		Time:            cfg.Time.Uint64(),
+		Difficulty:      cfg.Difficulty,
+		GasLimit:        cfg.GasLimit,
+		BaseFee:         cfg.BaseFee,
 	}
 
 	return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 799e49274507a3bd334912bfdcb087ac13fc2e2c..82757cabde1080e8fe11186bdc940f9f928e57b2 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -22,6 +22,7 @@ import (
 	"time"
 
 	"github.com/holiman/uint256"
+	"github.com/ledgerwatch/erigon-lib/kv"
 	"github.com/ledgerwatch/erigon-lib/kv/memdb"
 	"github.com/ledgerwatch/erigon/ethdb/olddb"
 
@@ -51,8 +52,8 @@ type Config struct {
 	State     *state.IntraBlockState
 	r         state.StateReader
 	w         state.StateWriter
+	kv        kv.Has
 	GetHashFn func(n uint64) common.Hash
-	CheckTEVM func(common.Hash) (bool, error)
 }
 
 // sets defaults on the config
@@ -100,9 +101,6 @@ func setDefaults(cfg *Config) {
 			return common.BytesToHash(crypto.Keccak256([]byte(new(big.Int).SetUint64(n).String())))
 		}
 	}
-	if cfg.CheckTEVM == nil {
-		cfg.CheckTEVM = func(common.Hash) (bool, error) { return false, nil }
-	}
 }
 
 // Execute executes the code using the input as call data during the execution.
@@ -121,6 +119,7 @@ func Execute(code, input []byte, cfg *Config, blockNr uint64) ([]byte, *state.In
 		defer db.Close()
 		cfg.r = state.NewDbStateReader(db)
 		cfg.w = state.NewDbStateWriter(db, 0)
+		cfg.kv = db
 		cfg.State = state.New(cfg.r)
 	}
 	var (
@@ -159,6 +158,7 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, common.Address,
 		defer db.Close()
 		cfg.r = state.NewDbStateReader(db)
 		cfg.w = state.NewDbStateWriter(db, 0)
+		cfg.kv = db
 		cfg.State = state.New(cfg.r)
 	}
 	var (
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 6dd6e247595579e24c2c5d2b2e578efeac054e83..4f977937a0815cb42acc1439425eff2bfe71a666 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -115,7 +115,7 @@ func TestCall(t *testing.T) {
 		byte(vm.RETURN),
 	})
 
-	ret, _, err := Call(address, nil, &Config{State: state})
+	ret, _, err := Call(address, nil, &Config{State: state, kv: tx})
 	if err != nil {
 		t.Fatal("didn't expect error", err)
 	}
diff --git a/core/vm/tevm_interpreter.go b/core/vm/tevm_interpreter.go
index f5c73191e3c80fa01fd7b53508b363996010fab1..4053ce944b18f385c3c7c8b931dfe61bd30a8e89 100644
--- a/core/vm/tevm_interpreter.go
+++ b/core/vm/tevm_interpreter.go
@@ -18,3 +18,7 @@ const (
 func NewTEVMInterpreter(evm *EVM, cfg Config) *TEVMInterpreter {
 	return &TEVMInterpreter{NewEVMInterpreter(evm, cfg)}
 }
+
+func NewTEVMInterpreterByVM(vm *VM) *TEVMInterpreter {
+	return &TEVMInterpreter{NewEVMInterpreterByVM(vm)}
+}
diff --git a/eth/stagedsync/stage_execute.go b/eth/stagedsync/stage_execute.go
index 5ed5bbfd8726c48c9db5d61b4db1a72dc6a4aea3..82814cd2ab11b91d51e721d2d085ed531a8e9533 100644
--- a/eth/stagedsync/stage_execute.go
+++ b/eth/stagedsync/stage_execute.go
@@ -97,7 +97,7 @@ func executeBlock(
 	writeChangesets bool,
 	writeReceipts bool,
 	writeCallTraces bool,
-	checkTEVM func(contractHash common.Hash) (bool, error),
+	contractHasTEVM func(contractHash common.Hash) (bool, error),
 	initialCycle bool,
 ) error {
 	blockNum := block.NumberU64()
@@ -106,10 +106,10 @@ func executeBlock(
 	// where the magic happens
 	getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
 
-	callTracer := NewCallTracer(checkTEVM)
+	callTracer := NewCallTracer(contractHasTEVM)
 	vmConfig.Debug = true
 	vmConfig.Tracer = callTracer
-	receipts, err := core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHeader, cfg.engine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx}, checkTEVM)
+	receipts, err := core.ExecuteBlockEphemerally(cfg.chainConfig, &vmConfig, getHeader, cfg.engine, block, stateReader, stateWriter, epochReader{tx: tx}, chainReader{config: cfg.chainConfig, tx: tx}, contractHasTEVM)
 	if err != nil {
 		return err
 	}
@@ -274,17 +274,17 @@ Loop:
 
 		lastLogTx += uint64(block.Transactions().Len())
 
-		var checkTEVMCode func(contractHash common.Hash) (bool, error)
+		var contractHasTEVM func(contractHash common.Hash) (bool, error)
 
 		if cfg.vmConfig.EnableTEMV {
-			checkTEVMCode = ethdb.GetCheckTEVM(tx)
+			contractHasTEVM = ethdb.GetHasTEVM(tx)
 		}
 
 		// Incremental move of next stages depend on fully written ChangeSets, Receipts, CallTraceSet
 		writeChangeSets := nextStagesExpectData || blockNum > cfg.prune.History.PruneTo(to)
 		writeReceipts := nextStagesExpectData || blockNum > cfg.prune.Receipts.PruneTo(to)
 		writeCallTraces := nextStagesExpectData || blockNum > cfg.prune.CallTraces.PruneTo(to)
-		if err = executeBlock(block, tx, batch, cfg, *cfg.vmConfig, writeChangeSets, writeReceipts, writeCallTraces, checkTEVMCode, initialCycle); err != nil {
+		if err = executeBlock(block, tx, batch, cfg, *cfg.vmConfig, writeChangeSets, writeReceipts, writeCallTraces, contractHasTEVM, initialCycle); err != nil {
 			log.Error(fmt.Sprintf("[%s] Execution failed", logPrefix), "block", blockNum, "hash", block.Hash().String(), "error", err)
 			u.UnwindTo(blockNum-1, block.Hash())
 			break Loop
diff --git a/eth/stagedsync/stage_mining_exec.go b/eth/stagedsync/stage_mining_exec.go
index 5ba6050bd8a2c864141e16ab22bb13b35cbc76e5..173a622189d317f6c34a3e3b75e3dfaa7b1d851a 100644
--- a/eth/stagedsync/stage_mining_exec.go
+++ b/eth/stagedsync/stage_mining_exec.go
@@ -74,14 +74,14 @@ func SpawnMiningExecStage(s *StageState, tx kv.RwTx, cfg MiningExecCfg, quit <-c
 	}
 
 	getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
-	checkTEVM := ethdb.GetCheckTEVM(tx)
+	contractHasTEVM := ethdb.GetHasTEVM(tx)
 
 	// Short circuit if there is no available pending transactions.
 	// But if we disable empty precommit already, ignore it. Since
 	// empty block is necessary to keep the liveness of the network.
 	if noempty {
 		if !localTxs.Empty() {
-			logs, err := addTransactionsToMiningBlock(current, cfg.chainConfig, cfg.vmConfig, getHeader, checkTEVM, cfg.engine, localTxs, cfg.miningState.MiningConfig.Etherbase, ibs, quit)
+			logs, err := addTransactionsToMiningBlock(current, cfg.chainConfig, cfg.vmConfig, getHeader, contractHasTEVM, cfg.engine, localTxs, cfg.miningState.MiningConfig.Etherbase, ibs, quit)
 			if err != nil {
 				return err
 			}
@@ -93,7 +93,7 @@ func SpawnMiningExecStage(s *StageState, tx kv.RwTx, cfg MiningExecCfg, quit <-c
 			//}
 		}
 		if !remoteTxs.Empty() {
-			logs, err := addTransactionsToMiningBlock(current, cfg.chainConfig, cfg.vmConfig, getHeader, checkTEVM, cfg.engine, remoteTxs, cfg.miningState.MiningConfig.Etherbase, ibs, quit)
+			logs, err := addTransactionsToMiningBlock(current, cfg.chainConfig, cfg.vmConfig, getHeader, contractHasTEVM, cfg.engine, remoteTxs, cfg.miningState.MiningConfig.Etherbase, ibs, quit)
 			if err != nil {
 				return err
 			}
@@ -145,7 +145,7 @@ func SpawnMiningExecStage(s *StageState, tx kv.RwTx, cfg MiningExecCfg, quit <-c
 	return nil
 }
 
-func addTransactionsToMiningBlock(current *MiningBlock, chainConfig params.ChainConfig, vmConfig *vm.Config, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), engine consensus.Engine, txs types.TransactionsStream, coinbase common.Address, ibs *state.IntraBlockState, quit <-chan struct{}) (types.Logs, error) {
+func addTransactionsToMiningBlock(current *MiningBlock, chainConfig params.ChainConfig, vmConfig *vm.Config, getHeader func(hash common.Hash, number uint64) *types.Header, contractHasTEVM func(common.Hash) (bool, error), engine consensus.Engine, txs types.TransactionsStream, coinbase common.Address, ibs *state.IntraBlockState, quit <-chan struct{}) (types.Logs, error) {
 	header := current.Header
 	tcount := 0
 	gasPool := new(core.GasPool).AddGas(current.Header.GasLimit)
@@ -156,7 +156,7 @@ func addTransactionsToMiningBlock(current *MiningBlock, chainConfig params.Chain
 
 	var miningCommitTx = func(txn types.Transaction, coinbase common.Address, vmConfig *vm.Config, chainConfig params.ChainConfig, ibs *state.IntraBlockState, current *MiningBlock) ([]*types.Log, error) {
 		snap := ibs.Snapshot()
-		receipt, _, err := core.ApplyTransaction(&chainConfig, getHeader, engine, &coinbase, gasPool, ibs, noop, header, txn, &header.GasUsed, *vmConfig, checkTEVM)
+		receipt, _, err := core.ApplyTransaction(&chainConfig, getHeader, engine, &coinbase, gasPool, ibs, noop, header, txn, &header.GasUsed, *vmConfig, contractHasTEVM)
 		if err != nil {
 			ibs.RevertToSnapshot(snap)
 			return nil, err
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index 1be589ed252d31c0bed26f4320485c53594c4b03..c7ffca2faa73b620941134b4c0ca0e87ef129602 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -57,8 +57,8 @@ type vmContext struct {
 
 func testCtx() *vmContext {
 	return &vmContext{blockCtx: vm.BlockContext{
-		BlockNumber: 1,
-		CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
+		BlockNumber:     1,
+		ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 	}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
 }
 
@@ -88,8 +88,8 @@ func TestTracer(t *testing.T) {
 	execTracer := func(code string) []byte {
 		t.Helper()
 		ctx := &vmContext{blockCtx: vm.BlockContext{
-			BlockNumber: 1,
-			CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
+			BlockNumber:     1,
+			ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 		}, txCtx: vm.TxContext{GasPrice: big.NewInt(100000)}}
 		tracer, err := New(code, ctx.txCtx)
 		if err != nil {
@@ -161,8 +161,8 @@ func TestHaltBetweenSteps(t *testing.T) {
 		t.Fatal(err)
 	}
 	env := vm.NewEVM(vm.BlockContext{
-		BlockNumber: 1,
-		CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
+		BlockNumber:     1,
+		ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 	}, vm.TxContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
 	contract := vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0, false, false)
 
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 65dfac255fa651d07466ed781b747c51171f1eda..742beaaf80cc7a8fa154dc3b67c93e670e209464 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -152,14 +152,14 @@ func TestPrestateTracerCreate2(t *testing.T) {
 		GasPrice: big.NewInt(1),
 	}
 	context := vm.BlockContext{
-		CanTransfer: core.CanTransfer,
-		Transfer:    core.Transfer,
-		Coinbase:    common.Address{},
-		CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
-		BlockNumber: 8000000,
-		Time:        5,
-		Difficulty:  big.NewInt(0x30000),
-		GasLimit:    uint64(6000000),
+		CanTransfer:     core.CanTransfer,
+		Transfer:        core.Transfer,
+		Coinbase:        common.Address{},
+		ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
+		BlockNumber:     8000000,
+		Time:            5,
+		Difficulty:      big.NewInt(0x30000),
+		GasLimit:        uint64(6000000),
 	}
 	alloc := core.GenesisAlloc{}
 
@@ -244,14 +244,14 @@ func TestCallTracer(t *testing.T) {
 				GasPrice: big.NewInt(int64(txn.GetPrice().Uint64())),
 			}
 			context := vm.BlockContext{
-				CanTransfer: core.CanTransfer,
-				Transfer:    core.Transfer,
-				Coinbase:    test.Context.Miner,
-				BlockNumber: uint64(test.Context.Number),
-				Time:        uint64(test.Context.Time),
-				Difficulty:  (*big.Int)(test.Context.Difficulty),
-				GasLimit:    uint64(test.Context.GasLimit),
-				CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
+				CanTransfer:     core.CanTransfer,
+				Transfer:        core.Transfer,
+				Coinbase:        test.Context.Miner,
+				BlockNumber:     uint64(test.Context.Number),
+				Time:            uint64(test.Context.Time),
+				Difficulty:      (*big.Int)(test.Context.Difficulty),
+				GasLimit:        uint64(test.Context.GasLimit),
+				ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
 			}
 
 			_, tx := memdb.NewTestTx(t)
diff --git a/ethdb/kv_util.go b/ethdb/kv_util.go
index 586d294e076e262b1830c1c6b63e2ebe43cb7f74..c64656521ce05ad398e0636df53736c136424798 100644
--- a/ethdb/kv_util.go
+++ b/ethdb/kv_util.go
@@ -32,16 +32,16 @@ func Walk(c kv.Cursor, startkey []byte, fixedbits int, walker func(k, v []byte)
 }
 
 // todo: return TEVM code and use it
-func GetCheckTEVM(db kv.Getter) func(contractHash common.Hash) (bool, error) {
-	checked := map[common.Hash]struct{}{}
+func GetHasTEVM(db kv.Has) func(contractHash common.Hash) (bool, error) {
+	contractsWithTEVM := map[common.Hash]struct{}{}
 	var ok bool
 
 	return func(contractHash common.Hash) (bool, error) {
 		if contractHash == (common.Hash{}) {
-			return true, nil
+			return false, nil
 		}
 
-		if _, ok = checked[contractHash]; ok {
+		if _, ok = contractsWithTEVM[contractHash]; ok {
 			return true, nil
 		}
 
@@ -51,11 +51,15 @@ func GetCheckTEVM(db kv.Getter) func(contractHash common.Hash) (bool, error) {
 				contractHash.String(), err)
 		}
 
-		if !ok {
-			checked[contractHash] = struct{}{}
+		if errors.Is(err, ErrKeyNotFound) {
+			return false, nil
+		}
+
+		if ok {
+			contractsWithTEVM[contractHash] = struct{}{}
 		}
 
-		return ok, nil
+		return true, nil
 	}
 }
 
diff --git a/migrations/receipt_repair.go b/migrations/receipt_repair.go
index 41cc0684a5a6ddaa67f8377ade26ff4567abccb5..8cf8055c6ff6e4bc16835ff7e529423feb95ac9c 100644
--- a/migrations/receipt_repair.go
+++ b/migrations/receipt_repair.go
@@ -17,6 +17,7 @@ import (
 	"github.com/ledgerwatch/erigon/core/state"
 	"github.com/ledgerwatch/erigon/core/types"
 	"github.com/ledgerwatch/erigon/core/vm"
+	"github.com/ledgerwatch/erigon/ethdb"
 	"github.com/ledgerwatch/erigon/ethdb/cbor"
 	"github.com/ledgerwatch/erigon/params"
 	"github.com/ledgerwatch/log/v3"
@@ -114,7 +115,8 @@ var ReceiptRepair = Migration{
 			intraBlockState := state.New(dbstate)
 
 			getHeader := func(hash common.Hash, number uint64) *types.Header { return rawdb.ReadHeader(tx, hash, number) }
-			receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, nil /* checkTEVM */, block, vmConfig)
+			contractHasTEVM := ethdb.GetHasTEVM(tx)
+			receipts1, err1 := runBlock(intraBlockState, noOpWriter, noOpWriter, chainConfig, getHeader, contractHasTEVM, block, vmConfig)
 			if err1 != nil {
 				return err1
 			}
@@ -147,7 +149,7 @@ var ReceiptRepair = Migration{
 }
 
 func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWriter state.StateWriter,
-	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
+	chainConfig *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, contractHasTEVM func(common.Hash) (bool, error), block *types.Block, vmConfig vm.Config) (types.Receipts, error) {
 	header := block.Header()
 	vmConfig.TraceJumpDest = true
 	engine := ethash.NewFullFaker()
@@ -159,7 +161,7 @@ func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWrit
 	}
 	for i, tx := range block.Transactions() {
 		ibs.Prepare(tx.Hash(), block.Hash(), i)
-		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, checkTEVM)
+		receipt, _, err := core.ApplyTransaction(chainConfig, getHeader, engine, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, contractHasTEVM)
 		if err != nil {
 			return nil, fmt.Errorf("could not apply tx %d [%x] failed: %v", i, tx.Hash(), err)
 		}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 750002e79d2b95ef1cf7bd2353f00cb09c961790..82cfd4dc2b7ce854bd6dd6c5d82efc89b6421562 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -213,8 +213,8 @@ func (t *StateTest) RunNoVerify(rules params.Rules, tx kv.RwTx, subtest StateSub
 
 	// Prepare the EVM.
 	txContext := core.NewEVMTxContext(msg)
-	checkTEVM := func(common.Hash) (bool, error) { return false, nil }
-	context := core.NewEVMBlockContext(block.Header(), nil, nil, &t.json.Env.Coinbase, checkTEVM)
+	contractHasTEVM := func(common.Hash) (bool, error) { return false, nil }
+	context := core.NewEVMBlockContext(block.Header(), nil, nil, &t.json.Env.Coinbase, contractHasTEVM)
 	context.GetHash = vmTestBlockHash
 	if baseFee != nil {
 		context.BaseFee = new(uint256.Int)
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index c4de0d2cfcb86d344b981c578de2d3e66628c953..163b1b2f6bc50f0c6063591a4c0767c3fb01527f 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -150,15 +150,15 @@ func (t *VMTest) newEVM(state vm.IntraBlockState, vmconfig vm.Config) *vm.EVM {
 	}
 	transfer := func(db vm.IntraBlockState, sender, recipient common.Address, amount *uint256.Int, bailout bool) {}
 	context := vm.BlockContext{
-		CanTransfer: canTransfer,
-		Transfer:    transfer,
-		GetHash:     vmTestBlockHash,
-		CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
-		Coinbase:    t.json.Env.Coinbase,
-		BlockNumber: t.json.Env.Number,
-		Time:        t.json.Env.Timestamp,
-		GasLimit:    t.json.Env.GasLimit,
-		Difficulty:  t.json.Env.Difficulty,
+		CanTransfer:     canTransfer,
+		Transfer:        transfer,
+		GetHash:         vmTestBlockHash,
+		ContractHasTEVM: func(common.Hash) (bool, error) { return false, nil },
+		Coinbase:        t.json.Env.Coinbase,
+		BlockNumber:     t.json.Env.Number,
+		Time:            t.json.Env.Timestamp,
+		GasLimit:        t.json.Env.GasLimit,
+		Difficulty:      t.json.Env.Difficulty,
 	}
 	vmconfig.NoRecursion = true
 	return vm.NewEVM(context, txContext, state, params.MainnetChainConfig, vmconfig)
diff --git a/turbo/transactions/call.go b/turbo/transactions/call.go
index f58889073d2b304d369d3660a8af4691ce285d23..21ee15706b74a11b4c27263adc7b46bf8929ddab 100644
--- a/turbo/transactions/call.go
+++ b/turbo/transactions/call.go
@@ -24,7 +24,7 @@ import (
 
 const callTimeout = 5 * time.Minute
 
-func DoCall(ctx context.Context, args ethapi.CallArgs, tx kv.Tx, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account, gasCap uint64, chainConfig *params.ChainConfig, filters *filters.Filters) (*core.ExecutionResult, error) {
+func DoCall(ctx context.Context, args ethapi.CallArgs, tx kv.Tx, blockNrOrHash rpc.BlockNumberOrHash, overrides *map[common.Address]ethapi.Account, gasCap uint64, chainConfig *params.ChainConfig, filters *filters.Filters, contractHasTEVM func(hash common.Hash) (bool, error)) (*core.ExecutionResult, error) {
 	// todo: Pending state is only known by the miner
 	/*
 		if blockNrOrHash.BlockNumber != nil && *blockNrOrHash.BlockNumber == rpc.PendingBlockNumber {
@@ -112,7 +112,7 @@ func DoCall(ctx context.Context, args ethapi.CallArgs, tx kv.Tx, blockNrOrHash r
 	if err != nil {
 		return nil, err
 	}
-	blockCtx, txCtx := GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, tx)
+	blockCtx, txCtx := GetEvmContext(msg, header, blockNrOrHash.RequireCanonical, tx, contractHasTEVM)
 
 	evm := vm.NewEVM(blockCtx, txCtx, state, chainConfig, vm.Config{NoBaseFee: true})
 
@@ -136,7 +136,7 @@ func DoCall(ctx context.Context, args ethapi.CallArgs, tx kv.Tx, blockNrOrHash r
 	return result, nil
 }
 
-func GetEvmContext(msg core.Message, header *types.Header, requireCanonical bool, tx kv.Tx) (vm.BlockContext, vm.TxContext) {
+func GetEvmContext(msg core.Message, header *types.Header, requireCanonical bool, tx kv.Tx, contractHasTEVM func(address common.Hash) (bool, error)) (vm.BlockContext, vm.TxContext) {
 	var baseFee uint256.Int
 	if header.Eip1559 {
 		overflow := baseFee.SetFromBig(header.BaseFee)
@@ -145,16 +145,16 @@ func GetEvmContext(msg core.Message, header *types.Header, requireCanonical bool
 		}
 	}
 	return vm.BlockContext{
-			CanTransfer: core.CanTransfer,
-			Transfer:    core.Transfer,
-			GetHash:     getHashGetter(requireCanonical, tx),
-			CheckTEVM:   func(common.Hash) (bool, error) { return false, nil },
-			Coinbase:    header.Coinbase,
-			BlockNumber: header.Number.Uint64(),
-			Time:        header.Time,
-			Difficulty:  new(big.Int).Set(header.Difficulty),
-			GasLimit:    header.GasLimit,
-			BaseFee:     &baseFee,
+			CanTransfer:     core.CanTransfer,
+			Transfer:        core.Transfer,
+			GetHash:         getHashGetter(requireCanonical, tx),
+			ContractHasTEVM: contractHasTEVM,
+			Coinbase:        header.Coinbase,
+			BlockNumber:     header.Number.Uint64(),
+			Time:            header.Time,
+			Difficulty:      new(big.Int).Set(header.Difficulty),
+			GasLimit:        header.GasLimit,
+			BaseFee:         &baseFee,
 		},
 		vm.TxContext{
 			Origin:   msg.From(),
diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go
index a005581fcffd1c7c5403fc26fea2a65425125353..2ada4759de89484bc01bf31caa191c482c3e94bd 100644
--- a/turbo/transactions/tracing.go
+++ b/turbo/transactions/tracing.go
@@ -31,7 +31,7 @@ type BlockGetter interface {
 }
 
 // computeTxEnv returns the execution environment of a certain transaction.
-func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, checkTEVM func(common.Hash) (bool, error), engine consensus.Engine, dbtx kv.Tx, blockHash common.Hash, txIndex uint64) (core.Message, vm.BlockContext, vm.TxContext, *state.IntraBlockState, *state.PlainState, error) {
+func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConfig, getHeader func(hash common.Hash, number uint64) *types.Header, contractHasTEVM func(common.Hash) (bool, error), engine consensus.Engine, dbtx kv.Tx, blockHash common.Hash, txIndex uint64) (core.Message, vm.BlockContext, vm.TxContext, *state.IntraBlockState, *state.PlainState, error) {
 	// Create the parent state database
 	reader := state.NewPlainState(dbtx, block.NumberU64()-1)
 	statedb := state.New(reader)
@@ -42,7 +42,7 @@ func ComputeTxEnv(ctx context.Context, block *types.Block, cfg *params.ChainConf
 	// Recompute transactions up to the target index.
 	signer := types.MakeSigner(cfg, block.NumberU64())
 
-	BlockContext := core.NewEVMBlockContext(block.Header(), getHeader, engine, nil, checkTEVM)
+	BlockContext := core.NewEVMBlockContext(block.Header(), getHeader, engine, nil, contractHasTEVM)
 	vmenv := vm.NewEVM(BlockContext, vm.TxContext{}, statedb, cfg, vm.Config{})
 	for idx, tx := range block.Transactions() {
 		select {