diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go
index 2e57d725891b5d17380a3ff3de956bc0f8c04819..d110af2c30aeccb30c09e2efe37d0277ea266aaa 100644
--- a/cmd/evm/internal/t8ntool/flags.go
+++ b/cmd/evm/internal/t8ntool/flags.go
@@ -38,6 +38,10 @@ var (
 		Name:  "trace.nostack",
 		Usage: "Disable stack output in traces",
 	}
+	TraceDisableReturnDataFlag = cli.BoolFlag{
+		Name:  "trace.noreturndata",
+		Usage: "Disable return data output in traces",
+	}
 	OutputAllocFlag = cli.StringFlag{
 		Name: "output.alloc",
 		Usage: "Determines where to put the `alloc` of the post-state.\n" +
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index a4908d763ea0efdf90ad244c5c6496311f49364a..079307b97502763194320239d278bf8e974a2b6a 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -83,9 +83,10 @@ func Main(ctx *cli.Context) error {
 	if ctx.Bool(TraceFlag.Name) {
 		// Configure the EVM logger
 		logConfig := &vm.LogConfig{
-			DisableStack:  ctx.Bool(TraceDisableStackFlag.Name),
-			DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
-			Debug:         true,
+			DisableStack:      ctx.Bool(TraceDisableStackFlag.Name),
+			DisableMemory:     ctx.Bool(TraceDisableMemoryFlag.Name),
+			DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
+			Debug:             true,
 		}
 		var prevFile *os.File
 		// This one closes the last file
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 2d5e9763dffa2376390e1fac9d8e1c10e5c1fe46..7b472350d98881e07975641164d2c07d5c2ab6c6 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -121,6 +121,14 @@ var (
 		Name:  "nostack",
 		Usage: "disable stack output",
 	}
+	DisableStorageFlag = cli.BoolFlag{
+		Name:  "nostorage",
+		Usage: "disable storage output",
+	}
+	DisableReturnDataFlag = cli.BoolFlag{
+		Name:  "noreturndata",
+		Usage: "disable return data output",
+	}
 	EVMInterpreterFlag = cli.StringFlag{
 		Name:  "vm.evm",
 		Usage: "External EVM configuration (default = built-in interpreter)",
@@ -137,6 +145,7 @@ var stateTransitionCommand = cli.Command{
 		t8ntool.TraceFlag,
 		t8ntool.TraceDisableMemoryFlag,
 		t8ntool.TraceDisableStackFlag,
+		t8ntool.TraceDisableReturnDataFlag,
 		t8ntool.OutputAllocFlag,
 		t8ntool.OutputResultFlag,
 		t8ntool.InputAllocFlag,
@@ -172,6 +181,8 @@ func init() {
 		ReceiverFlag,
 		DisableMemoryFlag,
 		DisableStackFlag,
+		DisableStorageFlag,
+		DisableReturnDataFlag,
 		EVMInterpreterFlag,
 	}
 	app.Commands = []cli.Command{
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 639f0c0ac9cfd1a74e40a5031835d0a8160b5a37..d0be6ca1e1e0eff2e707af793b24848d3e6fa865 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -108,9 +108,11 @@ func runCmd(ctx *cli.Context) error {
 	glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
 	log.Root().SetHandler(glogger)
 	logconfig := &vm.LogConfig{
-		DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
-		DisableStack:  ctx.GlobalBool(DisableStackFlag.Name),
-		Debug:         ctx.GlobalBool(DebugFlag.Name),
+		DisableMemory:     ctx.GlobalBool(DisableMemoryFlag.Name),
+		DisableStack:      ctx.GlobalBool(DisableStackFlag.Name),
+		DisableStorage:    ctx.GlobalBool(DisableStorageFlag.Name),
+		DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
+		Debug:             ctx.GlobalBool(DebugFlag.Name),
 	}
 
 	var (
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index 6f9e47cf509c6b470f760aac2d6b1efae946f5fa..f9a6b06b8fe468a0a24530c1175bf85dd417cc1c 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -59,8 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
 
 	// Configure the EVM logger
 	config := &vm.LogConfig{
-		DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
-		DisableStack:  ctx.GlobalBool(DisableStackFlag.Name),
+		DisableMemory:     ctx.GlobalBool(DisableMemoryFlag.Name),
+		DisableStack:      ctx.GlobalBool(DisableStackFlag.Name),
+		DisableStorage:    ctx.GlobalBool(DisableStorageFlag.Name),
+		DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
 	}
 	var (
 		tracer   vm.Tracer
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 45cf1f7c42af3a88c32caf444fa36e34baa28e41..17dd474314aa20b7df1ba2d5a07ce038db62e09b 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -505,7 +505,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
 	}
 	// If no live objects are available, attempt to use snapshots
 	var (
-		data Account
+		data *Account
 		err  error
 	)
 	if s.snap != nil {
@@ -517,11 +517,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
 			if acc == nil {
 				return nil
 			}
-			data.Nonce, data.Balance, data.CodeHash = acc.Nonce, acc.Balance, acc.CodeHash
+			data = &Account{
+				Nonce:    acc.Nonce,
+				Balance:  acc.Balance,
+				CodeHash: acc.CodeHash,
+				Root:     common.BytesToHash(acc.Root),
+			}
 			if len(data.CodeHash) == 0 {
 				data.CodeHash = emptyCodeHash
 			}
-			data.Root = common.BytesToHash(acc.Root)
 			if data.Root == (common.Hash{}) {
 				data.Root = emptyRoot
 			}
@@ -540,13 +544,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
 		if len(enc) == 0 {
 			return nil
 		}
-		if err := rlp.DecodeBytes(enc, &data); err != nil {
+		data = new(Account)
+		if err := rlp.DecodeBytes(enc, data); err != nil {
 			log.Error("Failed to decode state object", "addr", addr, "err", err)
 			return nil
 		}
 	}
 	// Insert into the live set
-	obj := newObject(s, addr, data)
+	obj := newObject(s, addr, *data)
 	s.setStateObject(obj)
 	return obj
 }
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 6493d45895cfd8152dfa19160f3efd71b953e34d..8930a06266b8ccbcb8b5f3d54c9632b16dcf4fba 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
 }
 
 // RunPrecompiledContract runs and evaluates the output of a precompiled contract.
-func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
-	gas := p.RequiredGas(input)
-	if contract.UseGas(gas) {
-		return p.Run(input)
+// It returns
+// - the returned bytes,
+// - the _remaining_ gas,
+// - any error that occurred
+func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+	gasCost := p.RequiredGas(input)
+	if suppliedGas < gasCost {
+		return nil, 0, ErrOutOfGas
 	}
-	return nil, ErrOutOfGas
+	suppliedGas -= gasCost
+	output, err := p.Run(input)
+	return output, suppliedGas, err
 }
 
 // ECRECOVER implemented as a native contract.
@@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
 type bigModExp struct{}
 
 var (
+	big0      = big.NewInt(0)
 	big1      = big.NewInt(1)
 	big4      = big.NewInt(4)
 	big8      = big.NewInt(8)
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index 65a9f8b797a93aa19a916c7d5724b2a918f3ca11..5bc365949d875283a2e66bd58365740c91ae20dc 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -21,7 +21,6 @@ import (
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
-	"math/big"
 	"testing"
 	"time"
 
@@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
 func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
 	p := allPrecompiles[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.Input)
-	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(big.Int), p.RequiredGas(in))
-	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
-		if res, err := RunPrecompiledContract(p, in, contract); err != nil {
+	gas := p.RequiredGas(in)
+	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
+		if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
 			t.Error(err)
 		} else if common.Bytes2Hex(res) != test.Expected {
 			t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
 func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
 	p := allPrecompiles[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.Input)
-	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(big.Int), p.RequiredGas(in)-1)
-	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
-		_, err := RunPrecompiledContract(p, in, contract)
+	gas := p.RequiredGas(in) - 1
+
+	t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
+		_, _, err := RunPrecompiledContract(p, in, gas)
 		if err.Error() != "out of gas" {
 			t.Errorf("Expected error [out of gas], got [%v]", err)
 		}
@@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
 func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
 	p := allPrecompiles[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.Input)
-	contract := NewContract(AccountRef(common.HexToAddress("31337")),
-		nil, new(big.Int), p.RequiredGas(in))
-
+	gas := p.RequiredGas(in)
 	t.Run(test.Name, func(t *testing.T) {
-		_, err := RunPrecompiledContract(p, in, contract)
+		_, _, err := RunPrecompiledContract(p, in, gas)
 		if err.Error() != test.ExpectedError {
 			t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
 		}
@@ -132,8 +128,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
 	p := allPrecompiles[common.HexToAddress(addr)]
 	in := common.Hex2Bytes(test.Input)
 	reqGas := p.RequiredGas(in)
-	contract := NewContract(AccountRef(common.HexToAddress("1337")),
-		nil, new(big.Int), reqGas)
 
 	var (
 		res  []byte
@@ -141,14 +135,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
 		data = make([]byte, len(in))
 	)
 
-	bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) {
+	bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) {
 		bench.ReportAllocs()
 		start := time.Now().Nanosecond()
 		bench.ResetTimer()
 		for i := 0; i < bench.N; i++ {
-			contract.Gas = reqGas
 			copy(data, in)
-			res, err = RunPrecompiledContract(p, data, contract)
+			res, _, err = RunPrecompiledContract(p, data, reqGas)
 		}
 		bench.StopTimer()
 		elapsed := float64(time.Now().Nanosecond() - start)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 880198bd78f51556f299e15213935fdf8ee2a1f2..f5469c500c8af19d42ac89e534cb8554a6cfbb36 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -25,6 +25,7 @@ import (
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/params"
+	"github.com/holiman/uint256"
 )
 
 // emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -41,23 +42,24 @@ type (
 	GetHashFunc func(uint64) common.Hash
 )
 
+func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
+	var precompiles map[common.Address]PrecompiledContract
+	switch {
+	case evm.chainRules.IsYoloV1:
+		precompiles = PrecompiledContractsYoloV1
+	case evm.chainRules.IsIstanbul:
+		precompiles = PrecompiledContractsIstanbul
+	case evm.chainRules.IsByzantium:
+		precompiles = PrecompiledContractsByzantium
+	default:
+		precompiles = PrecompiledContractsHomestead
+	}
+	p, ok := precompiles[addr]
+	return p, ok
+}
+
 // run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
 func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
-	if contract.CodeAddr != nil {
-		precompiles := PrecompiledContractsHomestead
-		if evm.chainRules.IsByzantium {
-			precompiles = PrecompiledContractsByzantium
-		}
-		if evm.chainRules.IsIstanbul {
-			precompiles = PrecompiledContractsIstanbul
-		}
-		if evm.chainRules.IsYoloV1 {
-			precompiles = PrecompiledContractsYoloV1
-		}
-		if p := precompiles[*contract.CodeAddr]; p != nil {
-			return RunPrecompiledContract(p, input, contract)
-		}
-	}
 	for _, interpreter := range evm.interpreters {
 		if interpreter.CanRun(contract.Code) {
 			if evm.interpreter != interpreter {
@@ -199,22 +201,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 		return nil, gas, ErrDepth
 	}
 	// Fail if we're trying to transfer more than the available balance
-	if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+	if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
 		return nil, gas, ErrInsufficientBalance
 	}
-	var (
-		to       = AccountRef(addr)
-		snapshot = evm.StateDB.Snapshot()
-	)
+	snapshot := evm.StateDB.Snapshot()
+	p, isPrecompile := evm.precompile(addr)
+
 	if !evm.StateDB.Exist(addr) {
-		precompiles := PrecompiledContractsHomestead
-		if evm.chainRules.IsByzantium {
-			precompiles = PrecompiledContractsByzantium
-		}
-		if evm.chainRules.IsIstanbul {
-			precompiles = PrecompiledContractsIstanbul
-		}
-		if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+		if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
 			// Calling a non existing account, don't do anything, but ping the tracer
 			if evm.vmConfig.Debug && evm.depth == 0 {
 				evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
@@ -224,35 +218,47 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 		}
 		evm.StateDB.CreateAccount(addr)
 	}
-	evm.Transfer(evm.StateDB, 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.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
-	// Even if the account has no code, we need to continue because it might be a precompile
-	start := time.Now()
+	evm.Transfer(evm.StateDB, caller.Address(), addr, value)
 
 	// Capture the tracer start/end events in debug mode
 	if evm.vmConfig.Debug && evm.depth == 0 {
 		evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
-
-		defer func() { // Lazy evaluation of the parameters
-			evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
-		}()
+		defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
+			evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
+		}(gas, time.Now())
 	}
-	ret, err = run(evm, contract, input, false)
 
+	if isPrecompile {
+		ret, gas, err = RunPrecompiledContract(p, input, gas)
+	} else {
+		// 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.
+		code := evm.StateDB.GetCode(addr)
+		if len(code) == 0 {
+			ret, err = nil, nil // gas is unchanged
+		} else {
+			addrCopy := addr
+			// If the account has no code, we can abort here
+			// The depth-check is already done, and precompiles handled above
+			contract := NewContract(caller, AccountRef(addrCopy), value, gas)
+			contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
+			ret, err = run(evm, contract, input, false)
+			gas = contract.Gas
+		}
+	}
 	// When an error was returned by the EVM or when setting the creation code
 	// above we revert to the snapshot and consume any gas remaining. Additionally
 	// when we're in homestead this also counts for code storage gas errors.
 	if err != nil {
 		evm.StateDB.RevertToSnapshot(snapshot)
 		if err != ErrExecutionReverted {
-			contract.UseGas(contract.Gas)
+			gas = 0
 		}
+		// TODO: consider clearing up unused snapshots:
+		//} else {
+		//	evm.StateDB.DiscardSnapshot(snapshot)
 	}
-	return ret, contract.Gas, err
+	return ret, gas, err
 }
 
 // CallCode executes the contract associated with the addr with the given input
@@ -277,23 +283,27 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
 	if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
 		return nil, gas, ErrInsufficientBalance
 	}
-	var (
-		snapshot = evm.StateDB.Snapshot()
-		to       = AccountRef(caller.Address())
-	)
-	// 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.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
-	ret, err = run(evm, contract, input, false)
+	var snapshot = evm.StateDB.Snapshot()
+
+	// It is allowed to call precompiles, even via delegatecall
+	if p, isPrecompile := evm.precompile(addr); isPrecompile {
+		ret, gas, err = RunPrecompiledContract(p, input, gas)
+	} else {
+		addrCopy := addr
+		// 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(caller.Address()), value, gas)
+		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+		ret, err = run(evm, contract, input, false)
+		gas = contract.Gas
+	}
 	if err != nil {
 		evm.StateDB.RevertToSnapshot(snapshot)
 		if err != ErrExecutionReverted {
-			contract.UseGas(contract.Gas)
+			gas = 0
 		}
 	}
-	return ret, contract.Gas, err
+	return ret, gas, err
 }
 
 // DelegateCall executes the contract associated with the addr with the given input
@@ -309,22 +319,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 	if evm.depth > int(params.CallCreateDepth) {
 		return nil, gas, ErrDepth
 	}
-	var (
-		snapshot = evm.StateDB.Snapshot()
-		to       = AccountRef(caller.Address())
-	)
-	// Initialise a new contract and make initialise the delegate values
-	contract := NewContract(caller, to, nil, gas).AsDelegate()
-	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
-
-	ret, err = run(evm, contract, input, false)
+	var snapshot = evm.StateDB.Snapshot()
+
+	// It is allowed to call precompiles, even via delegatecall
+	if p, isPrecompile := evm.precompile(addr); isPrecompile {
+		ret, gas, err = RunPrecompiledContract(p, input, gas)
+	} else {
+		addrCopy := addr
+		// Initialise a new contract and make initialise the delegate values
+		contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
+		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+		ret, err = run(evm, contract, input, false)
+		gas = contract.Gas
+	}
 	if err != nil {
 		evm.StateDB.RevertToSnapshot(snapshot)
 		if err != ErrExecutionReverted {
-			contract.UseGas(contract.Gas)
+			gas = 0
 		}
 	}
-	return ret, contract.Gas, err
+	return ret, gas, err
 }
 
 // StaticCall executes the contract associated with the addr with the given input
@@ -339,32 +353,43 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
 	if evm.depth > int(params.CallCreateDepth) {
 		return nil, gas, ErrDepth
 	}
-	var (
-		to       = AccountRef(addr)
-		snapshot = evm.StateDB.Snapshot()
-	)
-	// 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(big.Int), gas)
-	contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
+	// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
+	// However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
+	// after all empty accounts were deleted, so this is not required. However, if we omit this,
+	// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
+	// We could change this, but for now it's left for legacy reasons
+	var snapshot = evm.StateDB.Snapshot()
 
 	// 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,
 	// but is the correct thing to do and matters on other networks, in tests, and potential
 	// future scenarios
-	evm.StateDB.AddBalance(addr, big.NewInt(0))
-
-	// When an error was returned by the EVM or when setting the creation code
-	// above we revert to the snapshot and consume any gas remaining. Additionally
-	// when we're in Homestead this also counts for code storage gas errors.
-	ret, err = run(evm, contract, input, true)
+	evm.StateDB.AddBalance(addr, big0)
+
+	if p, isPrecompile := evm.precompile(addr); isPrecompile {
+		ret, gas, err = RunPrecompiledContract(p, input, gas)
+	} else {
+		// At this point, we use a copy of address. If we don't, the go compiler will
+		// leak the 'contract' to the outer scope, and make allocation for 'contract'
+		// even if the actual execution ends on RunPrecompiled above.
+		addrCopy := addr
+		// 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(addrCopy), new(big.Int), gas)
+		contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+		// When an error was returned by the EVM or when setting the creation code
+		// above we revert to the snapshot and consume any gas remaining. Additionally
+		// when we're in Homestead this also counts for code storage gas errors.
+		ret, err = run(evm, contract, input, true)
+		gas = contract.Gas
+	}
 	if err != nil {
 		evm.StateDB.RevertToSnapshot(snapshot)
 		if err != ErrExecutionReverted {
-			contract.UseGas(contract.Gas)
+			gas = 0
 		}
 	}
-	return ret, contract.Gas, err
+	return ret, gas, err
 }
 
 type codeAndHash struct {
@@ -466,9 +491,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
 //
 // The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
 // instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
-func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
 	codeAndHash := &codeAndHash{code: code}
-	contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
+	contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes())
 	return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
 }
 
diff --git a/core/vm/gen_structlog.go b/core/vm/gen_structlog.go
index 7ef909954a00bb01b57747f1cf8324c5d650b179..ac1a9070c8972b307bfda6314a5ad2d05679bb7c 100644
--- a/core/vm/gen_structlog.go
+++ b/core/vm/gen_structlog.go
@@ -24,6 +24,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
 		MemorySize    int                         `json:"memSize"`
 		Stack         []*math.HexOrDecimal256     `json:"stack"`
 		ReturnStack   []math.HexOrDecimal64       `json:"returnStack"`
+		ReturnData    []byte                      `json:"returnData"`
 		Storage       map[common.Hash]common.Hash `json:"-"`
 		Depth         int                         `json:"depth"`
 		RefundCounter uint64                      `json:"refund"`
@@ -50,6 +51,7 @@ func (s StructLog) MarshalJSON() ([]byte, error) {
 			enc.ReturnStack[k] = math.HexOrDecimal64(v)
 		}
 	}
+	enc.ReturnData = s.ReturnData
 	enc.Storage = s.Storage
 	enc.Depth = s.Depth
 	enc.RefundCounter = s.RefundCounter
@@ -70,6 +72,7 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
 		MemorySize    *int                        `json:"memSize"`
 		Stack         []*math.HexOrDecimal256     `json:"stack"`
 		ReturnStack   []math.HexOrDecimal64       `json:"returnStack"`
+		ReturnData    []byte                      `json:"returnData"`
 		Storage       map[common.Hash]common.Hash `json:"-"`
 		Depth         *int                        `json:"depth"`
 		RefundCounter *uint64                     `json:"refund"`
@@ -104,11 +107,14 @@ func (s *StructLog) UnmarshalJSON(input []byte) error {
 		}
 	}
 	if dec.ReturnStack != nil {
-		s.ReturnStack = make([]uint64, len(dec.ReturnStack))
+		s.ReturnStack = make([]uint32, len(dec.ReturnStack))
 		for k, v := range dec.ReturnStack {
-			s.ReturnStack[k] = uint64(v)
+			s.ReturnStack[k] = uint32(v)
 		}
 	}
+	if dec.ReturnData != nil {
+		s.ReturnData = dec.ReturnData
+	}
 	if dec.Storage != nil {
 		s.Storage = dec.Storage
 	}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 38d0d09e0416598c7f675d45290e288883c61f41..adf44b7f481f96c46262055527bc4737579bc1d1 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -563,7 +563,7 @@ func opJumpSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
 	if !callContext.contract.validJumpSubdest(posU64) {
 		return nil, ErrInvalidJump
 	}
-	callContext.rstack.push(*pc)
+	callContext.rstack.push(uint32(*pc))
 	*pc = posU64 + 1
 	return nil, nil
 }
