diff --git a/execution/evm/vm.go b/execution/evm/vm.go
index 35a3ac63a96805f35e209ba74dfcbe484ca64b32..aca675675cf1feffb53603685e804f2c1d7a712f 100644
--- a/execution/evm/vm.go
+++ b/execution/evm/vm.go
@@ -42,6 +42,7 @@ var (
 	ErrMemoryOutOfBounds      = errors.New("Memory out of bounds")
 	ErrCodeOutOfBounds        = errors.New("Code out of bounds")
 	ErrInputOutOfBounds       = errors.New("Input out of bounds")
+	ErrReturnDataOutOfBounds  = errors.New("Return data out of bounds")
 	ErrCallStackOverflow      = errors.New("Call stack overflow")
 	ErrCallStackUnderflow     = errors.New("Call stack underflow")
 	ErrDataStackOverflow      = errors.New("Data stack overflow")
@@ -113,6 +114,7 @@ type VM struct {
 	nestedCallErrors []ErrNestedCall
 	publisher        event.Publisher
 	logger           *logging.Logger
+	returnData       []byte
 	debugOpcodes     bool
 	dumpTokens       bool
 }
@@ -666,6 +668,28 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 			}
 			vm.Debugf(" => [%v, %v, %v] %X\n", memOff, codeOff, length, data)
 
+		case RETURNDATASIZE: // 0x3D
+			stack.Push64(int64(len(vm.returnData)))
+			vm.Debugf(" => %d\n", len(vm.returnData))
+
+		case RETURNDATACOPY: // 0x3E
+			memOff, outputOff, length := stack.PopBigInt(), stack.PopBigInt(), stack.PopBigInt()
+
+			end := new(big.Int).Add(outputOff, length)
+
+			if end.BitLen() > 64 || uint64(len(vm.returnData)) < end.Uint64() {
+				return nil, ErrReturnDataOutOfBounds
+			}
+
+			data := vm.returnData
+
+			memErr := memory.Write(memOff, data)
+			if memErr != nil {
+				vm.Debugf(" => Memory err: %s", memErr)
+				return nil, firstErr(err, ErrMemoryOutOfBounds)
+			}
+			vm.Debugf(" => [%v, %v, %v] %X\n", memOff, outputOff, length, data)
+
 		case BLOCKHASH: // 0x40
 			stack.Push(Zero256)
 			vm.Debugf(" => 0x%X (NOT SUPPORTED)\n", stack.Peek().Bytes())
@@ -834,6 +858,8 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 			vm.Debugf(" => T:%X D:%X\n", topics, data)
 
 		case CREATE: // 0xF0
+			vm.returnData = nil
+
 			if !HasPermission(vm.stateWriter, callee, permission.CreateContract) {
 				return nil, ErrPermission{"create_contract"}
 			}
@@ -868,6 +894,7 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 			ret, err_ := vm.Call(callee, newAccount, input, input, contractValue, gas)
 			if err_ != nil {
 				stack.Push(Zero256)
+				vm.returnData = ret
 			} else {
 				newAccount.SetCode(ret) // Set the code (ret need not be copied as per Call contract)
 				stack.Push(newAccount.Address().Word256())
@@ -878,6 +905,8 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 			}
 
 		case CALL, CALLCODE, DELEGATECALL: // 0xF1, 0xF2, 0xF4
+			vm.returnData = nil
+
 			if !HasPermission(vm.stateWriter, callee, permission.Call) {
 				return nil, ErrPermission{"call"}
 			}
@@ -973,6 +1002,8 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 					ret, callErr = vm.Call(callee, acc, acc.Code(), args, value, &gasLimit)
 				}
 			}
+			vm.returnData = ret
+
 			// In case any calls deeper in the stack (particularly SNatives) has altered either of two accounts to which
 			// we hold a reference, we need to freshen our state for subsequent iterations of this call frame's EVM loop
 			var getErr error
