diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index e5bb21dceb3fa04f38982c4da0a964c239fba35f..465e8b10cee5630017e15feadbe493bda2b678fd 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -87,7 +87,7 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
 	genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
 	genesisBlock := genesis.MustCommit(database)
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(database, nil, genesis.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(database, nil, genesis.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		panic(fmt.Sprintf("%v", err))
 	}
@@ -117,7 +117,7 @@ func NewSimulatedBackendWithConfig(alloc core.GenesisAlloc, config *params.Chain
 	genesis := core.Genesis{Config: config, GasLimit: gasLimit, Alloc: alloc}
 	genesisBlock := genesis.MustCommit(database)
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(database, nil, genesis.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(database, nil, genesis.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		panic(err)
 	}
@@ -562,7 +562,7 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
 	evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
 	// Create a new environment which holds all relevant information
 	// about the transaction and calling mechanisms.
-	vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
+	vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{}, b.blockchain.DestsCache)
 	gaspool := new(core.GasPool).AddGas(math.MaxUint64)
 
 	return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
@@ -589,7 +589,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
 		&b.pendingHeader.Coinbase, b.gasPool,
 		b.pendingState, b.pendingTds.TrieStateWriter(),
 		b.pendingHeader, tx,
-		&b.pendingHeader.GasUsed, vm.Config{}); err != nil {
+		&b.pendingHeader.GasUsed, vm.Config{}, b.blockchain.DestsCache); err != nil {
 		return err
 	}
 	//fmt.Printf("==== Start producing block %d\n", (b.prependBlock.NumberU64() + 1))
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 6d68409216c4c81c1b9294697232a0eb36760993..a8e89a03408ec2501f12449637cfee6a094f6915 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -32,6 +32,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core"
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/eth/downloader"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/event"
@@ -431,7 +432,7 @@ func copyDb(ctx *cli.Context) error {
 	start := time.Now()
 
 	currentHeader := hc.CurrentHeader()
-	if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(nil, currentHeader.Hash(), currentHeader.Number.Uint64()), syncMode); err != nil {
+	if err = dl.Synchronise("local", currentHeader.Hash(), hc.GetTd(nil, currentHeader.Hash(), currentHeader.Number.Uint64()), syncMode, vm.NewDestsCache(10000)); err != nil {
 		return err
 	}
 	for dl.Synchronising() {
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index a083bc4315f77209ea61a350d9a85acf7de41429..ad9a1e2e30d7907fbd1fd1293dd24b3210fe9e22 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -472,7 +472,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
 			log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
 		}
 
-		if err := ethereum.StartMining(threads); err != nil {
+		if err := ethereum.StartMining(threads, ethereum.BlockChain().DestsCache); err != nil {
 			utils.Fatalf("Failed to start mining: %v", err)
 		}
 	}
diff --git a/cmd/geth/retesteth.go b/cmd/geth/retesteth.go
index dbec667928d9fbcbab4d544d44224108a426d51d..7cb6fc8ae7667fdbd6817465ddde2879bdd1ff9c 100644
--- a/cmd/geth/retesteth.go
+++ b/cmd/geth/retesteth.go
@@ -406,7 +406,7 @@ func (api *RetestethAPI) SetChainParams(_ context.Context, chainParams ChainPara
 	}
 	engine := &NoRewardEngine{inner: inner, rewardsOn: chainParams.SealEngine != "NoReward"}
 
-	blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil, api.blockchain.DestsCache)
 	if err != nil {
 		return false, err
 	}
@@ -536,6 +536,7 @@ func (api *RetestethAPI) mineBlock() error {
 					statedb,
 					tds.TrieStateWriter(),
 					header, tx, &header.GasUsed, *api.blockchain.GetVMConfig(),
+					api.blockchain.DestsCache,
 				)
 				if err != nil {
 					statedb.RevertToSnapshot(snap)
@@ -686,7 +687,7 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), api.blockchain, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{})
+			vmenv := vm.NewEVM(context, statedb, api.blockchain.Config(), vm.Config{}, api.blockchain.DestsCache)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				return AccountRangeResult{}, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
 			}