@@ -575,7 +575,7 @@ func opReturnSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
 	// Other than the check that the return stack is not empty, there is no
 	// need to validate the pc from 'returns', since we only ever push valid
 	//values onto it via jumpsub.
-	*pc = callContext.rstack.pop() + 1
+	*pc = uint64(callContext.rstack.pop()) + 1
 	return nil, nil
 }
 
@@ -608,7 +608,13 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
 	stackvalue := size
 
 	callContext.contract.UseGas(gas)
-	res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, value.ToBig())
+	//TODO: use uint256.Int instead of converting with toBig()
+	var bigVal = big0
+	if !value.IsZero() {
+		bigVal = value.ToBig()
+	}
+
+	res, addr, returnGas, suberr := interpreter.evm.Create(callContext.contract, input, gas, bigVal)
 	// Push item on the stack based on the returned error. If the ruleset is
 	// homestead we must check for CodeStoreOutOfGasError (homestead only
 	// rule) and treat as an error, if the ruleset is frontier we must
@@ -643,8 +649,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
 	callContext.contract.UseGas(gas)
 	// reuse size int for stackvalue
 	stackvalue := size
+	//TODO: use uint256.Int instead of converting with toBig()
+	bigEndowment := big0
+	if !endowment.IsZero() {
+		bigEndowment = endowment.ToBig()
+	}
 	res, addr, returnGas, suberr := interpreter.evm.Create2(callContext.contract, input, gas,
-		endowment.ToBig(), salt.ToBig())
+		bigEndowment, &salt)
 	// Push item on the stack based on the returned error.
 	if suberr != nil {
 		stackvalue.Clear()
@@ -672,10 +683,17 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
 	// Get the arguments from the memory.
 	args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
 
+	var bigVal = big0
+	//TODO: use uint256.Int instead of converting with toBig()
+	// By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+	// but it would make more sense to extend the usage of uint256.Int
 	if !value.IsZero() {
 		gas += params.CallStipend
+		bigVal = value.ToBig()
 	}
-	ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, value.ToBig())
+
+	ret, returnGas, err := interpreter.evm.Call(callContext.contract, toAddr, args, gas, bigVal)
+
 	if err != nil {
 		temp.Clear()
 	} else {
@@ -702,10 +720,14 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
 	// Get arguments from the memory.
 	args := callContext.memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
 
+	//TODO: use uint256.Int instead of converting with toBig()
+	var bigVal = big0
 	if !value.IsZero() {
 		gas += params.CallStipend
+		bigVal = value.ToBig()
 	}
-	ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, value.ToBig())
+
+	ret, returnGas, err := interpreter.evm.CallCode(callContext.contract, toAddr, args, gas, bigVal)
 	if err != nil {
 		temp.Clear()
 	} else {
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 9c7c2b410027b954028b1924b72f667b07a71cc2..89feab0e2fe502af00fce81feaa134ddf39a0e76 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -182,13 +182,20 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		logged  bool   // deferred Tracer should ignore already logged steps
 		res     []byte // result of the opcode execution function
 	)