@@ -1080,7 +1111,7 @@ func (vm *VM) call(caller acm.Account, callee acm.MutableAccount, code, input []
 		case STOP: // 0x00
 			return nil, nil
 
-		case STATICCALL, SHL, SHR, SAR, RETURNDATASIZE, RETURNDATACOPY:
+		case STATICCALL, SHL, SHR, SAR:
 			return nil, fmt.Errorf("%s not yet implemented", op.Name())
 		default:
 			vm.Debugf("(pc) %-3v Unknown opcode %X\n", pc, op)
diff --git a/execution/evm/vm_test.go b/execution/evm/vm_test.go
index 23f942a8cb152c46fc0eed94ef2a9e7b77585665..ffa5454c83bf4931eaa34095c6c16ebaef029730 100644
--- a/execution/evm/vm_test.go
+++ b/execution/evm/vm_test.go
@@ -394,6 +394,83 @@ func TestInvalid(t *testing.T) {
 
 }
 
+func TestReturnDataSize(t *testing.T) {
+	cache := state.NewCache(newAppState())
+	ourVm := NewVM(cache, newParams(), acm.ZeroAddress, nil, logger)
+
+	accountName := "account2addresstests"
+
+	callcode := MustSplice(PUSH32, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61,
+		0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, PUSH1, 0x00, MSTORE, PUSH1, 0x0E, PUSH1, 0x00, RETURN)
+
+	// Create accounts
+	account1 := newAccount(1)
+	account2, _ := makeAccountWithCode(cache, accountName, callcode)
+
+	var gas uint64 = 100000
+
+	gas1, gas2 := byte(0x1), byte(0x1)
+	value := byte(0x69)
+	inOff, inSize := byte(0x0), byte(0x0) // no call data
+	retOff, retSize := byte(0x0), byte(0x0E)
+
+	bytecode := MustSplice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, inOff, PUSH1, value, PUSH20,
+		0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x32, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x74, 0x65,
+		0x73, 0x74, 0x73, PUSH2, gas1, gas2, CALL, RETURNDATASIZE, PUSH1, 0x00, MSTORE, PUSH1, 0x20, PUSH1, 0x00, RETURN)
+
+	expected := LeftPadBytes([]byte{0x0E}, 32)
+
+	output, err := ourVm.Call(account1, account2, bytecode, []byte{}, 0, &gas)
+
+	assert.Equal(t, expected, output)
+
+	t.Logf("Output: %v Error: %v\n", output, err)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestReturnDataCopy(t *testing.T) {
+	cache := state.NewCache(newAppState())
+	ourVm := NewVM(cache, newParams(), acm.ZeroAddress, nil, logger)
+
+	accountName := "account2addresstests"
+
+	callcode := MustSplice(PUSH32, 0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61,
+		0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, PUSH1, 0x00, MSTORE, PUSH1, 0x0E, PUSH1, 0x00, RETURN)
+
+	// Create accounts
+	account1 := newAccount(1)
+	account2, _ := makeAccountWithCode(cache, accountName, callcode)
+
+	var gas uint64 = 100000
+
+	gas1, gas2 := byte(0x1), byte(0x1)
+	value := byte(0x69)
+	inOff, inSize := byte(0x0), byte(0x0) // no call data
+	retOff, retSize := byte(0x0), byte(0x0E)
+
+	bytecode := MustSplice(PUSH1, retSize, PUSH1, retOff, PUSH1, inSize, PUSH1, inOff, PUSH1, value, PUSH20,
+		0x61, 0x63, 0x63, 0x6F, 0x75, 0x6E, 0x74, 0x32, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x74, 0x65,
+		0x73, 0x74, 0x73, PUSH2, gas1, gas2, CALL, RETURNDATASIZE, PUSH1, 0x00, PUSH1, 0x00, RETURNDATACOPY,
+		RETURNDATASIZE, PUSH1, 0x00, RETURN)
+
+	expected := []byte{0x72, 0x65, 0x76, 0x65, 0x72, 0x74, 0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65}
+
+	output, err := ourVm.Call(account1, account2, bytecode, []byte{}, 0, &gas)
+
+	assert.Equal(t, expected, output)
+
+	t.Logf("Output: %v Error: %v\n", output, err)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
 // These code segment helpers exercise the MSTORE MLOAD MSTORE cycle to test
 // both of the memory operations. Each MSTORE is done on the memory boundary
 // (at MSIZE) which Solidity uses to find guaranteed unallocated memory.