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
552 ecx.gas.record_refund(gp.sstore_refund(is_istanbul, &state_load.data));
553 Ok(())
554}
555
556#[unsafe(no_mangle)]
557pub unsafe extern "C" fn __revmc_builtin_tload(ecx: &mut EvmContext<'_>, key: &mut EvmWord) {
558 *key = ecx.host.tload(ecx.input.target_address, key.to_u256()).into();
559}
560
561#[unsafe(no_mangle)]
562pub unsafe extern "C" fn __revmc_builtin_tstore(
563 ecx: &mut EvmContext<'_>,
564 sp: &mut [EvmWord; 2],
565) -> BuiltinResult {
566 let rev![key, value] = sp;
567 ensure_non_staticcall!(ecx);
568 ecx.host.tstore(ecx.input.target_address, key.to_u256(), value.to_u256());
569 Ok(())
570}
571
572#[unsafe(no_mangle)]
573pub unsafe extern "C" fn __revmc_builtin_mcopy(
574 ecx: &mut EvmContext<'_>,
575 sp: &mut [EvmWord; 3],
576) -> BuiltinResult {
577 let rev![dst, src, len] = sp;
578 let len = try_into_usize!(len);
579 gas!(ecx, ecx.gas_params.mcopy_cost(len));
580 if len != 0 {
581 let dst = try_into_usize!(dst);
582 let src = try_into_usize!(src);
583 ensure_memory(ecx, dst.max(src), len)?;
584 ecx.memory.copy(dst, src, len);
585 }
586 Ok(())
587}
588
589#[unsafe(no_mangle)]
590pub unsafe extern "C" fn __revmc_builtin_log(
591 ecx: &mut EvmContext<'_>,
592 sp: *mut EvmWord,
593 n: u8,
594) -> BuiltinResult {
595 ensure_non_staticcall!(ecx);
596 assume!(n <= 4, "invalid log topic count: {n}");
597 let sp = sp.add(n as usize);
598 read_words!(sp, offset, len);
599 let len = try_into_usize!(len);
600 gas!(ecx, ecx.gas_params.log_cost(n, len as u64));
601 let data = if len != 0 {
602 let offset = try_into_usize!(offset);
603 ensure_memory(ecx, offset, len)?;
604 Bytes::copy_from_slice(&ecx.memory.slice(offset..offset + len))
605 } else {
606 Bytes::new()
607 };
608
609 let mut topics = Vec::with_capacity(n as usize);
610 for i in 1..=n {
611 topics.push(sp.sub(i as usize).read().to_be_bytes());
612 }
613
614 let log = Log {
615 address: ecx.input.target_address,
616 data: LogData::new(topics, data).expect("too many topics"),
617 };
618 if let Some(on_log) = &mut ecx.on_log {
619 ecx.host.log(log.clone());
620 on_log(&log);
621 } else {
622 ecx.host.log(log);
623 }
624 Ok(())
625}
626
627#[unsafe(no_mangle)]
628pub unsafe extern "C" fn __revmc_builtin_create(
629 ecx: &mut EvmContext<'_>,
630 sp: *mut EvmWord,
631 create_kind: CreateKind,
632) -> BuiltinResult {
633 ensure_non_staticcall!(ecx);
634
635 let len = match create_kind {
636 CreateKind::Create => 3,
637 CreateKind::Create2 => 4,
638 };
639 let mut sp = sp.add(len);
640 pop!(sp; value, code_offset, len);
641
642 let len = try_into_usize!(len);
643 let code = if len != 0 {
644 if ecx.spec_id.is_enabled_in(SpecId::SHANGHAI) {
645 let max_initcode_size = ecx.host.max_initcode_size();
647 if len > max_initcode_size {
648 return Err(InstructionResult::CreateInitCodeSizeLimit.into());
649 }
650 gas!(ecx, ecx.gas_params.initcode_cost(len));
651 }
652
653 let code_offset = try_into_usize!(code_offset);
654 ensure_memory(ecx, code_offset, len)?;
655 Bytes::copy_from_slice(&ecx.memory.slice(code_offset..code_offset + len))
656 } else {
657 Bytes::new()
658 };
659
660 let is_create2 = create_kind == CreateKind::Create2;
661 let gp = &ecx.gas_params;
662 gas!(ecx, if is_create2 { gp.create2_cost(len) } else { gp.create_cost() });
663
664 let scheme = if is_create2 {
665 pop!(sp; salt);
666 CreateScheme::Create2 { salt: salt.to_u256() }
667 } else {
668 CreateScheme::Create
669 };
670
671 if ecx.host.is_amsterdam_eip8037_enabled() {
673 state_gas!(ecx, ecx.gas_params.create_state_gas());
674 }
675
676 let mut gas_limit = ecx.gas.remaining();
677 if ecx.spec_id.is_enabled_in(SpecId::TANGERINE) {
678 gas_limit = ecx.gas_params.call_stipend_reduction(gas_limit);
679 }
680 gas!(ecx, gas_limit);
681
682 *ecx.next_action =
683 Some(InterpreterAction::NewFrame(FrameInput::Create(Box::new(CreateInputs::new(
684 ecx.input.target_address,
685 scheme,
686 value.to_u256(),
687 code,
688 gas_limit,
689 ecx.gas.reservoir(),
690 )))));
691
692 Ok(())
693}
694
695#[unsafe(no_mangle)]
696pub unsafe extern "C" fn __revmc_builtin_call(
697 ecx: &mut EvmContext<'_>,
698 sp: *mut EvmWord,
699 call_kind: CallKind,
700) -> BuiltinResult {
701 let len = match call_kind {
702 CallKind::Call | CallKind::CallCode => 7,
703 CallKind::DelegateCall | CallKind::StaticCall => 6,
704 };
705 let mut sp = sp.add(len);
706
707 pop!(sp; local_gas_limit, to);
708 let local_gas_limit = local_gas_limit.to_u256();
709 let to = to.to_address();
710
711 let local_gas_limit = as_u64_saturated!(local_gas_limit);
715
716 let value = match call_kind {
717 CallKind::Call | CallKind::CallCode => {
718 pop!(sp; value);
719 let value = value.to_u256();
720 if call_kind == CallKind::Call && ecx.is_static && value != U256::ZERO {
721 return Err(InstructionResult::CallNotAllowedInsideStatic.into());
722 }
723 value
724 }
725 CallKind::DelegateCall | CallKind::StaticCall => U256::ZERO,
726 };
727 let transfers_value = value != U256::ZERO;
728
729 pop!(sp; in_offset, in_len, out_offset, out_len);
730
731 let in_len = try_into_usize!(in_len);
732 let input = if in_len != 0 {
733 let in_offset = try_into_usize!(in_offset);
734 ensure_memory(ecx, in_offset, in_len)?;
735 let local_offset = ecx.memory.local_memory_offset();
736 let start = in_offset.saturating_add(local_offset);
737 start..start.saturating_add(in_len)
738 } else {
739 usize::MAX..usize::MAX
740 };
741
742 let out_len = try_into_usize!(out_len);
743 let out_offset = if out_len != 0 {
744 let out_offset = try_into_usize!(out_offset);
745 ensure_memory(ecx, out_offset, out_len)?;
746 out_offset
747 } else {
748 usize::MAX };
750
751 if transfers_value {
752 gas!(ecx, ecx.gas_params.transfer_value_cost());
753 }
754
755 let (dynamic_gas, state_gas_cost, bytecode, code_hash) =
758 revm_interpreter::instructions::contract::load_account_delegated(
759 ecx.host,
760 ecx.spec_id,
761 ecx.gas.remaining(),
762 to,
763 transfers_value,
764 call_kind == CallKind::Call,
765 )?;
766
767 gas!(ecx, dynamic_gas);
768
769 state_gas!(ecx, state_gas_cost);
771
772 let mut gas_limit = if ecx.spec_id.is_enabled_in(SpecId::TANGERINE) {
774 let gas = ecx.gas.remaining();
775 ecx.gas_params.call_stipend_reduction(gas).min(local_gas_limit)
776 } else {
777 local_gas_limit
778 };
779
780 gas!(ecx, gas_limit);
781
782 if matches!(call_kind, CallKind::Call | CallKind::CallCode) && transfers_value {
784 gas_limit = gas_limit.saturating_add(ecx.gas_params.call_stipend());
785 }
786
787 *ecx.next_action = Some(InterpreterAction::NewFrame(revm_interpreter::FrameInput::Call(
788 Box::new(CallInputs {
789 input: CallInput::SharedBuffer(input),
790 return_memory_offset: out_offset..out_offset + out_len,
791 gas_limit,
792 bytecode_address: to,
793 known_bytecode: (code_hash, bytecode),
794 target_address: if matches!(call_kind, CallKind::DelegateCall | CallKind::CallCode) {
795 ecx.input.target_address
796 } else {
797 to
798 },
799 caller: if call_kind == CallKind::DelegateCall {
800 ecx.input.caller_address
801 } else {
802 ecx.input.target_address
803 },
804 value: if call_kind == CallKind::DelegateCall {
805 CallValue::Apparent(ecx.input.call_value)
806 } else {
807 CallValue::Transfer(value)
808 },
809 scheme: call_kind.into(),
810 is_static: ecx.is_static || call_kind == CallKind::StaticCall,
811 reservoir: ecx.gas.reservoir(),
812 }),
813 )));
814
815 Ok(())
816}
817
818#[unsafe(no_mangle)]
819pub unsafe extern "C" fn __revmc_builtin_do_return(
820 ecx: &mut EvmContext<'_>,
821 sp: &mut [EvmWord; 2],
822 result: InstructionResult,
823) -> BuiltinResult {
824 let rev![offset, len] = sp;
825 let len = try_into_usize!(len);
826 let output = if len != 0 {
827 let offset = try_into_usize!(offset);
828 ensure_memory(ecx, offset, len)?;
829 ecx.memory.slice(offset..offset + len).to_vec().into()
830 } else {
831 Bytes::new()
832 };
833 *ecx.next_action =
834 Some(InterpreterAction::Return(InterpreterResult { output, gas: ecx.gas, result }));
835 Err(result.into())
836}
837
838#[unsafe(no_mangle)]
839pub unsafe extern "C" fn __revmc_builtin_do_return_cc(
840 ecx: &mut EvmContext<'_>,
841 offset: u64,
842 len: u64,
843 result: InstructionResult,
844) -> BuiltinResult {
845 let offset = offset as usize;
846 let len = len as usize;
847 let output = if len != 0 {
848 ensure_memory(ecx, offset, len)?;
849 ecx.memory.slice(offset..offset + len).to_vec().into()
850 } else {
851 Bytes::new()
852 };
853 *ecx.next_action =
854 Some(InterpreterAction::Return(InterpreterResult { output, gas: ecx.gas, result }));
855 Err(result.into())
856}
857
858#[unsafe(no_mangle)]
859pub unsafe extern "C" fn __revmc_builtin_selfdestruct(
860 ecx: &mut EvmContext<'_>,
861 target: &mut EvmWord,
862) -> BuiltinResult {
863 ensure_non_staticcall!(ecx);
864
865 let cold_load_gas = ecx.gas_params.selfdestruct_cold_cost();
866 let skip_cold_load = ecx.gas.remaining() < cold_load_gas;
867 let res =
868 ecx.host.selfdestruct(ecx.input.target_address, target.to_address(), skip_cold_load)?;
869
870 let should_charge_topup = if ecx.spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) {
872 res.had_value && !res.target_exists
873 } else {
874 !res.target_exists
875 };
876
877 gas!(ecx, ecx.gas_params.selfdestruct_cost(should_charge_topup, res.is_cold));
878
879 if ecx.host.is_amsterdam_eip8037_enabled() && should_charge_topup {
881 state_gas!(ecx, ecx.gas_params.new_account_state_gas());
882 }
883
884 if !res.previously_destroyed {
885 ecx.gas.record_refund(ecx.gas_params.selfdestruct_refund());
886 }
887
888 Err(InstructionResult::SelfDestruct.into())
889}
890
891}