+	// Don't move this deferrred function, it's placed before the capturestate-deferred method,
+	// so that it get's executed _after_: the capturestate needs the stacks before
+	// they are returned to the pools
+	defer func() {
+		returnStack(stack)
+		returnRStack(returns)
+	}()
 	contract.Input = input
 
 	if in.cfg.Debug {
 		defer func() {
 			if err != nil {
 				if !logged {
-					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
+					in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
 				} else {
 					in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
 				}
@@ -272,7 +279,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		}
 
 		if in.cfg.Debug {
-			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, contract, in.evm.depth, err)
+			in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, stack, returns, in.returnData, contract, in.evm.depth, err)
 			logged = true
 		}
 
@@ -281,7 +288,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		// if the operation clears the return data (e.g. it has returning data)
 		// set the last return to the result of the operation.
 		if operation.returns {
-			in.returnData = res
+			in.returnData = common.CopyBytes(res)
 		}
 
 		switch {
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 2c90399aca6510f4db8e7f2cf3bce80c742eeef2..e1d7c67ef177d9a62c2ac56661d94020c8bbd5f5 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -47,11 +47,12 @@ func (s Storage) Copy() Storage {
 
 // LogConfig are the configuration options for structured logger the EVM
 type LogConfig struct {
-	DisableMemory  bool // disable memory capture
-	DisableStack   bool // disable stack capture
-	DisableStorage bool // disable storage capture
-	Debug          bool // print output during capture end
-	Limit          int  // maximum length of output, but zero means unlimited
+	DisableMemory     bool // disable memory capture
+	DisableStack      bool // disable stack capture
+	DisableStorage    bool // disable storage capture
+	DisableReturnData bool // disable return data capture
+	Debug             bool // print output during capture end
+	Limit             int  // maximum length of output, but zero means unlimited
 }
 
 //go:generate gencodec -type StructLog -field-override structLogMarshaling -out gen_structlog.go
@@ -66,7 +67,8 @@ type StructLog struct {
 	Memory        []byte                      `json:"memory"`
 	MemorySize    int                         `json:"memSize"`
 	Stack         []*big.Int                  `json:"stack"`
-	ReturnStack   []uint64                    `json:"returnStack"`
+	ReturnStack   []uint32                    `json:"returnStack"`
+	ReturnData    []byte                      `json:"returnData"`
 	Storage       map[common.Hash]common.Hash `json:"-"`
 	Depth         int                         `json:"depth"`
 	RefundCounter uint64                      `json:"refund"`
@@ -104,7 +106,7 @@ func (s *StructLog) ErrorString() string {
 // if you need to retain them beyond the current call.
 type Tracer interface {
 	CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error
-	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
+	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error
 	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error
 	CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) error
 }
@@ -142,7 +144,7 @@ func (l *StructLogger) CaptureStart(from common.Address, to common.Address, crea
 // CaptureState logs a new structured log message and pushes it out to the environment
 //
 // CaptureState also tracks SLOAD/SSTORE ops to track storage change.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
 	// check if already accumulated the specified number of logs
 	if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
 		return errTraceLimitReached
@@ -161,9 +163,9 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 			stck[i] = new(big.Int).Set(item.ToBig())
 		}
 	}
-	var rstack []uint64
+	var rstack []uint32
 	if !l.cfg.DisableStack && rStack != nil {
-		rstck := make([]uint64, len(rStack.data))
+		rstck := make([]uint32, len(rStack.data))
 		copy(rstck, rStack.data)
 	}
 	// Copy a snapshot of the current storage to a new container
@@ -192,8 +194,13 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 		}
 		storage = l.storage[contract.Address()].Copy()
 	}