@@ -786,7 +787,7 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
 		dbstate = state.NewDbState(api.kv, header.Number.Uint64())
 	} else {
 		var err error
-		_, _, _, dbstate, err = eth.ComputeTxEnv(ctx, api.blockchain, api.blockchain.Config(), api.blockchain, api.kv, block.Hash(), txIndex)
+		_, _, _, dbstate, err = eth.ComputeTxEnv(ctx, api.blockchain, api.blockchain.Config(), api.blockchain, api.kv, block.Hash(), txIndex, api.blockchain.DestsCache)
 		if err != nil {
 			return StorageRangeResult{}, err
 		}
diff --git a/cmd/hack/hack.go b/cmd/hack/hack.go
index 276e9fcdaf97652a8a6328f508ab34445f971e74..68d4d33d4f5d7381cb04ad341768529bc93b433a 100644
--- a/cmd/hack/hack.go
+++ b/cmd/hack/hack.go
@@ -628,7 +628,7 @@ func mgrSchedule(chaindata string, block uint64) {
 	db, err := ethdb.NewBoltDatabase(chaindata)
 	check(err)
 
-	bc, err := core.NewBlockChain(db, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(db, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	defer db.Close()
 	tds, err := bc.GetTrieDbState()
@@ -773,7 +773,7 @@ func execToBlock(chaindata string, block uint64, fromScratch bool) {
 	state.MaxTrieCacheSize = 100 * 1024
 	blockDb, err := ethdb.NewBoltDatabase(chaindata)
 	check(err)
-	bcb, err := core.NewBlockChain(blockDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bcb, err := core.NewBlockChain(blockDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	defer blockDb.Close()
 	if fromScratch {
@@ -786,7 +786,7 @@ func execToBlock(chaindata string, block uint64, fromScratch bool) {
 	//_, _, _, err = core.SetupGenesisBlock(stateDB, core.DefaultGenesisBlock())
 	_, _, _, err = core.SetupGenesisBlock(stateDB, nil, false /* history */)
 	check(err)
-	bc, err := core.NewBlockChain(stateDB, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(stateDB, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	tds, err := bc.GetTrieDbState()
 	check(err)
@@ -842,7 +842,7 @@ func extractTrie(block int) {
 	stateDb, err := ethdb.NewBoltDatabase("statedb")
 	check(err)
 	defer stateDb.Close()
-	bc, err := core.NewBlockChain(stateDb, nil, params.RopstenChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(stateDb, nil, params.RopstenChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	baseBlock := bc.GetBlockByNumber(uint64(block))
 	tds := state.NewTrieDbState(baseBlock.Root(), stateDb, baseBlock.NumberU64())
@@ -861,7 +861,7 @@ func testRewind(chaindata string, block, rewind int) {
 	ethDb, err := ethdb.NewBoltDatabase(chaindata)
 	check(err)
 	defer ethDb.Close()
-	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	currentBlock := bc.CurrentBlock()
 	currentBlockNr := currentBlock.NumberU64()
@@ -923,7 +923,7 @@ func testStartup() {
 	ethDb, err := ethdb.NewBoltDatabase("/home/akhounov/.ethereum/geth/chaindata")
 	check(err)
 	defer ethDb.Close()
-	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	currentBlock := bc.CurrentBlock()
 	currentBlockNr := currentBlock.NumberU64()
diff --git a/cmd/pics/state.go b/cmd/pics/state.go
index 6b0a825ede36a4e55049ed18ecf1b1510a823f3f..63870f327508a9919acf1c0eaea62553a53b7b5c 100644
--- a/cmd/pics/state.go
+++ b/cmd/pics/state.go
@@ -320,7 +320,7 @@ func initialState1() error {
 	genesisDb := db.MemCopy()
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/restapi/apis/retrace_tx_api.go b/cmd/restapi/apis/retrace_tx_api.go
index 7c94187d0c3f286a2a2d33fe1e26499cf6c1ddef..d27a6cc4cc1d971807f8851fba1334a57af17a45 100644
--- a/cmd/restapi/apis/retrace_tx_api.go
+++ b/cmd/restapi/apis/retrace_tx_api.go
@@ -114,7 +114,7 @@ func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWrit
 		misc.ApplyDAOHardFork(ibs)
 	}
 	for _, tx := range block.Transactions() {
-		receipt, err := core.ApplyTransaction(chainConfig, bcb, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig)
+		receipt, err := core.ApplyTransaction(chainConfig, bcb, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, nil)
 		if err != nil {
 			return fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
 		}
diff --git a/cmd/rpcdaemon/commands/daemon.go b/cmd/rpcdaemon/commands/daemon.go
index e773a8d135a1b757656e1740d93c3afc4a09160d..f83e35fa9b936d6ee165c953f09514c1510f2e9e 100644
--- a/cmd/rpcdaemon/commands/daemon.go
+++ b/cmd/rpcdaemon/commands/daemon.go
@@ -14,6 +14,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core/rawdb"
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/eth"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/ethdb/remote/remotechain"
@@ -226,7 +227,7 @@ func (api *APIImpl) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber
 
 // StorageRangeAt re-implementation of eth/api.go:StorageRangeAt
 func (api *PrivateDebugAPIImpl) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (eth.StorageRangeResult, error) {
-	_, _, _, dbstate, err := eth.ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.db, blockHash, txIndex)
+	_, _, _, dbstate, err := eth.ComputeTxEnv(ctx, &blockGetter{api.dbReader}, params.MainnetChainConfig, &chainContext{db: api.dbReader}, api.db, blockHash, txIndex, vm.NewDestsCache(10))
 	if err != nil {
 		return eth.StorageRangeResult{}, err
 	}
diff --git a/cmd/rpctest/proofs.go b/cmd/rpctest/proofs.go
index 928efb52f40d7a09dc8b0908b5c7baa6605cec3f..6d667cf7708dbef35a8ddc3f4794be01570e379b 100644
--- a/cmd/rpctest/proofs.go
+++ b/cmd/rpctest/proofs.go
@@ -179,7 +179,7 @@ func fixState(chaindata string, url string) {
 	defer stateDb.Close()
 	engine := ethash.NewFullFaker()
 	chainConfig := params.MainnetChainConfig
-	bc, err := core.NewBlockChain(stateDb, nil, chainConfig, engine, vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(stateDb, nil, chainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/cmd/state/commands/verify_export.go b/cmd/state/commands/verify_export.go
index 7b9b6b76771be9571f68c9b026e4ef0b01c20a43..1d010cd9b692d37679c59f8165f4a3246723bc4e 100644
--- a/cmd/state/commands/verify_export.go
+++ b/cmd/state/commands/verify_export.go
@@ -2,6 +2,7 @@ package commands
 
 import (
 	"github.com/ledgerwatch/turbo-geth/cmd/state/verify"
+
 	"github.com/spf13/cobra"
 )
 
diff --git a/cmd/state/stateless/check_change_sets.go b/cmd/state/stateless/check_change_sets.go
index d1d3a49cc04ee3d17616ddd52e9ef4f0a6b2164c..5f34e7bda4c7c9b0aa0ad2cb01d96d614d2d4e7e 100644
--- a/cmd/state/stateless/check_change_sets.go
+++ b/cmd/state/stateless/check_change_sets.go
@@ -52,7 +52,7 @@ func CheckChangeSets(genesis *core.Genesis, blockNum uint64, chaindata string, h
 	chainConfig := genesis.Config
 	engine := ethash.NewFaker()
 	vmConfig := vm.Config{}
-	bc, err := core.NewBlockChain(chainDb, nil, chainConfig, engine, vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(chainDb, nil, chainConfig, engine, vmConfig, nil, nil, nil)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/state/stateless/deps.go b/cmd/state/stateless/deps.go
index 52d88d3872b8956cd43b5f6a8f8f33ed72478dd5..e25152745eb99add50b4a2f54fa105d7f5858605 100644
--- a/cmd/state/stateless/deps.go
+++ b/cmd/state/stateless/deps.go
@@ -133,7 +133,7 @@ func dataDependencies(blockNum uint64) {
 	defer w.Flush()
 	dt := NewDepTracer()
 	vmConfig := vm.Config{Tracer: dt, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	for !interrupt {
@@ -160,7 +160,7 @@ func dataDependencies(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			} else {
diff --git a/cmd/state/stateless/fee_market.go b/cmd/state/stateless/fee_market.go
index 3d16d6f5f121e30a17e651280e5c409c72d74e8b..1d04ada8af303f4de55a46a3a7396fa67e871676 100644
--- a/cmd/state/stateless/fee_market.go
+++ b/cmd/state/stateless/fee_market.go
@@ -40,7 +40,7 @@ func feemarket(blockNum uint64) {
 	defer w.Flush()
 	vmConfig := vm.Config{}
 	engine := ethash.NewFullFaker()
-	bcb, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vmConfig, nil, nil)
+	bcb, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	txCount := 0
diff --git a/cmd/state/stateless/naked_accouts.go b/cmd/state/stateless/naked_accouts.go
index 351b519a41a57dcc282469789125f7575810f103..14c9d57fcd96fe755c8450efd6e03a1385a7075d 100644
--- a/cmd/state/stateless/naked_accouts.go
+++ b/cmd/state/stateless/naked_accouts.go
@@ -94,7 +94,7 @@ func accountsReadWrites(blockNum uint64) {
 	defer w.Flush()
 	at := NewAccountsTracer()
 	vmConfig := vm.Config{Tracer: at, Debug: false}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	totalWrites := 0
@@ -120,7 +120,7 @@ func accountsReadWrites(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
diff --git a/cmd/state/stateless/naked_storage.go b/cmd/state/stateless/naked_storage.go
index 1ea17bb9fe0a7569715def6084bd903b6baa2127..46a0c8e2b7cf0973657da8fc5c613778548250aa 100644
--- a/cmd/state/stateless/naked_storage.go
+++ b/cmd/state/stateless/naked_storage.go
@@ -122,7 +122,7 @@ func storageReadWrites(blockNum uint64) {
 	defer w.Flush()
 	st := NewStorageTracer()
 	vmConfig := vm.Config{Tracer: st, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	totalSstores := 0
@@ -147,7 +147,7 @@ func storageReadWrites(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
diff --git a/cmd/state/stateless/spec_exec.go b/cmd/state/stateless/spec_exec.go
index 4282cacac10aecf1673e41868c14b5b11c5d7481..35a839f7000633e3c61ffebfcdf054e57b124cbf 100644
--- a/cmd/state/stateless/spec_exec.go
+++ b/cmd/state/stateless/spec_exec.go
@@ -173,7 +173,7 @@ func speculativeExecution(blockNum uint64) {
 	vmConfig1 := vm.Config{Tracer: ct1, Debug: true}
 	vmConfig2 := vm.Config{Tracer: ct2, Debug: true}
 	vmConfig3 := vm.Config{Tracer: ct3, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig1, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig1, nil, nil, nil)
 	check(err)
 	interrupt := false
 	for !interrupt {
@@ -194,7 +194,7 @@ func speculativeExecution(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb1, chainConfig, vmConfig1)
+			vmenv := vm.NewEVM(context, statedb1, chainConfig, vmConfig1, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
@@ -219,7 +219,7 @@ func speculativeExecution(blockNum uint64) {
 			msg = types.NewMessage(msg.From(), msg.To(), msg.Nonce(), msg.Value(), msg.Gas(), msg.GasPrice(), msg.Data(), false)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb2, chainConfig, vmConfig2)
+			vmenv := vm.NewEVM(context, statedb2, chainConfig, vmConfig2, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
@@ -238,7 +238,7 @@ func speculativeExecution(blockNum uint64) {
 			msg = types.NewMessage(msg.From(), msg.To(), msg.Nonce(), msg.Value(), msg.Gas(), msg.GasPrice(), msg.Data(), false)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb3, chainConfig, vmConfig3)
+			vmenv := vm.NewEVM(context, statedb3, chainConfig, vmConfig3, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
@@ -261,7 +261,7 @@ func speculativeExecution(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb4, chainConfig, vmConfig2)
+			vmenv := vm.NewEVM(context, statedb4, chainConfig, vmConfig2, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
diff --git a/cmd/state/stateless/state.go b/cmd/state/stateless/state.go
index cc18c6f13a447aaefc667ff889717167bc52e1ca..3e6ac583cce19b8e266f215fad8740f7042d9fbd 100644
--- a/cmd/state/stateless/state.go
+++ b/cmd/state/stateless/state.go
@@ -1384,7 +1384,7 @@ func makeCreators(blockNum uint64) {
 	ct := NewCreationTracer(w)
 	chainConfig := params.MainnetChainConfig
 	vmConfig := vm.Config{Tracer: ct, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	for !interrupt {
@@ -1400,7 +1400,7 @@ func makeCreators(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
@@ -1962,7 +1962,7 @@ func makeSha3Preimages(blockNum uint64) {
 	bucket := []byte("sha3")
 	chainConfig := params.MainnetChainConfig
 	vmConfig := vm.Config{EnablePreimageRecording: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	tx, err := f.Begin(true)
@@ -1986,7 +1986,7 @@ func makeSha3Preimages(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
diff --git a/cmd/state/stateless/stateless.go b/cmd/state/stateless/stateless.go
index 9a3b8c83d983f0d5f0cef404f1988b6a148d5e46..4ca47bcd462d057fd90ef6bb87ed96c10c87d55f 100644
--- a/cmd/state/stateless/stateless.go
+++ b/cmd/state/stateless/stateless.go
@@ -52,7 +52,7 @@ func runBlock(ibs *state.IntraBlockState, txnWriter state.StateWriter, blockWrit
 		misc.ApplyDAOHardFork(ibs)
 	}
 	for _, tx := range block.Transactions() {
-		receipt, err := core.ApplyTransaction(chainConfig, bcb, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig)
+		receipt, err := core.ApplyTransaction(chainConfig, bcb, nil, gp, ibs, txnWriter, header, tx, usedGas, vmConfig, nil)
 		if err != nil {
 			return fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
 		}
@@ -301,7 +301,7 @@ func Stateless(
 		for i, tx := range block.Transactions() {
 			statedb.Prepare(tx.Hash(), block.Hash(), i)
 			var receipt *types.Receipt
-			receipt, err = core.ApplyTransaction(chainConfig, blockProvider, nil, gp, statedb, tds.TrieStateWriter(), header, tx, usedGas, vmConfig)
+			receipt, err = core.ApplyTransaction(chainConfig, blockProvider, nil, gp, statedb, tds.TrieStateWriter(), header, tx, usedGas, vmConfig, nil)
 			if err != nil {
 				fmt.Printf("tx %x failed: %v\n", tx.Hash(), err)
 				return
diff --git a/cmd/state/stateless/stateless_block_providers.go b/cmd/state/stateless/stateless_block_providers.go
index 9d66139211d51a03ecd231de3ad424bb7c8b896b..1efecff66cde2db32b08f168ae8edfe23ef6e3fe 100644
--- a/cmd/state/stateless/stateless_block_providers.go
+++ b/cmd/state/stateless/stateless_block_providers.go
@@ -24,7 +24,7 @@ import (
 
 const (
 	fileSchemeExportfile = "exportfile"
-	fileSchemeDb         = "db"
+	fileSchemeDB         = "db"
 )
 
 type BlockProvider interface {
@@ -34,7 +34,7 @@ type BlockProvider interface {
 	NextBlock() (*types.Block, error)
 }
 
-func BlockProviderForURI(uri string, createDbFunc CreateDbFunc) (BlockProvider, error) {
+func BlockProviderForURI(uri string, createDBFunc CreateDbFunc) (BlockProvider, error) {
 	url, err := url.Parse(uri)
 	if err != nil {
 		return nil, err
@@ -43,11 +43,11 @@ func BlockProviderForURI(uri string, createDbFunc CreateDbFunc) (BlockProvider,
 	case fileSchemeExportfile:
 		fmt.Println("Source of blocks: export file @", url.Path)
 		return NewBlockProviderFromExportFile(url.Path)
-	case fileSchemeDb:
+	case fileSchemeDB:
 		fallthrough
 	default:
 		fmt.Println("Source of blocks: db @", url.Path)
-		return NewBlockProviderFromDb(url.Path, createDbFunc)
+		return NewBlockProviderFromDB(url.Path, createDBFunc)
 	}
 }
 
@@ -57,21 +57,21 @@ type BlockChainBlockProvider struct {
 	db           ethdb.Database
 }
 
-func NewBlockProviderFromDb(path string, createDbFunc CreateDbFunc) (BlockProvider, error) {
-	ethDb, err := createDbFunc(path)
+func NewBlockProviderFromDB(path string, createDBFunc CreateDbFunc) (BlockProvider, error) {
+	ethDB, err := createDBFunc(path)
 	if err != nil {
 		return nil, err
 	}
 	chainConfig := params.MainnetChainConfig
 	engine := ethash.NewFullFaker()
-	chain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := core.NewBlockChain(ethDB, nil, chainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, err
 	}
 
 	return &BlockChainBlockProvider{
 		bc: chain,
-		db: ethDb,
+		db: ethDB,
 	}, nil
 }
 
@@ -102,7 +102,7 @@ func (p *BlockChainBlockProvider) NextBlock() (*types.Block, error) {
 type ExportFileBlockProvider struct {
 	stream          *rlp.Stream
 	engine          consensus.Engine
-	headersDb       ethdb.Database
+	headersDB       ethdb.Database
 	batch           ethdb.DbWithPendingMutations
 	fh              *os.File
 	reader          io.Reader
@@ -125,8 +125,8 @@ func NewBlockProviderFromExportFile(fn string) (BlockProvider, error) {
 	stream := rlp.NewStream(reader, 0)
 	engine := ethash.NewFullFaker()
 	// keeping all the past block headers in memory
-	headersDb := mustCreateTempDatabase()
-	return &ExportFileBlockProvider{stream, engine, headersDb, nil, fh, reader, -1}, nil
+	headersDB := mustCreateTempDatabase()
+	return &ExportFileBlockProvider{stream, engine, headersDB, nil, fh, reader, -1}, nil
 }
 
 func getTempFileName() string {
@@ -153,7 +153,7 @@ func (p *ExportFileBlockProvider) Close() error {
 
 func (p *ExportFileBlockProvider) WriteHeader(h *types.Header) {
 	if p.batch == nil {
-		p.batch = p.headersDb.NewBatch()
+		p.batch = p.headersDB.NewBatch()
 	}
 
 	rawdb.WriteHeader(context.TODO(), p.batch, h)
@@ -233,5 +233,5 @@ func (p *ExportFileBlockProvider) GetHeader(h common.Hash, i uint64) *types.Head
 	if p.batch != nil {
 		return rawdb.ReadHeader(p.batch, h, i)
 	}
-	return rawdb.ReadHeader(p.headersDb, h, i)
+	return rawdb.ReadHeader(p.headersDB, h, i)
 }
diff --git a/cmd/state/stateless/tokens.go b/cmd/state/stateless/tokens.go
index 712bcd3efa1bfc841d70e85b23641b1e7392262c..68ac2f727a5543efd4f682b1d386e80eea89782a 100644
--- a/cmd/state/stateless/tokens.go
+++ b/cmd/state/stateless/tokens.go
@@ -136,7 +136,7 @@ func makeTokens(blockNum uint64) {
 	chainConfig := params.MainnetChainConfig
 	tt := NewTokenTracer()
 	vmConfig := vm.Config{Tracer: tt, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	if blockNum > 1 {
 		tokenFile, err := os.Open("/Volumes/tb41/turbo-geth/tokens.csv")
@@ -161,7 +161,7 @@ func makeTokens(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			if _, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 				panic(fmt.Errorf("tx %x failed: %v", tx.Hash(), err))
 			}
@@ -195,7 +195,7 @@ func makeTokenBalances() {
 	//ethDb, err := ethdb.NewBoltDatabase("/Users/alexeyakhunov/Library/Ethereum/geth/chaindata")
 	check(err)
 	defer ethDb.Close()
-	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	currentBlock := bc.CurrentBlock()
 	currentBlockNr := currentBlock.NumberU64()
@@ -252,7 +252,7 @@ func makeTokenBalances() {
 		vmConfig := vm.Config{EnablePreimageRecording: true}
 		context := core.NewEVMContext(msg, currentBlock.Header(), bc, nil)
 		// Not yet the searched for transaction, execute on top of the current state
-		vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+		vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 		result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(math.MaxUint64))
 		if err != nil {
 			fmt.Printf("Call failed with error: %v\n", err)
@@ -415,7 +415,7 @@ func makeTokenAllowances() {
 	//ethDb, err := ethdb.NewBoltDatabase("/Users/alexeyakhunov/Library/Ethereum/geth/chaindata")
 	check(err)
 	defer ethDb.Close()
-	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, params.MainnetChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	check(err)
 	currentBlock := bc.CurrentBlock()
 	currentBlockNr := currentBlock.NumberU64()
@@ -475,7 +475,7 @@ func makeTokenAllowances() {
 		vmConfig := vm.Config{EnablePreimageRecording: true}
 		context := core.NewEVMContext(msg, currentBlock.Header(), bc, nil)
 		// Not yet the searched for transaction, execute on top of the current state
-		vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+		vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 		result, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(math.MaxUint64))
 		if err != nil {
 			fmt.Printf("Call failed with error: %v\n", err)
diff --git a/cmd/state/stateless/transaction_stats.go b/cmd/state/stateless/transaction_stats.go
index de010a0d721a76551117b93705a7099dd3e988b7..57ad4b7e64490005a2dc90d532e7fcf3155b99fe 100644
--- a/cmd/state/stateless/transaction_stats.go
+++ b/cmd/state/stateless/transaction_stats.go
@@ -195,7 +195,7 @@ func transactionStats(blockNum uint64) {
 	tt := NewTxTracer()
 	chainConfig := params.MainnetChainConfig
 	vmConfig := vm.Config{Tracer: tt, Debug: true}
-	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil)
+	bc, err := core.NewBlockChain(ethDb, nil, chainConfig, ethash.NewFaker(), vmConfig, nil, nil, nil)
 	check(err)
 	interrupt := false
 	for !interrupt {
@@ -211,7 +211,7 @@ func transactionStats(blockNum uint64) {
 			msg, _ := tx.AsMessage(signer)
 			context := core.NewEVMContext(msg, block.Header(), bc, nil)
 			// Not yet the searched for transaction, execute on top of the current state
-			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig)
+			vmenv := vm.NewEVM(context, statedb, chainConfig, vmConfig, nil)
 			tt.ResetCounters()
 			tt.currentBlock = blockNum
 			tt.measureCreate = tx.To() == nil
diff --git a/cmd/state/verify/verify_export_file.go b/cmd/state/verify/verify_export_file.go
index 945c80a11214b0de4ef64a15daacdd24f1105293..068fd5820fed12f6293405a2c975fc4c351c27af 100644
--- a/cmd/state/verify/verify_export_file.go
+++ b/cmd/state/verify/verify_export_file.go
@@ -22,7 +22,7 @@ func ExportFile(filePath, chaindataPath string) error {
 		return ethdb.NewBoltDatabase(path)
 	}
 
-	chaindata, err := stateless.NewBlockProviderFromDb(chaindataPath, createDb)
+	chaindata, err := stateless.NewBlockProviderFromDB(chaindataPath, createDb)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/tester/block_generator.go b/cmd/tester/block_generator.go
index 18fc5af9a3a73932d99f424861bbf1193e0b0552..60e425c165ebceeecbf4ddd7912fb1258f4a93d7 100644
--- a/cmd/tester/block_generator.go
+++ b/cmd/tester/block_generator.go
@@ -392,7 +392,7 @@ func NewBlockGenerator(ctx context.Context, outputFile string, initialHeight int
 		return nil, err
 	}
 
-	blockchain, err := core.NewBlockChain(db, nil, genesis.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, genesis.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, err
 	}
@@ -474,7 +474,7 @@ func NewForkGenerator(ctx context.Context, base *BlockGenerator, outputFile stri
 		return nil, err
 	}
 
-	blockchain, err := core.NewBlockChain(db, nil, genesis.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, genesis.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 93494b5d1efe3785a6013647bf05124a5865233b..a60b09bf16903f1bed648a30bf6e1bfd07e87283 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -1814,7 +1814,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readOnly bool) (chain *core.B
 		l := ctx.GlobalUint64(TxLookupLimitFlag.Name)
 		limit = &l
 	}
-	chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, limit)
+	chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, limit, vm.NewDestsCache(10000))
 	if err != nil {
 		Fatalf("Can't create BlockChain: %v", err)
 	}
diff --git a/common/pool/buffer.go b/common/pool/buffer.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c5082322b02f8654e79410f99e54dae54515842
--- /dev/null
+++ b/common/pool/buffer.go
@@ -0,0 +1,24 @@
+package pool
+
+import "github.com/valyala/bytebufferpool"
+
+type ByteBuffer struct {
+	*bytebufferpool.ByteBuffer
+}
+
+func (b ByteBuffer) Get(pos int) byte {
+	return b.B[pos]
+}
+
+func (b ByteBuffer) SetBitPos(pos uint64) {
+	b.B[pos/8] |= 0x80 >> (pos % 8)
+}
+
+func (b ByteBuffer) SetBit8Pos(pos uint64) {
+	b.B[pos/8] |= 0xFF >> (pos % 8)
+	b.B[pos/8+1] |= ^(0xFF >> (pos % 8))
+}
+
+func (b ByteBuffer) CodeSegment(pos uint64) bool {
+	return b.B[pos/8]&(0x80>>(pos%8)) == 0
+}
diff --git a/common/pool/global_pool.go b/common/pool/global_pool.go
index 5196065ec4d1d513336965b8ee852b713cc39522..a493bc6c2d6f66ac6f37b303aaecd31b66b5b400 100644
--- a/common/pool/global_pool.go
+++ b/common/pool/global_pool.go
@@ -1,9 +1,5 @@
 package pool
 
-import (
-	"github.com/valyala/bytebufferpool"
-)
-
 var (
 	chunkSizeClasses = []uint{
 		8,
@@ -14,10 +10,16 @@ var (
 		1 << 8,
 		1 << 9,
 		1 << 10,
-		2 << 10,
-		4 << 10,
-		8 << 10,
-		16 << 10,
+		1 << 11,
+		1 << 12,
+		1 << 13,
+		1 << 14,
+		1 << 15,
+		1 << 16,
+		1 << 17,
+		1 << 18,
+		1 << 19,
+		1 << 20,
 	}
 	chunkPools []*pool
 )
@@ -30,11 +32,7 @@ func init() {
 
 	// preallocate some buffers
 	const preAlloc = 32
-	const lastSmallChunkIndex = 5
-	for i, n := range chunkSizeClasses {
-		if i > lastSmallChunkIndex {
-			break
-		}
+	for _, n := range chunkSizeClasses {
 
 		for i := 0; i < preAlloc; i++ {
 			PutBuffer(GetBuffer(n))
@@ -42,7 +40,7 @@ func init() {
 	}
 }
 
-func GetBuffer(size uint) *bytebufferpool.ByteBuffer {
+func GetBuffer(size uint) *ByteBuffer {
 	var i int
 	for i = 0; i < len(chunkSizeClasses)-1; i++ {
 		if size <= chunkSizeClasses[i] {
@@ -67,7 +65,15 @@ func GetBuffer(size uint) *bytebufferpool.ByteBuffer {
 	return pp
 }
 
-func PutBuffer(p *bytebufferpool.ByteBuffer) {
+func GetBufferZeroed(size uint) *ByteBuffer {
+	pp := GetBuffer(size)
+	for i := range pp.B {
+		pp.B[i] = 0
+	}
+	return pp
+}
+
+func PutBuffer(p *ByteBuffer) {
 	if p == nil || cap(p.B) == 0 {
 		return
 	}
diff --git a/common/pool/pool.go b/common/pool/pool.go
index ec8e3cdc4f6cadeb908bd34a4258fa404c2a697e..d5873d8033bc3a0477b6920da90dd639cd5b3ac5 100644
--- a/common/pool/pool.go
+++ b/common/pool/pool.go
@@ -24,27 +24,33 @@ func newPool(defaultSize uint) *pool {
 		defaultSize: uint64(defaultSize),
 		maxSize:     uint64(defaultSize),
 		pool: sync.Pool{
-			New: func() interface{} {
-				return &bytebufferpool.ByteBuffer{
-					B: make([]byte, 0, defaultSize),
-				}
-			},
+			New: getFn(defaultSize),
 		},
 	}
 }
 
+func getFn(defaultSize uint) func() interface{} {
+	return func() interface{} {
+		return &ByteBuffer{
+			&bytebufferpool.ByteBuffer{
+				B: make([]byte, 0, defaultSize),
+			},
+		}
+	}
+}
+
 // Get returns new byte buffer with zero length.
 //
 // The byte buffer may be returned to the pool via Put after the use
 // in order to minimize GC overhead.
-func (p *pool) Get() *bytebufferpool.ByteBuffer {
-	return p.pool.Get().(*bytebufferpool.ByteBuffer)
+func (p *pool) Get() *ByteBuffer {
+	return p.pool.Get().(*ByteBuffer)
 }
 
 // Put releases byte buffer obtained via Get to the pool.
 //
 // The buffer mustn't be accessed after returning to the pool.
-func (p *pool) Put(b *bytebufferpool.ByteBuffer) {
+func (p *pool) Put(b *ByteBuffer) {
 	if b == nil || cap(b.B) == 0 {
 		return
 	}
diff --git a/consensus/clique/clique_test.go b/consensus/clique/clique_test.go
index cd95afb2fb9bcb07ffe3aa973bd794f188eebac6..ccf564a777ed6c9eee56577b01689d99df9b1398 100644
--- a/consensus/clique/clique_test.go
+++ b/consensus/clique/clique_test.go
@@ -57,7 +57,7 @@ func TestReimportMirroredState(t *testing.T) {
 	genesis := genspec.MustCommit(db)
 
 	// Generate a batch of blocks, each properly signed
-	chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
+	chain, _ := core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	defer chain.Stop()
 
 	ctx := context.Background()
@@ -93,7 +93,7 @@ func TestReimportMirroredState(t *testing.T) {
 	db = ethdb.NewMemDatabase()
 	genspec.MustCommit(db)
 
-	chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
+	chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	defer chain.Stop()
 
 	if _, err := chain.InsertChain(context.Background(), blocks[:2]); err != nil {
@@ -106,7 +106,7 @@ func TestReimportMirroredState(t *testing.T) {
 	// Simulate a crash by creating a new chain on top of the database, without
 	// flushing the dirty states out. Insert the last block, trigerring a sidechain
 	// reimport.
-	chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil)
+	chain, _ = core.NewBlockChain(db, nil, params.AllCliqueProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	defer chain.Stop()
 
 	if _, err := chain.InsertChain(context.Background(), blocks[2:]); err != nil {
diff --git a/consensus/clique/snapshot_test.go b/consensus/clique/snapshot_test.go
index 30c67f8829c13ea002470f15b33b469ef088d71b..8fcb2c370944d70debc46cbbec4f57d69ad9a418 100644
--- a/consensus/clique/snapshot_test.go
+++ b/consensus/clique/snapshot_test.go
@@ -415,7 +415,7 @@ func TestClique(t *testing.T) {
 		engine := New(config.Clique, db)
 		engine.fakeDiff = true
 
-		chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil)
+		chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil, vm.NewDestsCache(100))
 		if err != nil {
 			t.Errorf("test %d: failed to create test chain: %v", i, err)
 			continue
diff --git a/core/bench_test.go b/core/bench_test.go
index 18fb74283f000d2d565ea598fc70fa86c673c394..56ec1650ff1dae69074043fb111cfc354f90311b 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -176,7 +176,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
 	}
 	genesis := gspec.MustCommit(db)
 
-	chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := chainman.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer chainman.Stop()
 	chain, _ := GenerateChain(ctx, gspec.Config, genesis, ethash.NewFaker(), db, b.N, gen)
@@ -295,7 +295,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
 		if err != nil {
 			b.Fatalf("error opening database at %v: %v", dir, err)
 		}
-		chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+		chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 		if err != nil {
 			b.Fatalf("error creating chain: %v", err)
 		}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 76600f1ebe505ad896e77d478620725c82aee5b4..d34fe8dbc1eff82463025d453a1bba2e3ab09adf 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -38,7 +38,8 @@ func TestHeaderVerification(t *testing.T) {
 		gspec   = &Genesis{Config: params.TestChainConfig}
 		genesis = gspec.MustCommit(testdb)
 	)
-	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	dests := vm.NewDestsCache(100)
+	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, dests)
 	ctx := chain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer chain.Stop()
 
@@ -107,17 +108,19 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
 	old := runtime.GOMAXPROCS(threads)
 	defer runtime.GOMAXPROCS(old)
 
+	dests := vm.NewDestsCache(100)
+
 	// Run the header checker for the entire block chain at once both for a valid and
 	// also an invalid chain (enough if one arbitrary block is invalid).
 	for i, valid := range []bool{true, false} {
 		var results <-chan error
 
 		if valid {
-			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, dests)
 			_, results = chain.engine.VerifyHeaders(chain, headers, seals)
 			chain.Stop()
 		} else {
-			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil)
+			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil, dests)
 			_, results = chain.engine.VerifyHeaders(chain, headers, seals)
 			chain.Stop()
 		}
@@ -182,7 +185,8 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
 	defer runtime.GOMAXPROCS(old)
 
 	// Start the verifications and immediately abort
-	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil)
+	dests := vm.NewDestsCache(100)
+	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil, dests)
 	defer chain.Stop()
 
 	abort, results := chain.engine.VerifyHeaders(chain, headers, seals)
diff --git a/core/blockchain.go b/core/blockchain.go
index 19fc1a374db6ad9d05d424492bdcb076ff5680d9..60b35e191cdb801fc51ce0c7c29458854a6615ae 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -199,12 +199,14 @@ type BlockChain struct {
 	enablePreimages     bool // Whether we store preimages into the database
 	resolveReads        bool
 	pruner              Pruner
+
+	DestsCache vm.Cache
 }
 
 // NewBlockChain returns a fully initialised block chain using information
 // available in the database. It initialises the default Ethereum Validator and
 // Processor.
-func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64) (*BlockChain, error) {
+func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64, dests vm.Cache) (*BlockChain, error) {
 	if cacheConfig == nil {
 		cacheConfig = &CacheConfig{
 			Pruning:             false,
@@ -248,6 +250,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
 		enableTxLookupIndex: true,
 		enableReceipts:      false,
 		enablePreimages:     true,
+		DestsCache:          dests,
 	}
 	bc.validator = NewBlockValidator(chainConfig, bc, engine)
 	bc.prefetcher = newStatePrefetcher(chainConfig, bc, engine)
@@ -1762,7 +1765,7 @@ func (bc *BlockChain) insertChain(ctx context.Context, chain types.Blocks, verif
 		if !bc.cacheConfig.DownloadOnly && execute {
 			stateDB = state.New(bc.trieDbState)
 			// Process block using the parent state as reference point.
-			receipts, logs, usedGas, root, err = bc.processor.PreProcess(block, stateDB, bc.trieDbState, bc.vmConfig)
+			receipts, logs, usedGas, root, err = bc.processor.PreProcess(block, stateDB, bc.trieDbState, bc.vmConfig, bc.DestsCache)
 			reuseTrieDbState := true
 			if err != nil {
 				bc.rollbackBadBlock(block, receipts, err, reuseTrieDbState)
@@ -2481,6 +2484,7 @@ func ExecuteBlockEuphemerally(
 	block *types.Block,
 	stateReader state.StateReader,
 	stateWriter state.WriterWithChangeSets,
+	dests vm.Cache,
 ) error {
 	ibs := state.New(stateReader)
 	header := block.Header()
@@ -2494,7 +2498,7 @@ func ExecuteBlockEuphemerally(
 	noop := state.NewNoopWriter()
 	for i, tx := range block.Transactions() {
 		ibs.Prepare(tx.Hash(), block.Hash(), i)
-		receipt, err := ApplyTransaction(chainConfig, chainContext, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig)
+		receipt, err := ApplyTransaction(chainConfig, chainContext, nil, gp, ibs, noop, header, tx, usedGas, *vmConfig, dests)
 		if err != nil {
 			return fmt.Errorf("tx %x failed: %v", tx.Hash(), err)
 		}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index ed9ea4b41aed9ba88c849652e233cc646d500094..f3cf936c6e4ff1a2b657ab2f637faa1c48aa14f8 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -70,7 +70,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (context.Context, e
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
 	// Create and inject the requested chain
@@ -167,7 +167,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
 		parent := blockchain.GetBlockByHash(block.ParentHash())
 		tds := state.NewTrieDbState(parent.Root(), blockchain.db, parent.NumberU64())
 		statedb := state.New(tds)
-		receipts, _, usedGas, root, err := blockchain.Processor().PreProcess(block, statedb, tds, vm.Config{})
+		receipts, _, usedGas, root, err := blockchain.Processor().PreProcess(block, statedb, tds, vm.Config{}, nil)
 		if err != nil {
 			blockchain.reportBlock(block, receipts, err)
 			return err
@@ -568,7 +568,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	ncm, err := NewBlockChain(blockchain.db, cacheConfig, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ncm, err := NewBlockChain(blockchain.db, cacheConfig, blockchain.chainConfig, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create new chain manager: %v", err)
 	}
@@ -698,7 +698,7 @@ func TestFastVsFullChains(t *testing.T) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	archive, _ := NewBlockChain(archiveDb, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	archive, _ := NewBlockChain(archiveDb, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer archive.Stop()
 
 	if n, err := archive.InsertChain(context.Background(), blocks); err != nil {
@@ -707,7 +707,7 @@ func TestFastVsFullChains(t *testing.T) {
 	// Fast import the chain as a non-archive node to test
 	fastDb := ethdb.NewMemDatabase()
 	gspec.MustCommit(fastDb)
-	fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer fast.Stop()
 
 	headers := make([]*types.Header, len(blocks))
@@ -731,7 +731,7 @@ func TestFastVsFullChains(t *testing.T) {
 		t.Fatalf("failed to create temp freezer db: %v", err)
 	}
 	gspec.MustCommit(ancientDb)
-	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer ancient.Stop()
 
 	if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
@@ -835,7 +835,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
 		TrieTimeLimit:  5 * time.Minute,
 		NoHistory:      false,
 	}
-	archive, _ := NewBlockChain(archiveDb, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	archive, _ := NewBlockChain(archiveDb, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	if n, err := archive.InsertChain(context.Background(), blocks); err != nil {
 		t.Fatalf("failed to process block %d: %v", n, err)
 	}
@@ -848,7 +848,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
 	// Import the chain as a non-archive node and ensure all pointers are updated
 	fastDb, delfn := makeDb()
 	defer delfn()
-	fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer fast.Stop()
 
 	headers := make([]*types.Header, len(blocks))
@@ -868,7 +868,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
 	// Import the chain as a ancient-first node and ensure all pointers are updated
 	ancientDb, delfn := makeDb()
 	defer delfn()
-	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer ancient.Stop()
 
 	if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
@@ -887,7 +887,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
 	// Import the chain as a light node and ensure all pointers are updated
 	lightDb, delfn := makeDb()
 	defer delfn()
-	light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	if n, err := light.InsertHeaderChain(headers, 1); err != nil {
 		t.Fatalf("failed to insert header %d: %v", n, err)
 	}
@@ -947,7 +947,7 @@ func TestChainTxReorgs(t *testing.T) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	blockchain.EnableReceipts(true)
 
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
@@ -1049,7 +1049,7 @@ func TestLogReorgs(t *testing.T) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	blockchain.EnableReceipts(true)
 	defer blockchain.Stop()
@@ -1106,7 +1106,7 @@ func TestLogRebirth(t *testing.T) {
 		genesis       = gspec.MustCommit(db)
 		signer        = types.NewEIP155Signer(gspec.Config.ChainID)
 		engine        = ethash.NewFaker()
-		blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+		blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	)
 	blockchain.EnableReceipts(true)
 
@@ -1171,7 +1171,7 @@ func TestSideLogRebirth(t *testing.T) {
 		gspec         = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
 		genesis       = gspec.MustCommit(db)
 		signer        = types.NewEIP155Signer(gspec.Config.ChainID)
-		blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+		blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	)
 
 	defer blockchain.Stop()
@@ -1250,7 +1250,7 @@ func TestReorgSideEvent(t *testing.T) {
 		signer    = types.NewEIP155Signer(gspec.Config.ChainID)
 	)
 
-	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer blockchain.Stop()
 
@@ -1383,7 +1383,7 @@ func TestEIP155Transition(t *testing.T) {
 		genesis = gspec.MustCommit(db)
 	)
 
-	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer blockchain.Stop()
 
@@ -1507,7 +1507,7 @@ func doModesTest(history, preimages, receipts, txlookup bool) error {
 		NoHistory:           !history,
 	}
 
-	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	blockchain.EnableReceipts(receipts)
 	blockchain.EnablePreimages(preimages)
 	blockchain.EnableTxLookupIndex(txlookup)
@@ -1654,7 +1654,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
 		genesis   = gspec.MustCommit(db)
 		genesisDb = db.MemCopy()
 	)
-	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer blockchain.Stop()
 
@@ -1720,7 +1720,7 @@ func TestDoubleAccountRemoval(t *testing.T) {
 		genDb   = db.MemCopy()
 	)
 
-	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 	defer blockchain.Stop()
 
@@ -1797,7 +1797,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	chain, err := NewBlockChain(diskdb, cacheConfig, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, cacheConfig, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -1857,7 +1857,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
 		NoHistory:      false,
 		Pruning:        false,
 	}
-	chain, err := NewBlockChain(diskdb, cacheConfig, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, cacheConfig, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -1921,7 +1921,7 @@ func TestBlockchainRecovery(t *testing.T) {
 
 	gspec.MustCommit(ancientDb)
 
-	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 
 	headers := make([]*types.Header, len(blocks))
 	for i, block := range blocks {
@@ -1941,7 +1941,7 @@ func TestBlockchainRecovery(t *testing.T) {
 	rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
 
 	// Reopen broken blockchain again
-	ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ancient, _ = NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer ancient.Stop()
 	if num := ancient.CurrentBlock().NumberU64(); num != 0 {
 		t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
@@ -1979,7 +1979,7 @@ func TestIncompleteAncientReceiptChainInsertion(t *testing.T) {
 		t.Fatalf("failed to create temp freezer db: %v", err)
 	}
 	gspec.MustCommit(ancientDb)
-	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer ancient.Stop()
 
 	headers := make([]*types.Header, len(blocks))
@@ -2041,7 +2041,7 @@ func TestLowDiffLongChain(t *testing.T) {
 	diskDB := ethdb.NewMemDatabase()
 	new(Genesis).MustCommit(diskDB)
 
-	chain, err := NewBlockChain(diskDB, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskDB, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2100,7 +2100,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
 
 	diskdb := ethdb.NewMemDatabase()
 	new(Genesis).MustCommit(diskdb)
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2198,7 +2198,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
 	new(Genesis).MustCommit(chaindb)
 	defer os.RemoveAll(dir)
 
-	chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(chaindb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2315,7 +2315,7 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error
 	diskdb := ethdb.NewMemDatabase()
 	new(Genesis).MustCommit(diskdb)
 
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
 	}
@@ -2471,7 +2471,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
 	diskdb := ethdb.NewMemDatabase()
 	defer diskdb.Close()
 	gspec.MustCommit(diskdb)
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		b.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2485,7 +2485,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
 		diskdb := ethdb.NewMemDatabase()
 		gspec.MustCommit(diskdb)
 
-		chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+		chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 		if err != nil {
 			b.Fatalf("failed to create tester chain: %v", err)
 		}
@@ -2569,7 +2569,7 @@ func TestSideImportPrunedBlocks(t *testing.T) {
 	blocks, _ := GenerateChain(context.Background(), params.TestChainConfig, genesis, engine, db, 2*triesInMemory, nil)
 	diskdb := ethdb.NewMemDatabase()
 	new(Genesis).MustCommit(diskdb)
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2664,7 +2664,7 @@ func TestDeleteCreateRevert(t *testing.T) {
 	diskdb := ethdb.NewMemDatabase()
 	gspec.MustCommit(diskdb)
 
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2760,7 +2760,6 @@ func TestDeleteRecreateSlots(t *testing.T) {
 		},
 	}
 	genesis := gspec.MustCommit(db)
-
 	blocks, _ := GenerateChain(context.Background(), params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) {
 		b.SetCoinbase(common.Address{1})
 		// One transaction to AA, to kill it
@@ -2776,7 +2775,7 @@ func TestDeleteRecreateSlots(t *testing.T) {
 	diskdb := ethdb.NewMemDatabase()
 	gspec.MustCommit(diskdb)
 
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -2856,7 +2855,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
 		Pruning:        false,
 	}
 
-	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
 	blocks, _ := GenerateChain(ctx, params.TestChainConfig, genesis, engine, db, 1, func(i int, b *BlockGen) {
@@ -2874,7 +2873,7 @@ func TestDeleteRecreateAccount(t *testing.T) {
 	diskdb := ethdb.NewMemDatabase()
 	defer diskdb.Close()
 	gspec.MustCommit(diskdb)
-	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil)
+	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -3029,7 +3028,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
 		Pruning:        false,
 	}
 
-	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
 	blocks, _ := GenerateChain(ctx, params.TestChainConfig, genesis, engine, db, 150, func(i int, b *BlockGen) {
@@ -3063,7 +3062,7 @@ func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
 	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
 		//Debug:  true,
 		//Tracer: vm.NewJSONLogger(nil, os.Stdout),
-	}, nil, nil)
+	}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
@@ -3198,7 +3197,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
 		Pruning:        false,
 	}
 
-	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, cacheConfig, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil, nil)
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
 	blocks, _ := GenerateChain(ctx, params.TestChainConfig, genesis, engine, db, 4, func(i int, b *BlockGen) {
@@ -3216,7 +3215,7 @@ func TestInitThenFailCreateContract(t *testing.T) {
 	chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{
 		//Debug:  true,
 		//Tracer: vm.NewJSONLogger(nil, os.Stdout),
-	}, nil, nil)
+	}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create tester chain: %v", err)
 	}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index d113c3adf8c25a5c06a6432dbb37f804197a0f79..035550c6fa333b8efab914b1e64f0c09e81e04bd 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -100,12 +100,12 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
 // further limitations on the content of transactions that can be
 // added. If contract code relies on the BLOCKHASH instruction,
 // the block in chain will be returned.
-func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
+func (b *BlockGen) AddTxWithChain(bc ChainContext, tx *types.Transaction) {
 	if b.gasPool == nil {
 		b.SetCoinbase(common.Address{})
 	}
 	b.statedb.Prepare(tx.Hash(), common.Hash{}, len(b.txs))
-	receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.triedbstate.TrieStateWriter(), b.header, tx, &b.header.GasUsed, vm.Config{})
+	receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.triedbstate.TrieStateWriter(), b.header, tx, &b.header.GasUsed, vm.Config{}, nil)
 	if err != nil {
 		panic(err)
 	}
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 2aed6e5558d2eabadd3811ab77f49b8510d05aba..48a0b5195447c0d4b1b6ba708d3be749e773b528 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -57,7 +57,7 @@ func ExampleGenerateChain() {
 	}
 	genesis := gspec.MustCommit(db)
 
-	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	defer blockchain.Stop()
 	ctx := blockchain.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
diff --git a/core/dao_test.go b/core/dao_test.go
index 8784dbb347af0db89ad14a80769e9eef0e21b314..aaab1dabea97274e0d1b8507d7f1bc36d3fb75f2 100644
--- a/core/dao_test.go
+++ b/core/dao_test.go
@@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 	proConf := *params.TestChainConfig
 	proConf.DAOForkBlock = forkBlock
 	proConf.DAOForkSupport = true
-	proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+	proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 	defer proBc.Stop()
 	ctx := proBc.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
@@ -58,7 +58,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 	conConf.DAOForkBlock = forkBlock
 	conConf.DAOForkSupport = false
 
-	conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+	conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 	defer conBc.Stop()
 
 	if _, err := proBc.InsertChain(context.Background(), prefix); err != nil {
@@ -75,7 +75,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 			db = ethdb.NewMemDatabase()
 			defer db.Close()
 			gspec.MustCommit(db)
-			bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+			bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 			defer bc.Stop()
 			ctx = bc.WithContext(context.Background(), big.NewInt(conBc.CurrentBlock().Number().Int64()+1))
 
@@ -101,7 +101,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 			defer db.Close()
 
 			gspec.MustCommit(db)
-			bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+			bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 			defer bc.Stop()
 			ctx = bc.WithContext(context.Background(), big.NewInt(proBc.CurrentBlock().Number().Int64()+1))
 
@@ -127,7 +127,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 	db = ethdb.NewMemDatabase()
 	defer db.Close()
 	gspec.MustCommit(db)
-	bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 	defer bc.Stop()
 	ctx = bc.WithContext(context.Background(), big.NewInt(conBc.CurrentBlock().Number().Int64()+1))
 
@@ -146,7 +146,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
 	db = ethdb.NewMemDatabase()
 	defer db.Close()
 	gspec.MustCommit(db)
-	bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil)
+	bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 	defer bc.Stop()
 	ctx = bc.WithContext(context.Background(), big.NewInt(proBc.CurrentBlock().Number().Int64()+1))
 
diff --git a/core/genesis_test.go b/core/genesis_test.go
index a3ed3c66e17ecd84f0f40267747a2a6e57f765db..5110496eca19c66ceead51cc0db4848ac0ba8412 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -126,7 +126,7 @@ func TestSetupGenesis(t *testing.T) {
 				// Advance to block #4, past the homestead transition block of customg.
 				genesis := oldcustomg.MustCommit(db)
 
-				bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil)
+				bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}, nil, nil, vm.NewDestsCache(100))
 				defer bc.Stop()
 				ctx := bc.WithContext(context.Background(), big.NewInt(genesis.Number().Int64()+1))
 
diff --git a/core/state/database_test.go b/core/state/database_test.go
index 6cb2fcf4555a2c51eaa7c2f4f4576f3a567da40e..e99318161730b1cfa2771cdeaed2bbda3dffc5f8 100644
--- a/core/state/database_test.go
+++ b/core/state/database_test.go
@@ -72,7 +72,7 @@ func TestCreate2Revive(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -243,7 +243,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -348,7 +348,7 @@ func TestReorgOverSelfDestruct(t *testing.T) {
 	}
 
 	// Reload blockchain from the database
-	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -385,7 +385,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -480,7 +480,7 @@ func TestReorgOverStateChange(t *testing.T) {
 	}
 
 	// Reload blockchain from the database
-	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -535,7 +535,7 @@ func TestDatabaseStateChangeDBSizeDebug(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -701,7 +701,7 @@ func TestCreateOnExistingStorage(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -839,7 +839,7 @@ func TestEip2200Gas(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -927,7 +927,7 @@ func TestWrongIncarnation(t *testing.T) {
 	)
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -996,7 +996,7 @@ func TestWrongIncarnation(t *testing.T) {
 	}
 
 	// Reload blockchain from the database
-	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -1055,7 +1055,7 @@ func TestWrongIncarnation2(t *testing.T) {
 	knownContractAddress := common.HexToAddress("0xdb7d6ab1f17c6b31909ae466702703daef9269cf")
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go
index 15915a5cd97ba2f74f2ae45df73c121fdc894c62..03aca935a212df132c881e2c62a8d5f614a9553c 100644
--- a/core/state_prefetcher.go
+++ b/core/state_prefetcher.go
@@ -48,7 +48,7 @@ func newStatePrefetcher(config *params.ChainConfig, bc *BlockChain, engine conse
 // Prefetch processes the state changes according to the Ethereum rules by running
 // the transaction messages using the statedb, but any changes are discarded. The
 // only goal is to pre-cache transaction signatures and state trie nodes.
-func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.IntraBlockState, cfg vm.Config, interrupt *uint32) {
+func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.IntraBlockState, cfg vm.Config, interrupt *uint32, dests vm.Cache) {
 	var (
 		header  = block.Header()
 		gaspool = new(GasPool).AddGas(block.GasLimit())
@@ -60,7 +60,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.IntraBlock
 		}
 		// Block precaching permitted to continue, execute the transaction
 		statedb.Prepare(tx.Hash(), block.Hash(), i)
-		if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg); err != nil {
+		if err := precacheTransaction(p.config, p.bc, nil, gaspool, statedb, header, tx, cfg, dests); err != nil {
 			return // Ugh, something went horribly wrong, bail out
 		}
 	}
@@ -69,7 +69,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.IntraBlock
 // precacheTransaction attempts to apply a transaction to the given state database
 // and uses the input parameters for its environment. The goal is not to execute
 // the transaction successfully, rather to warm up touched data slots.
-func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gaspool *GasPool, statedb *state.IntraBlockState, header *types.Header, tx *types.Transaction, cfg vm.Config) error {
+func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gaspool *GasPool, statedb vm.IntraBlockState, header *types.Header, tx *types.Transaction, cfg vm.Config, dests vm.Cache) error {
 	// Convert the transaction into an executable message and pre-cache its sender
 	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
 	if err != nil {
@@ -77,7 +77,7 @@ func precacheTransaction(config *params.ChainConfig, bc ChainContext, author *co
 	}
 	// Create the EVM and execute the transaction
 	context := NewEVMContext(msg, header, bc, author)
-	vm := vm.NewEVM(context, statedb, config, cfg)
+	vm := vm.NewEVM(context, statedb, config, cfg, dests)
 
 	_, err = ApplyMessage(vm, msg, gaspool)
 	return err
diff --git a/core/state_processor.go b/core/state_processor.go
index 2760c147fa5758ab8fec30a57cf58d3abb2901ed..063c432e0ec45e73ae4332adb89c661b772b125e 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -121,7 +121,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
 //
 // PreProcess does not calculate receipt roots (required pre-Byzantium)
 // and does not update the TrieDbState. For those two call PostProcess afterwards.
-func (p *StateProcessor) PreProcess(block *types.Block, ibs *state.IntraBlockState, tds *state.TrieDbState, cfg vm.Config) (
+func (p *StateProcessor) PreProcess(block *types.Block, ibs *state.IntraBlockState, tds *state.TrieDbState, cfg vm.Config, dests vm.Cache) (
 	receipts types.Receipts, allLogs []*types.Log, usedGas uint64, root common.Hash, err error) {
 
 	header := block.Header()
@@ -147,7 +147,7 @@ func (p *StateProcessor) PreProcess(block *types.Block, ibs *state.IntraBlockSta
 			writeTrace = true
 		}
 		var receipt *types.Receipt
-		receipt, err = ApplyTransaction(p.config, p.bc, nil, gp, ibs, tds.TrieStateWriter(), header, tx, &usedGas, cfg)
+		receipt, err = ApplyTransaction(p.config, p.bc, nil, gp, ibs, tds.TrieStateWriter(), header, tx, &usedGas, cfg, dests)
 		// This code is useful when debugging a certain transaction. If uncommented, together with the code
 		// at the end of this function, after the execution of transaction with given hash, the file
 		// structlogs.txt will contain full trace of the transactin in JSON format. This can be compared
@@ -213,7 +213,7 @@ func (p *StateProcessor) PostProcess(block *types.Block, tds *state.TrieDbState,
 // 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, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
+func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.IntraBlockState, stateWriter state.StateWriter, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config, dests vm.Cache) (*types.Receipt, error) {
 	msg, err := tx.AsMessage(types.MakeSigner(config, header.Number))
 	if err != nil {
 		return nil, err
@@ -223,7 +223,7 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
 	context := NewEVMContext(msg, header, bc, author)
 	// Create a new environment which holds all relevant information
 	// about the transaction and calling mechanisms.
-	vmenv := vm.NewEVM(context, statedb, config, cfg)
+	vmenv := vm.NewEVM(context, statedb, config, cfg, dests)
 	// Apply the transaction to the current state (included in the env)
 	result, err := ApplyMessage(vmenv, msg, gp)
 	if err != nil {
diff --git a/core/types.go b/core/types.go
index 9018368b577d0d2cbd85352e011c3e69b9067b1d..4cbd59a92e213549deb09fc3a7b2f2fb28a383b4 100644
--- a/core/types.go
+++ b/core/types.go
@@ -44,11 +44,11 @@ type Prefetcher interface {
 	// Prefetch processes the state changes according to the Ethereum rules by running
 	// the transaction messages using the statedb, but any changes are discarded. The
 	// only goal is to pre-cache transaction signatures and state trie nodes.
-	Prefetch(block *types.Block, statedb *state.IntraBlockState, cfg vm.Config, interrupt *uint32)
+	Prefetch(block *types.Block, statedb *state.IntraBlockState, cfg vm.Config, interrupt *uint32, dests vm.Cache)
 }
 
 // Processor is an interface for processing blocks using a given initial state.
 type Processor interface {
-	PreProcess(block *types.Block, statedb *state.IntraBlockState, tds *state.TrieDbState, cfg vm.Config) (receipts types.Receipts, allLogs []*types.Log, usedGas uint64, root common.Hash, err error)
+	PreProcess(block *types.Block, statedb *state.IntraBlockState, tds *state.TrieDbState, cfg vm.Config, dests vm.Cache) (receipts types.Receipts, allLogs []*types.Log, usedGas uint64, root common.Hash, err error)
 	PostProcess(block *types.Block, tds *state.TrieDbState, receipts types.Receipts) error
 }
diff --git a/core/vm/analysis.go b/core/vm/analysis.go
index 0ccf47b97903a7d547d1dbb0256cd2cff5ead0cc..1779b796480723930069943834b08aa0ca02df70 100644
--- a/core/vm/analysis.go
+++ b/core/vm/analysis.go
@@ -16,30 +16,63 @@
 
 package vm
 
-// bitvec is a bit vector which maps bytes in a program.
-// An unset bit means the byte is an opcode, a set bit means
-// it's data (i.e. argument of PUSHxx).
-type bitvec []byte
+import (
+	"github.com/hashicorp/golang-lru"
+	"github.com/ledgerwatch/turbo-geth/common"
+	"github.com/ledgerwatch/turbo-geth/common/pool"
+)
 
-func (bits *bitvec) set(pos uint64) {
-	(*bits)[pos/8] |= 0x80 >> (pos % 8)
+type Cache interface {
+	Len() int
+	Set(hash common.Hash, v *pool.ByteBuffer)
+	Get(hash common.Hash) (*pool.ByteBuffer, bool)
+	Clear(codeHash common.Hash, local *pool.ByteBuffer)
 }
-func (bits *bitvec) set8(pos uint64) {
-	(*bits)[pos/8] |= 0xFF >> (pos % 8)
-	(*bits)[pos/8+1] |= ^(0xFF >> (pos % 8))
+
+type DestsCache struct {
+	*lru.Cache
+}
+
+func NewDestsCache(maxSize int) *DestsCache {
+	c, _ := lru.New(maxSize)
+	return &DestsCache{c}
 }
 
-// codeSegment checks if the position is in a code segment.
-func (bits *bitvec) codeSegment(pos uint64) bool {
-	return ((*bits)[pos/8] & (0x80 >> (pos % 8))) == 0
+func (d *DestsCache) Set(hash common.Hash, v *pool.ByteBuffer) {
+	d.Add(hash, v)
+}
+
+func (d DestsCache) Get(hash common.Hash) (*pool.ByteBuffer, bool) {
+	v, ok := d.Cache.Get(hash)
+	if !ok {
+		return nil, false
+	}
+	return v.(*pool.ByteBuffer), ok
+}
+
+func (d *DestsCache) Clear(codeHash common.Hash, local *pool.ByteBuffer) {
+	if codeHash == (common.Hash{}) {
+		return
+	}
+	_, ok := d.Get(codeHash)
+	if ok {
+		return
+	}
+	// analysis is a local one
+	pool.PutBuffer(local)
+}
+
+func (d *DestsCache) Len() int {
+	return d.Cache.Len()
 }
 
 // codeBitmap collects data locations in code.
-func codeBitmap(code []byte) bitvec {
+func codeBitmap(code []byte) *pool.ByteBuffer {
 	// The bitmap is 4 bytes longer than necessary, in case the code
 	// ends with a PUSH32, the algorithm will push zeroes onto the
 	// bitvector outside the bounds of the actual code.
-	bits := make(bitvec, len(code)/8+1+4)
+	bits := pool.GetBufferZeroed(uint(len(code)/8 + 1 + 4))
+
 	for pc := uint64(0); pc < uint64(len(code)); {
 		op := OpCode(code[pc])
 
@@ -47,11 +80,11 @@ func codeBitmap(code []byte) bitvec {
 			numbits := op - PUSH1 + 1
 			pc++
 			for ; numbits >= 8; numbits -= 8 {
-				bits.set8(pc) // 8
+				bits.SetBit8Pos(pc) // 8
 				pc += 8
 			}
 			for ; numbits > 0; numbits-- {
-				bits.set(pc)
+				bits.SetBitPos(pc)
 				pc++
 			}
 		} else {
diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go
index 372b104b14009e5f321f2a677e4899fcf95bb723..cc8f651390a2c7521e80ceadcf71ad5768a4e7cd 100644
--- a/core/vm/analysis_test.go
+++ b/core/vm/analysis_test.go
@@ -17,8 +17,14 @@
 package vm
 
 import (
+	"fmt"
+	"io/ioutil"
 	"testing"
 
+	"github.com/holiman/uint256"
+
+	"github.com/ledgerwatch/turbo-geth/common"
+	"github.com/ledgerwatch/turbo-geth/common/pool"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 )
 
@@ -49,21 +55,59 @@ func TestJumpDestAnalysis(t *testing.T) {
 	}
 	for _, test := range tests {
 		ret := codeBitmap(test.code)
-		if ret[test.which] != test.exp {
-			t.Fatalf("expected %x, got %02x", test.exp, ret[test.which])
+		if ret.Get(test.which) != test.exp {
+			t.Fatalf("expected %x, got %02x", test.exp, ret.Get(test.which))
 		}
 	}
 }
 
-func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
+func BenchmarkJumpdestSet8(b *testing.B) {
+	const size = 1000
+	bits := pool.GetBuffer(size)
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		for i := uint64(0); i < size*8-8; i++ {
+			bits.SetBit8Pos(i)
+		}
+	}
+	b.StopTimer()
+	fmt.Fprint(ioutil.Discard, bits.Len())
+}
+
+func BenchmarkJumpdestSet(b *testing.B) {
+	const size = 1000
+	bits := pool.GetBuffer(size)
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		for i := uint64(0); i < 8*size; i++ {
+			bits.SetBitPos(i)
+		}
+	}
+	b.StopTimer()
+	fmt.Fprint(ioutil.Discard, bits.Len())
+}
+
+func BenchmarkJumpdestAnalysisEmpty_1200k(bench *testing.B) {
 	// 1.4 ms
 	code := make([]byte, 1200000)
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
-		codeBitmap(code)
+		b := codeBitmap(code)
+		pool.PutBuffer(b)
 	}
 	bench.StopTimer()
 }
+
+func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
+	code := common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056")
+	bench.ResetTimer()
+	for i := 0; i < bench.N; i++ {
+		b := codeBitmap(code)
+		pool.PutBuffer(b)
+	}
+	bench.StopTimer()
+}
+
 func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
 	// 4 ms
 	code := make([]byte, 1200000)
@@ -73,3 +117,25 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
 	}
 	bench.StopTimer()
 }
+
+func BenchmarkJumpDest(b *testing.B) {
+	code := common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056")
+	pc := new(uint256.Int)
+	hash := common.Hash{1, 2, 3, 4, 5}
+
+	contractRef := dummyContractRef{}
+	dests := NewDestsCache(10)
+
+	b.ResetTimer()
+	for n := 0; n < b.N; n++ {
+		contract := NewContract(contractRef, contractRef, nil, 0, dests)
+		contract.Code = code
+		contract.CodeHash = hash
+
+		b.StartTimer()
+		for i := range contract.Code {
+			contract.validJumpdest(pc.SetUint64(uint64(i)))
+		}
+		b.StopTimer()
+	}
+}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index 768c7e6202cb4222121764b88dd2681dd3f8f34b..9bab9b1f3cc90a756b8f0a04bf76f4c64f425976 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -20,6 +20,7 @@ import (
 	"github.com/holiman/uint256"
 
 	"github.com/ledgerwatch/turbo-geth/common"
+	"github.com/ledgerwatch/turbo-geth/common/pool"
 )
 
 // ContractRef is a reference to the contract's backing object
@@ -49,8 +50,8 @@ type Contract struct {
 	caller        ContractRef
 	self          ContractRef
 
-	jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
-	analysis  bitvec                 // Locally cached result of JUMPDEST analysis
+	analysis *pool.ByteBuffer // Locally cached result of JUMPDEST analysis
+	dests    Cache
 
 	Code     []byte
 	CodeHash common.Hash
@@ -62,22 +63,17 @@ type Contract struct {
 }
 
 // NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64, dests Cache) *Contract {
 	c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
 
-	if parent, ok := caller.(*Contract); ok {
-		// Reuse JUMPDEST analysis from parent context if available.
-		c.jumpdests = parent.jumpdests
-	} else {
-		c.jumpdests = make(map[common.Hash]bitvec)
-	}
-
 	// Gas should be a pointer so it can safely be reduced through the run
 	// This pointer will be off the state transition
 	c.Gas = gas
 	// ensures a value is set
 	c.value = value
 
+	c.dests = dests
+
 	return c
 }
 
@@ -94,27 +90,27 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
 	}
 	// Do we have it locally already?
 	if c.analysis != nil {
-		return c.analysis.codeSegment(udest)
+		return c.analysis.CodeSegment(udest)
 	}
 	// If we have the code hash (but no analysis), we should look into the
 	// parent analysis map and see if the analysis has been made previously
 	if c.CodeHash != (common.Hash{}) {
-		analysis, exist := c.jumpdests[c.CodeHash]
+		var exist bool
+		// Also stash it in current contract for faster access
+		c.analysis, exist = c.dests.Get(c.CodeHash)
 		if !exist {
 			// Do the analysis and save in parent context
-			analysis = codeBitmap(c.Code)
-			c.jumpdests[c.CodeHash] = analysis
+			c.analysis = codeBitmap(c.Code)
+			c.dests.Set(c.CodeHash, c.analysis)
 		}
-		// Also stash it in current contract for faster access
-		c.analysis = analysis
-		return analysis.codeSegment(udest)
+		return c.analysis.CodeSegment(udest)
 	}
 	// We don't have the code hash, most likely a piece of initcode not already
 	// in state trie. In that case, we do an analysis, and save it locally, so
 	// we don't have to recalculate it for every JUMP instruction in the execution
 	// However, we don't save it within the parent context
 	c.analysis = codeBitmap(c.Code)
-	return c.analysis.codeSegment(udest)
+	return c.analysis.CodeSegment(udest)
 }
 
 // AsDelegate sets the contract to be a delegate call and returns the current
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index bbbc029ec6aa4ca3d517d281544b3577eba970a3..715f622b13bb48015ac015f1932d8242d3f179da 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -403,7 +403,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
 	p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.input)
 	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(uint256.Int), p.RequiredGas(in))
+		nil, new(uint256.Int), p.RequiredGas(in), NewDestsCache(10))
 	t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
 		if res, err := RunPrecompiledContract(p, in, contract); err != nil {
 			t.Error(err)
@@ -422,7 +422,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
 	p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.input)
 	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(uint256.Int), p.RequiredGas(in)-1)
+		nil, new(uint256.Int), p.RequiredGas(in)-1, NewDestsCache(10))
 	t.Run(fmt.Sprintf("%s-Gas=%d", test.name, contract.Gas), func(t *testing.T) {
 		_, err := RunPrecompiledContract(p, in, contract)
 		if err.Error() != "out of gas" {
@@ -440,7 +440,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
 	p := PrecompiledContractsIstanbul[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.input)
 	contract := NewContract(AccountRef(common.HexToAddress("31337")),
-		nil, new(uint256.Int), p.RequiredGas(in))
+		nil, new(uint256.Int), p.RequiredGas(in), NewDestsCache(10))
 
 	t.Run(test.name, func(t *testing.T) {
 		_, err := RunPrecompiledContract(p, in, contract)
@@ -463,7 +463,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
 	in := common.Hex2Bytes(test.input)
 	reqGas := p.RequiredGas(in)
 	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(uint256.Int), reqGas)
+		nil, new(uint256.Int), reqGas, NewDestsCache(10))
 
 	var (
 		res  []byte
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 9255e9d5cbfae9706c3f2de9cb05e70b901c41c4..74a37b009a69a9bc85ff8cb53e72916f1dcf7b51 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -133,11 +133,16 @@ type EVM struct {
 	// available gas is calculated in gasCall* according to the 63/64 rule and later
 	// applied in opCall*.
 	callGasTemp uint64
+
+	dests Cache
 }
 
 // NewEVM returns a new EVM. The returned EVM is not thread safe and should
 // only ever be used *once*.
-func NewEVM(ctx Context, state IntraBlockState, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
+func NewEVM(ctx Context, state IntraBlockState, chainConfig *params.ChainConfig, vmConfig Config, dests Cache) *EVM {
+	if dests == nil {
+		dests = NewDestsCache(10)
+	}
 	evm := &EVM{
 		Context:         ctx,
 		IntraBlockState: state,
@@ -145,6 +150,7 @@ func NewEVM(ctx Context, state IntraBlockState, chainConfig *params.ChainConfig,
 		chainConfig:     chainConfig,
 		chainRules:      chainConfig.Rules(ctx.BlockNumber),
 		interpreters:    make([]Interpreter, 0, 1),
+		dests:           dests,
 	}
 
 	if chainConfig.IsEWASM(ctx.BlockNumber) {
@@ -235,8 +241,9 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 	evm.Transfer(evm.IntraBlockState, caller.Address(), to.Address(), value)
 	// Initialise a new contract and set the code that is to be used by the EVM.
 	// The contract is a scoped environment for this execution context only.
-	contract := NewContract(caller, to, value, gas)
+	contract := NewContract(caller, to, value, gas, evm.GetJumpsDests())
 	contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr))
+	defer evm.GetJumpsDests().Clear(contract.CodeHash, contract.analysis)
 
 	// Even if the account has no code, we need to continue because it might be a precompile
 	start := time.Now()
@@ -291,8 +298,9 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
 	)
 	// Initialise a new contract and set the code that is to be used by the EVM.
 	// The contract is a scoped environment for this execution context only.
-	contract := NewContract(caller, to, value, gas)
+	contract := NewContract(caller, to, value, gas, evm.GetJumpsDests())
 	contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr))
+	defer evm.GetJumpsDests().Clear(contract.CodeHash, contract.analysis)
 
 	ret, err = run(evm, contract, input, false)
 	if err != nil {
@@ -322,8 +330,9 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 		to       = AccountRef(caller.Address())
 	)
 	// Initialise a new contract and make initialise the delegate values
-	contract := NewContract(caller, to, nil, gas).AsDelegate()
+	contract := NewContract(caller, to, nil, gas, evm.GetJumpsDests()).AsDelegate()
 	contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr))
+	defer evm.GetJumpsDests().Clear(contract.CodeHash, contract.analysis)
 
 	ret, err = run(evm, contract, input, false)
 	if err != nil {
@@ -353,8 +362,9 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
 	)
 	// Initialise a new contract and set the code that is to be used by the EVM.
 	// The contract is a scoped environment for this execution context only.
-	contract := NewContract(caller, to, new(uint256.Int), gas)
+	contract := NewContract(caller, to, new(uint256.Int), gas, evm.GetJumpsDests())
 	contract.SetCallCode(&addr, evm.IntraBlockState.GetCodeHash(addr), evm.IntraBlockState.GetCode(addr))
+	defer evm.GetJumpsDests().Clear(contract.CodeHash, contract.analysis)
 
 	// We do an AddBalance of zero here, just in order to trigger a touch.
 	// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
@@ -415,8 +425,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
 
 	// Initialise a new contract and set the code that is to be used by the EVM.
 	// The contract is a scoped environment for this execution context only.
-	contract := NewContract(caller, AccountRef(address), value, gas)
+	contract := NewContract(caller, AccountRef(address), value, gas, evm.GetJumpsDests())
 	contract.SetCodeOptionalHash(&address, codeAndHash)
+	defer evm.GetJumpsDests().Clear(contract.CodeHash, contract.analysis)
 
 	if evm.vmConfig.NoRecursion && evm.depth > 0 {
 		return nil, address, gas, nil
@@ -487,3 +498,10 @@ func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *
 
 // ChainConfig returns the environment's chain configuration
 func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
+
+func (evm *EVM) GetJumpsDests() Cache {
+	if evm.dests == nil {
+		evm.dests = NewDestsCache(50000)
+	}
+	return evm.dests
+}
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 92b60f1f4e89a48923a9cde13879a105bab017b8..e95c39ce0e3ceaf2d84da8acf9831917296cfa22 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -99,7 +99,8 @@ func TestEIP2200(t *testing.T) {
 			CanTransfer: func(IntraBlockState, common.Address, *uint256.Int) bool { return true },
 			Transfer:    func(IntraBlockState, common.Address, common.Address, *uint256.Int) {},
 		}
-		vmenv := NewEVM(vmctx, state, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
+		dests := NewDestsCache(100)
+		vmenv := NewEVM(vmctx, state, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}}, dests)
 
 		_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
 		if err != tt.failure {
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 3b27658c49235821d165dbd2ede67f49c237f765..d36f9951327f5fb9d08e1a3905d67367bc510b78 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -94,7 +94,8 @@ func init() {
 func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFunc, name string) {
 
 	var (
-		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests          = NewDestsCache(100)
+		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack          = stack.New()
 		pc             = uint64(0)
 		evmInterpreter = env.interpreter.(*EVMInterpreter)
@@ -191,7 +192,8 @@ func TestSAR(t *testing.T) {
 // getResult is a convenience function to generate the expected values
 func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
 	var (
-		env         = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests       = NewDestsCache(100)
+		env         = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack       = stack.New()
 		pc          = uint64(0)
 		interpreter = env.interpreter.(*EVMInterpreter)
@@ -241,7 +243,8 @@ func TestJsonTestcases(t *testing.T) {
 
 func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 	var (
-		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests          = NewDestsCache(100)
+		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack          = stack.New()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -474,7 +477,8 @@ func BenchmarkOpIsZero(b *testing.B) {
 
 func TestOpMstore(t *testing.T) {
 	var (
-		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests          = NewDestsCache(100)
+		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
@@ -500,7 +504,8 @@ func TestOpMstore(t *testing.T) {
 
 func BenchmarkOpMstore(bench *testing.B) {
 	var (
-		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests          = NewDestsCache(100)
+		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
@@ -522,7 +527,8 @@ func BenchmarkOpMstore(bench *testing.B) {
 
 func BenchmarkOpSHA3(bench *testing.B) {
 	var (
-		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
+		dests          = NewDestsCache(100)
+		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{}, dests)
 		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 8b4fc7b251d58f4e13737cbd8ac5588c578d2851..76cc2d58fc519fe24e55aa6423a6f30ee8c20eec 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -53,11 +53,12 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
 
 func TestStoreCapture(t *testing.T) {
 	var (
-		env      = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
+		dests    = NewDestsCache(100)
+		env      = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{}, dests)
 		logger   = NewStructLogger(nil)
 		mem      = NewMemory()
 		stack    = stack.New()
-		contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 0)
+		contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 0, NewDestsCache(10))
 	)
 	stack.Push(uint256.NewInt().SetOne())
 	stack.Push(uint256.NewInt())
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
index 3fb12770c9d25a7538cf76cf9be571e31ecc627a..07d4c1e842104e459c1e269cf9136d25199b2a4b 100644
--- a/core/vm/runtime/env.go
+++ b/core/vm/runtime/env.go
@@ -21,7 +21,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core/vm"
 )
 
-func NewEnv(cfg *Config) *vm.EVM {
+func NewEnv(cfg *Config, dests vm.Cache) *vm.EVM {
 	context := vm.Context{
 		CanTransfer: core.CanTransfer,
 		Transfer:    core.Transfer,
@@ -35,5 +35,5 @@ func NewEnv(cfg *Config) *vm.EVM {
 		GasPrice:    cfg.GasPrice,
 	}
 
-	return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
+	return vm.NewEVM(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig, dests)
 }
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 5676929cc03db30fd242e90232bb384eabd0ea7c..9e8bfb6ece0e7c692b6b273e0f1f9a5fc372962b 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -108,7 +108,7 @@ func Execute(code, input []byte, cfg *Config, blockNr uint64) ([]byte, *state.In
 	}
 	var (
 		address = common.BytesToAddress([]byte("contract"))
-		vmenv   = NewEnv(cfg)
+		vmenv   = NewEnv(cfg, nil)
 		sender  = vm.AccountRef(cfg.Origin)
 	)
 	cfg.State.CreateAccount(address, true)
@@ -139,7 +139,7 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, common.Address,
 		cfg.State = state.New(cfg.TrieDbSt)
 	}
 	var (
-		vmenv  = NewEnv(cfg)
+		vmenv  = NewEnv(cfg, nil)
 		sender = vm.AccountRef(cfg.Origin)
 	)
 
@@ -161,7 +161,7 @@ func Create(input []byte, cfg *Config, blockNr uint64) ([]byte, common.Address,
 func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, error) {
 	setDefaults(cfg)
 
-	vmenv := NewEnv(cfg)
+	vmenv := NewEnv(cfg, nil)
 
 	sender := cfg.State.GetOrNewStateObject(cfg.Origin)
 	// Call the code with the given configuration.
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index da0ba392f80adbe10da180139c6cc5c9af8861af..96ed8c5b3eb9f3510ccf8cd2689b94a2ee1acbbf 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -148,9 +148,9 @@ func BenchmarkCall(b *testing.B) {
 	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		for j := 0; j < 400; j++ {
-			Execute(code, cpurchase, nil, 0)
-			Execute(code, creceived, nil, 0)
-			Execute(code, refund, nil, 0)
+			_, _, _ = Execute(code, cpurchase, nil, 0)
+			_, _, _ = Execute(code, creceived, nil, 0)
+			_, _, _ = Execute(code, refund, nil, 0)
 		}
 	}
 }
@@ -188,7 +188,7 @@ func benchmarkEVM_Create(bench *testing.B, code string) {
 	// Warm up the intpools and stuff
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
-		Call(receiver, []byte{}, &runtimeConfig)
+		_, _, _ = Call(receiver, []byte{}, &runtimeConfig)
 	}
 	bench.StopTimer()
 }
@@ -344,6 +344,6 @@ func BenchmarkSimpleLoop(b *testing.B) {
 	//	}})
 
 	for i := 0; i < b.N; i++ {
-		Execute(code, nil, nil, 0)
+		_, _, _ = Execute(code, nil, nil, 0)
 	}
 }
diff --git a/eth/api.go b/eth/api.go
index 419dff87041cd8c0c163e60812e20e581ef2902b..1f5d2a8829d9881a0836d267360565ae03d6f5c6 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -114,9 +114,9 @@ func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI {
 // transaction pool.
 func (api *PrivateMinerAPI) Start(threads *int) error {
 	if threads == nil {
-		return api.e.StartMining(runtime.NumCPU())
+		return api.e.StartMining(runtime.NumCPU(), api.e.blockchain.DestsCache)
 	}
-	return api.e.StartMining(*threads)
+	return api.e.StartMining(*threads, api.e.blockchain.DestsCache)
 }
 
 // Stop terminates the miner, both at the consensus engine level as well as at
@@ -397,7 +397,7 @@ type StorageEntry struct {
 
 // StorageRangeAt returns the storage at the given block height and transaction index.
 func (api *PrivateDebugAPI) StorageRangeAt(ctx context.Context, blockHash common.Hash, txIndex uint64, contractAddress common.Address, keyStart hexutil.Bytes, maxResult int) (StorageRangeResult, error) {
-	_, _, _, dbstate, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, txIndex)
+	_, _, _, dbstate, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, txIndex, api.eth.blockchain.DestsCache)
 	if err != nil {
 		return StorageRangeResult{}, err
 	}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index c537c4fdbfb58945bdefc49c78f8734fb42cd87c..02c79c79f1d6a78d510b09a3d8518ef7446b7771 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -209,7 +209,7 @@ func (b *EthAPIBackend) getReceiptsByReApplyingTransactions(block *types.Block,
 	for i, tx := range block.Transactions() {
 		statedb.Prepare(tx.Hash(), block.Hash(), i)
 
-		receipt, err := core.ApplyTransaction(b.ChainConfig(), b.eth.blockchain, nil, gp, statedb, dbstate, header, tx, usedGas, vmConfig)
+		receipt, err := core.ApplyTransaction(b.ChainConfig(), b.eth.blockchain, nil, gp, statedb, dbstate, header, tx, usedGas, vmConfig, b.eth.blockchain.DestsCache)
 		if err != nil {
 			return nil, err
 		}
@@ -256,7 +256,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *sta
 	vmError := func() error { return nil }
 
 	context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
-	return vm.NewEVM(context, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig()), vmError, nil
+	return vm.NewEVM(context, state, b.eth.blockchain.Config(), *b.eth.blockchain.GetVMConfig(), b.eth.blockchain.DestsCache), vmError, nil
 }
 
 func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
diff --git a/eth/api_tracer.go b/eth/api_tracer.go
index 0d718f66ae762f68bcdd6894c628de0424f0af4d..9c8c14d6f43ea285c7bede78109ef8b206dbdc22 100644
--- a/eth/api_tracer.go
+++ b/eth/api_tracer.go
@@ -285,7 +285,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
 			}
 			// Generate the next state snapshot fast without tracing
 			processor := api.eth.blockchain.Processor()
-			receipts, _, _, _, err := processor.PreProcess(block, statedb, tds, vm.Config{})
+			receipts, _, _, _, err := processor.PreProcess(block, statedb, tds, vm.Config{}, api.eth.blockchain.DestsCache)
 			if err != nil {
 				failed = err
 				break
@@ -481,7 +481,7 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
 		msg, _ := tx.AsMessage(signer)
 		vmctx := core.NewEVMContext(msg, block.Header(), api.eth.blockchain, nil)
 
-		vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{})
+		vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vm.Config{}, api.eth.blockchain.DestsCache)
 		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
 			failed = err
 			break
@@ -574,7 +574,7 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
 			}
 		}
 		// Execute the transaction and flush any traces to disk
-		vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf)
+		vmenv := vm.NewEVM(vmctx, statedb, api.eth.blockchain.Config(), vmConf, api.eth.blockchain.DestsCache)
 		_, err = core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas()))
 		if writer != nil {
 			writer.Flush()
@@ -627,7 +627,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, hash common.Ha
 	if tx == nil {
 		return nil, fmt.Errorf("transaction %#x not found", hash)
 	}
-	msg, vmctx, statedb, _, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, index)
+	msg, vmctx, statedb, _, err := ComputeTxEnv(ctx, api.eth.blockchain, api.eth.blockchain.Config(), api.eth.blockchain, api.eth.ChainKV(), blockHash, index, api.eth.blockchain.DestsCache)
 	if err != nil {
 		return nil, err
 	}
@@ -673,7 +673,7 @@ func (api *PrivateDebugAPI) traceTx(ctx context.Context, message core.Message, v
 		tracer = vm.NewStructLogger(config.LogConfig)
 	}
 	// Run the transaction with tracing enabled.
-	vmenv := vm.NewEVM(vmctx, state, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer})
+	vmenv := vm.NewEVM(vmctx, state, api.eth.blockchain.Config(), vm.Config{Debug: true, Tracer: tracer}, api.eth.blockchain.DestsCache)
 
 	result, err := core.ApplyMessage(vmenv, message, new(core.GasPool).AddGas(message.Gas()))
 	if err != nil {
@@ -706,7 +706,7 @@ type BlockGetter interface {
 }
 
 // computeTxEnv returns the execution environment of a certain transaction.
-func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainKV ethdb.KV, blockHash common.Hash, txIndex uint64) (core.Message, vm.Context, *state.IntraBlockState, *state.DbState, error) {
+func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.ChainConfig, chain core.ChainContext, chainKV ethdb.KV, blockHash common.Hash, txIndex uint64, dests vm.Cache) (core.Message, vm.Context, *state.IntraBlockState, *state.DbState, error) {
 	// Create the parent state database
 	block := blockGetter.GetBlockByHash(blockHash)
 	if block == nil {
@@ -739,7 +739,7 @@ func ComputeTxEnv(ctx context.Context, blockGetter BlockGetter, cfg *params.Chai
 			return msg, EVMcontext, statedb, dbstate, nil
 		}
 		// Not yet the searched for transaction, execute on top of the current state
-		vmenv := vm.NewEVM(EVMcontext, statedb, cfg, vm.Config{})
+		vmenv := vm.NewEVM(EVMcontext, statedb, cfg, vm.Config{}, dests)
 		if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil {
 			return nil, vm.Context{}, nil, nil, fmt.Errorf("transaction %x failed: %v", tx.Hash(), err)
 		}
diff --git a/eth/backend.go b/eth/backend.go
index ab11205164c328c89aeb485679bdff7a48b4022c..1ca9742ec261b2771134763cf82fa91cebd185cf 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -238,7 +238,9 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
 			ArchiveSyncInterval: uint64(config.ArchiveSyncInterval),
 		}
 	)
-	eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit)
+
+	dests := vm.NewDestsCache(50000)
+	eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, dests)
 	if err != nil {
 		return nil, err
 	}
@@ -282,7 +284,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
 	eth.protocolManager.SetDataDir(ctx.Config.DataDir)
 
 	if config.SyncMode != downloader.StagedSync {
-		eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
+		eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock, dests)
 		_ = eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
 	}
 
@@ -515,7 +517,7 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) {
 // StartMining starts the miner with the given number of CPU threads. If mining
 // is already running, this method adjust the number of threads allowed to use
 // and updates the minimum price required by the transaction pool.
-func (s *Ethereum) StartMining(threads int) error {
+func (s *Ethereum) StartMining(threads int, dests vm.Cache) error {
 	// Update the thread count within the consensus engine
 	type threaded interface {
 		SetThreads(threads int)
@@ -553,7 +555,7 @@ func (s *Ethereum) StartMining(threads int) error {
 		// introduced to speed sync times.
 		atomic.StoreUint32(&s.protocolManager.acceptTxs, 1)
 
-		go s.miner.Start(eb)
+		go s.miner.Start(eb, dests)
 	}
 	return nil
 }
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index cc668e82763acff1a347e16eb54dac0a860372bf..83ce9f7b8eb62dd729773d80fbd297fb9462d085 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -345,8 +345,8 @@ func (d *Downloader) UnregisterPeer(id string) error {
 
 // Synchronise tries to sync up our local block chain with a remote peer, both
 // adding various sanity checks as well as wrapping it with various log entries.
-func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
-	err := d.synchronise(id, head, td, mode)
+func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode, dests vm.Cache) error {
+	err := d.synchronise(id, head, td, mode, dests)
 	switch err {
 	case nil:
 	case errBusy, errCanceled:
@@ -371,7 +371,7 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
 // synchronise will select the peer and use it for synchronising. If an empty string is given
 // it will use the best peer possible and synchronize if its TD is higher than our own. If any of the
 // checks fail an error will be returned. This method is synchronous
-func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error {
+func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode, dests vm.Cache) error {
 	// Mock out the synchronisation if testing
 	if d.synchroniseMock != nil {
 		return d.synchroniseMock(id, hash)
@@ -434,12 +434,12 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
 	if p == nil {
 		return errUnknownPeer
 	}
-	return d.syncWithPeer(p, hash, td)
+	return d.syncWithPeer(p, hash, td, dests)
 }
 
 // syncWithPeer starts a block synchronization based on the hash chain from the
 // specified peer and head hash.
-func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) {
+func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int, dests vm.Cache) (err error) {
 	d.mux.Post(StartEvent{})
 	defer func() {
 		// reset on error
@@ -554,6 +554,7 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
 			d.datadir,
 			d.quitCh,
 			fetchers,
+			dests,
 		)
 	}
 
diff --git a/eth/downloader/downloader_stagedsync_test.go b/eth/downloader/downloader_stagedsync_test.go
index fe554dfff9d78f30d202c706a3c3e092bbc612bf..ad1e24aa7d0049e83fd4a5374efedd78534f6d91 100644
--- a/eth/downloader/downloader_stagedsync_test.go
+++ b/eth/downloader/downloader_stagedsync_test.go
@@ -265,7 +265,7 @@ func (st *stagedSyncTester) sync(id string, td *big.Int) error {
 	st.lock.RUnlock()
 
 	// Synchronise with the chosen peer and ensure proper cleanup afterwards
-	err := st.downloader.synchronise(id, hash, td, StagedSync)
+	err := st.downloader.synchronise(id, hash, td, StagedSync, vm.NewDestsCache(100))
 	select {
 	case <-st.downloader.cancelCh:
 		// Ok, downloader fully cancelled after sync cycle
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index f4fa3048933c3b18f329e3d2f22327677a976a48..b30597a0348454314e4148267f397fc30a65b810 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -114,7 +114,7 @@ func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error {
 	dl.lock.RUnlock()
 
 	// Synchronise with the chosen peer and ensure proper cleanup afterwards
-	err := dl.downloader.synchronise(id, hash, td, mode)
+	err := dl.downloader.synchronise(id, hash, td, mode, vm.NewDestsCache(100))
 	select {
 	case <-dl.downloader.cancelCh:
 		// Ok, downloader fully cancelled after sync cycle
@@ -1189,6 +1189,7 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
 	defer tester.terminate()
 	defer tester.peerDb.Close()
 	chain := testChainBase.shorten(1)
+	dests := vm.NewDestsCache(100)
 
 	for i, tt := range tests {
 		// Register a new peer and ensure its presence
@@ -1202,7 +1203,7 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
 		// Simulate a synchronisation and check the required result
 		tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result }
 
-		tester.downloader.Synchronise(id, tester.genesis.Hash(), big.NewInt(1000), FullSync)
+		_ = tester.downloader.Synchronise(id, tester.genesis.Hash(), big.NewInt(1000), FullSync, dests)
 		if _, ok := tester.peers[id]; !ok != tt.drop {
 			t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop)
 		}
diff --git a/eth/handler.go b/eth/handler.go
index ce491d9f3991ef16a5ead891f4ae7f60c77c7894..96366269c3adfc2c0a432a462ce8a355c4edef58 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -1013,7 +1013,7 @@ func (pm *ProtocolManager) handleDebugMsg(p *debugPeer) error {
 
 		engine := pm.blockchain.Engine()
 		pm.blockchain.ChainDb().Close()
-		blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil)
+		blockchain, err := core.NewBlockChain(ethDb, nil, chainConfig, engine, vm.Config{}, nil, nil, vm.NewDestsCache(10))
 		if err != nil {
 			return fmt.Errorf("fail in NewBlockChain: %w", err)
 		}
diff --git a/eth/handler_test.go b/eth/handler_test.go
index c879029ddd3e189c907972ec6aa4edffde20b0ea..3a09759870e0041a61e96817cf843441c44967e2 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -468,7 +468,7 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
 		}
 	}
 	// Create a checkpoint aware protocol manager
-	blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create new blockchain: %v", err)
 	}
@@ -555,7 +555,7 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
 		gspec   = &core.Genesis{Config: config}
 		genesis = gspec.MustCommit(db)
 	)
-	blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, config, pow, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create new blockchain: %v", err)
 	}
@@ -1324,7 +1324,7 @@ func TestBroadcastMalformedBlock(t *testing.T) {
 		gspec   = &core.Genesis{Config: config}
 		genesis = gspec.MustCommit(db)
 	)
-	blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatalf("failed to create new blockchain: %v", err)
 	}
diff --git a/eth/helper_test.go b/eth/helper_test.go
index aaefea030b4b1dad78184369d3f32f763316df9c..791c3f49bff0f120ef6cf2b6cda90d60d4214039 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -70,7 +70,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
 	db := ethdb.NewMemDatabase()
 	// Regenerate genesis block in the fresh database
 	gspec.MustCommit(db)
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
index e212d30260b648383553e7f1a4622d4ba16c9396..399688ad08eecfce835946b7c6a36ed71fe85914 100644
--- a/eth/protocol_test.go
+++ b/eth/protocol_test.go
@@ -177,8 +177,8 @@ func TestForkIDSplit(t *testing.T) {
 		genesisNoFork  = gspecNoFork.MustCommit(dbNoFork)
 		genesisProFork = gspecProFork.MustCommit(dbProFork)
 
-		chainNoFork, _  = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil)
-		chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil)
+		chainNoFork, _  = core.NewBlockChain(dbNoFork, nil, configNoFork, engine, vm.Config{}, nil, nil, nil)
+		chainProFork, _ = core.NewBlockChain(dbProFork, nil, configProFork, engine, vm.Config{}, nil, nil, nil)
 
 		ctxNoFork  = chainNoFork.WithContext(context.Background(), big.NewInt(genesisNoFork.Number().Int64()+1))
 		ctxProFork = chainProFork.WithContext(context.Background(), big.NewInt(genesisProFork.Number().Int64()+1))
diff --git a/eth/stagedsync/stage_execute.go b/eth/stagedsync/stage_execute.go
index 0a1ce10a3712bfee7cfa7cfddd4f8411306adb27..c7798ce0940033c4a87e57ee5b6b343d6e4caf19 100644
--- a/eth/stagedsync/stage_execute.go
+++ b/eth/stagedsync/stage_execute.go
@@ -16,6 +16,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core/rawdb"
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/log"
@@ -80,7 +81,7 @@ const StateBatchSize = 50 * 1024 * 1024 // 50 Mb
 const ChangeBatchSize = 1024 * 2014     // 1 Mb
 const prof = false
 
-func spawnExecuteBlocksStage(s *StageState, stateDB ethdb.Database, blockchain BlockChain, quit chan struct{}) error {
+func spawnExecuteBlocksStage(s *StageState, stateDB ethdb.Database, blockchain BlockChain, quit chan struct{}, dests vm.Cache) error {
 	lastProcessedBlockNumber := s.BlockNumber
 
 	nextBlockNumber := uint64(0)
@@ -159,7 +160,7 @@ func spawnExecuteBlocksStage(s *StageState, stateDB ethdb.Database, blockchain B
 		stateWriter.SetCodeSizeCache(codeSizeCache)
 
 		// where the magic happens
-		err := core.ExecuteBlockEuphemerally(chainConfig, vmConfig, blockchain, engine, block, stateReader, stateWriter)
+		err := core.ExecuteBlockEuphemerally(chainConfig, vmConfig, blockchain, engine, block, stateReader, stateWriter, dests)
 		if err != nil {
 			return err
 		}
diff --git a/eth/stagedsync/stagedsync.go b/eth/stagedsync/stagedsync.go
index cda3600a2ea92be542c5e483a1eeb4e66b28a801..8feab00ff9e21e0f2a40fd852652c51c1b4a7357 100644
--- a/eth/stagedsync/stagedsync.go
+++ b/eth/stagedsync/stagedsync.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/ledgerwatch/turbo-geth/core"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/eth/stagedsync/stages"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/log"
@@ -18,6 +19,7 @@ func DoStagedSyncWithFetchers(
 	datadir string,
 	quitCh chan struct{},
 	headersFetchers []func() error,
+	dests vm.Cache,
 ) error {
 	defer log.Info("Staged sync finished")
 
@@ -47,7 +49,7 @@ func DoStagedSyncWithFetchers(
 			ID:          stages.Execution,
 			Description: "Executing blocks w/o hash checks",
 			ExecFunc: func(s *StageState) error {
-				return spawnExecuteBlocksStage(s, stateDB, blockchain, quitCh)
+				return spawnExecuteBlocksStage(s, stateDB, blockchain, quitCh, dests)
 			},
 		},
 		{
diff --git a/eth/sync.go b/eth/sync.go
index 7a11828a6b2b73a4a4c90d69fdf36f1a8bd0fa80..13306fab531f4de33def80a115724520e4ccdd64 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -308,7 +308,7 @@ func (pm *ProtocolManager) doSync(op *chainSyncOp) error {
 		}
 	*/
 	// Run the sync cycle, and disable fast sync if we're past the pivot block
-	err := pm.downloader.Synchronise(op.peer.id, op.head, op.td, op.mode)
+	err := pm.downloader.Synchronise(op.peer.id, op.head, op.td, op.mode, pm.blockchain.DestsCache)
 	if err != nil {
 		return err
 	}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index b926aecddb3723d1ff69b8bcb6d91194bd554cf5..fce04bd9f64c431220d1b3a4c4c5308446914ed6 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -53,9 +53,9 @@ type dummyStatedb struct {
 func (*dummyStatedb) GetRefund() uint64 { return 1337 }
 
 func runTrace(tracer *Tracer) (json.RawMessage, error) {
-	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
+	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}, nil)
 
-	contract := vm.NewContract(account{}, account{}, uint256.NewInt(), 10000)
+	contract := vm.NewContract(account{}, account{}, uint256.NewInt(), 10000, vm.NewDestsCache(50000))
 	contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
 
 	_, err := env.Interpreter().Run(contract, []byte{}, false)
@@ -168,8 +168,8 @@ func TestHaltBetweenSteps(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
-	contract := vm.NewContract(&account{}, &account{}, uint256.NewInt(), 0)
+	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer}, nil)
+	contract := vm.NewContract(&account{}, &account{}, uint256.NewInt(), 0, vm.NewDestsCache(50000))
 
 	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)
 	timeout := errors.New("stahp")
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 2e127a256d9bbb620cc86deaa556c2e113375ed6..d3c0f1c49a6552aaeb061163006651df3f23cc97 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -182,7 +182,7 @@ func TestPrestateTracerCreate2(t *testing.T) {
 	if err != nil {
 		t.Fatalf("failed to create call tracer: %v", err)
 	}
-	evm := vm.NewEVM(evmContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+	evm := vm.NewEVM(evmContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer}, nil)
 
 	msg, err := tx.AsMessage(signer)
 	if err != nil {
@@ -262,7 +262,7 @@ func TestCallTracer(t *testing.T) {
 			if err != nil {
 				t.Fatalf("failed to create call tracer: %v", err)
 			}
-			evm := vm.NewEVM(evmContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+			evm := vm.NewEVM(evmContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer}, nil)
 
 			msg, err := tx.AsMessage(signer)
 			if err != nil {
diff --git a/miner/miner.go b/miner/miner.go
index 06339c750e670f18d8925041c985e52ca631a190..abbaff8047b66dbe2d5842135057d2fb9f64cdad 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -30,6 +30,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core"
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/eth/downloader"
 	"github.com/ledgerwatch/turbo-geth/event"
 	"github.com/ledgerwatch/turbo-geth/log"
@@ -68,7 +69,7 @@ type Miner struct {
 	shouldStart int32 // should start indicates whether we should start after sync
 }
 
-func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner {
+func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool, dests vm.Cache) *Miner {
 	miner := &Miner{
 		eth:      eth,
 		mux:      mux,
@@ -77,7 +78,7 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
 		worker:   newWorker(config, chainConfig, engine, eth, mux, hooks{isLocalBlock: isLocalBlock}, false),
 		canStart: 1,
 	}
-	go miner.update()
+	go miner.update(dests)
 
 	return miner
 }
@@ -86,7 +87,7 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
 // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
 // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
 // and halt your mining operation for as long as the DOS continues.
-func (miner *Miner) update() {
+func (miner *Miner) update(dests vm.Cache) {
 	events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
 	defer events.Unsubscribe()
 
@@ -110,7 +111,7 @@ func (miner *Miner) update() {
 				atomic.StoreInt32(&miner.canStart, 1)
 				atomic.StoreInt32(&miner.shouldStart, 0)
 				if shouldStart {
-					miner.Start(miner.coinbase)
+					miner.Start(miner.coinbase, dests)
 				}
 				// stop immediately and ignore all further pending events
 				return
@@ -121,7 +122,7 @@ func (miner *Miner) update() {
 	}
 }
 
-func (miner *Miner) Start(coinbase common.Address) {
+func (miner *Miner) Start(coinbase common.Address, dests vm.Cache) {
 	atomic.StoreInt32(&miner.shouldStart, 1)
 	miner.SetEtherbase(coinbase)
 
@@ -129,7 +130,7 @@ func (miner *Miner) Start(coinbase common.Address) {
 		log.Info("Network syncing, will start miner afterwards")
 		return
 	}
-	miner.worker.start()
+	miner.worker.start(dests)
 }
 
 func (miner *Miner) Stop() {
diff --git a/miner/worker.go b/miner/worker.go
index 54c35251d79fd1eb4d356b1583a1d3352f34d586..52b7b14fe52f453898befe0a533754b4aa4989a1 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -36,6 +36,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core"
 	"github.com/ledgerwatch/turbo-geth/core/state"
 	"github.com/ledgerwatch/turbo-geth/core/types"
+	"github.com/ledgerwatch/turbo-geth/core/vm"
 	"github.com/ledgerwatch/turbo-geth/event"
 	"github.com/ledgerwatch/turbo-geth/log"
 	"github.com/ledgerwatch/turbo-geth/params"
@@ -260,7 +261,7 @@ func (w *worker) pendingBlock() *types.Block {
 	return w.snapshotBlock
 }
 
-func (w *worker) init() {
+func (w *worker) init(dests vm.Cache) {
 	w.initOnce.Do(func() {
 		time.Sleep(5 * time.Second)
 		w.txsCh = make(chan core.NewTxsEvent, txChanSize)
@@ -283,7 +284,7 @@ func (w *worker) init() {
 		// commit aborts in-flight transaction execution with given signal and resubmits a new one.
 		commit, timestamp := w.getCommit()
 
-		go w.mainLoop()
+		go w.mainLoop(dests)
 		go w.newWorkLoop(recommit)
 		go w.chainEvents(timestamp, commit)
 		go w.taskLoop()
@@ -291,10 +292,10 @@ func (w *worker) init() {
 }
 
 // start sets the running status as 1 and triggers new work submitting.
-func (w *worker) start() {
+func (w *worker) start(dests vm.Cache) {
 	if atomic.CompareAndSwapInt32(&w.running, 0, 1) {
 		log.Warn("worker start")
-		w.init()
+		w.init(dests)
 		w.startCh <- struct{}{}
 	}
 }
@@ -404,14 +405,14 @@ func (w *worker) getCommit() (func(ctx consensus.Cancel, noempty bool, s int32),
 }
 
 // mainLoop is a standalone goroutine to regenerate the sealing task based on the received event.
-func (w *worker) mainLoop() {
+func (w *worker) mainLoop(dests vm.Cache) {
 	defer w.txsSub.Unsubscribe()
 
 	for {
 		select {
 		case req := <-w.newWorkCh:
 			log.Warn("mining: a new work")
-			w.commitNewWork(req.cancel, req.interrupt, req.noempty, req.timestamp)
+			w.commitNewWork(req.cancel, req.interrupt, req.noempty, req.timestamp, dests)
 
 		case ev := <-w.txsCh:
 			//fixme can be removed?
@@ -437,7 +438,7 @@ func (w *worker) mainLoop() {
 				}
 				txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs)
 				tcount := w.current.tcount
-				w.commitTransactions(txset, coinbase, nil)
+				w.commitTransactions(txset, coinbase, nil, dests)
 				// Only update the snapshot if any new transactons were added
 				// to the pending block
 				if tcount != w.current.tcount {
@@ -448,7 +449,7 @@ func (w *worker) mainLoop() {
 				// submit mining work here since all empty submission will be rejected
 				// by clique. Of course the advance sealing(empty submission) is disabled.
 				if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {
-					w.commitNewWork(consensus.StabCancel(), nil, true, time.Now().Unix())
+					w.commitNewWork(consensus.StabCancel(), nil, true, time.Now().Unix(), dests)
 				}
 			}
 			atomic.AddInt32(&w.newTxs, int32(len(ev.Txs)))
@@ -758,11 +759,11 @@ func (w *worker) updateSnapshot() {
 	w.snapshotTds = w.current.tds.WithNewBuffer()
 }
 
-func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address) ([]*types.Log, error) {
+func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Address, dests vm.Cache) ([]*types.Log, error) {
 	snap := w.current.state.Snapshot()
 
 	header := w.current.GetHeader()
-	receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.tds.TrieStateWriter(), header, tx, &header.GasUsed, *w.chain.GetVMConfig())
+	receipt, err := core.ApplyTransaction(w.chainConfig, w.chain, &coinbase, w.current.gasPool, w.current.state, w.current.tds.TrieStateWriter(), header, tx, &header.GasUsed, *w.chain.GetVMConfig(), dests)
 	if err != nil {
 		w.current.state.RevertToSnapshot(snap)
 		return nil, err
@@ -777,7 +778,7 @@ func (w *worker) commitTransaction(tx *types.Transaction, coinbase common.Addres
 	return receipt.Logs, nil
 }
 
-func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32) bool {
+func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, interrupt *int32, dests vm.Cache) bool {
 	// Short circuit if current is nil
 	if w.current == nil {
 		return true
@@ -838,7 +839,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
 		// Start executing the transaction
 		w.current.state.Prepare(tx.Hash(), common.Hash{}, w.current.tcount)
 
-		logs, err := w.commitTransaction(tx, coinbase)
+		logs, err := w.commitTransaction(tx, coinbase, dests)
 		switch err {
 		case core.ErrGasLimitReached:
 			// Pop the current out-of-gas transaction without shifting in the next from the account
@@ -893,7 +894,7 @@ func (w *worker) commitTransactions(txs *types.TransactionsByPriceAndNonce, coin
 }
 
 // commitNewWork generates several new sealing tasks based on the parent block.
-func (w *worker) commitNewWork(ctx consensus.Cancel, interrupt *int32, noempty bool, timestamp int64) {
+func (w *worker) commitNewWork(ctx consensus.Cancel, interrupt *int32, noempty bool, timestamp int64, dests vm.Cache) {
 	select {
 	case <-ctx.Done():
 		return
@@ -1034,13 +1035,13 @@ func (w *worker) commitNewWork(ctx consensus.Cancel, interrupt *int32, noempty b
 	}
 	if len(localTxs) > 0 {
 		txs := types.NewTransactionsByPriceAndNonce(w.current.signer, localTxs)
-		if w.commitTransactions(txs, w.coinbase, interrupt) {
+		if w.commitTransactions(txs, w.coinbase, interrupt, dests) {
 			return
 		}
 	}
 	if len(remoteTxs) > 0 {
 		txs := types.NewTransactionsByPriceAndNonce(w.current.signer, remoteTxs)
-		if w.commitTransactions(txs, w.coinbase, interrupt) {
+		if w.commitTransactions(txs, w.coinbase, interrupt, dests) {
 			return
 		}
 	}
diff --git a/miner/worker_test.go b/miner/worker_test.go
index d483f7ce56df99a138c59c6ea182f6d03b4d7456..bc687c3ed4622f3176e660a8b5af530c9dbdc8f9 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -157,7 +157,7 @@ func newTestWorkerBackend(t *testing.T, testCase *testCase, chainConfig *params.
 	genesis := gspec.MustCommit(db)
 
 	dbCopy := db.MemCopy()
-	chain, _ := core.NewBlockChain(dbCopy, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	chain, _ := core.NewBlockChain(dbCopy, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	txpool := core.NewTxPool(testCase.testTxPoolConfig, chainConfig, chain)
 
 	// Generate a small n-block chain and an uncle block for it
@@ -214,7 +214,7 @@ func newTestWorker(t *testCase, chainConfig *params.ChainConfig, engine consensu
 	w := newWorker(t.testConfig, chainConfig, engine, backend, new(event.TypeMux), h, true)
 	w.setEtherbase(t.testBankAddress)
 	if waitInit {
-		w.init()
+		w.init(vm.NewDestsCache(100))
 
 		// Ensure worker has finished initialization
 		timer := time.NewTicker(10 * time.Millisecond)
@@ -283,7 +283,7 @@ func testGenerateBlockAndImport(t *testing.T, testCase *testCase, isClique bool)
 	db2 := ethdb.NewMemDatabase()
 	defer db2.Close()
 	b.genesis.MustCommit(db2)
-	chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil)
+	chain, _ := core.NewBlockChain(db2, nil, b.chain.Config(), engine, vm.Config{}, nil, nil, nil)
 	defer chain.Stop()
 
 	// Ignore empty commit here for less noise
@@ -296,7 +296,7 @@ func testGenerateBlockAndImport(t *testing.T, testCase *testCase, isClique bool)
 	defer sub.Unsubscribe()
 
 	// Start mining!
-	w.start()
+	w.start(vm.NewDestsCache(100))
 
 	for i := 0; i < 5; i++ {
 		if err := b.txPool.AddLocal(b.newRandomTx(testCase, true)); err != nil {
@@ -438,7 +438,7 @@ func testEmptyWork(t *testing.T, testCase *testCase, chainConfig *params.ChainCo
 	w := newTestWorker(testCase, chainConfig, engine, backend, h, true)
 	defer w.close()
 
-	w.start()
+	w.start(vm.NewDestsCache(100))
 	for i := 0; i < 3; i++ {
 		select {
 		case <-taskCh:
@@ -507,7 +507,7 @@ func TestStreamUncleBlock(t *testing.T) {
 	w := newTestWorker(testCase, testCase.ethashChainConfig, ethash, b, h, true)
 	defer w.close()
 
-	w.start()
+	w.start(vm.NewDestsCache(100))
 
 	// Ignore the first two works
 	for i := 0; i < 2; i += 1 {
@@ -577,7 +577,7 @@ func testRegenerateMiningBlock(t *testing.T, testCase *testCase, chainConfig *pa
 	w := newTestWorker(testCase, chainConfig, engine, b, h, true)
 	defer w.close()
 
-	w.start()
+	w.start(vm.NewDestsCache(100))
 	// Ignore the first two works
 	for i := 0; i < 2; i += 1 {
 		select {
@@ -670,7 +670,7 @@ func testAdjustInterval(t *testing.T, testCase *testCase, chainConfig *params.Ch
 	w := newTestWorker(testCase, chainConfig, engine, backend, h, true)
 	defer w.close()
 
-	w.start()
+	w.start(vm.NewDestsCache(100))
 
 	time.Sleep(time.Second) // Ensure two tasks have been summitted due to start opt
 	atomic.StoreUint32(&start, 1)
diff --git a/tests/block_test_util.go b/tests/block_test_util.go
index 9e993e36a3d55768cf2379ce7e3cbf6c9c790ea7..947c32aef1cab9519a32ea8ee4df58b85a371047 100644
--- a/tests/block_test_util.go
+++ b/tests/block_test_util.go
@@ -120,7 +120,7 @@ func (t *BlockTest) Run(_ bool) error {
 	} else {
 		engine = ethash.NewShared()
 	}
-	chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0, Pruning: false}, config, engine, vm.Config{}, nil, nil)
+	chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanLimit: 0, Pruning: false}, config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return err
 	}
diff --git a/tests/pruner_test.go b/tests/pruner_test.go
index d2c6768ee1f8e7e6d511b98b7b0e6922e5d9f887..f63619c336c102f28c8ff920e5be8d0009aea349 100644
--- a/tests/pruner_test.go
+++ b/tests/pruner_test.go
@@ -69,7 +69,7 @@ func TestBasisAccountPruning(t *testing.T) {
 
 	numBlocks := 10
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -232,7 +232,7 @@ func TestBasisAccountPruningNoHistory(t *testing.T) {
 		NoHistory:           true,
 		ArchiveSyncInterval: 1,
 	}
-	blockchain, err := core.NewBlockChain(db, &cacheConfig, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, &cacheConfig, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -382,7 +382,7 @@ func TestStoragePruning(t *testing.T) {
 	defer genesisDb.Close()
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -584,7 +584,7 @@ func TestBasisAccountPruningStrategy(t *testing.T) {
 
 	numBlocks := 25
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 7b01ad3f9e381d5ca9c06392b32f3000b62bbe3b..f37c2ce6ae318c7102cffd47dc3a91490fede5a4 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -194,7 +194,7 @@ func (t *StateTest) RunNoVerify(ctx context.Context, subtest StateSubtest, vmcon
 	}
 	context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
 	context.GetHash = vmTestBlockHash
-	evm := vm.NewEVM(context, statedb, config, vmconfig)
+	evm := vm.NewEVM(context, statedb, config, vmconfig, nil)
 
 	gaspool := new(core.GasPool)
 	gaspool.AddGas(block.GasLimit())
diff --git a/tests/statedb_chain_test.go b/tests/statedb_chain_test.go
index b4eda4b35c96b831a9218c218fbfd0b1d07f9148..7104542df25eb4068669df259230312b88f8b820 100644
--- a/tests/statedb_chain_test.go
+++ b/tests/statedb_chain_test.go
@@ -65,7 +65,7 @@ func TestSelfDestructReceive(t *testing.T) {
 	defer genesisDB.Close()
 
 	engine := ethash.NewFaker()
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -120,7 +120,7 @@ func TestSelfDestructReceive(t *testing.T) {
 	}
 
 	// Reload blockchain from the database, then inserting an empty block (3) will cause rebuilding of the trie
-	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/tests/statedb_insert_chain_transaction_test.go b/tests/statedb_insert_chain_transaction_test.go
index 4ffb81b1ea2699768d857e4c02704519b0899842..29dbda2e69c0a110c667b242cfead127a675ffe7 100644
--- a/tests/statedb_insert_chain_transaction_test.go
+++ b/tests/statedb_insert_chain_transaction_test.go
@@ -697,7 +697,7 @@ func genBlocks(gspec *core.Genesis, txs map[int]tx) (*core.BlockChain, ethdb.KV,
 	genesis := gspec.MustCommit(db)
 	genesisDb := db.MemCopy()
 
-	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil)
+	blockchain, err := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil, nil, nil)
 	if err != nil {
 		return nil, nil, nil, nil, nil, err
 	}
diff --git a/tests/vm_test_util.go b/tests/vm_test_util.go
index e19d68373374a9b9cf9bfbe7533c152be740322b..44b637ee84437fce2ec9acfa9651a5f9d893ad0e 100644
--- a/tests/vm_test_util.go
+++ b/tests/vm_test_util.go
@@ -162,7 +162,7 @@ func (t *VMTest) newEVM(state vm.IntraBlockState, vmconfig vm.Config) *vm.EVM {
 		GasPrice:    t.json.Exec.GasPrice,
 	}
 	vmconfig.NoRecursion = true
-	return vm.NewEVM(context, state, params.MainnetChainConfig, vmconfig)
+	return vm.NewEVM(context, state, params.MainnetChainConfig, vmconfig, nil)
 }
 
 func vmTestBlockHash(n uint64) common.Hash {