diff --git a/cmd/state/stateless/deps.go b/cmd/state/stateless/deps.go
index 8ac1579d19b4e143f7efa9b2650bf58b7c2e1ab8..52d88d3872b8956cd43b5f6a8f8f33ed72478dd5 100644
--- a/cmd/state/stateless/deps.go
+++ b/cmd/state/stateless/deps.go
@@ -15,6 +15,7 @@ import (
 	"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/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -42,7 +43,7 @@ func NewDepTracer() *DepTracer {
 func (dt *DepTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
 	return nil
 }
-func (dt *DepTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (dt *DepTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if op == vm.SSTORE {
 		addr := contract.Address()
 		if stack.Len() == 0 {
@@ -72,7 +73,7 @@ func (dt *DepTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cos
 	}
 	return nil
 }
-func (dt *DepTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (dt *DepTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	dt.accountsWriteSetFrame = make(map[common.Address]struct{})
 	dt.storageWriteSetFrame = make(map[common.Address]map[common.Hash]struct{})
 	return nil
diff --git a/cmd/state/stateless/naked_accouts.go b/cmd/state/stateless/naked_accouts.go
index 41a6bcd51c884d443fa7693c26dfe04b92df0f55..351b519a41a57dcc282469789125f7575810f103 100644
--- a/cmd/state/stateless/naked_accouts.go
+++ b/cmd/state/stateless/naked_accouts.go
@@ -20,6 +20,7 @@ import (
 	"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/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -41,10 +42,10 @@ func NewAccountsTracer() *AccountsTracer {
 func (at *AccountsTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
 	return nil
 }
-func (at *AccountsTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (at *AccountsTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
-func (at *AccountsTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (at *AccountsTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (at *AccountsTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/cmd/state/stateless/naked_storage.go b/cmd/state/stateless/naked_storage.go
index 0871df264945c26c949ceb546ecd6a353509fff4..1ea17bb9fe0a7569715def6084bd903b6baa2127 100644
--- a/cmd/state/stateless/naked_storage.go
+++ b/cmd/state/stateless/naked_storage.go
@@ -21,6 +21,7 @@ import (
 	"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/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -42,7 +43,7 @@ func NewStorageTracer() *StorageTracer {
 func (st *StorageTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
 	return nil
 }
-func (st *StorageTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (st *StorageTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if op == vm.SSTORE {
 		addr := contract.Address()
 		if stack.Len() == 0 {
@@ -82,7 +83,7 @@ func (st *StorageTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas,
 	}
 	return nil
 }
-func (st *StorageTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (st *StorageTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (st *StorageTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/cmd/state/stateless/spec_exec.go b/cmd/state/stateless/spec_exec.go
index 7d54e375b4075cd59c554a4715e41c4bd3fa1d36..4282cacac10aecf1673e41868c14b5b11c5d7481 100644
--- a/cmd/state/stateless/spec_exec.go
+++ b/cmd/state/stateless/spec_exec.go
@@ -20,6 +20,7 @@ import (
 	"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/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -69,7 +70,7 @@ func (ct *CombTracer) ResetSets() {
 func (ct *CombTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
 	return nil
 }
-func (ct *CombTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (ct *CombTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if op == vm.SSTORE {
 		addr := contract.Address()
 		if stack.Len() == 0 {
@@ -119,7 +120,7 @@ func (ct *CombTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, co
 	}
 	return nil
 }
-func (ct *CombTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (ct *CombTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (ct *CombTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/cmd/state/stateless/state.go b/cmd/state/stateless/state.go
index 0eef615050afe5204daa29e9bf8927c732a9d862..cc18c6f13a447aaefc667ff889717167bc52e1ca 100644
--- a/cmd/state/stateless/state.go
+++ b/cmd/state/stateless/state.go
@@ -33,6 +33,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/core/types"
 	"github.com/ledgerwatch/turbo-geth/core/types/accounts"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/ethdb/codecpool"
@@ -1339,10 +1340,10 @@ func NewCreationTracer(w io.Writer) CreationTracer {
 func (ct CreationTracer) CaptureStart(depth int, from common.Address, to common.Address, call bool, input []byte, gas uint64, value *big.Int) error {
 	return nil
 }
-func (ct CreationTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (ct CreationTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
-func (ct CreationTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (ct CreationTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (ct CreationTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/cmd/state/stateless/tokens.go b/cmd/state/stateless/tokens.go
index 04e554338193e357e8c66831b846b65d405cc7f2..712bcd3efa1bfc841d70e85b23641b1e7392262c 100644
--- a/cmd/state/stateless/tokens.go
+++ b/cmd/state/stateless/tokens.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/holiman/uint256"
 	"github.com/ledgerwatch/bolt"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/dbutils"
@@ -68,10 +69,10 @@ func (tt TokenTracer) CaptureStart(depth int, from common.Address, to common.Add
 	tt.startMode[depth] = true
 	return nil
 }
-func (tt TokenTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (tt TokenTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
-func (tt TokenTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (tt TokenTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (tt TokenTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/cmd/state/stateless/transaction_stats.go b/cmd/state/stateless/transaction_stats.go
index b47479bba2b29a9651e349d9fca27d493602982f..de010a0d721a76551117b93705a7099dd3e988b7 100644
--- a/cmd/state/stateless/transaction_stats.go
+++ b/cmd/state/stateless/transaction_stats.go
@@ -15,6 +15,7 @@ import (
 	"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/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/ethdb"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -94,7 +95,7 @@ func (tt *TxTracer) queryStorageAccess(account common.Address, storageKey common
 	}
 }
 
-func (tt *TxTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (tt *TxTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if tt.measureCreate && tt.measureDepth+1 == depth {
 		tt.gasForCREATE += (tt.measureCurrentGas - gas)
 	}
@@ -135,7 +136,7 @@ func (tt *TxTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 	}
 	return nil
 }
-func (tt *TxTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (tt *TxTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	return nil
 }
 func (tt *TxTracer) CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error {
diff --git a/common/pool/pool_stack.go b/common/pool/pool_stack.go
new file mode 100644
index 0000000000000000000000000000000000000000..459a251370df16241fe6f74f0e8f5b2c993ee6f9
--- /dev/null
+++ b/common/pool/pool_stack.go
@@ -0,0 +1,38 @@
+package pool
+
+import (
+	"sync"
+
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
+)
+
+var StackPool = NewStack()
+
+type Stack struct {
+	*sync.Pool
+}
+
+const maxCap = 1024 * 2
+
+func NewStack() *Stack {
+	return &Stack{
+		&sync.Pool{
+			New: func() interface{} {
+				return stack.New(maxCap)
+			},
+		},
+	}
+}
+
+func (p *Stack) Get() *stack.Stack {
+	return p.Pool.Get().(*stack.Stack)
+}
+
+func (p *Stack) Put(s *stack.Stack) {
+	if s == nil || s.Cap() == 0 || s.Cap() > maxCap {
+		return
+	}
+
+	s.Reset()
+	p.Pool.Put(s)
+}
diff --git a/core/vm/eips.go b/core/vm/eips.go
index d2290c7cb944fe783b398d2277f9e84d09db9b29..7007813b782f2021533390069cebc1ddbc69c9ae 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -64,7 +64,7 @@ func enable1884(jt *JumpTable) {
 
 func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	balance := interpreter.evm.IntraBlockState.GetBalance(callContext.contract.Address())
-	callContext.stack.push(balance)
+	callContext.stack.Push(balance)
 	return nil, nil
 }
 
@@ -84,7 +84,7 @@ func enable1344(jt *JumpTable) {
 // opChainID implements CHAINID opcode
 func opChainID(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	chainID, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
-	callContext.stack.push(chainID)
+	callContext.stack.Push(chainID)
 	return nil, nil
 }
 
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 4aa186dbc53df56a2aebbe7eb561036faae81687..5dd1f8eafb8179c0680e98a5adbb0402d854f813 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -23,6 +23,7 @@ import (
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/math"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
 
@@ -65,7 +66,7 @@ func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
 // EXTCODECOPY (stack poition 3)
 // RETURNDATACOPY (stack position 2)
 func memoryCopierGas(stackpos int) gasFunc {
-	return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	return func(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 		// Gas for expanding the memory
 		gas, err := memoryGasCost(mem, memorySize)
 		if err != nil {
@@ -95,7 +96,7 @@ var (
 	gasReturnDataCopy = memoryCopierGas(2)
 )
 
-func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasSStore(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	value, x := stack.Back(1), stack.Back(0)
 	key := common.Hash(x.Bytes32())
 	var current uint256.Int
@@ -179,7 +180,7 @@ func gasSStore(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySi
 //     2.2.2. If original value equals new value (this storage slot is reset):
 //       2.2.2.1. If original value is 0, add SSTORE_INIT_REFUND to refund counter.
 //       2.2.2.2. Otherwise, add SSTORE_CLEAN_REFUND gas to refund counter.
-func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	// If we fail the minimum gas availability invariant, fail (0)
 	if contract.Gas <= params.SstoreSentryGasEIP2200 {
 		return 0, errors.New("not enough gas for reentrancy sentry")
@@ -223,7 +224,7 @@ func gasSStoreEIP2200(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
 }
 
 func makeGasLog(n uint64) gasFunc {
-	return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	return func(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 		requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
 		if overflow {
 			return 0, ErrGasUintOverflow
@@ -252,7 +253,7 @@ func makeGasLog(n uint64) gasFunc {
 	}
 }
 
-func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasSha3(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	gas, err := memoryGasCost(mem, memorySize)
 	if err != nil {
 		return 0, err
@@ -273,7 +274,7 @@ func gasSha3(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
 // pureMemoryGascost is used by several operations, which aside from their
 // static cost have a dynamic cost which is solely based on the memory
 // expansion
-func pureMemoryGascost(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func pureMemoryGascost(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	return memoryGasCost(mem, memorySize)
 }
 
@@ -286,7 +287,7 @@ var (
 	gasCreate  = pureMemoryGascost
 )
 
-func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasCreate2(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	gas, err := memoryGasCost(mem, memorySize)
 	if err != nil {
 		return 0, err
@@ -304,8 +305,8 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS
 	return gas, nil
 }
 
-func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
-	expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+func gasExpFrontier(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	expByteLen := uint64((stack.Data[stack.Len()-2].BitLen() + 7) / 8)
 
 	var (
 		gas      = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
@@ -317,8 +318,8 @@ func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem
 	return gas, nil
 }
 
-func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
-	expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+func gasExpEIP158(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	expByteLen := uint64((stack.Data[stack.Len()-2].BitLen() + 7) / 8)
 
 	var (
 		gas      = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
@@ -330,7 +331,7 @@ func gasExpEIP158(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memor
 	return gas, nil
 }
 
-func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	var (
 		gas            uint64
 		transfersValue = !stack.Back(2).IsZero()
@@ -365,7 +366,7 @@ func gasCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize
 	return gas, nil
 }
 
-func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasCallCode(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	memoryGas, err := memoryGasCost(mem, memorySize)
 	if err != nil {
 		return 0, err
@@ -390,7 +391,7 @@ func gasCallCode(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory
 	return gas, nil
 }
 
-func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasDelegateCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	gas, err := memoryGasCost(mem, memorySize)
 	if err != nil {
 		return 0, err
@@ -406,7 +407,7 @@ func gasDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
 	return gas, nil
 }
 
-func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasStaticCall(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	gas, err := memoryGasCost(mem, memorySize)
 	if err != nil {
 		return 0, err
@@ -422,7 +423,7 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
 	return gas, nil
 }
 
-func gasSelfdestruct(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+func gasSelfdestruct(evm *EVM, contract *Contract, stack *stack.Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	var gas uint64
 	// EIP150 homestead gas reprice fork:
 	if evm.chainRules.IsEIP150 {
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index a17ef13a300feca734e218d0a1d6306500c70e49..4138616f8709c5d118421fcb0a43b3e4d87214e3 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -26,49 +26,49 @@ import (
 )
 
 func opAdd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Add(&x, y)
 	return nil, nil
 }
 
 func opSub(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Sub(&x, y)
 	return nil, nil
 }
 
 func opMul(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Mul(&x, y)
 	return nil, nil
 }
 
 func opDiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Div(&x, y)
 	return nil, nil
 }
 
 func opSdiv(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.SDiv(&x, y)
 	return nil, nil
 }
 
 func opMod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Mod(&x, y)
 	return nil, nil
 }
 
 func opSmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.SMod(&x, y)
 	return nil, nil
 }
 
 func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	base, exponent := callContext.stack.pop(), callContext.stack.peek()
+	base, exponent := callContext.stack.Pop(), callContext.stack.Peek()
 	switch {
 	case exponent.IsZero():
 		// x ^ 0 == 1
@@ -97,20 +97,20 @@ func opExp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 }
 
 func opSignExtend(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	back := callContext.stack.pop()
-	num := callContext.stack.peek()
+	back := callContext.stack.Pop()
+	num := callContext.stack.Peek()
 	num.ExtendSign(num, &back)
 	return nil, nil
 }
 
 func opNot(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x := callContext.stack.peek()
+	x := callContext.stack.Peek()
 	x.Not(x)
 	return nil, nil
 }
 
 func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	if x.Lt(y) {
 		y.SetOne()
 	} else {
@@ -120,7 +120,7 @@ func opLt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
 }
 
 func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	if x.Gt(y) {
 		y.SetOne()
 	} else {
@@ -130,7 +130,7 @@ func opGt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
 }
 
 func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	if x.Slt(y) {
 		y.SetOne()
 	} else {
@@ -140,7 +140,7 @@ func opSlt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 }
 
 func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	if x.Sgt(y) {
 		y.SetOne()
 	} else {
@@ -150,7 +150,7 @@ func opSgt(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 }
 
 func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	if x.Eq(y) {
 		y.SetOne()
 	} else {
@@ -160,7 +160,7 @@ func opEq(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte
 }
 
 func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x := callContext.stack.peek()
+	x := callContext.stack.Peek()
 	if x.IsZero() {
 		x.SetOne()
 	} else {
@@ -170,31 +170,31 @@ func opIszero(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
 }
 
 func opAnd(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.And(&x, y)
 	return nil, nil
 }
 
 func opOr(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Or(&x, y)
 	return nil, nil
 }
 
 func opXor(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y := callContext.stack.pop(), callContext.stack.peek()
+	x, y := callContext.stack.Pop(), callContext.stack.Peek()
 	y.Xor(&x, y)
 	return nil, nil
 }
 
 func opByte(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	th, val := callContext.stack.pop(), callContext.stack.peek()
+	th, val := callContext.stack.Pop(), callContext.stack.Peek()
 	val.Byte(&th)
 	return nil, nil
 }
 
 func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+	x, y, z := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Peek()
 	if z.IsZero() {
 		z.Clear()
 	} else {
@@ -204,7 +204,7 @@ func opAddmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
 }
 
 func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x, y, z := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.peek()
+	x, y, z := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Peek()
 	if z.IsZero() {
 		z.Clear()
 	} else {
@@ -218,7 +218,7 @@ func opMulmod(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
 // and pushes on the stack arg2 shifted to the left by arg1 number of bits.
 func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
-	shift, value := callContext.stack.pop(), callContext.stack.peek()
+	shift, value := callContext.stack.Pop(), callContext.stack.Peek()
 	if shift.LtUint64(256) {
 		value.Lsh(value, uint(shift.Uint64()))
 	} else {
@@ -232,7 +232,7 @@ func opSHL(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 // and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
 func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
-	shift, value := callContext.stack.pop(), callContext.stack.peek()
+	shift, value := callContext.stack.Pop(), callContext.stack.Peek()
 	if shift.LtUint64(256) {
 		value.Rsh(value, uint(shift.Uint64()))
 	} else {
@@ -245,7 +245,7 @@ func opSHR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 // The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
 // and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
 func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	shift, value := callContext.stack.pop(), callContext.stack.peek()
+	shift, value := callContext.stack.Pop(), callContext.stack.Peek()
 	if shift.GtUint64(255) {
 		if value.Sign() >= 0 {
 			value.Clear()
@@ -261,7 +261,7 @@ func opSAR(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byt
 }
 
 func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	offset, size := callContext.stack.pop(), callContext.stack.peek()
+	offset, size := callContext.stack.Pop(), callContext.stack.Peek()
 	data := callContext.memory.GetPtr(offset.Uint64(), size.Uint64())
 
 	if interpreter.hasher == nil {
@@ -281,49 +281,49 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
 }
 
 func opAddress(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
+	callContext.stack.Push(new(uint256.Int).SetBytes(callContext.contract.Address().Bytes()))
 	return nil, nil
 }
 
 func opBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	slot := callContext.stack.peek()
+	slot := callContext.stack.Peek()
 	address := common.Address(slot.Bytes20())
 	slot.Set(interpreter.evm.IntraBlockState.GetBalance(address))
 	return nil, nil
 }
 
 func opOrigin(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
+	callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
 	return nil, nil
 }
 
 func opCaller(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
+	callContext.stack.Push(new(uint256.Int).SetBytes(callContext.contract.Caller().Bytes()))
 	return nil, nil
 }
 
 func opCallValue(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(callContext.contract.value)
+	callContext.stack.Push(callContext.contract.value)
 	return nil, nil
 }
 
 func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	x := callContext.stack.peek()
+	x := callContext.stack.Peek()
 	data := getDataBig(callContext.contract.Input, x, 32)
 	x.SetBytes(data)
 	return nil, nil
 }
 
 func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
+	callContext.stack.Push(new(uint256.Int).SetUint64(uint64(len(callContext.contract.Input))))
 	return nil, nil
 }
 
 func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		memOffset  = callContext.stack.pop()
-		dataOffset = callContext.stack.pop()
-		length     = callContext.stack.pop()
+		memOffset  = callContext.stack.Pop()
+		dataOffset = callContext.stack.Pop()
+		length     = callContext.stack.Pop()
 	)
 	len64 := length.Uint64()
 	callContext.memory.Set(memOffset.Uint64(), len64, getDataBig(callContext.contract.Input, &dataOffset, len64))
@@ -331,15 +331,15 @@ func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
 }
 
 func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
+	callContext.stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
 	return nil, nil
 }
 
 func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		memOffset  = callContext.stack.pop()
-		dataOffset = callContext.stack.pop()
-		length     = callContext.stack.pop()
+		memOffset  = callContext.stack.Pop()
+		dataOffset = callContext.stack.Pop()
+		length     = callContext.stack.Pop()
 	)
 
 	offset64, overflow := dataOffset.Uint64WithOverflow()
@@ -364,7 +364,7 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, callContext *call
 }
 
 func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	slot := callContext.stack.peek()
+	slot := callContext.stack.Peek()
 	slot.SetUint64(uint64(interpreter.evm.IntraBlockState.GetCodeSize(common.Address(slot.Bytes20()))))
 
 	return nil, nil
@@ -373,16 +373,16 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
 func opCodeSize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	l := new(uint256.Int)
 	l.SetUint64(uint64(len(callContext.contract.Code)))
-	callContext.stack.push(l)
+	callContext.stack.Push(l)
 
 	return nil, nil
 }
 
 func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		memOffset  = callContext.stack.pop()
-		codeOffset = callContext.stack.pop()
-		length     = callContext.stack.pop()
+		memOffset  = callContext.stack.Pop()
+		codeOffset = callContext.stack.Pop()
+		length     = callContext.stack.Pop()
 	)
 	len64 := length.Uint64()
 	codeCopy := getDataBig(callContext.contract.Code, &codeOffset, len64)
@@ -392,10 +392,10 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
 
 func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		a          = callContext.stack.pop()
-		memOffset  = callContext.stack.pop()
-		codeOffset = callContext.stack.pop()
-		length     = callContext.stack.pop()
+		a          = callContext.stack.Pop()
+		memOffset  = callContext.stack.Pop()
+		codeOffset = callContext.stack.Pop()
+		length     = callContext.stack.Pop()
 	)
 	addr := common.Address(a.Bytes20())
 	len64 := length.Uint64()
@@ -431,7 +431,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
 //   (6) Caller tries to get the code hash for an account which is marked as deleted,
 // this account should be regarded as a non-existent account and zero should be returned.
 func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	slot := callContext.stack.peek()
+	slot := callContext.stack.Peek()
 	address := common.Address(slot.Bytes20())
 	if interpreter.evm.IntraBlockState.Empty(address) {
 		slot.Clear()
@@ -443,12 +443,12 @@ func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
 
 func opGasprice(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	v, _ := uint256.FromBig(interpreter.evm.GasPrice)
-	callContext.stack.push(v)
+	callContext.stack.Push(v)
 	return nil, nil
 }
 
 func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	num := callContext.stack.peek()
+	num := callContext.stack.Peek()
 	num64, overflow := num.Uint64WithOverflow()
 	if overflow {
 		num.Clear()
@@ -468,74 +468,74 @@ func opBlockhash(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
 }
 
 func opCoinbase(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
+	callContext.stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Coinbase.Bytes()))
 	return nil, nil
 }
 
 func opTimestamp(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	v, _ := uint256.FromBig(interpreter.evm.Time)
-	callContext.stack.push(v)
+	callContext.stack.Push(v)
 	return nil, nil
 }
 
 func opNumber(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	v, _ := uint256.FromBig(interpreter.evm.BlockNumber)
-	callContext.stack.push(v)
+	callContext.stack.Push(v)
 	return nil, nil
 }
 
 func opDifficulty(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	v, _ := uint256.FromBig(interpreter.evm.Difficulty)
-	callContext.stack.push(v)
+	callContext.stack.Push(v)
 	return nil, nil
 }
 
 func opGasLimit(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
+	callContext.stack.Push(new(uint256.Int).SetUint64(interpreter.evm.GasLimit))
 	return nil, nil
 }
 
 func opPop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.pop()
+	callContext.stack.Pop()
 	return nil, nil
 }
 
 func opMload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	v := callContext.stack.peek()
+	v := callContext.stack.Peek()
 	offset := v.Uint64()
 	v.SetBytes(callContext.memory.GetPtr(offset, 32))
 	return nil, nil
 }
 
 func opMstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	mStart, val := callContext.stack.pop(), callContext.stack.pop()
+	mStart, val := callContext.stack.Pop(), callContext.stack.Pop()
 	callContext.memory.Set32(mStart.Uint64(), &val)
 	return nil, nil
 }
 
 func opMstore8(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	off, val := callContext.stack.pop(), callContext.stack.pop()
+	off, val := callContext.stack.Pop(), callContext.stack.Pop()
 	callContext.memory.store[off.Uint64()] = byte(val.Uint64() & 0xff)
 	return nil, nil
 }
 
 func opSload(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	loc := callContext.stack.peek()
+	loc := callContext.stack.Peek()
 	interpreter.hasherBuf = loc.Bytes32()
 	interpreter.evm.IntraBlockState.GetState(callContext.contract.Address(), &interpreter.hasherBuf, loc)
 	return nil, nil
 }
 
 func opSstore(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	loc := callContext.stack.pop()
-	val := callContext.stack.pop()
+	loc := callContext.stack.Pop()
+	val := callContext.stack.Pop()
 	interpreter.hasherBuf = loc.Bytes32()
 	interpreter.evm.IntraBlockState.SetState(callContext.contract.Address(), &interpreter.hasherBuf, val)
 	return nil, nil
 }
 
 func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	pos := callContext.stack.pop()
+	pos := callContext.stack.Pop()
 	if !callContext.contract.validJumpdest(&pos) {
 		return nil, ErrInvalidJump
 	}
@@ -545,7 +545,7 @@ func opJump(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
 }
 
 func opJumpi(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	pos, cond := callContext.stack.pop(), callContext.stack.pop()
+	pos, cond := callContext.stack.Pop(), callContext.stack.Pop()
 	if !cond.IsZero() {
 		if !callContext.contract.validJumpdest(&pos) {
 			return nil, ErrInvalidJump
@@ -563,25 +563,25 @@ func opJumpdest(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
 }
 
 func opPc(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(*pc))
+	callContext.stack.Push(new(uint256.Int).SetUint64(*pc))
 	return nil, nil
 }
 
 func opMsize(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
+	callContext.stack.Push(new(uint256.Int).SetUint64(uint64(callContext.memory.Len())))
 	return nil, nil
 }
 
 func opGas(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	callContext.stack.push(new(uint256.Int).SetUint64(callContext.contract.Gas))
+	callContext.stack.Push(new(uint256.Int).SetUint64(callContext.contract.Gas))
 	return nil, nil
 }
 
 func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		value  = callContext.stack.pop()
-		offset = callContext.stack.pop()
-		size   = callContext.stack.peek()
+		value  = callContext.stack.Pop()
+		offset = callContext.stack.Pop()
+		size   = callContext.stack.Peek()
 		input  = callContext.memory.GetCopy(offset.Uint64(), size.Uint64())
 		gas    = callContext.contract.Gas
 	)
@@ -615,9 +615,9 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]
 
 func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	var (
-		endowment    = callContext.stack.pop()
-		offset, size = callContext.stack.pop(), callContext.stack.pop()
-		salt         = callContext.stack.peek()
+		endowment    = callContext.stack.Pop()
+		offset, size = callContext.stack.Pop(), callContext.stack.Pop()
+		salt         = callContext.stack.Peek()
 		input        = callContext.memory.GetCopy(offset.Uint64(), size.Uint64())
 		gas          = callContext.contract.Gas
 	)
@@ -645,11 +645,11 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
 
 func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Pop gas. The actual gas in interpreter.evm.callGasTemp.
-	callContext.stack.pop()
+	callContext.stack.Pop()
 	gas := interpreter.evm.callGasTemp
 	// Pop other call parameters.
-	addr, value, inOffset, inSize, retOffset := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
-	retSize := callContext.stack.peek()
+	addr, value, inOffset, inSize, retOffset := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop()
+	retSize := callContext.stack.Peek()
 	toAddr := common.Address(addr.Bytes20())
 	// Get the arguments from the memory.
 	args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
@@ -673,11 +673,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
 
 func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
-	callContext.stack.pop()
+	callContext.stack.Pop()
 	gas := interpreter.evm.callGasTemp
 	// Pop other call parameters.
-	addr, value, inOffset, inSize, retOffset := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
-	retSize := callContext.stack.peek()
+	addr, value, inOffset, inSize, retOffset := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop()
+	retSize := callContext.stack.Peek()
 	toAddr := common.Address(addr.Bytes20())
 	// Get arguments from the memory.
 	args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
@@ -701,11 +701,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) (
 
 func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
-	callContext.stack.pop()
+	callContext.stack.Pop()
 	gas := interpreter.evm.callGasTemp
 	// Pop other call parameters.
-	addr, inOffset, inSize, retOffset := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
-	retSize := callContext.stack.peek()
+	addr, inOffset, inSize, retOffset := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop()
+	retSize := callContext.stack.Peek()
 	toAddr := common.Address(addr.Bytes20())
 	// Get arguments from the memory.
 	args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
@@ -726,11 +726,11 @@ func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCt
 
 func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 	// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
-	callContext.stack.pop()
+	callContext.stack.Pop()
 	gas := interpreter.evm.callGasTemp
 	// Pop other call parameters.
-	addr, inOffset, inSize, retOffset := callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop(), callContext.stack.pop()
-	retSize := callContext.stack.peek()
+	addr, inOffset, inSize, retOffset := callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop(), callContext.stack.Pop()
+	retSize := callContext.stack.Peek()
 	toAddr := common.Address(addr.Bytes20())
 	// Get arguments from the memory.
 	args := callContext.memory.GetPtr(inOffset.Uint64(), inSize.Uint64())
@@ -750,13 +750,13 @@ func opStaticCall(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx)
 }
 
 func opReturn(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	offset, size := callContext.stack.pop(), callContext.stack.pop()
+	offset, size := callContext.stack.Pop(), callContext.stack.Pop()
 	ret := callContext.memory.GetPtr(offset.Uint64(), size.Uint64())
 	return ret, nil
 }
 
 func opRevert(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	offset, size := callContext.stack.pop(), callContext.stack.pop()
+	offset, size := callContext.stack.Pop(), callContext.stack.Pop()
 	ret := callContext.memory.GetPtr(offset.Uint64(), size.Uint64())
 	return ret, nil
 }
@@ -766,7 +766,7 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]by
 }
 
 func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-	beneficiary := callContext.stack.pop()
+	beneficiary := callContext.stack.Pop()
 	balance := interpreter.evm.IntraBlockState.GetBalance(callContext.contract.Address())
 	interpreter.evm.IntraBlockState.AddBalance(common.Address(beneficiary.Bytes20()), balance)
 
@@ -780,9 +780,9 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([
 func makeLog(size int) executionFunc {
 	return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
 		topics := make([]common.Hash, size)
-		mStart, mSize := callContext.stack.pop(), callContext.stack.pop()
+		mStart, mSize := callContext.stack.Pop(), callContext.stack.Pop()
 		for i := 0; i < size; i++ {
-			topic := callContext.stack.pop()
+			topic := callContext.stack.Pop()
 			topics[i] = common.Hash(topic.Bytes32())
 		}
 
@@ -808,9 +808,9 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]b
 	)
 	*pc++
 	if *pc < codeLen {
-		callContext.stack.push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
+		callContext.stack.Push(integer.SetUint64(uint64(callContext.contract.Code[*pc])))
 	} else {
-		callContext.stack.push(integer)
+		callContext.stack.Push(integer)
 	}
 	return nil, nil
 }
@@ -831,7 +831,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
 		}
 
 		integer := new(uint256.Int)
-		callContext.stack.push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize)))
+		callContext.stack.Push(integer.SetBytes(common.RightPadBytes(callContext.contract.Code[startMin:endMin], pushByteSize)))
 
 		*pc += size
 		return nil, nil
@@ -841,7 +841,7 @@ func makePush(size uint64, pushByteSize int) executionFunc {
 // make dup instruction function
 func makeDup(size int64) executionFunc {
 	return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-		callContext.stack.dup(int(size))
+		callContext.stack.Dup(int(size))
 		return nil, nil
 	}
 }
@@ -851,7 +851,7 @@ func makeSwap(size int64) executionFunc {
 	// switch n + 1 otherwise n would be swapped with n
 	size++
 	return func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error) {
-		callContext.stack.swap(int(size))
+		callContext.stack.Swap(int(size))
 		return nil, nil
 	}
 }
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index 5379bfb03ac65d9fc8bc4176d13f61ebfd5c7dd5..3b27658c49235821d165dbd2ede67f49c237f765 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -26,6 +26,7 @@ import (
 	"github.com/holiman/uint256"
 
 	"github.com/ledgerwatch/turbo-geth/common"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
@@ -94,7 +95,7 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
 
 	var (
 		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack          = newstack()
+		stack          = stack.New()
 		pc             = uint64(0)
 		evmInterpreter = env.interpreter.(*EVMInterpreter)
 	)
@@ -103,10 +104,10 @@ func testTwoOperandOp(t *testing.T, tests []TwoOperandTestcase, opFn executionFu
 		x := new(uint256.Int).SetBytes(common.Hex2Bytes(test.X))
 		y := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Y))
 		expected := new(uint256.Int).SetBytes(common.Hex2Bytes(test.Expected))
-		stack.push(x)
-		stack.push(y)
+		stack.Push(x)
+		stack.Push(y)
 		opFn(&pc, evmInterpreter, &callCtx{nil, stack, nil})
-		actual := stack.pop()
+		actual := stack.Pop()
 
 		if actual.Cmp(expected) != 0 {
 			t.Errorf("Testcase %v %d, %v(%x, %x): expected  %x, got %x", name, i, name, x, y, expected, &actual)
@@ -191,7 +192,7 @@ func TestSAR(t *testing.T) {
 func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcase {
 	var (
 		env         = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack       = newstack()
+		stack       = stack.New()
 		pc          = uint64(0)
 		interpreter = env.interpreter.(*EVMInterpreter)
 	)
@@ -199,10 +200,10 @@ func getResult(args []*twoOperandParams, opFn executionFunc) []TwoOperandTestcas
 	for i, param := range args {
 		x := new(uint256.Int).SetBytes(common.Hex2Bytes(param.x))
 		y := new(uint256.Int).SetBytes(common.Hex2Bytes(param.y))
-		stack.push(x)
-		stack.push(y)
+		stack.Push(x)
+		stack.Push(y)
 		opFn(&pc, interpreter, &callCtx{nil, stack, nil})
-		actual := stack.pop()
+		actual := stack.Pop()
 		result[i] = TwoOperandTestcase{param.x, param.y, fmt.Sprintf("%064x", actual)}
 	}
 	return result
@@ -241,7 +242,7 @@ func TestJsonTestcases(t *testing.T) {
 func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 	var (
 		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack          = newstack()
+		stack          = stack.New()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
 
@@ -256,10 +257,10 @@ func opBenchmark(bench *testing.B, op executionFunc, args ...string) {
 	for i := 0; i < bench.N; i++ {
 		for _, arg := range byteArgs {
 			a := new(uint256.Int).SetBytes(arg)
-			stack.push(a)
+			stack.Push(a)
 		}
 		op(&pc, evmInterpreter, &callCtx{nil, stack, nil})
-		stack.pop()
+		stack.Pop()
 	}
 }
 
@@ -474,7 +475,7 @@ func BenchmarkOpIsZero(b *testing.B) {
 func TestOpMstore(t *testing.T) {
 	var (
 		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack          = newstack()
+		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -483,14 +484,14 @@ func TestOpMstore(t *testing.T) {
 	mem.Resize(64)
 	pc := uint64(0)
 	v := "abcdef00000000000000abba000000000deaf000000c0de00100000000133700"
-	stack.push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
-	stack.push(new(uint256.Int))
+	stack.Push(new(uint256.Int).SetBytes(common.Hex2Bytes(v)))
+	stack.Push(new(uint256.Int))
 	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	if got := common.Bytes2Hex(mem.GetCopy(0, 32)); got != v {
 		t.Fatalf("Mstore fail, got %v, expected %v", got, v)
 	}
-	stack.push(new(uint256.Int).SetOne())
-	stack.push(new(uint256.Int))
+	stack.Push(new(uint256.Int).SetOne())
+	stack.Push(new(uint256.Int))
 	opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	if common.Bytes2Hex(mem.GetCopy(0, 32)) != "0000000000000000000000000000000000000000000000000000000000000001" {
 		t.Fatalf("Mstore failed to overwrite previous value")
@@ -500,7 +501,7 @@ func TestOpMstore(t *testing.T) {
 func BenchmarkOpMstore(bench *testing.B) {
 	var (
 		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack          = newstack()
+		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -513,8 +514,8 @@ func BenchmarkOpMstore(bench *testing.B) {
 
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
-		stack.push(value)
-		stack.push(memStart)
+		stack.Push(value)
+		stack.Push(memStart)
 		opMstore(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	}
 }
@@ -522,7 +523,7 @@ func BenchmarkOpMstore(bench *testing.B) {
 func BenchmarkOpSHA3(bench *testing.B) {
 	var (
 		env            = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
-		stack          = newstack()
+		stack          = stack.New()
 		mem            = NewMemory()
 		evmInterpreter = NewEVMInterpreter(env, env.vmConfig)
 	)
@@ -533,8 +534,8 @@ func BenchmarkOpSHA3(bench *testing.B) {
 
 	bench.ResetTimer()
 	for i := 0; i < bench.N; i++ {
-		stack.push(uint256.NewInt().SetUint64(32))
-		stack.push(start)
+		stack.Push(uint256.NewInt().SetUint64(32))
+		stack.Push(start)
 		opSha3(&pc, evmInterpreter, &callCtx{mem, stack, nil})
 	}
 }
@@ -600,9 +601,9 @@ func TestCreate2Addreses(t *testing.T) {
 		/*
 			stack          := newstack()
 			// salt, but we don't need that for this test
-			stack.push(big.NewInt(int64(len(code)))) //size
-			stack.push(big.NewInt(0)) // memstart
-			stack.push(big.NewInt(0)) // value
+			stack.Push(big.NewInt(int64(len(code)))) //size
+			stack.Push(big.NewInt(0)) // memstart
+			stack.Push(big.NewInt(0)) // value
 			gas, _ := gasCreate2(params.GasTable{}, nil, nil, stack, nil, 0)
 			fmt.Printf("Example %d\n* address `0x%x`\n* salt `0x%x`\n* init_code `0x%x`\n* gas (assuming no mem expansion): `%v`\n* result: `%s`\n\n", i,origin, salt, code, gas, address.String())
 		*/
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 41df4c18a3200c80ec6925538bb5ea75bd6869b7..3b3431bfa7e9aad262637fca073121259b8cbfac 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -22,6 +22,8 @@ import (
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/math"
+	"github.com/ledgerwatch/turbo-geth/common/pool"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/log"
 )
 
@@ -64,7 +66,7 @@ type Interpreter interface {
 // but not transients like pc and gas
 type callCtx struct {
 	memory   *Memory
-	stack    *Stack
+	stack    *stack.Stack
 	contract *Contract
 }
 
@@ -158,10 +160,10 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 	var (
 		op          OpCode        // current opcode
 		mem         = NewMemory() // bound memory
-		stack       = newstack()  // local stack
+		locStack    = pool.StackPool.Get()
 		callContext = &callCtx{
 			memory:   mem,
-			stack:    stack,
+			stack:    locStack,
 			contract: contract,
 		}
 		// For optimisation reason we're using uint64 as the program counter.
@@ -175,15 +177,16 @@ 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
 	)
+	defer pool.StackPool.Put(locStack)
 	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, contract, in.evm.depth, err)
+					_ = in.cfg.Tracer.CaptureState(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, contract, in.evm.depth, err)
 				} else {
-					in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, stack, contract, in.evm.depth, err)
+					_ = in.cfg.Tracer.CaptureFault(in.evm, pcCopy, op, gasCopy, cost, mem, locStack, contract, in.evm.depth, err)
 				}
 			}
 		}()
@@ -212,7 +215,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 			return nil, &ErrInvalidOpCode{opcode: op}
 		}
 		// Validate stack
-		if sLen := stack.len(); sLen < operation.minStack {
+		if sLen := locStack.Len(); sLen < operation.minStack {
 			return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
 		} else if sLen > operation.maxStack {
 			return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
@@ -224,7 +227,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 			// for a call operation is the value. Transferring value from one
 			// account to the others means the state is modified and should also
 			// return with an error.
-			if operation.writes || (op == CALL && stack.Back(2).Sign() != 0) {
+			if operation.writes || (op == CALL && locStack.Back(2).Sign() != 0) {
 				return nil, ErrWriteProtection
 			}
 		}
@@ -240,7 +243,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		// Memory check needs to be done prior to evaluating the dynamic gas portion,
 		// to detect calculation overflows
 		if operation.memorySize != nil {
-			memSize, overflow := operation.memorySize(stack)
+			memSize, overflow := operation.memorySize(locStack)
 			if overflow {
 				return nil, ErrGasUintOverflow
 			}
@@ -255,7 +258,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
 		// cost is explicitly set so that the capture state defer method can get the proper cost
 		if operation.dynamicGas != nil {
 			var dynamicCost uint64
-			dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
+			dynamicCost, err = operation.dynamicGas(in.evm, contract, locStack, mem, memorySize)
 			cost += dynamicCost // total cost, for debug tracing
 			if err != nil || !contract.UseGas(dynamicCost) {
 				return nil, ErrOutOfGas
@@ -266,7 +269,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, contract, in.evm.depth, err)
+			_ = in.cfg.Tracer.CaptureState(in.evm, pc, op, gasCopy, cost, mem, locStack, contract, in.evm.depth, err)
 			logged = true
 		}
 
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index b05f95c36b6d63e7c406a103de43cb9e1809c60f..e1201c507b67fc574f52f7b234eeb63504c82725 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -17,14 +17,15 @@
 package vm
 
 import (
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
 
 type (
 	executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx) ([]byte, error)
-	gasFunc       func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
+	gasFunc       func(*EVM, *Contract, *stack.Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
 	// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
-	memorySizeFunc func(*Stack) (size uint64, overflow bool)
+	memorySizeFunc func(*stack.Stack) (size uint64, overflow bool)
 )
 
 type operation struct {
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 2b5c85b229f982bb9582a624569263aabd7c5d80..d2956f6f0db84510170b0c0883b827e2654e950e 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -28,6 +28,7 @@ import (
 	"github.com/ledgerwatch/turbo-geth/common/hexutil"
 	"github.com/ledgerwatch/turbo-geth/common/math"
 	"github.com/ledgerwatch/turbo-geth/core/types"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 )
 
 var errTraceLimitReached = errors.New("the number of logs reached the specified limit")
@@ -102,8 +103,8 @@ func (s *StructLog) ErrorString() string {
 // if you need to retain them beyond the current call.
 type Tracer interface {
 	CaptureStart(depth int, 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, contract *Contract, depth int, err error) error
-	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
+	CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error
+	CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error
 	CaptureEnd(depth int, output []byte, gasUsed uint64, t time.Duration, err error) error
 	CaptureCreate(creator common.Address, creation common.Address) error
 	CaptureAccountRead(account common.Address) error
@@ -143,7 +144,7 @@ func (l *StructLogger) CaptureStart(depth int, from common.Address, to common.Ad
 // CaptureState logs a new structured log message and pushes it out to the environment
 //
 // CaptureState also tracks SSTORE ops to track dirty values.
-func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, 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
@@ -157,10 +158,10 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 
 	// capture SSTORE opcodes and determine the changed value and store
 	// it in the local storage container.
-	if op == SSTORE && stack.len() >= 2 {
+	if op == SSTORE && stack.Len() >= 2 {
 		var (
-			value   = common.Hash(stack.data[stack.len()-2].Bytes32())
-			address = common.Hash(stack.data[stack.len()-1].Bytes32())
+			value   = common.Hash(stack.Data[stack.Len()-2].Bytes32())
+			address = common.Hash(stack.Data[stack.Len()-1].Bytes32())
 		)
 		l.changedValues[contract.Address()][address] = value
 	}
@@ -173,8 +174,8 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 	// Copy a snapshot of the current stack state to a new buffer
 	var stck []*big.Int
 	if !l.cfg.DisableStack {
-		stck = make([]*big.Int, len(stack.Data()))
-		for i, item := range stack.Data() {
+		stck = make([]*big.Int, len(stack.GetData()))
+		for i, item := range stack.GetData() {
 			stck[i] = item.ToBig()
 		}
 	}
@@ -192,7 +193,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
 
 // CaptureFault implements the Tracer interface to trace an execution fault
 // while running an opcode.
-func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *StructLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error {
 	return nil
 }
 
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 421571ef7f5a47fdc9b73c905fb36668eedb4e99..6c2a490a789ef80036f7d88d5f2f9ebfa1d773df 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/math"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 )
 
 type JSONLogger struct {
@@ -46,7 +47,7 @@ func (l *JSONLogger) CaptureStart(depth int, from common.Address, to common.Addr
 }
 
 // CaptureState outputs state information on the logger.
-func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error {
 	log := StructLog{
 		Pc:            pc,
 		Op:            op,
@@ -62,8 +63,8 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
 		log.Memory = memory.Data()
 	}
 	if !l.cfg.DisableStack {
-		log.Stack = make([]*big.Int, len(stack.Data()))
-		for i, item := range stack.Data() {
+		log.Stack = make([]*big.Int, len(stack.GetData()))
+		for i, item := range stack.GetData() {
 			log.Stack[i] = item.ToBig()
 		}
 	}
@@ -71,7 +72,7 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
 }
 
 // CaptureFault outputs state information on the logger.
-func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
+func (l *JSONLogger) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, memory *Memory, stack *stack.Stack, contract *Contract, depth int, err error) error {
 	return nil
 }
 
diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go
index 96a09555ff064a414c1f9e4f36b9757211579b08..8b4fc7b251d58f4e13737cbd8ac5588c578d2851 100644
--- a/core/vm/logger_test.go
+++ b/core/vm/logger_test.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/core/state"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/params"
 )
 
@@ -55,11 +56,11 @@ func TestStoreCapture(t *testing.T) {
 		env      = NewEVM(Context{}, &dummyStatedb{}, params.TestChainConfig, Config{})
 		logger   = NewStructLogger(nil)
 		mem      = NewMemory()
-		stack    = newstack()
+		stack    = stack.New()
 		contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 0)
 	)
-	stack.push(uint256.NewInt().SetOne())
-	stack.push(uint256.NewInt())
+	stack.Push(uint256.NewInt().SetOne())
+	stack.Push(uint256.NewInt())
 	var index common.Hash
 	logger.CaptureState(env, 0, SSTORE, 0, 0, mem, stack, contract, 0, nil)
 	if len(logger.changedValues[contract.Address()]) == 0 {
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 4fcb41442c4e28a593a80b20e5caba007fd641cb..4b0007eda7cfc3e007cb3887ee84c6aa1aeac63f 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -16,47 +16,49 @@
 
 package vm
 
-func memorySha3(stack *Stack) (uint64, bool) {
+import "github.com/ledgerwatch/turbo-geth/core/vm/stack"
+
+func memorySha3(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(1))
 }
 
-func memoryCallDataCopy(stack *Stack) (uint64, bool) {
+func memoryCallDataCopy(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(2))
 }
 
-func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
+func memoryReturnDataCopy(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(2))
 }
 
-func memoryCodeCopy(stack *Stack) (uint64, bool) {
+func memoryCodeCopy(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(2))
 }
 
-func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
+func memoryExtCodeCopy(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(1), stack.Back(3))
 }
 
-func memoryMLoad(stack *Stack) (uint64, bool) {
+func memoryMLoad(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64WithUint(stack.Back(0), 32)
 }
 
-func memoryMStore8(stack *Stack) (uint64, bool) {
+func memoryMStore8(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64WithUint(stack.Back(0), 1)
 }
 
-func memoryMStore(stack *Stack) (uint64, bool) {
+func memoryMStore(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64WithUint(stack.Back(0), 32)
 }
 
-func memoryCreate(stack *Stack) (uint64, bool) {
+func memoryCreate(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(1), stack.Back(2))
 }
 
-func memoryCreate2(stack *Stack) (uint64, bool) {
+func memoryCreate2(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(1), stack.Back(2))
 }
 
-func memoryCall(stack *Stack) (uint64, bool) {
+func memoryCall(stack *stack.Stack) (uint64, bool) {
 	x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
 	if overflow {
 		return 0, true
@@ -70,7 +72,7 @@ func memoryCall(stack *Stack) (uint64, bool) {
 	}
 	return y, false
 }
-func memoryDelegateCall(stack *Stack) (uint64, bool) {
+func memoryDelegateCall(stack *stack.Stack) (uint64, bool) {
 	x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
 	if overflow {
 		return 0, true
@@ -85,7 +87,7 @@ func memoryDelegateCall(stack *Stack) (uint64, bool) {
 	return y, false
 }
 
-func memoryStaticCall(stack *Stack) (uint64, bool) {
+func memoryStaticCall(stack *stack.Stack) (uint64, bool) {
 	x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
 	if overflow {
 		return 0, true
@@ -100,14 +102,14 @@ func memoryStaticCall(stack *Stack) (uint64, bool) {
 	return y, false
 }
 
-func memoryReturn(stack *Stack) (uint64, bool) {
+func memoryReturn(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(1))
 }
 
-func memoryRevert(stack *Stack) (uint64, bool) {
+func memoryRevert(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(1))
 }
 
-func memoryLog(stack *Stack) (uint64, bool) {
+func memoryLog(stack *stack.Stack) (uint64, bool) {
 	return calcMemSize64(stack.Back(0), stack.Back(1))
 }
diff --git a/core/vm/stack.go b/core/vm/stack/stack.go
similarity index 64%
rename from core/vm/stack.go
rename to core/vm/stack/stack.go
index 687b05632bf33938afe1dbcb0ae0f383f96e80c9..f336c17d4347c977dd5f9bed6b000aca638b83b6 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack/stack.go
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU Lesser General Public License
 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
 
-package vm
+package stack
 
 import (
 	"fmt"
@@ -26,59 +26,66 @@ import (
 // expected to be changed and modified. stack does not take care of adding newly
 // initialised objects.
 type Stack struct {
-	data []uint256.Int
+	Data []uint256.Int
 }
 
-func newstack() *Stack {
+func New(n ...int) *Stack {
+	if len(n) > 0 {
+		return &Stack{make([]uint256.Int, 0, n[0])}
+	}
 	return &Stack{}
 }
 
 // Data returns the underlying uint256.Int array.
-func (st *Stack) Data() []uint256.Int {
-	return st.data
+func (st *Stack) GetData() []uint256.Int {
+	return st.Data
 }
 
-func (st *Stack) push(d *uint256.Int) {
+func (st *Stack) Push(d *uint256.Int) {
 	// NOTE push limit (1024) is checked in baseCheck
-	st.data = append(st.data, *d)
+	st.Data = append(st.Data, *d)
 }
 
-func (st *Stack) pop() (ret uint256.Int) {
-	ret = st.data[len(st.data)-1]
-	st.data = st.data[:len(st.data)-1]
-	return
+func (st *Stack) Pop() uint256.Int {
+	ret := st.Data[len(st.Data)-1]
+	st.Data = st.Data[:len(st.Data)-1]
+	return ret
 }
 
-func (st *Stack) len() int {
-	return len(st.data)
+func (st *Stack) Len() int {
+	return len(st.Data)
 }
 
-func (st *Stack) Len() int {
-	return len(st.data)
+func (st *Stack) Cap() int {
+	return cap(st.Data)
 }
 
-func (st *Stack) swap(n int) {
-	st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
+func (st *Stack) Swap(n int) {
+	st.Data[st.Len()-n], st.Data[st.Len()-1] = st.Data[st.Len()-1], st.Data[st.Len()-n]
 }
 
-func (st *Stack) dup(n int) {
-	st.push(&st.data[st.len()-n])
+func (st *Stack) Dup(n int) {
+	st.Push(&st.Data[st.Len()-n])
 }
 
-func (st *Stack) peek() *uint256.Int {
-	return &st.data[st.len()-1]
+func (st *Stack) Peek() *uint256.Int {
+	return &st.Data[st.Len()-1]
 }
 
 // Back returns the n'th item in stack
 func (st *Stack) Back(n int) *uint256.Int {
-	return &st.data[st.len()-n-1]
+	return &st.Data[st.Len()-n-1]
+}
+
+func (st *Stack) Reset() {
+	st.Data = st.Data[:0]
 }
 
 // Print dumps the content of the stack
 func (st *Stack) Print() {
 	fmt.Println("### stack ###")
-	if len(st.data) > 0 {
-		for i, val := range st.data {
+	if len(st.Data) > 0 {
+		for i, val := range st.Data {
 			fmt.Printf("%-3d  %v\n", i, val)
 		}
 	} else {
diff --git a/eth/tracers/tracer.go b/eth/tracers/tracer.go
index 81f8b11968de286651cd6fc37571c5a9a0e1f78b..093b7359c4869fc4bcdbd40ce090b223d5e9f650 100644
--- a/eth/tracers/tracer.go
+++ b/eth/tracers/tracer.go
@@ -26,11 +26,12 @@ import (
 	"unsafe"
 
 	"github.com/holiman/uint256"
-	duktape "gopkg.in/olebedev/go-duktape.v3"
+	"gopkg.in/olebedev/go-duktape.v3"
 
 	"github.com/ledgerwatch/turbo-geth/common"
 	"github.com/ledgerwatch/turbo-geth/common/hexutil"
 	"github.com/ledgerwatch/turbo-geth/core/vm"
+	"github.com/ledgerwatch/turbo-geth/core/vm/stack"
 	"github.com/ledgerwatch/turbo-geth/crypto"
 	"github.com/ledgerwatch/turbo-geth/log"
 )
@@ -153,15 +154,15 @@ func (mw *memoryWrapper) pushObject(vm *duktape.Context) {
 
 // stackWrapper provides a JavaScript wrapper around vm.Stack.
 type stackWrapper struct {
-	stack *vm.Stack
+	stack *stack.Stack
 }
 
 // peek returns the nth-from-the-top element of the stack.
 func (sw *stackWrapper) peek(idx int) *big.Int {
-	if len(sw.stack.Data()) <= idx || idx < 0 {
+	if len(sw.stack.GetData()) <= idx || idx < 0 {
 		// TODO(karalabe): We can't js-throw from Go inside duktape inside Go. The Go
 		// runtime goes belly up https://github.com/golang/go/issues/15639.
-		log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.Data()), "index", idx)
+		log.Warn("Tracer accessed out of bound stack", "size", len(sw.stack.GetData()), "index", idx)
 		return new(big.Int)
 	}
 	return sw.stack.Back(idx).ToBig()
@@ -172,7 +173,7 @@ func (sw *stackWrapper) peek(idx int) *big.Int {
 func (sw *stackWrapper) pushObject(vm *duktape.Context) {
 	obj := vm.PushObject()
 
-	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.Data())); return 1 })
+	vm.PushGoFunction(func(ctx *duktape.Context) int { ctx.PushInt(len(sw.stack.GetData())); return 1 })
 	vm.PutPropString(obj, "length")
 
 	// Generate the `peek` method which takes an int and returns a bigint
@@ -548,7 +549,7 @@ func (jst *Tracer) CaptureStart(depth int, from common.Address, to common.Addres
 }
 
 // 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, 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 *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if jst.err == nil {
 		// Initialize the context if it wasn't done yet
 		if !jst.inited {
@@ -587,7 +588,7 @@ func (jst *Tracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost
 
 // CaptureFault implements the Tracer interface to trace an execution fault
 // while running an opcode.
-func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *vm.Stack, contract *vm.Contract, depth int, err error) error {
+func (jst *Tracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, memory *vm.Memory, stack *stack.Stack, contract *vm.Contract, depth int, err error) error {
 	if jst.err == nil {
 		// Apart from the error, everything matches the previous invocation
 		jst.errorValue = new(string)