+	var rdata []byte
+	if !l.cfg.DisableReturnData {
+		rdata = make([]byte, len(rData))
+		copy(rdata, rData)
+	}
 	// create a new snapshot of the EVM.
-	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, storage, depth, env.StateDB.GetRefund(), err}
+	log := StructLog{pc, op, gas, cost, mem, memory.Len(), stck, rstack, rdata, storage, depth, env.StateDB.GetRefund(), err}
 	l.logs = append(l.logs, log)
 	return nil
 }
@@ -257,6 +264,10 @@ func WriteTrace(writer io.Writer, logs []StructLog) {
 				fmt.Fprintf(writer, "%x: %x\n", h, item)
 			}
 		}
+		if len(log.ReturnData) > 0 {
+			fmt.Fprintln(writer, "ReturnData:")
+			fmt.Fprint(writer, hex.Dump(log.ReturnData))
+		}
 		fmt.Fprintln(writer)
 	}
 }
@@ -308,7 +319,7 @@ func (t *mdLogger) CaptureStart(from common.Address, to common.Address, create b
 	return nil
 }
 
-func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (t *mdLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
 	fmt.Fprintf(t.out, "| %4d  | %10v  |  %3d |", pc, op, cost)
 
 	if !t.cfg.DisableStack { // format stack
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index e37c3ce2bdcb80b6a8eb90d71b51c00788c29f67..5f3f2c42f786d69130382b37884451d4229531f2 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -46,7 +46,7 @@ func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create
 }
 
 // CaptureState outputs state information on the logger.
-func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, rStack *ReturnStack, rData []byte, contract *Contract, depth int, err error) error {
 	log := StructLog{
 		Pc:            pc,
 		Op:            op,
@@ -70,6 +70,9 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
 		log.Stack = logstack
 		log.ReturnStack = rStack.data
 	}
+	if !l.cfg.DisableReturnData {
+		log.ReturnData = rData
+	}
 	return l.encoder.Encode(log)
 }
 
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 572edf98071acc14b00b46d5b2fffabbb88969f8..e287f0c7aaed0f05c1b7834654185ac0d2dfa8ca 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -61,7 +61,7 @@ func TestStoreCapture(t *testing.T) {
 	stack.push(uint256.NewInt().SetUint64(1))
 	stack.push(uint256.NewInt())
 	var index common.Hash
-	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, contract, 0, nil)
+	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, rstack, nil, contract, 0, nil)
 	if len(logger.storage[contract.Address()]) == 0 {
 		t.Fatalf("expected exactly 1 changed value on address %x, got %d", contract.Address(), len(logger.storage[contract.Address()]))
 	}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 9cb492786b55b5cd03ac24963a48d7e16f869391..7ebaa9a7e3865df62197ccc5561a23f720ba40ca 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -52,13 +52,20 @@ type Config struct {
 func setDefaults(cfg *Config) {
 	if cfg.ChainConfig == nil {
 		cfg.ChainConfig = &params.ChainConfig{
-			ChainID:        big.NewInt(1),
-			HomesteadBlock: new(big.Int),
-			DAOForkBlock:   new(big.Int),
-			DAOForkSupport: false,
-			EIP150Block:    new(big.Int),
-			EIP155Block:    new(big.Int),
-			EIP158Block:    new(big.Int),
+			ChainID:             big.NewInt(1),
+			HomesteadBlock:      new(big.Int),
+			DAOForkBlock:        new(big.Int),
+			DAOForkSupport:      false,
+			EIP150Block:         new(big.Int),
+			EIP150Hash:          common.Hash{},
+			EIP155Block:         new(big.Int),
+			EIP158Block:         new(big.Int),
+			ByzantiumBlock:      new(big.Int),
+			ConstantinopleBlock: new(big.Int),
+			PetersburgBlock:     new(big.Int),
+			IstanbulBlock:       new(big.Int),
+			MuirGlacierBlock:    new(big.Int),
+			YoloV1Block:         nil,
 		}
 	}
 
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 991813bf817e2f01e7cee1e5ad4f2c9ab69f951f..108ee80e41e65a64b614b63a629527eba0064e4f 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -321,34 +321,6 @@ func TestBlockhash(t *testing.T) {
 	}
 }
 
-// BenchmarkSimpleLoop test a pretty simple loop which loops
-// 1M (1 048 575) times.
-// Takes about 200 ms
-func BenchmarkSimpleLoop(b *testing.B) {
-	// 0xfffff = 1048575 loops
-	code := []byte{
-		byte(vm.PUSH3), 0x0f, 0xff, 0xff,
-		byte(vm.JUMPDEST), //  [ count ]
-		byte(vm.PUSH1), 1, // [count, 1]
-		byte(vm.SWAP1),    // [1, count]
-		byte(vm.SUB),      // [ count -1 ]
-		byte(vm.DUP1),     //  [ count -1 , count-1]
-		byte(vm.PUSH1), 4, // [count-1, count -1, label]
-		byte(vm.JUMPI), // [ 0 ]
-		byte(vm.STOP),
-	}
-	//tracer := vm.NewJSONLogger(nil, os.Stdout)
-	//Execute(code, nil, &Config{
-	//	EVMConfig: vm.Config{
-	//		Debug:  true,
-	//		Tracer: tracer,
-	//	}})
-
-	for i := 0; i < b.N; i++ {
-		Execute(code, nil, nil)
-	}
-}
-
 type stepCounter struct {
 	inner *vm.JSONLogger
 	steps int
@@ -358,7 +330,7 @@ func (s *stepCounter) CaptureStart(from common.Address, to common.Address, creat
 	return nil
 }
 
-func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
+func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rData []byte, contract *vm.Contract, depth int, err error) error {
 	s.steps++
 	// Enable this for more output
 	//s.inner.CaptureState(env, pc, op, gas, cost, memory, stack, rStack, contract, depth, err)
@@ -593,3 +565,160 @@ func DisabledTestEipExampleCases(t *testing.T) {
 			"allowed, and causes an error", code)
 	}
 }
