diff --git a/core/vm/evm.go b/core/vm/evm.go
index b8af9bd15fae6a2b688b8b9359bef615706bfe65..cc4214a163d569b3a564cc1310b71fbe13ca8120 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -123,19 +123,20 @@ func (evm *EVM) Cancel() {
 	atomic.StoreInt32(&evm.abort, 1)
 }
 
-// Call executes the contract associated with the addr with the given input as parameters. It also handles any
-// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
-// case of an execution error or failed value transfer.
+// Call executes the contract associated with the addr with the given input as
+// parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
 func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
 	if evm.vmConfig.NoRecursion && evm.depth > 0 {
 		return nil, gas, nil
 	}
 
-	// Depth check execution. Fail if we're trying to execute above the
-	// limit.
+	// Fail if we're trying to execute above the call depth limit
 	if evm.depth > int(params.CallCreateDepth) {
 		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) {
 		return nil, gas, ErrInsufficientBalance
 	}
@@ -173,21 +174,23 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
 	return ret, contract.Gas, err
 }
 
-// CallCode executes the contract associated with the addr with the given input as parameters. It also handles any
-// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
-// case of an execution error or failed value transfer.
+// CallCode executes the contract associated with the addr with the given input
+// as parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
 //
-// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
+// CallCode differs from Call in the sense that it executes the given address'
+// code with the caller as context.
 func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
 	if evm.vmConfig.NoRecursion && evm.depth > 0 {
 		return nil, gas, nil
 	}
 
-	// Depth check execution. Fail if we're trying to execute above the
-	// limit.
+	// Fail if we're trying to execute above the call depth limit
 	if evm.depth > int(params.CallCreateDepth) {
 		return nil, gas, ErrDepth
 	}
+	// Fail if we're trying to transfer more than the available balance
 	if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
 		return nil, gas, ErrInsufficientBalance
 	}
@@ -211,18 +214,16 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
 	return ret, contract.Gas, err
 }
 
-// DelegateCall executes the contract associated with the addr with the given input as parameters.
-// It reverses the state in case of an execution error.
+// DelegateCall executes the contract associated with the addr with the given input
+// as parameters. It reverses the state in case of an execution error.
 //
-// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
-// and the caller is set to the caller of the caller.
+// DelegateCall differs from CallCode in the sense that it executes the given address'
+// code with the caller as context and the caller is set to the caller of the caller.
 func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
 	if evm.vmConfig.NoRecursion && evm.depth > 0 {
 		return nil, gas, nil
 	}
-
-	// Depth check execution. Fail if we're trying to execute above the
-	// limit.
+	// Fail if we're trying to execute above the call depth limit
 	if evm.depth > int(params.CallCreateDepth) {
 		return nil, gas, ErrDepth
 	}
@@ -232,7 +233,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 		to       = AccountRef(caller.Address())
 	)
 
-	// Iinitialise a new contract and make initialise the delegate values
+	// 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))
 
@@ -245,6 +246,47 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
 	return ret, contract.Gas, err
 }
 
