revm_interpreter/interpreter_action/call_inputs.rs
1use crate::primitives::{Address, Bytes, TxEnv, TxKind, U256};
2use core::ops::Range;
3use std::boxed::Box;
4
5/// Inputs for a call.
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub struct CallInputs {
9 /// The call data of the call.
10 pub input: Bytes,
11 /// The return memory offset where the output of the call is written.
12 ///
13 /// In EOF, this range is invalid as EOF calls do not write output to memory.
14 pub return_memory_offset: Range<usize>,
15 /// The gas limit of the call.
16 pub gas_limit: u64,
17 /// The account address of bytecode that is going to be executed.
18 ///
19 /// Previously `context.code_address`.
20 pub bytecode_address: Address,
21 /// Target address, this account storage is going to be modified.
22 ///
23 /// Previously `context.address`.
24 pub target_address: Address,
25 /// This caller is invoking the call.
26 ///
27 /// Previously `context.caller`.
28 pub caller: Address,
29 /// Call value.
30 ///
31 /// NOTE: This value may not necessarily be transferred from caller to callee, see [`CallValue`].
32 ///
33 /// Previously `transfer.value` or `context.apparent_value`.
34 pub value: CallValue,
35 /// The call scheme.
36 ///
37 /// Previously `context.scheme`.
38 pub scheme: CallScheme,
39 /// Whether the call is a static call, or is initiated inside a static call.
40 pub is_static: bool,
41 /// Whether the call is initiated from EOF bytecode.
42 pub is_eof: bool,
43}
44
45impl CallInputs {
46 /// Creates new call inputs.
47 ///
48 /// Returns `None` if the transaction is not a call.
49 pub fn new(tx_env: &TxEnv, gas_limit: u64) -> Option<Self> {
50 let TxKind::Call(target_address) = tx_env.transact_to else {
51 return None;
52 };
53 Some(CallInputs {
54 input: tx_env.data.clone(),
55 gas_limit,
56 target_address,
57 bytecode_address: target_address,
58 caller: tx_env.caller,
59 value: CallValue::Transfer(tx_env.value),
60 scheme: CallScheme::Call,
61 is_static: false,
62 is_eof: false,
63 return_memory_offset: 0..0,
64 })
65 }
66
67 /// Creates new boxed call inputs.
68 ///
69 /// Returns `None` if the transaction is not a call.
70 pub fn new_boxed(tx_env: &TxEnv, gas_limit: u64) -> Option<Box<Self>> {
71 Self::new(tx_env, gas_limit).map(Box::new)
72 }
73
74 /// Returns `true` if the call will transfer a non-zero value.
75 #[inline]
76 pub fn transfers_value(&self) -> bool {
77 self.value.transfer().is_some_and(|x| x > U256::ZERO)
78 }
79
80 /// Returns the transfer value.
81 ///
82 /// This is the value that is transferred from caller to callee, see [`CallValue`].
83 #[inline]
84 pub const fn transfer_value(&self) -> Option<U256> {
85 self.value.transfer()
86 }
87
88 /// Returns the **apparent** call value.
89 ///
90 /// This value is not actually transferred, see [`CallValue`].
91 #[inline]
92 pub const fn apparent_value(&self) -> Option<U256> {
93 self.value.apparent()
94 }
95
96 /// Returns the address of the transfer source account.
97 ///
98 /// This is only meaningful if `transfers_value` is `true`.
99 #[inline]
100 pub const fn transfer_from(&self) -> Address {
101 self.caller
102 }
103
104 /// Returns the address of the transfer target account.
105 ///
106 /// This is only meaningful if `transfers_value` is `true`.
107 #[inline]
108 pub const fn transfer_to(&self) -> Address {
109 self.target_address
110 }
111
112 /// Returns the call value, regardless of the transfer value type.
113 ///
114 /// NOTE: this value may not necessarily be transferred from caller to callee, see [`CallValue`].
115 #[inline]
116 pub const fn call_value(&self) -> U256 {
117 self.value.get()
118 }
119}
120
121/// Call scheme.
122#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
124pub enum CallScheme {
125 /// `CALL`.
126 Call,
127 /// `CALLCODE`
128 CallCode,
129 /// `DELEGATECALL`
130 DelegateCall,
131 /// `STATICCALL`
132 StaticCall,
133 /// `EXTCALL`
134 ExtCall,
135 /// `EXTSTATICCALL`
136 ExtStaticCall,
137 /// `EXTDELEGATECALL`
138 ExtDelegateCall,
139}
140
141impl CallScheme {
142 /// Returns true if it is EOF EXT*CALL.
143 pub fn is_ext(&self) -> bool {
144 matches!(
145 self,
146 Self::ExtCall | Self::ExtStaticCall | Self::ExtDelegateCall
147 )
148 }
149
150 /// Returns true if it is ExtDelegateCall.
151 pub fn is_ext_delegate_call(&self) -> bool {
152 matches!(self, Self::ExtDelegateCall)
153 }
154}
155
156/// Call value.
157#[derive(Clone, Debug, PartialEq, Eq, Hash)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
159pub enum CallValue {
160 /// Concrete value, transferred from caller to callee at the end of the transaction.
161 Transfer(U256),
162 /// Apparent value, that is **not** actually transferred.
163 ///
164 /// Set when in a `DELEGATECALL` call type, and used by the `CALLVALUE` opcode.
165 Apparent(U256),
166}
167
168impl Default for CallValue {
169 #[inline]
170 fn default() -> Self {
171 CallValue::Transfer(U256::ZERO)
172 }
173}
174
175impl CallValue {
176 /// Returns the call value, regardless of the type.
177 #[inline]
178 pub const fn get(&self) -> U256 {
179 match *self {
180 Self::Transfer(value) | Self::Apparent(value) => value,
181 }
182 }
183
184 /// Returns the transferred value, if any.
185 #[inline]
186 pub const fn transfer(&self) -> Option<U256> {
187 match *self {
188 Self::Transfer(transfer) => Some(transfer),
189 Self::Apparent(_) => None,
190 }
191 }
192
193 /// Returns whether the call value will be transferred.
194 #[inline]
195 pub const fn is_transfer(&self) -> bool {
196 matches!(self, Self::Transfer(_))
197 }
198
199 /// Returns the apparent value, if any.
200 #[inline]
201 pub const fn apparent(&self) -> Option<U256> {
202 match *self {
203 Self::Transfer(_) => None,
204 Self::Apparent(apparent) => Some(apparent),
205 }
206 }
207
208 /// Returns whether the call value is apparent, and not actually transferred.
209 #[inline]
210 pub const fn is_apparent(&self) -> bool {
211 matches!(self, Self::Apparent(_))
212 }
213}