+
+// benchmarkNonModifyingCode benchmarks code, but if the code modifies the
+// state, this should not be used, since it does not reset the state between runs.
+func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
+	cfg := new(Config)
+	setDefaults(cfg)
+	cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+	cfg.GasLimit = gas
+	var (
+		destination = common.BytesToAddress([]byte("contract"))
+		vmenv       = NewEnv(cfg)
+		sender      = vm.AccountRef(cfg.Origin)
+	)
+	cfg.State.CreateAccount(destination)
+	eoa := common.HexToAddress("E0")
+	{
+		cfg.State.CreateAccount(eoa)
+		cfg.State.SetNonce(eoa, 100)
+	}
+	reverting := common.HexToAddress("EE")
+	{
+		cfg.State.CreateAccount(reverting)
+		cfg.State.SetCode(reverting, []byte{
+			byte(vm.PUSH1), 0x00,
+			byte(vm.PUSH1), 0x00,
+			byte(vm.REVERT),
+		})
+	}
+
+	//cfg.State.CreateAccount(cfg.Origin)
+	// set the receiver's (the executing contract) code for execution.
+	cfg.State.SetCode(destination, code)
+	vmenv.Call(sender, destination, nil, gas, cfg.Value)
+
+	b.Run(name, func(b *testing.B) {
+		b.ReportAllocs()
+		for i := 0; i < b.N; i++ {
+			vmenv.Call(sender, destination, nil, gas, cfg.Value)
+		}
+	})
+}
+
+// BenchmarkSimpleLoop test a pretty simple loop which loops until OOG
+// 55 ms
+func BenchmarkSimpleLoop(b *testing.B) {
+
+	staticCallIdentity := []byte{
+		byte(vm.JUMPDEST), //  [ count ]
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),       // out offset
+		byte(vm.DUP1),       // out insize
+		byte(vm.DUP1),       // in offset
+		byte(vm.PUSH1), 0x4, // address of identity
+		byte(vm.GAS), // gas
+		byte(vm.STATICCALL),
+		byte(vm.POP),      // pop return value
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	callIdentity := []byte{
+		byte(vm.JUMPDEST), //  [ count ]
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),       // out offset
+		byte(vm.DUP1),       // out insize
+		byte(vm.DUP1),       // in offset
+		byte(vm.DUP1),       // value
+		byte(vm.PUSH1), 0x4, // address of identity
+		byte(vm.GAS), // gas
+		byte(vm.CALL),
+		byte(vm.POP),      // pop return value
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	callInexistant := []byte{
+		byte(vm.JUMPDEST), //  [ count ]
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),        // out offset
+		byte(vm.DUP1),        // out insize
+		byte(vm.DUP1),        // in offset
+		byte(vm.DUP1),        // value
+		byte(vm.PUSH1), 0xff, // address of existing contract
+		byte(vm.GAS), // gas
+		byte(vm.CALL),
+		byte(vm.POP),      // pop return value
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	callEOA := []byte{
+		byte(vm.JUMPDEST), //  [ count ]
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),        // out offset
+		byte(vm.DUP1),        // out insize
+		byte(vm.DUP1),        // in offset
+		byte(vm.DUP1),        // value
+		byte(vm.PUSH1), 0xE0, // address of EOA
+		byte(vm.GAS), // gas
+		byte(vm.CALL),
+		byte(vm.POP),      // pop return value
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	loopingCode := []byte{
+		byte(vm.JUMPDEST), //  [ count ]
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),       // out offset
+		byte(vm.DUP1),       // out insize
+		byte(vm.DUP1),       // in offset
+		byte(vm.PUSH1), 0x4, // address of identity
+		byte(vm.GAS), // gas
+
+		byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP), byte(vm.POP),
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	calllRevertingContractWithInput := []byte{
+		byte(vm.JUMPDEST), //
+		// push args for the call
+		byte(vm.PUSH1), 0, // out size
+		byte(vm.DUP1),        // out offset
+		byte(vm.PUSH1), 0x20, // in size
+		byte(vm.PUSH1), 0x00, // in offset
+		byte(vm.PUSH1), 0x00, // value
+		byte(vm.PUSH1), 0xEE, // address of reverting contract
+		byte(vm.GAS), // gas
+		byte(vm.CALL),
+		byte(vm.POP),      // pop return value
+		byte(vm.PUSH1), 0, // jumpdestination
+		byte(vm.JUMP),
+	}
+
+	//tracer := vm.NewJSONLogger(nil, os.Stdout)
+	//Execute(loopingCode, nil, &Config{
+	//	EVMConfig: vm.Config{
+	//		Debug:  true,
+	//		Tracer: tracer,
+	//	}})
+	// 100M gas
+	benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b)
+	benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", b)
+	benchmarkNonModifyingCode(100000000, loopingCode, "loop-100M", b)
+	benchmarkNonModifyingCode(100000000, callInexistant, "call-nonexist-100M", b)
+	benchmarkNonModifyingCode(100000000, callEOA, "call-EOA-100M", b)
+	benchmarkNonModifyingCode(100000000, calllRevertingContractWithInput, "call-reverting-100M", b)
+
+	//benchmarkNonModifyingCode(10000000, staticCallIdentity, "staticcall-identity-10M", b)
+	//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
+}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index 99de4d79c8dbe788737d53bf9595ec8eb045df30..af27d6552cebdb50df72dd6e4b5eed82b05fc244 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -18,10 +18,17 @@ package vm
 
 import (
 	"fmt"
+	"sync"
 
 	"github.com/holiman/uint256"
 )
 
