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 CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, FrameInput,
16 InstructionResult, InterpreterAction, InterpreterResult, as_u64_saturated, as_usize_saturated,
17 interpreter_types::{InputsTr, MemoryTr},
18};
19use revm_primitives::{B256, Bytes, KECCAK_EMPTY, Log, LogData, U256, hardfork::SpecId};
20use revmc_context::{EvmContext, EvmWord};
21
22pub mod gas;
23
24#[cfg(feature = "ir")]
25mod ir;
26#[cfg(feature = "ir")]
27pub use ir::*;
28
29#[macro_use]
30mod macros;
31
32mod utils;
33use utils::*;
34pub use utils::{BuiltinError, BuiltinResult};
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#[inline(always)]
78#[cold]
79fn fail(ecx: &mut EvmContext<'_>, e: BuiltinError) -> ! {
80 ecx.exit_result = e.into();
81 unsafe { revmc_context::revmc_exit(ecx) }
82}
83
84macro_rules! builtins {
85 () => {};
86
87 ($(#[$attr:meta])* pub unsafe extern "C" fn $name:ident($ecx:ident : &mut EvmContext<'_> $(, $rest_i:ident : $rest_t:ty)* $(,)?) -> BuiltinResult $block:block $($more:tt)*) => {
89 $(#[$attr])*
90 pub unsafe extern "C" fn $name($ecx: &mut EvmContext<'_> $(, $rest_i : $rest_t)*) {
91 #[inline(always)]
92 unsafe fn imp($ecx: &mut EvmContext<'_> $(, $rest_i : $rest_t)*) -> BuiltinResult $block
93 match unsafe { imp($ecx $(, $rest_i)*) } {
94 Ok(()) => {}
95 Err(e) => fail($ecx, e),
96 }
97 }
98
99 builtins! { $($more)* }
100 };
101
102 ($item:item $($rest:tt)*) => {
104 $item
105 builtins! { $($rest)* }
106 };
107}
108
109builtins! {
110
111#[unsafe(no_mangle)]
112pub unsafe extern "C" fn __revmc_builtin_panic(data: *const u8, len: usize) -> ! {
113 let msg = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(data, len)) };
114 panic!("{msg}");
115}
116
117#[unsafe(no_mangle)]
118pub unsafe extern "C" fn __revmc_builtin_assert_spec_id(ecx: &EvmContext<'_>, expected: SpecId) {
119 assert_eq!(
120 ecx.spec_id, expected,
121 "revmc panic: runtime spec_id does not match compilation spec_id"
122 );
123}
124
125#[unsafe(no_mangle)]
126pub unsafe extern "C" fn __revmc_builtin_div(rev![a, b]: &mut [EvmWord; 2]) {
127 let divisor = b.to_u256();
128 *b = if divisor.is_zero() { U256::ZERO } else { a.to_u256().wrapping_div(divisor) }.into();
129}
130
131#[unsafe(no_mangle)]
132pub unsafe extern "C" fn __revmc_builtin_sdiv(rev![a, b]: &mut [EvmWord; 2]) {
133 *b = revm_interpreter::instructions::i256::i256_div(a.to_u256(), b.to_u256()).into();
134}
135
136#[unsafe(no_mangle)]
137pub unsafe extern "C" fn __revmc_builtin_mod(rev![a, b]: &mut [EvmWord; 2]) {
138 let divisor = b.to_u256();
139 *b = if divisor.is_zero() { U256::ZERO } else { a.to_u256().wrapping_rem(divisor) }.into();
140}
141
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn __revmc_builtin_smod(rev![a, b]: &mut [EvmWord; 2]) {
144 *b = revm_interpreter::instructions::i256::i256_mod(a.to_u256(), b.to_u256()).into();
145}
146
147#[unsafe(no_mangle)]
148pub unsafe extern "C" fn __revmc_builtin_addmod(rev![a, b, c]: &mut [EvmWord; 3]) {
149 *c = a.to_u256().add_mod(b.to_u256(), c.to_u256()).into();
150}
151
152#[unsafe(no_mangle)]
153pub unsafe extern "C" fn __revmc_builtin_mulmod(rev![a, b, c]: &mut [EvmWord; 3]) {
154 *c = a.to_u256().mul_mod(b.to_u256(), c.to_u256()).into();
155}
156
157#[unsafe(no_mangle)]
158pub unsafe extern "C" fn __revmc_builtin_exp(
159 ecx: &mut EvmContext<'_>,
160 sp: &mut [EvmWord; 2],
161) -> BuiltinResult {
162 let rev![base, exponent_ptr] = sp;
163 let exponent = exponent_ptr.to_u256();
164 gas!(ecx, ecx.gas_params.exp_cost(exponent));
165 *exponent_ptr = base.to_u256().pow(exponent).into();
166 Ok(())
167}
168
169#[unsafe(no_mangle)]
170pub unsafe extern "C" fn __revmc_builtin_exp_gas(
171 ecx: &mut EvmContext<'_>,
172 exponent: &EvmWord,
173) -> BuiltinResult {
174 gas!(ecx, ecx.gas_params.exp_cost(exponent.to_u256()));
175 Ok(())
176}
177
178#[unsafe(no_mangle)]
179pub unsafe extern "C" fn __revmc_builtin_keccak256(
180 ecx: &mut EvmContext<'_>,
181 sp: &mut [EvmWord; 2],
182) -> BuiltinResult {
183 let rev![offset, len_ptr] = sp;
184 let len = try_into_usize!(len_ptr);
185 do_keccak256(ecx, len_ptr, *offset, len)
186}
187
188#[unsafe(no_mangle)]
189pub unsafe extern "C" fn __revmc_builtin_keccak256_cc(
190 ecx: &mut EvmContext<'_>,
191 out: &mut EvmWord,
192 offset: u64,
193 len: u64,
194) -> BuiltinResult {
195 do_keccak256(ecx, out, U256::from(offset).into(), len as usize)
196}
197
198fn do_keccak256(
199 ecx: &mut EvmContext<'_>,
200 out: &mut EvmWord,
201 offset: EvmWord,
202 len: usize,
203) -> BuiltinResult {
204 *out = EvmWord::from_be_bytes(if len == 0 {
205 KECCAK_EMPTY
206 } else {
207 gas!(ecx, ecx.gas_params.keccak256_cost(len));
208 let offset = try_into_usize!(offset);
209 ensure_memory(ecx, offset, len)?;
210 let data = ecx.memory.slice(offset..offset + len);
211 revm_primitives::keccak256(&*data)
212 });
213 Ok(())
214}
215
216#[unsafe(no_mangle)]
217pub unsafe extern "C" fn __revmc_builtin_balance(
218 ecx: &mut EvmContext<'_>,
219 address: &mut EvmWord,
220) -> BuiltinResult {
221 let addr = address.to_address();
222 let account = load_account(ecx, addr, false)?;
223 *address = account.balance.into();
224 Ok(())
225}
226
227#[unsafe(no_mangle)]
228pub unsafe extern "C" fn __revmc_builtin_origin(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
229 *slot = EvmWord::from_be_bytes(ecx.host.caller().into_word());
230}
231
232#[unsafe(no_mangle)]
233pub unsafe extern "C" fn __revmc_builtin_calldataload(
234 ecx: &EvmContext<'_>,
235 offset_ptr: &mut EvmWord,
236) {
237 do_calldataload(ecx, offset_ptr, as_usize_saturated!(offset_ptr.to_u256()));
238}
239
240#[unsafe(no_mangle)]
241pub unsafe extern "C" fn __revmc_builtin_calldataload_c(
242 ecx: &EvmContext<'_>,
243 offset_ptr: &mut EvmWord,
244 offset: u64,
245) {
246 do_calldataload(ecx, offset_ptr, offset as usize);
247}
248
249fn do_calldataload(ecx: &EvmContext<'_>, out: &mut EvmWord, offset: usize) {
250 let mut word = B256::ZERO;
251 let input = ecx.input.input();
252 let input_len = input.len();
253 if offset < input_len {
254 let count = 32.min(input_len - offset);
255 let input = ecx.input.input().as_bytes_memory(ecx.memory);
256 unsafe {
261 core::ptr::copy_nonoverlapping(input.as_ptr().add(offset), word.as_mut_ptr(), count)
262 };
263 }
264 *out = EvmWord::from_be_bytes(word);
265}
266
267#[unsafe(no_mangle)]
268pub unsafe extern "C" fn __revmc_builtin_calldatacopy(
269 ecx: &mut EvmContext<'_>,
270 sp: &mut [EvmWord; 3],
271) -> BuiltinResult {
272 let rev![memory_offset, data_offset, len] = sp;
273 let len = try_into_usize!(len);
274 if len != 0 {
275 gas!(ecx, ecx.gas_params.copy_cost(len));
276 let memory_offset = try_into_usize!(memory_offset);
277 ensure_memory(ecx, memory_offset, len)?;
278 let data_offset = as_usize_saturated!(data_offset.to_u256());
279
280 match ecx.input.input() {
281 CallInput::Bytes(bytes) => {
282 ecx.memory.set_data(memory_offset, data_offset, len, bytes.as_ref());
283 }
284 CallInput::SharedBuffer(range) => {
285 ecx.memory.set_data_from_global(memory_offset, data_offset, len, range.clone());
286 }
287 }
288 }
289 Ok(())
290}
291
292#[unsafe(no_mangle)]
293pub unsafe extern "C" fn __revmc_builtin_codecopy(
294 ecx: &mut EvmContext<'_>,
295 sp: &mut [EvmWord; 3],
296) -> BuiltinResult {
297 let bytecode = unsafe { &*ecx.bytecode };
298 copy_operation(ecx, sp, bytecode)
299}
300
301#[unsafe(no_mangle)]
302pub unsafe extern "C" fn __revmc_builtin_gas_price(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
303 *slot = ecx.host.effective_gas_price().into();
304}
305
306#[unsafe(no_mangle)]
307pub unsafe extern "C" fn __revmc_builtin_extcodesize(
308 ecx: &mut EvmContext<'_>,
309 address: &mut EvmWord,
310) -> BuiltinResult {
311 let addr = address.to_address();
312 let account = load_account(ecx, addr, true)?;
313 *address = U256::from(account.code.as_ref().unwrap().len()).into();
314 Ok(())
315}
316
317#[unsafe(no_mangle)]
318pub unsafe extern "C" fn __revmc_builtin_extcodecopy(
319 ecx: &mut EvmContext<'_>,
320 sp: &mut [EvmWord; 4],
321) -> BuiltinResult {
322 let rev![address, memory_offset, code_offset, len] = sp;
323 let addr = address.to_address();
324 let len = try_into_usize!(len);
325 gas!(ecx, ecx.gas_params.extcodecopy(len));
326
327 let mut memory_offset_usize = 0;
328 if len != 0 {
329 memory_offset_usize = try_into_usize!(memory_offset);
330 ensure_memory(ecx, memory_offset_usize, len)?;
331 }
332
333 let account = load_account(ecx, addr, true)?;
334 let code = account.code.as_ref().unwrap().original_bytes();
335
336 let code_offset_usize = core::cmp::min(as_usize_saturated!(code_offset.to_u256()), code.len());
337 ecx.memory.set_data(memory_offset_usize, code_offset_usize, len, &code);
338 Ok(())
339}
340
341#[unsafe(no_mangle)]
342pub unsafe extern "C" fn __revmc_builtin_returndatacopy(
343 ecx: &mut EvmContext<'_>,
344 sp: &mut [EvmWord; 3],
345) -> BuiltinResult {
346 let rev![memory_offset, offset, len] = sp;
347 let len = try_into_usize!(len);
348 let data_offset = as_usize_saturated!(offset.to_u256());
349
350 let data_end = data_offset.saturating_add(len);
352 if data_end > ecx.return_data.len() {
353 return Err(InstructionResult::OutOfOffset.into());
354 }
355
356 gas!(ecx, ecx.gas_params.copy_cost(len));
357 if len != 0 {
358 let memory_offset = try_into_usize!(memory_offset);
359 ensure_memory(ecx, memory_offset, len)?;
360 ecx.memory.set(memory_offset, &ecx.return_data[data_offset..data_end]);
361 }
362 Ok(())
363}
364
365#[unsafe(no_mangle)]
366pub unsafe extern "C" fn __revmc_builtin_extcodehash(
367 ecx: &mut EvmContext<'_>,
368 address: &mut EvmWord,
369) -> BuiltinResult {
370 let addr = address.to_address();
371 let account = load_account(ecx, addr, false)?;
372 let code_hash = if account.is_empty { revm_primitives::B256::ZERO } else { account.code_hash };
373 *address = EvmWord::from_be_bytes(code_hash);
374 Ok(())
375}
376
377#[unsafe(no_mangle)]
378pub unsafe extern "C" fn __revmc_builtin_blockhash(
379 ecx: &mut EvmContext<'_>,
380 number_ptr: &mut EvmWord,
381) -> BuiltinResult {
382 let requested_number = number_ptr.to_u256();
383 let block_number = ecx.host.block_number();
384
385 let Some(diff) = block_number.checked_sub(requested_number) else {
387 *number_ptr = EvmWord::ZERO;
388 return Ok(());
389 };
390
391 let diff = as_u64_saturated!(diff);
392
393 if diff == 0 {
395 *number_ptr = EvmWord::ZERO;
396 return Ok(());
397 }
398
399 const BLOCK_HASH_HISTORY: u64 = 256;
401
402 if diff <= BLOCK_HASH_HISTORY {
403 let hash = ecx.host.block_hash(as_u64_saturated!(requested_number)).ok_or_fatal()?;
404 *number_ptr = EvmWord::from_be_bytes(hash);
405 } else {
406 *number_ptr = EvmWord::ZERO;
408 }
409
410 Ok(())
411}
412
413#[unsafe(no_mangle)]
414pub unsafe extern "C" fn __revmc_builtin_coinbase(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
415 *slot = EvmWord::from_be_bytes(ecx.host.beneficiary().into_word());
416}
417
418#[unsafe(no_mangle)]
419pub unsafe extern "C" fn __revmc_builtin_timestamp(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
420 *slot = ecx.host.timestamp().into();
421}
422
423#[unsafe(no_mangle)]
424pub unsafe extern "C" fn __revmc_builtin_number(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
425 *slot = ecx.host.block_number().into();
426}
427
428#[unsafe(no_mangle)]
429pub unsafe extern "C" fn __revmc_builtin_difficulty(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
430 *slot = if ecx.spec_id.is_enabled_in(SpecId::MERGE) {
431 ecx.host.prevrandao().unwrap().into()
432 } else {
433 ecx.host.difficulty().into()
434 };
435}
436
437#[unsafe(no_mangle)]
438pub unsafe extern "C" fn __revmc_builtin_gaslimit(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
439 *slot = ecx.host.gas_limit().into();
440}
441
442#[unsafe(no_mangle)]
443pub unsafe extern "C" fn __revmc_builtin_chainid(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
444 *slot = ecx.host.chain_id().into();
445}
446
447#[unsafe(no_mangle)]
448pub unsafe extern "C" fn __revmc_builtin_self_balance(
449 ecx: &mut EvmContext<'_>,
450 slot: &mut EvmWord,
451) -> BuiltinResult {
452 let state = ecx.host.balance(ecx.input.target_address).ok_or_fatal()?;
453 *slot = state.data.into();
454 Ok(())
455}
456
457#[unsafe(no_mangle)]
458pub unsafe extern "C" fn __revmc_builtin_basefee(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
459 *slot = ecx.host.basefee().into();
460}
461
462#[unsafe(no_mangle)]
463pub unsafe extern "C" fn __revmc_builtin_blob_hash(ecx: &EvmContext<'_>, index_ptr: &mut EvmWord) {
464 let index = index_ptr.to_u256();
465 let index_usize = as_usize_saturated!(index);
466 *index_ptr = ecx.host.blob_hash(index_usize).unwrap_or_default().into();
467}
468
469#[unsafe(no_mangle)]
470pub unsafe extern "C" fn __revmc_builtin_blob_base_fee(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
471 *slot = ecx.host.blob_gasprice().into();
472}
473
474#[unsafe(no_mangle)]
475pub unsafe extern "C" fn __revmc_builtin_mresize(ecx: &mut EvmContext<'_>, min_size: u64) -> BuiltinResult {
476 ensure_memory(ecx, min_size as usize, 0)
477}
478
479#[unsafe(no_mangle)]
480pub unsafe extern "C" fn __revmc_builtin_slot_num(ecx: &EvmContext<'_>, slot: &mut EvmWord) {
481 *slot = ecx.host.slot_num().into();
482}
483
484#[unsafe(no_mangle)]
485pub unsafe extern "C" fn __revmc_builtin_sload(
486 ecx: &mut EvmContext<'_>,
487 index: &mut EvmWord,
488) -> BuiltinResult {
489 let address = ecx.input.target_address;
490 let key = index.to_u256();
491 if ecx.spec_id.is_enabled_in(SpecId::BERLIN) {
492 let additional_cold_cost = ecx.gas_params.cold_storage_additional_cost();
493 let skip_cold = ecx.gas.remaining() < additional_cold_cost;
494 let storage = ecx.host.sload_skip_cold_load(address, key, skip_cold)?;
495 if storage.is_cold {
496 gas!(ecx, additional_cold_cost);
497 }
498 *index = storage.data.into();
499 } else {
500 let storage = ecx.host.sload(address, key).ok_or_fatal()?;
501 *index = storage.data.into();
502 }
503
504 Ok(())
505}
506
507#[unsafe(no_mangle)]
508pub unsafe extern "C" fn __revmc_builtin_sload_c(
509 ecx: &mut EvmContext<'_>,
510 index: &mut EvmWord,
511 key: u64,
512) -> BuiltinResult {
513 *index = U256::from(key).into();
514 __revmc_builtin_sload(ecx, index);
515 Ok(())
516}
517
518#[unsafe(no_mangle)]
519pub unsafe extern "C" fn __revmc_builtin_sstore(
520 ecx: &mut EvmContext<'_>,
521 sp: &mut [EvmWord; 2],
522) -> BuiltinResult {
523 let rev![index, value] = sp;
524 ensure_non_staticcall!(ecx);
525
526 let target = ecx.input.target_address;
527 let is_istanbul = ecx.spec_id.is_enabled_in(SpecId::ISTANBUL);
528
529 if is_istanbul && ecx.gas.remaining() <= ecx.gas_params.call_stipend() {
531 return Err(InstructionResult::ReentrancySentryOOG.into());
532 }
533
534 gas!(ecx, ecx.gas_params.sstore_static_gas());
535
536 let state_load = if ecx.spec_id.is_enabled_in(SpecId::BERLIN) {
537 let additional_cold_cost = ecx.gas_params.cold_storage_additional_cost();
538 let skip_cold = ecx.gas.remaining() < additional_cold_cost;
539 ecx.host.sstore_skip_cold_load(target, index.to_u256(), value.to_u256(), skip_cold)?
540 } else {
541 ecx.host.sstore(target, index.to_u256(), value.to_u256()).ok_or_fatal()?
542 };
543
544 let gp = &ecx.gas_params;
545 gas!(ecx, gp.sstore_dynamic_gas(is_istanbul, &state_load.data, state_load.is_cold));
546
547 if ecx.host.is_amsterdam_eip8037_enabled() {
549 state_gas!(ecx, gp.sstore_state_gas(&state_load.data));
550
551 let refill = gp.sstore_state_gas_refill(&state_load.data);
552 if refill > 0 {
553 ecx.gas.refill_reservoir(refill);
554 }
555 }
556
557 ecx.gas.record_refund(gp.sstore_refund(is_istanbul, &state_load.data));
558 Ok(())
559}
560
561#[unsafe(no_mangle)]
562pub unsafe extern "C" fn __revmc_builtin_tload(ecx: &mut EvmContext<'_>, key: &mut EvmWord) {
563 *key = ecx.host.tload(ecx.input.target_address, key.to_u256()).into();
564}
565
566#[unsafe(no_mangle)]
567pub unsafe extern "C" fn __revmc_builtin_tstore(
568 ecx: &mut EvmContext<'_>,
569 sp: &mut [EvmWord; 2],
570) -> BuiltinResult {
571 let rev![key, value] = sp;
572 ensure_non_staticcall!(ecx);
573 ecx.host.tstore(ecx.input.target_address, key.to_u256(), value.to_u256());
574 Ok(())
575}
576
577#[unsafe(no_mangle)]
578pub unsafe extern "C" fn __revmc_builtin_mcopy(
579 ecx: &mut EvmContext<'_>,
580 sp: &mut [EvmWord; 3],
581) -> BuiltinResult {
582 let rev![dst, src, len] = sp;
583 let len = try_into_usize!(len);
584 gas!(ecx, ecx.gas_params.mcopy_cost(len));
585 if len != 0 {
586 let dst = try_into_usize!(dst);
587 let src = try_into_usize!(src);
588 ensure_memory(ecx, dst.max(src), len)?;
589 ecx.memory.copy(dst, src, len);
590 }
591 Ok(())
592}
593
594#[unsafe(no_mangle)]
595pub unsafe extern "C" fn __revmc_builtin_log(
596 ecx: &mut EvmContext<'_>,
597 sp: *mut EvmWord,
598 n: u8,
599) -> BuiltinResult {
600 ensure_non_staticcall!(ecx);
601 assume!(n <= 4, "invalid log topic count: {n}");
602 let sp = sp.add(n as usize);
603 read_words!(sp, offset, len);
604 let len = try_into_usize!(len);
605 gas!(ecx, ecx.gas_params.log_cost(n, len as u64));
606 let data = if len != 0 {
607 let offset = try_into_usize!(offset);
608 ensure_memory(ecx, offset, len)?;
609 Bytes::copy_from_slice(&ecx.memory.slice(offset..offset + len))
610 } else {
611 Bytes::new()
612 };
613
614 let mut topics = Vec::with_capacity(n as usize);
615 for i in 1..=n {
616 topics.push(sp.sub(i as usize).read().to_be_bytes());
617 }
618
619 let log = Log {
620 address: ecx.input.target_address,
621 data: LogData::new(topics, data).expect("too many topics"),
622 };
623 if let Some(on_log) = &mut ecx.on_log {
624 ecx.host.log(log.clone());
625 on_log(&log);
626 } else {
627 ecx.host.log(log);
628 }
629 Ok(())
630}
631
632#[unsafe(no_mangle)]
633pub unsafe extern "C" fn __revmc_builtin_create(
634 ecx: &mut EvmContext<'_>,
635 sp: *mut EvmWord,
636 create_kind: CreateKind,
637) -> BuiltinResult {
638 ensure_non_staticcall!(ecx);
639
640 let len = match create_kind {
641 CreateKind::Create => 3,
642 CreateKind::Create2 => 4,
643 };
644 let mut sp = sp.add(len);
645 pop!(sp; value, code_offset, len);
646
647 let len = try_into_usize!(len);
648 let code = if len != 0 {
649 if ecx.spec_id.is_enabled_in(SpecId::SHANGHAI) {
650 let max_initcode_size = ecx.host.max_initcode_size();
652 if len > max_initcode_size {
653 return Err(InstructionResult::CreateInitCodeSizeLimit.into());
654 }
655 gas!(ecx, ecx.gas_params.initcode_cost(len));
656 }
657
658 let code_offset = try_into_usize!(code_offset);
659 ensure_memory(ecx, code_offset, len)?;
660 Bytes::copy_from_slice(&ecx.memory.slice(code_offset..code_offset + len))
661 } else {
662 Bytes::new()
663 };
664
665 let is_create2 = create_kind == CreateKind::Create2;
666 let gp = &ecx.gas_params;
667 gas!(ecx, if is_create2 { gp.create2_cost(len) } else { gp.create_cost() });
668
669 let scheme = if is_create2 {
670 pop!(sp; salt);
671 CreateScheme::Create2 { salt: salt.to_u256() }
672 } else {
673 CreateScheme::Create
674 };
675
676 if ecx.host.is_amsterdam_eip8037_enabled() {
678 state_gas!(ecx, ecx.gas_params.create_state_gas());
679 }
680
681 let mut gas_limit = ecx.gas.remaining();
682 if ecx.spec_id.is_enabled_in(SpecId::TANGERINE) {
683 gas_limit = ecx.gas_params.call_stipend_reduction(gas_limit);
684 }
685 gas!(ecx, gas_limit);
686
687 *ecx.next_action =
688 Some(InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs::new(
689 ecx.input.target_address,
690 scheme,
691 value.to_u256(),
692 code,
693 gas_limit,
694 ecx.gas.reservoir(),
695 )))));
696
697 Ok(())
698}
699
700#[unsafe(no_mangle)]
701pub unsafe extern "C" fn __revmc_builtin_call(
702 ecx: &mut EvmContext<'_>,
703 sp: *mut EvmWord,
704 call_kind: CallKind,
705) -> BuiltinResult {
706 let len = match call_kind {
707 CallKind::Call | CallKind::CallCode => 7,
708 CallKind::DelegateCall | CallKind::StaticCall => 6,
709 };
710 let mut sp = sp.add(len);
711
712 pop!(sp; local_gas_limit, to);
713 let local_gas_limit = local_gas_limit.to_u256();
714 let to = to.to_address();
715
716 let local_gas_limit = as_u64_saturated!(local_gas_limit);
720
721 let value = match call_kind {
722 CallKind::Call | CallKind::CallCode => {
723 pop!(sp; value);
724 let value = value.to_u256();
725 if call_kind == CallKind::Call && ecx.is_static && value != U256::ZERO {
726 return Err(InstructionResult::CallNotAllowedInsideStatic.into());
727 }
728 value
729 }
730 CallKind::DelegateCall | CallKind::StaticCall => U256::ZERO,
731 };
732 let transfers_value = value != U256::ZERO;
733
734 pop!(sp; in_offset, in_len, out_offset, out_len);
735
736 let in_len = try_into_usize!(in_len);
737 let input = if in_len != 0 {
738 let in_offset = try_into_usize!(in_offset);
739 ensure_memory(ecx, in_offset, in_len)?;
740 let local_offset = ecx.memory.local_memory_offset();
741 let start = in_offset.saturating_add(local_offset);
742 start..start.saturating_add(in_len)
743 } else {
744 usize::MAX..usize::MAX
745 };
746
747 let out_len = try_into_usize!(out_len);
748 let out_offset = if out_len != 0 {
749 let out_offset = try_into_usize!(out_offset);
750 ensure_memory(ecx, out_offset, out_len)?;
751 out_offset
752 } else {
753 usize::MAX };
755
756 if transfers_value {
757 gas!(ecx, ecx.gas_params.transfer_value_cost());
758 }
759
760 let (dynamic_gas, state_gas_cost, bytecode, code_hash) =
763 revm_interpreter::instructions::contract::load_account_delegated(
764 ecx.host,
765 ecx.spec_id,
766 ecx.gas.remaining(),
767 to,
768 transfers_value,
769 call_kind == CallKind::Call,
770 )?;
771 let charged_new_account_state_gas = state_gas_cost > 0;
772
773 gas!(ecx, dynamic_gas);
774
775 state_gas!(ecx, state_gas_cost);
777
778 let mut gas_limit = if ecx.spec_id.is_enabled_in(SpecId::TANGERINE) {
780 let gas = ecx.gas.remaining();
781 ecx.gas_params.call_stipend_reduction(gas).min(local_gas_limit)
782 } else {
783 local_gas_limit
784 };
785
786 gas!(ecx, gas_limit);
787
788 if matches!(call_kind, CallKind::Call | CallKind::CallCode) && transfers_value {
790 gas_limit = gas_limit.saturating_add(ecx.gas_params.call_stipend());
791 }
792
793 *ecx.next_action = Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Call(
794 Box::new(CallInputs {
795 input: CallInput::SharedBuffer(input),
796 return_memory_offset: out_offset..out_offset + out_len,
797 gas_limit,
798 bytecode_address: to,
799 known_bytecode: (code_hash, bytecode),
800 target_address: if matches!(call_kind, CallKind::DelegateCall | CallKind::CallCode) {
801 ecx.input.target_address
802 } else {
803 to
804 },
805 caller: if call_kind == CallKind::DelegateCall {
806 ecx.input.caller_address
807 } else {
808 ecx.input.target_address
809 },
810 value: if call_kind == CallKind::DelegateCall {
811 CallValue::Apparent(ecx.input.call_value)
812 } else {
813 CallValue::Transfer(value)
814 },
815 scheme: call_kind.into(),
816 is_static: ecx.is_static || call_kind == CallKind::StaticCall,
817 reservoir: ecx.gas.reservoir(),
818 charged_new_account_state_gas,
819 }),
820 )));
821
822 Ok(())
823}
824
825#[unsafe(no_mangle)]
826pub unsafe extern "C" fn __revmc_builtin_do_return(
827 ecx: &mut EvmContext<'_>,
828 sp: &mut [EvmWord; 2],
829 result: InstructionResult,
830) -> BuiltinResult {
831 let rev![offset, len] = sp;
832 let len = try_into_usize!(len);
833 let output = if len != 0 {
834 let offset = try_into_usize!(offset);
835 ensure_memory(ecx, offset, len)?;
836 ecx.memory.slice(offset..offset + len).to_vec().into()
837 } else {
838 Bytes::new()
839 };
840 *ecx.next_action =
841 Some(InterpreterAction::Return(InterpreterResult { output, gas: ecx.gas, result }));
842 Err(result.into())
843}
844
845#[unsafe(no_mangle)]
846pub unsafe extern "C" fn __revmc_builtin_do_return_cc(
847 ecx: &mut EvmContext<'_>,
848 offset: u64,
849 len: u64,
850 result: InstructionResult,
851) -> BuiltinResult {
852 let offset = offset as usize;
853 let len = len as usize;
854 let output = if len != 0 {
855 ensure_memory(ecx, offset, len)?;
856 ecx.memory.slice(offset..offset + len).to_vec().into()
857 } else {
858 Bytes::new()
859 };
860 *ecx.next_action =
861 Some(InterpreterAction::Return(InterpreterResult { output, gas: ecx.gas, result }));
862 Err(result.into())
863}
864
865#[unsafe(no_mangle)]
866pub unsafe extern "C" fn __revmc_builtin_selfdestruct(
867 ecx: &mut EvmContext<'_>,
868 target: &mut EvmWord,
869) -> BuiltinResult {
870 ensure_non_staticcall!(ecx);
871
872 let cold_load_gas = ecx.gas_params.selfdestruct_cold_cost();
873 let skip_cold_load = ecx.gas.remaining() < cold_load_gas;
874 let res =
875 ecx.host.selfdestruct(ecx.input.target_address, target.to_address(), skip_cold_load)?;
876
877 let should_charge_topup = if ecx.spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
879 res.had_value && !res.target_exists
880 } else {
881 !res.target_exists
882 };
883
884 gas!(ecx, ecx.gas_params.selfdestruct_cost(should_charge_topup, res.is_cold));
885
886 if ecx.host.is_amsterdam_eip8037_enabled() && should_charge_topup {
888 state_gas!(ecx, ecx.gas_params.new_account_state_gas());
889 }
890
891 if !res.previously_destroyed {
892 ecx.gas.record_refund(ecx.gas_params.selfdestruct_refund());
893 }
894
895 Err(InstructionResult::SelfDestruct.into())
896}
897
898}