+// StaticCall executes the contract associated with the addr with the given input
+// as parameters while disallowing any modifications to the state during the call.
+// Opcodes that attempt to perform such modifications will result in exceptions
+// instead of performing the modifications.
+func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
+	if evm.vmConfig.NoRecursion && evm.depth > 0 {
+		return nil, gas, nil
+	}
+	// Fail if we're trying to execute above the call depth limit
+	if evm.depth > int(params.CallCreateDepth) {
+		return nil, gas, ErrDepth
+	}
+	// Make sure the readonly is only set if we aren't in readonly yet
+	// this makes also sure that the readonly flag isn't removed for
+	// child calls.
+	if !evm.interpreter.readonly {
+		evm.interpreter.readonly = true
+		defer func() { evm.interpreter.readonly = false }()
+	}
+
+	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))
+
+	// 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, snapshot, contract, input)
+	if err != nil {
+		contract.UseGas(contract.Gas)
+		evm.StateDB.RevertToSnapshot(snapshot)
+	}
+	return ret, contract.Gas, err
+}
+
 // Create creates a new contract using code as deployment code.
 func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
 	if evm.vmConfig.NoRecursion && evm.depth > 0 {
diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go
index 761ca44507c926b4bc10e3619de3d829fa542f18..8a6c2741de796b68385c9658c435774ee32a28bd 100644
--- a/core/vm/gas_table.go
+++ b/core/vm/gas_table.go
@@ -423,6 +423,33 @@ func gasDelegateCall(gt params.GasTable, evm *EVM, contract *Contract, stack *St
 	return gas, nil
 }
 
+func gasStaticCall(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+	gas, err := memoryGasCost(mem, memorySize)
+	if err != nil {
+		return 0, err
+	}
+	var overflow bool
+	if gas, overflow = math.SafeAdd(gas, gt.Calls); overflow {
+		return 0, errGasUintOverflow
+	}
+
+	cg, err := callGas(gt, contract.Gas, gas, stack.Back(0))
+	if err != nil {
+		return 0, err
+	}
+	// Replace the stack item with the new gas calculation. This means that
+	// either the original item is left on the stack or the item is replaced by:
+	// (availableGas - gas) * 63 / 64
+	// We replace the stack item so that it's available when the opCall instruction is
+	// called.
+	stack.data[stack.len()-1] = new(big.Int).SetUint64(cg)
+
+	if gas, overflow = math.SafeAdd(gas, cg); overflow {
+		return 0, errGasUintOverflow
+	}
+	return gas, nil
+}
+
 func gasPush(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
 	return GasFastestStep, nil
 }
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 4f9e45ffe8d1c7c4977334a4e42c98a18f30d5ea..aaa8d7945e6ee5a5aa6efe6675a4a23bef9054ed 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -17,6 +17,7 @@
 package vm
 
 import (
+	"errors"
 	"fmt"
 	"math/big"
 
@@ -28,7 +29,8 @@ import (
 )
 
 var (
-	bigZero = new(big.Int)
+	bigZero            = new(big.Int)
+	errWriteProtection = errors.New("evm: write protection")
 )
 
 func opAdd(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
@@ -656,6 +658,35 @@ func opDelegateCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, st
 	return ret, nil
 }
 
+func opStaticCall(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
+	// pop gas
+	gas := stack.pop().Uint64()
+	// pop address
+	addr := stack.pop()
+	// pop input size and offset
+	inOffset, inSize := stack.pop(), stack.pop()
+	// pop return size and offset
+	retOffset, retSize := stack.pop(), stack.pop()
+
+	address := common.BigToAddress(addr)
+
+	// Get the arguments from the memory
+	args := memory.Get(inOffset.Int64(), inSize.Int64())
+
+	ret, returnGas, err := evm.StaticCall(contract, address, args, gas)
+	if err != nil {
+		stack.push(new(big.Int))
+	} else {
+		stack.push(big.NewInt(1))
+
+		memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+	}
+	contract.Gas += returnGas
+
+	evm.interpreter.intPool.put(addr, inOffset, inSize, retOffset, retSize)
+	return ret, nil
+}
+
 func opReturn(pc *uint64, evm *EVM, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
 	offset, size := stack.pop(), stack.pop()
 	ret := memory.GetPtr(offset.Int64(), size.Int64())
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 3faa987047ebf2811bf468acbb7e76ca5bab0ba3..661ada6910c9370e398f724354c541454164584b 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -69,6 +69,8 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
 	// we'll set the default jump table.
 	if !cfg.JumpTable[STOP].valid {
 		switch {
+		case evm.ChainConfig().IsMetropolis(evm.BlockNumber):
+			cfg.JumpTable = metropolisInstructionSet
 		case evm.ChainConfig().IsHomestead(evm.BlockNumber):
 			cfg.JumpTable = homesteadInstructionSet
 		default:
@@ -85,6 +87,18 @@ func NewInterpreter(evm *EVM, cfg Config) *Interpreter {
 }
 
 func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack *Stack) error {
+	if in.evm.chainRules.IsMetropolis {
+		if in.readonly {
+			// If the interpreter is operating in readonly mode, make sure no
+			// state-modifying operation is performed. The 3rd stack item
+			// for a call operation is the value. Transfering 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).BitLen() > 0) {
+				return errWriteProtection
+			}
+		}
+	}
 	return nil
 }
 
@@ -95,6 +109,7 @@ func (in *Interpreter) enforceRestrictions(op OpCode, operation operation, stack
 // considered a revert-and-consume-all-gas operation. No error specific checks
 // should be handled to reduce complexity and errors further down the in.
 func (in *Interpreter) Run(snapshot int, contract *Contract, input []byte) (ret []byte, err error) {
+	// Increment the call depth which is restricted to 1024
 	in.evm.depth++
 	defer func() { in.evm.depth-- }()
 
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index 0034eacb7dfdec3132e68a0acf074271b7cc1dfd..f6e8dae6651a3511bbc4d680b0d0868dee05e918 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -56,10 +56,26 @@ type operation struct {
 }
 
 var (
-	frontierInstructionSet  = NewFrontierInstructionSet()
-	homesteadInstructionSet = NewHomesteadInstructionSet()
+	frontierInstructionSet   = NewFrontierInstructionSet()
+	homesteadInstructionSet  = NewHomesteadInstructionSet()
+	metropolisInstructionSet = NewMetropolisInstructionSet()
 )
 
+// NewMetropolisInstructionSet returns the frontier, homestead and
+// metropolis instructions.
+func NewMetropolisInstructionSet() [256]operation {
+	// instructions that can be executed during the homestead phase.
+	instructionSet := NewHomesteadInstructionSet()
+	instructionSet[STATICCALL] = operation{
+		execute:       opStaticCall,
+		gasCost:       gasStaticCall,
+		validateStack: makeStackFunc(6, 1),
+		memorySize:    memoryStaticCall,
+		valid:         true,
+	}
+	return instructionSet
+}
+
 // NewHomesteadInstructionSet returns the frontier and homestead
 // instructions that can be executed during the homestead phase.
 func NewHomesteadInstructionSet() [256]operation {
@@ -810,6 +826,7 @@ func NewFrontierInstructionSet() [256]operation {
 			validateStack: makeStackFunc(2, 0),
 			memorySize:    memoryLog,
 			valid:         true,
+			writes:        true,
 		},
 		LOG1: {
 			execute:       makeLog(1),
@@ -817,6 +834,7 @@ func NewFrontierInstructionSet() [256]operation {
 			validateStack: makeStackFunc(3, 0),
 			memorySize:    memoryLog,
 			valid:         true,
+			writes:        true,
 		},
 		LOG2: {
 			execute:       makeLog(2),
@@ -824,6 +842,7 @@ func NewFrontierInstructionSet() [256]operation {
 			validateStack: makeStackFunc(4, 0),
 			memorySize:    memoryLog,
 			valid:         true,
+			writes:        true,
 		},
 		LOG3: {
 			execute:       makeLog(3),
@@ -831,6 +850,7 @@ func NewFrontierInstructionSet() [256]operation {
 			validateStack: makeStackFunc(5, 0),
 			memorySize:    memoryLog,
 			valid:         true,
+			writes:        true,
 		},
 		LOG4: {
 			execute:       makeLog(4),
@@ -838,6 +858,7 @@ func NewFrontierInstructionSet() [256]operation {
 			validateStack: makeStackFunc(6, 0),
 			memorySize:    memoryLog,
 			valid:         true,
+			writes:        true,
 		},
 		CREATE: {
 			execute:       opCreate,
diff --git a/core/vm/memory_table.go b/core/vm/memory_table.go
index 654137c70bc63b2885beb59e934bda104d0dae02..9d293a2f2cc739e005bb2ca8e036ca899146d8e6 100644
--- a/core/vm/memory_table.go
+++ b/core/vm/memory_table.go
@@ -74,6 +74,13 @@ func memoryDelegateCall(stack *Stack) *big.Int {
 	return math.BigMax(x, y)
 }
 
+func memoryStaticCall(stack *Stack) *big.Int {
+	x := calcMemSize(stack.Back(4), stack.Back(5))
+	y := calcMemSize(stack.Back(2), stack.Back(3))
+
+	return math.BigMax(x, y)
+}
+
 func memoryReturn(stack *Stack) *big.Int {
 	return calcMemSize(stack.Back(0), stack.Back(1))
 }
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index d4ba7f1563f5c5dd7016a1cd3604bb751b172755..51925e8ddd096e5c3c0b93e293e3a893aa8984be 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -201,6 +201,7 @@ const (
 	CALLCODE
 	RETURN
 	DELEGATECALL
+	STATICCALL = 0xfa
 
 	SELFDESTRUCT = 0xff
 )
@@ -355,6 +356,7 @@ var opCodeToString = map[OpCode]string{
 	RETURN:       "RETURN",
 	CALLCODE:     "CALLCODE",
 	DELEGATECALL: "DELEGATECALL",
+	STATICCALL:   "STATICCALL",
 	SELFDESTRUCT: "SELFDESTRUCT",
 
 	PUSH: "PUSH",
@@ -405,6 +407,7 @@ var stringToOp = map[string]OpCode{
 	"CALLDATASIZE": CALLDATASIZE,
 	"CALLDATACOPY": CALLDATACOPY,
 	"DELEGATECALL": DELEGATECALL,
+	"STATICCALL":   STATICCALL,
 	"CODESIZE":     CODESIZE,
 	"CODECOPY":     CODECOPY,
 	"GASPRICE":     GASPRICE,