+var stackPool = sync.Pool{
+	New: func() interface{} {
+		return &Stack{data: make([]uint256.Int, 0, 16)}
+	},
+}
+
 // Stack is an object for basic stack operations. Items popped to the stack are
 // expected to be changed and modified. stack does not take care of adding newly
 // initialised objects.
@@ -30,7 +37,12 @@ type Stack struct {
 }
 
 func newstack() *Stack {
-	return &Stack{data: make([]uint256.Int, 0, 16)}
+	return stackPool.Get().(*Stack)
+}
+
+func returnStack(s *Stack) {
+	s.data = s.data[:0]
+	stackPool.Put(s)
 }
 
 // Data returns the underlying uint256.Int array.
@@ -87,20 +99,32 @@ func (st *Stack) Print() {
 	fmt.Println("#############")
 }
 
+var rStackPool = sync.Pool{
+	New: func() interface{} {
+		return &ReturnStack{data: make([]uint32, 0, 10)}
+	},
+}
+
 // ReturnStack is an object for basic return stack operations.
 type ReturnStack struct {
-	data []uint64
+	data []uint32
 }
 
 func newReturnStack() *ReturnStack {
-	return &ReturnStack{data: make([]uint64, 0, 1024)}
+	return rStackPool.Get().(*ReturnStack)
+}
+
+func returnRStack(rs *ReturnStack) {
+	rs.data = rs.data[:0]
+	rStackPool.Put(rs)
 }
 
