1#![doc = include_str!("../README.md")]
2#![allow(missing_docs, clippy::missing_safety_doc)]
3#![cfg_attr(not(test), warn(unused_extern_crates))]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![cfg_attr(not(feature = "std"), no_std)]
6
7extern crate alloc;
8
9#[macro_use]
10#[cfg(feature = "ir")]
11extern crate tracing;
12
13use alloc::{boxed::Box, vec::Vec};
14use revm_interpreter::{
15 as_u64_saturated, as_usize_saturated,
16 interpreter_types::{InputsTr, MemoryTr},
17 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, InstructionResult,
18 InterpreterAction, InterpreterResult,
19};
20use revm_primitives::{hardfork::SpecId, Bytes, Log, LogData, KECCAK_EMPTY, U256};
21use revmc_context::{EvmContext, EvmWord};
22
23pub mod gas;
24
25#[cfg(feature = "ir")]
26mod ir;
27#[cfg(feature = "ir")]
28pub use ir::*;
29
30#[macro_use]
31mod macros;
32
33mod utils;
34use utils::*;
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq)]
38#[repr(u8)]
39pub enum CallKind {
40 Call,
42 CallCode,
44 DelegateCall,
46 StaticCall,
48}
49
50impl From<CallKind> for CallScheme {
51 fn from(kind: CallKind) -> Self {
52 match kind {
53 CallKind::Call => Self::Call,
54 CallKind::CallCode => Self::CallCode,
55 CallKind::DelegateCall => Self::DelegateCall,
56 CallKind::StaticCall => Self::StaticCall,
57 }
58 }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63#[repr(u8)]
64pub enum CreateKind {
65 Create,
67 Create2,
69}
70
71#[no_mangle]
78pub unsafe extern "C-unwind" fn __revmc_builtin_panic(data: *const u8, len: usize) -> ! {
79 let msg = core::str::from_utf8_unchecked(core::slice::from_raw_parts(data, len));
80 panic!("{msg}");
81}
82
83#[no_mangle]
84pub unsafe extern "C" fn __revmc_builtin_addmod(rev![a, b, c]: &mut [EvmWord; 3]) {
85 *c = a.to_u256().add_mod(b.to_u256(), c.to_u256()).into();
86}
87
88#[no_mangle]
89pub unsafe extern "C" fn __revmc_builtin_mulmod(rev![a, b, c]: &mut [EvmWord; 3]) {
90 *c = a.to_u256().mul_mod(b.to_u256(), c.to_u256()).into();
91}
92
93#[no_mangle]
94pub unsafe extern "C" fn __revmc_builtin_exp(
95 ecx: &mut EvmContext<'_>,
96 rev![base, exponent_ptr]: &mut [EvmWord; 2],
97 spec_id: SpecId,
98) -> InstructionResult {
99 let exponent = exponent_ptr.to_u256();
100 gas_opt!(ecx, gas::dyn_exp_cost(spec_id, exponent));
101 *exponent_ptr = base.to_u256().pow(exponent).into();
102 InstructionResult::Stop
103}
104
105#[no_mangle]
106pub unsafe extern "C" fn __revmc_builtin_keccak256(
107 ecx: &mut EvmContext<'_>,
108 rev![offset, len_ptr]: &mut [EvmWord; 2],
109) -> InstructionResult {
110 let len = try_into_usize!(len_ptr);
111 *len_ptr = EvmWord::from_be_bytes(if len == 0 {
112 KECCAK_EMPTY.0
113 } else {
114 gas_opt!(ecx, gas::dyn_keccak256_cost(len as u64));
115 let offset = try_into_usize!(offset);
116 ensure_memory!(ecx, offset, len);
117 let data = ecx.memory.slice(offset..offset + len);
118 revm_primitives::keccak256(&*data).0
119 });
120 InstructionResult::Stop
121}
122
123#[no_mangle]
124pub unsafe extern "C" fn __revmc_builtin_balance(
125 ecx: &mut EvmContext<'_>,
126 address: &mut EvmWord,
127 spec_id: SpecId,
128) -> InstructionResult {
129 let state = try_host!(ecx.host.balance(address.to_address()));
130 *address = state.data.into();
131 let gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
132 gas::warm_cold_cost(state.is_cold)
133 } else if spec_id.is_enabled_in(SpecId::ISTANBUL) {
134 700
136 } else if spec_id.is_enabled_in(SpecId::TANGERINE) {
137 400
138 } else {
139 20
140 };
141 gas!(ecx, gas);
142 InstructionResult::Stop
143}
144
145#[no_mangle]
146pub unsafe extern "C" fn __revmc_builtin_origin(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
147 let addr = ecx.host.caller();
149 let mut word = [0u8; 32];
150 word[12..32].copy_from_slice(addr.as_slice());
151 *slot = EvmWord::from_be_bytes(word);
152}
153
154#[no_mangle]
155pub unsafe extern "C" fn __revmc_builtin_calldataload(
156 ecx: &mut EvmContext<'_>,
157 offset: &mut EvmWord,
158) {
159 let offset_usize = as_usize_saturated!(offset.to_u256());
160 let mut word = [0u8; 32];
161
162 match ecx.input.input() {
163 CallInput::Bytes(bytes) => {
164 let len = bytes.len().saturating_sub(offset_usize).min(32);
165 if len > 0 && offset_usize < bytes.len() {
166 word[..len].copy_from_slice(&bytes[offset_usize..offset_usize + len]);
167 }
168 }
169 CallInput::SharedBuffer(range) => {
170 let input_slice = ecx.memory.global_slice(range.clone());
171 let input_len = input_slice.len();
172 if offset_usize < input_len {
173 let count = 32.min(input_len - offset_usize);
174 word[..count].copy_from_slice(&input_slice[offset_usize..offset_usize + count]);
175 }
176 }
177 }
178
179 *offset = EvmWord::from_be_bytes(word);
180}
181
182#[no_mangle]
183pub unsafe extern "C" fn __revmc_builtin_calldatasize(ecx: &mut EvmContext<'_>) -> usize {
184 match ecx.input.input() {
185 CallInput::Bytes(bytes) => bytes.len(),
186 CallInput::SharedBuffer(range) => range.len(),
187 }
188}
189
190#[no_mangle]
191pub unsafe extern "C" fn __revmc_builtin_calldatacopy(
192 ecx: &mut EvmContext<'_>,
193 rev![memory_offset, data_offset, len]: &mut [EvmWord; 3],
194) -> InstructionResult {
195 let len = try_into_usize!(len);
196 if len != 0 {
197 gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64));
198 let memory_offset = try_into_usize!(memory_offset);
199 ensure_memory!(ecx, memory_offset, len);
200 let data_offset = as_usize_saturated!(data_offset.to_u256());
201
202 match ecx.input.input() {
203 CallInput::Bytes(bytes) => {
204 ecx.memory.set_data(memory_offset, data_offset, len, bytes.as_ref());
205 }
206 CallInput::SharedBuffer(range) => {
207 ecx.memory.set_data_from_global(memory_offset, data_offset, len, range.clone());
208 }
209 }
210 }
211 InstructionResult::Stop
212}
213
214#[no_mangle]
215pub unsafe extern "C" fn __revmc_builtin_codesize(bytecode_len: usize) -> usize {
216 bytecode_len
217}
218
219#[no_mangle]
220pub unsafe extern "C" fn __revmc_builtin_codecopy(
221 ecx: &mut EvmContext<'_>,
222 sp: &mut [EvmWord; 3],
223 bytecode_ptr: *const u8,
224 bytecode_len: usize,
225) -> InstructionResult {
226 let bytecode = core::slice::from_raw_parts(bytecode_ptr, bytecode_len);
227 copy_operation(ecx, sp, bytecode)
228}
229
230#[no_mangle]
231pub unsafe extern "C" fn __revmc_builtin_gas_price(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
232 *slot = ecx.host.effective_gas_price().into();
233}
234
235#[no_mangle]
236pub unsafe extern "C" fn __revmc_builtin_extcodesize(
237 ecx: &mut EvmContext<'_>,
238 address: &mut EvmWord,
239 spec_id: SpecId,
240) -> InstructionResult {
241 let state_load = try_opt!(ecx.host.load_account_code(address.to_address()));
242 *address = U256::from(state_load.data.len()).into();
243 let gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
244 gas::warm_cold_cost(state_load.is_cold)
245 } else if spec_id.is_enabled_in(SpecId::TANGERINE) {
246 700
247 } else {
248 20
249 };
250 gas!(ecx, gas);
251 InstructionResult::Stop
252}
253
254#[no_mangle]
255pub unsafe extern "C" fn __revmc_builtin_extcodecopy(
256 ecx: &mut EvmContext<'_>,
257 rev![address, memory_offset, code_offset, len]: &mut [EvmWord; 4],
258 spec_id: SpecId,
259) -> InstructionResult {
260 let state_load = try_opt!(ecx.host.load_account_code(address.to_address()));
261
262 let len = try_into_usize!(len);
263 gas_opt!(ecx, gas::extcodecopy_cost(spec_id, len as u64, state_load.is_cold));
264 if len != 0 {
265 let memory_offset = try_into_usize!(memory_offset);
266 let code_offset = code_offset.to_u256();
267 let code_offset = as_usize_saturated!(code_offset).min(state_load.data.len());
268 ensure_memory!(ecx, memory_offset, len);
269 ecx.memory.set_data(memory_offset, code_offset, len, &state_load.data);
270 }
271 InstructionResult::Stop
272}
273
274#[no_mangle]
275pub unsafe extern "C" fn __revmc_builtin_returndatacopy(
276 ecx: &mut EvmContext<'_>,
277 rev![memory_offset, offset, len]: &mut [EvmWord; 3],
278) -> InstructionResult {
279 let len = try_into_usize!(len);
280 gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64));
281 let data_offset = offset.to_u256();
282 let data_offset = as_usize_saturated!(data_offset);
283 let (data_end, overflow) = data_offset.overflowing_add(len);
284 if overflow || data_end > ecx.return_data.len() {
285 return InstructionResult::OutOfOffset;
286 }
287 if len != 0 {
288 let memory_offset = try_into_usize!(memory_offset);
289 ensure_memory!(ecx, memory_offset, len);
290 ecx.memory.set(memory_offset, &ecx.return_data[data_offset..data_end]);
291 }
292 InstructionResult::Stop
293}
294
295#[no_mangle]
296pub unsafe extern "C" fn __revmc_builtin_extcodehash(
297 ecx: &mut EvmContext<'_>,
298 address: &mut EvmWord,
299 spec_id: SpecId,
300) -> InstructionResult {
301 let state_load = try_opt!(ecx.host.load_account_code_hash(address.to_address()));
302 *address = EvmWord::from_be_bytes(state_load.data.0);
303 let gas = if spec_id.is_enabled_in(SpecId::BERLIN) {
304 gas::warm_cold_cost(state_load.is_cold)
305 } else if spec_id.is_enabled_in(SpecId::ISTANBUL) {
306 700
307 } else {
308 400
309 };
310 gas!(ecx, gas);
311 InstructionResult::Stop
312}
313
314#[no_mangle]
315pub unsafe extern "C" fn __revmc_builtin_blockhash(
316 ecx: &mut EvmContext<'_>,
317 number_ptr: &mut EvmWord,
318) -> InstructionResult {
319 let requested_number = number_ptr.to_u256();
320 let block_number = ecx.host.block_number();
321
322 let Some(diff) = block_number.checked_sub(requested_number) else {
324 *number_ptr = EvmWord::ZERO;
325 return InstructionResult::Stop;
326 };
327
328 let diff = as_u64_saturated!(diff);
329
330 if diff == 0 {
332 *number_ptr = EvmWord::ZERO;
333 return InstructionResult::Stop;
334 }
335
336 const BLOCK_HASH_HISTORY: u64 = 256;
338
339 if diff <= BLOCK_HASH_HISTORY {
340 let hash = try_host!(ecx.host.block_hash(as_u64_saturated!(requested_number)));
341 *number_ptr = EvmWord::from_be_bytes(hash.0);
342 } else {
343 *number_ptr = EvmWord::ZERO;
345 }
346
347 InstructionResult::Stop
348}
349
350#[no_mangle]
351pub unsafe extern "C" fn __revmc_builtin_coinbase(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
352 let addr = ecx.host.beneficiary();
354 let mut word = [0u8; 32];
355 word[12..32].copy_from_slice(addr.as_slice());
356 *slot = EvmWord::from_be_bytes(word);
357}
358
359#[no_mangle]
360pub unsafe extern "C" fn __revmc_builtin_timestamp(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
361 *slot = ecx.host.timestamp().into();
362}
363
364#[no_mangle]
365pub unsafe extern "C" fn __revmc_builtin_number(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
366 *slot = ecx.host.block_number().into();
367}
368
369#[no_mangle]
370pub unsafe extern "C" fn __revmc_builtin_gaslimit(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
371 *slot = ecx.host.gas_limit().into();
372}
373
374#[no_mangle]
375pub unsafe extern "C" fn __revmc_builtin_chainid(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
376 *slot = ecx.host.chain_id().into();
377}
378
379#[no_mangle]
380pub unsafe extern "C" fn __revmc_builtin_basefee(ecx: &mut EvmContext<'_>, slot: &mut EvmWord) {
381 *slot = ecx.host.basefee().into();
382}
383
384#[no_mangle]
385pub unsafe extern "C" fn __revmc_builtin_difficulty(
386 ecx: &mut EvmContext<'_>,
387 slot: &mut EvmWord,
388 spec_id: SpecId,
389) {
390 *slot = if spec_id.is_enabled_in(SpecId::MERGE) {
391 ecx.host.prevrandao().unwrap_or_default().into()
392 } else {
393 ecx.host.difficulty().into()
394 };
395}
396
397#[no_mangle]
398pub unsafe extern "C" fn __revmc_builtin_self_balance(
399 ecx: &mut EvmContext<'_>,
400 slot: &mut EvmWord,
401) -> InstructionResult {
402 let state = try_host!(ecx.host.balance(ecx.input.target_address));
403 *slot = state.data.into();
404 InstructionResult::Stop
405}
406
407#[no_mangle]
408pub unsafe extern "C" fn __revmc_builtin_blob_hash(
409 ecx: &mut EvmContext<'_>,
410 index_ptr: &mut EvmWord,
411) {
412 let index = index_ptr.to_u256();
413 let index_usize = as_usize_saturated!(index);
414 *index_ptr = ecx.host.blob_hash(index_usize).unwrap_or_default().into();
415}
416
417#[no_mangle]
418pub unsafe extern "C" fn __revmc_builtin_blob_base_fee(
419 ecx: &mut EvmContext<'_>,
420 slot: &mut EvmWord,
421) {
422 *slot = ecx.host.blob_gasprice().into();
423}
424
425#[no_mangle]
426pub unsafe extern "C" fn __revmc_builtin_sload(
427 ecx: &mut EvmContext<'_>,
428 index: &mut EvmWord,
429 spec_id: SpecId,
430) -> InstructionResult {
431 let address = ecx.input.target_address;
432 let state = try_opt!(ecx.host.sload(address, index.to_u256()));
433 gas!(ecx, gas::sload_cost(spec_id, state.is_cold));
434 *index = state.data.into();
435 InstructionResult::Stop
436}
437
438#[no_mangle]
439pub unsafe extern "C" fn __revmc_builtin_sstore(
440 ecx: &mut EvmContext<'_>,
441 rev![index, value]: &mut [EvmWord; 2],
442 spec_id: SpecId,
443) -> InstructionResult {
444 ensure_non_staticcall!(ecx);
445
446 let state =
447 try_opt!(ecx.host.sstore(ecx.input.target_address, index.to_u256(), value.to_u256()));
448
449 gas_opt!(ecx, gas::sstore_cost(spec_id, &state.data, ecx.gas.remaining(), state.is_cold));
450 ecx.gas.record_refund(gas::sstore_refund(spec_id, &state.data));
451 InstructionResult::Stop
452}
453
454#[no_mangle]
455pub unsafe extern "C" fn __revmc_builtin_msize(ecx: &mut EvmContext<'_>) -> usize {
456 ecx.memory.len()
457}
458
459#[no_mangle]
460pub unsafe extern "C" fn __revmc_builtin_tstore(
461 ecx: &mut EvmContext<'_>,
462 rev![key, value]: &mut [EvmWord; 2],
463) -> InstructionResult {
464 ensure_non_staticcall!(ecx);
465 ecx.host.tstore(ecx.input.target_address, key.to_u256(), value.to_u256());
466 InstructionResult::Stop
467}
468
469#[no_mangle]
470pub unsafe extern "C" fn __revmc_builtin_tload(ecx: &mut EvmContext<'_>, key: &mut EvmWord) {
471 *key = ecx.host.tload(ecx.input.target_address, key.to_u256()).into();
472}
473
474#[no_mangle]
475pub unsafe extern "C" fn __revmc_builtin_mcopy(
476 ecx: &mut EvmContext<'_>,
477 rev![dst, src, len]: &mut [EvmWord; 3],
478) -> InstructionResult {
479 let len = try_into_usize!(len);
480 gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64));
481 if len != 0 {
482 let dst = try_into_usize!(dst);
483 let src = try_into_usize!(src);
484 ensure_memory!(ecx, dst.max(src), len);
485 ecx.memory.copy(dst, src, len);
486 }
487 InstructionResult::Stop
488}
489
490#[no_mangle]
491pub unsafe extern "C" fn __revmc_builtin_log(
492 ecx: &mut EvmContext<'_>,
493 sp: *mut EvmWord,
494 n: u8,
495) -> InstructionResult {
496 ensure_non_staticcall!(ecx);
497 assume!(n <= 4, "invalid log topic count: {n}");
498 let sp = sp.add(n as usize);
499 read_words!(sp, offset, len);
500 let len = try_into_usize!(len);
501 gas_opt!(ecx, gas::dyn_log_cost(len as u64));
502 let data = if len != 0 {
503 let offset = try_into_usize!(offset);
504 ensure_memory!(ecx, offset, len);
505 Bytes::copy_from_slice(&ecx.memory.slice(offset..offset + len))
506 } else {
507 Bytes::new()
508 };
509
510 let mut topics = Vec::with_capacity(n as usize);
511 for i in 1..=n {
512 topics.push(sp.sub(i as usize).read().to_be_bytes().into());
513 }
514
515 ecx.host.log(Log {
516 address: ecx.input.target_address,
517 data: LogData::new(topics, data).expect("too many topics"),
518 });
519 InstructionResult::Stop
520}
521
522#[no_mangle]
526pub unsafe extern "C" fn __revmc_builtin_create(
527 ecx: &mut EvmContext<'_>,
528 sp: *mut EvmWord,
529 spec_id: SpecId,
530 create_kind: CreateKind,
531) -> InstructionResult {
532 ensure_non_staticcall!(ecx);
533
534 let len = match create_kind {
535 CreateKind::Create => 3,
536 CreateKind::Create2 => 4,
537 };
538 let mut sp = sp.add(len);
539 pop!(sp; value, code_offset, len);
540
541 let len = try_into_usize!(len);
542 let code = if len != 0 {
543 if spec_id.is_enabled_in(SpecId::SHANGHAI) {
544 let max_initcode_size = ecx.host.max_initcode_size();
546 if len > max_initcode_size {
547 return InstructionResult::CreateInitCodeSizeLimit;
548 }
549 gas!(ecx, gas::initcode_cost(len as u64));
550 }
551
552 let code_offset = try_into_usize!(code_offset);
553 ensure_memory!(ecx, code_offset, len);
554 Bytes::copy_from_slice(&ecx.memory.slice(code_offset..code_offset + len))
555 } else {
556 Bytes::new()
557 };
558
559 let is_create2 = create_kind == CreateKind::Create2;
560 gas_opt!(ecx, if is_create2 { gas::create2_cost(len as u64) } else { Some(gas::CREATE) });
561
562 let scheme = if is_create2 {
563 pop!(sp; salt);
564 CreateScheme::Create2 { salt: salt.to_u256() }
565 } else {
566 CreateScheme::Create
567 };
568
569 let mut gas_limit = ecx.gas.remaining();
570 if spec_id.is_enabled_in(SpecId::TANGERINE) {
571 gas_limit -= gas_limit / 64;
572 }
573 gas!(ecx, gas_limit);
574
575 *ecx.next_action =
576 Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Create(Box::new(
577 CreateInputs::new(ecx.input.target_address, scheme, value.to_u256(), code, gas_limit),
578 ))));
579
580 InstructionResult::Stop
581}
582
583#[no_mangle]
584pub unsafe extern "C" fn __revmc_builtin_call(
585 ecx: &mut EvmContext<'_>,
586 sp: *mut EvmWord,
587 spec_id: SpecId,
588 call_kind: CallKind,
589) -> InstructionResult {
590 let len = match call_kind {
591 CallKind::Call | CallKind::CallCode => 7,
592 CallKind::DelegateCall | CallKind::StaticCall => 6,
593 };
594 let mut sp = sp.add(len);
595
596 pop!(sp; local_gas_limit, to);
597 let local_gas_limit = local_gas_limit.to_u256();
598 let to = to.to_address();
599
600 let local_gas_limit = as_u64_saturated!(local_gas_limit);
604
605 let value = match call_kind {
606 CallKind::Call | CallKind::CallCode => {
607 pop!(sp; value);
608 let value = value.to_u256();
609 if call_kind == CallKind::Call && ecx.is_static && value != U256::ZERO {
610 return InstructionResult::CallNotAllowedInsideStatic;
611 }
612 value
613 }
614 CallKind::DelegateCall | CallKind::StaticCall => U256::ZERO,
615 };
616 let transfers_value = value != U256::ZERO;
617
618 pop!(sp; in_offset, in_len, out_offset, out_len);
619
620 let in_len = try_into_usize!(in_len);
621 let input = if in_len != 0 {
622 let in_offset = try_into_usize!(in_offset);
623 ensure_memory!(ecx, in_offset, in_len);
624 Bytes::copy_from_slice(&ecx.memory.slice(in_offset..in_offset + in_len))
625 } else {
626 Bytes::new()
627 };
628
629 let out_len = try_into_usize!(out_len);
630 let out_offset = if out_len != 0 {
631 let out_offset = try_into_usize!(out_offset);
632 ensure_memory!(ecx, out_offset, out_len);
633 out_offset
634 } else {
635 usize::MAX };
637
638 let mut account_load = try_host!(ecx.host.load_account_delegated(to));
640
641 if call_kind != CallKind::Call {
642 account_load.is_empty = false;
643 }
644
645 gas!(ecx, gas::call_cost(spec_id, transfers_value, account_load));
646
647 let mut gas_limit = if spec_id.is_enabled_in(SpecId::TANGERINE) {
649 let gas = ecx.gas.remaining();
650 (gas - gas / 64).min(local_gas_limit)
652 } else {
653 local_gas_limit
654 };
655
656 gas!(ecx, gas_limit);
657
658 if matches!(call_kind, CallKind::Call | CallKind::CallCode) && transfers_value {
660 gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND);
661 }
662
663 *ecx.next_action = Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Call(
664 Box::new(CallInputs {
665 input: CallInput::Bytes(input),
666 return_memory_offset: out_offset..out_offset + out_len,
667 gas_limit,
668 bytecode_address: to,
669 known_bytecode: None,
670 target_address: if matches!(call_kind, CallKind::DelegateCall | CallKind::CallCode) {
671 ecx.input.target_address
672 } else {
673 to
674 },
675 caller: if call_kind == CallKind::DelegateCall {
676 ecx.input.caller_address
677 } else {
678 ecx.input.target_address
679 },
680 value: if call_kind == CallKind::DelegateCall {
681 CallValue::Apparent(ecx.input.call_value)
682 } else {
683 CallValue::Transfer(value)
684 },
685 scheme: call_kind.into(),
686 is_static: ecx.is_static || call_kind == CallKind::StaticCall,
687 }),
688 )));
689
690 InstructionResult::Stop
691}
692
693#[no_mangle]
694pub unsafe extern "C" fn __revmc_builtin_do_return(
695 ecx: &mut EvmContext<'_>,
696 rev![offset, len]: &mut [EvmWord; 2],
697 result: InstructionResult,
698) -> InstructionResult {
699 let len = try_into_usize!(len);
700 let output = if len != 0 {
701 let offset = try_into_usize!(offset);
702 ensure_memory!(ecx, offset, len);
703 ecx.memory.slice(offset..offset + len).to_vec().into()
704 } else {
705 Bytes::new()
706 };
707 *ecx.next_action =
708 Some(InterpreterAction::Return(InterpreterResult { output, gas: *ecx.gas, result }));
709 InstructionResult::Stop
710}
711
712#[no_mangle]
713pub unsafe extern "C" fn __revmc_builtin_selfdestruct(
714 ecx: &mut EvmContext<'_>,
715 target: &mut EvmWord,
716 spec_id: SpecId,
717) -> InstructionResult {
718 ensure_non_staticcall!(ecx);
719
720 if spec_id.is_enabled_in(SpecId::TANGERINE) {
722 gas!(ecx, 5000);
723 }
724
725 let res = match ecx.host.selfdestruct(ecx.input.target_address, target.to_address(), false) {
726 Ok(r) => r,
727 Err(_) => return InstructionResult::FatalExternalError,
728 };
729
730 let should_charge_topup = if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
732 res.data.had_value && !res.data.target_exists
733 } else {
734 !res.data.target_exists
735 };
736
737 gas!(ecx, ecx.host.gas_params().selfdestruct_cost(should_charge_topup, res.is_cold));
738
739 if !res.data.previously_destroyed {
740 ecx.gas.record_refund(ecx.host.gas_params().selfdestruct_refund());
741 }
742
743 InstructionResult::SelfDestruct
744}
745
746#[no_mangle]
747pub unsafe extern "C" fn __revmc_builtin_resize_memory(
748 ecx: &mut EvmContext<'_>,
749 new_size: usize,
750) -> InstructionResult {
751 resize_memory(ecx, new_size)
752}
753
754#[no_mangle]
755pub unsafe extern "C" fn __revmc_builtin_mload(
756 ecx: &mut EvmContext<'_>,
757 rev![offset_ptr]: &mut [EvmWord; 1],
758) -> InstructionResult {
759 let offset = try_into_usize!(offset_ptr);
760 ensure_memory!(ecx, offset, 32);
761 let slice = ecx.memory.slice(offset..offset + 32);
762 let mut word = [0u8; 32];
763 word.copy_from_slice(&slice);
764 *offset_ptr = EvmWord::from_be_bytes(word);
765 InstructionResult::Stop
766}
767
768#[no_mangle]
769pub unsafe extern "C" fn __revmc_builtin_mstore(
770 ecx: &mut EvmContext<'_>,
771 rev![offset, value]: &mut [EvmWord; 2],
772) -> InstructionResult {
773 let offset = try_into_usize!(offset);
774 ensure_memory!(ecx, offset, 32);
775 ecx.memory.set(offset, &value.to_be_bytes());
776 InstructionResult::Stop
777}
778
779#[no_mangle]
780pub unsafe extern "C" fn __revmc_builtin_mstore8(
781 ecx: &mut EvmContext<'_>,
782 rev![offset, value]: &mut [EvmWord; 2],
783) -> InstructionResult {
784 let offset = try_into_usize!(offset);
785 ensure_memory!(ecx, offset, 1);
786 let byte = value.to_be_bytes()[31];
787 ecx.memory.set(offset, &[byte]);
788 InstructionResult::Stop
789}