-func (st *ReturnStack) push(d uint64) {
+func (st *ReturnStack) push(d uint32) {
 	st.data = append(st.data, d)
 }
 
-func (st *ReturnStack) pop() (ret uint64) {
+// A uint32 is sufficient as for code below 4.2G
+func (st *ReturnStack) pop() (ret uint32) {
 	ret = st.data[len(st.data)-1]
 	st.data = st.data[:len(st.data)-1]
 	return
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index a1394920f457a3bcfe66aa3ab9cb8bad789db0db..050fb05159f1fbdbbfa744dc8cebe622fdf4500e 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -541,7 +541,7 @@ func (jst *Tracer) CaptureStart(from common.Address, to common.Address, create b
 }
 
 // CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, contract *vm.Contract, depth int, err error) error {
+func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, rStack *vm.ReturnStack, rdata []byte, contract *vm.Contract, depth int, err error) error {
 	if jst.err == nil {
 		// Initialize the context if it wasn't done yet
 		if !jst.inited {
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index 911431a3fd31d1354a633425551258b291b36108..b4de99865153997a096ccf596082dbee366ef32a 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -169,10 +169,10 @@ func TestHaltBetweenSteps(t *testing.T) {
 	env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
 	contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
 
-	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
+	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
 	timeout := errors.New("stahp")
 	tracer.Stop(timeout)
-	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, contract, 0, nil)
+	tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, nil, nil, contract, 0, nil)
 
 	if _, err := tracer.GetResult(); err.Error() != timeout.Error() {
 		t.Errorf("Expected timeout error, got %v", err)
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index 955771495bfc6421cf829d0385b28b9726d1b609..bd8e51d989ee795b65cf4c1e5a42c365e4ce19a3 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -179,9 +179,9 @@ func (t *SecureTrie) hashKey(key []byte) []byte {
 	h := newHasher(false)
 	h.sha.Reset()
 	h.sha.Write(key)
-	buf := h.sha.Sum(t.hashKeyBuf[:0])
+	h.sha.Read(t.hashKeyBuf[:])
 	returnHasherToPool(h)
-	return buf
+	return t.hashKeyBuf[:]
 }
 
 // getSecKeyCache returns the current secure key cache, creating a new one if