1use yevm_base::math::lift;
2use yevm_base::{Acc, Int};
3use yevm_misc::keccak256;
4
5use crate::{
6 Call,
7 evm::{self, CallMode, Context, Evm, EvmResult, EvmYield, Fetch, HaltReason},
8 misc::{create_address, create2_address, is_precompile},
9 state::State,
10};
11
12fn sub_call_gas(evm: &Evm) -> u64 {
14 let remaining = evm.gas_remaining().max(0) as u64;
15 remaining - remaining / 64
16}
17
18pub fn create<S: State>(evm: &mut Evm, ctx: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
19 if ctx.is_static {
20 return Err(EvmYield::Halt(HaltReason::NonStatic));
21 }
22 evm.gas_charge(32_000)?;
23 let [value, offset, size] = evm.peek()?;
24 evm::mem_check_int(offset, size)?;
25 let (offset, size) = (offset.as_usize(), size.as_usize());
26 let initcode_cost = 2 * ((size as i64 + 31) / 32);
27 evm.gas_charge(initcode_cost)?;
28
29 let Some(nonce) = state.nonce(&ctx.this).map(|x| x.as_u64()) else {
30 return Err(EvmYield::Fetch(Fetch::Nonce(ctx.this)));
31 };
32 let address = create_address(&ctx.this, nonce);
33
34 let data = evm.mem_get(offset, size)?;
35 let data: Vec<u8> = data.to_vec();
36 let gas = sub_call_gas(evm);
37 evm.gas_charge(gas as i64)?;
38
39 let call = Call {
40 by: ctx.this,
41 to: None,
42 gas,
43 eth: value,
44 data: data.into(),
45 };
46
47 Err(EvmYield::Call(call, CallMode::Create(address)))
48}
49
50pub fn call<S: State>(evm: &mut Evm, ctx: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
51 let [
52 gas_arg,
53 address,
54 value,
55 args_offset,
56 args_size,
57 ret_offset,
58 ret_size,
59 ] = evm.peek()?;
60 evm::mem_check_int(args_offset, args_size)?;
61 evm::mem_check_int(ret_offset, ret_size)?;
62 let (args_offset, args_size) = (args_offset.as_usize(), args_size.as_usize());
63 let (ret_offset, ret_size) = (ret_offset.as_usize(), ret_size.as_usize());
64 let address: Acc = address.to();
65
66 if state.acc(&ctx.this).is_none() {
67 return Err(EvmYield::Fetch(Fetch::Account(ctx.this)));
68 }
69 if state.acc(&address).is_none() {
70 return Err(EvmYield::Fetch(Fetch::Account(address)));
71 }
72 if let Some(delegate) = state.auth(&address)
73 && state.acc(&address).is_none()
74 {
75 return Err(EvmYield::Fetch(Fetch::Account(delegate)));
76 }
77
78 let access_cost: i64 = if state.is_cold_acc(&address) {
80 2600
81 } else {
82 100
83 };
84 evm.warm_acc(&address);
85 evm.gas_charge(access_cost)?;
86
87 if let Some(delegate) = state.auth(&address) {
88 let access_cost: i64 = if state.is_cold_acc(&delegate) {
89 2600
90 } else {
91 100
92 };
93 evm.warm_acc(&delegate);
94 evm.gas_charge(access_cost)?;
95 };
96
97 let has_value = !value.is_zero();
99 if ctx.is_static && has_value {
100 return Err(EvmYield::Halt(HaltReason::NonStatic));
101 }
102 if has_value {
103 evm.gas_charge(9000)?;
104 }
105
106 if has_value {
110 if state.acc(&address).is_none() {
111 return Err(EvmYield::Fetch(Fetch::Account(address)));
112 }
113 let is_empty = state
114 .acc(&address)
115 .map(|a| a.value.is_zero() && a.nonce.is_zero() && a.code.0.0.is_empty())
116 .unwrap_or(true);
117 if is_empty {
118 evm.gas_charge(25000)?;
119 }
120 }
121
122 evm.mem_expand_max(&[(args_offset, args_size), (ret_offset, ret_size)])?;
124
125 let available = evm.gas_remaining().max(0) as u64;
127 let max_child = available - available / 64;
128 let mut gas = gas_arg.as_u64().min(max_child);
129 evm.gas_charge(gas as i64)?;
130
131 if has_value {
133 gas += 2300;
134 }
135
136 let data = evm.mem_get(args_offset, args_size)?;
137
138 let call = Call {
139 by: ctx.this,
140 to: Some(address),
141 gas,
142 eth: value,
143 data: data.to_vec().into(),
144 };
145
146 Err(EvmYield::Call(call, CallMode::Call(ret_offset, ret_size)))
147}
148
149pub fn callcode<S: State>(evm: &mut Evm, ctx: &Context, _: &Call, state: &mut S) -> EvmResult<()> {
150 let [
151 gas_arg,
152 address,
153 value,
154 args_offset,
155 args_size,
156 ret_offset,
157 ret_size,
158 ] = evm.peek()?;
159 evm::mem_check_int(args_offset, args_size)?;
160 evm::mem_check_int(ret_offset, ret_size)?;
161 let (args_offset, args_size) = (args_offset.as_usize(), args_size.as_usize());
162 let (ret_offset, ret_size) = (ret_offset.as_usize(), ret_size.as_usize());
163 let address: Acc = address.to();
164
165 let is_precompile = is_precompile(&address);
166 let needs_account = address != Acc::ZERO && !is_precompile;
168 if needs_account && state.acc(&address).is_none() {
169 return Err(EvmYield::Fetch(Fetch::Account(address)));
170 };
171 if let Some(delegate) = state.auth(&address)
172 && state.acc(&address).is_none()
173 {
174 return Err(EvmYield::Fetch(Fetch::Account(delegate)));
175 }
176
177 let access_cost: i64 = if state.is_cold_acc(&address) {
178 2600
179 } else {
180 100
181 };
182 evm.warm_acc(&address);
183 evm.gas_charge(access_cost)?;
184
185 if let Some(delegate) = state.auth(&address) {
186 let access_cost: i64 = if state.is_cold_acc(&delegate) {
187 2600
188 } else {
189 100
190 };
191 evm.warm_acc(&delegate);
192 evm.gas_charge(access_cost)?;
193 };
194
195 evm.mem_expand_max(&[(args_offset, args_size), (ret_offset, ret_size)])?;
196
197 let has_value = !value.is_zero();
198 if has_value {
199 evm.gas_charge(9000)?;
200 }
201
202 let available = evm.gas_remaining().max(0) as u64;
203 let max_child = available - available / 64;
204 let mut gas = gas_arg.as_u64().min(max_child);
205 evm.gas_charge(gas as i64)?;
206
207 if has_value {
208 gas += 2300;
209 }
210
211 let data = evm.mem_get(args_offset, args_size)?;
212
213 let call = Call {
214 by: ctx.this,
215 to: Some(address),
216 gas,
217 eth: value,
218 data: data.to_vec().into(),
219 };
220
221 Err(EvmYield::Call(
222 call,
223 CallMode::CallCode(ret_offset, ret_size),
224 ))
225}
226
227pub fn r#return(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
228 let [offset, size] = evm.peek()?;
229 evm::mem_check_int(offset, size)?;
230 let (offset, size) = (offset.as_usize(), size.as_usize());
231 let mem = evm.mem_get(offset, size)?;
232 Err(EvmYield::Return(mem.to_vec()))
233}
234
235pub fn delegatecall<S: State>(
236 evm: &mut Evm,
237 _ctx: &Context,
238 call: &Call,
239 state: &mut S,
240) -> EvmResult<()> {
241 let [
242 gas_arg,
243 address,
244 args_offset,
245 args_size,
246 ret_offset,
247 ret_size,
248 ] = evm.peek()?;
249 evm::mem_check_int(args_offset, args_size)?;
250 evm::mem_check_int(ret_offset, ret_size)?;
251 let (args_offset, args_size) = (args_offset.as_usize(), args_size.as_usize());
252 let (ret_offset, ret_size) = (ret_offset.as_usize(), ret_size.as_usize());
253 let address: Acc = address.to();
254
255 let is_precompile = is_precompile(&address);
256 let needs_account = address != Acc::ZERO && !is_precompile;
258 if needs_account && state.acc(&address).is_none() {
259 return Err(EvmYield::Fetch(Fetch::Account(address)));
260 };
261 if let Some(delegate) = state.auth(&address)
262 && state.acc(&address).is_none()
263 {
264 return Err(EvmYield::Fetch(Fetch::Account(delegate)));
265 }
266
267 let access_cost: i64 = if state.is_cold_acc(&address) {
268 2600
269 } else {
270 100
271 };
272 evm.warm_acc(&address);
273 evm.gas_charge(access_cost)?;
274
275 if let Some(delegate) = state.auth(&address) {
276 let access_cost: i64 = if state.is_cold_acc(&delegate) {
277 2600
278 } else {
279 100
280 };
281 evm.warm_acc(&delegate);
282 evm.gas_charge(access_cost)?;
283 };
284
285 evm.mem_expand_max(&[(args_offset, args_size), (ret_offset, ret_size)])?;
286
287 let available = evm.gas_remaining().max(0) as u64;
288 let max_child = available - available / 64;
289 let gas = gas_arg.as_u64().min(max_child);
290 evm.gas_charge(gas as i64)?;
291
292 let data = evm.mem_get(args_offset, args_size)?;
293
294 let inner_call = Call {
296 by: call.by,
297 to: Some(address),
298 gas,
299 eth: call.eth,
300 data: data.to_vec().into(),
301 };
302
303 Err(EvmYield::Call(
304 inner_call,
305 CallMode::Delegate(ret_offset, ret_size),
306 ))
307}
308
309pub fn create2(evm: &mut Evm, ctx: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
310 if ctx.is_static {
311 return Err(EvmYield::Halt(HaltReason::NonStatic));
312 }
313 evm.gas_charge(32_000)?;
314 let [value, offset, size, salt] = evm.peek()?;
315 evm::mem_check_int(offset, size)?;
316 let (offset, size) = (offset.as_usize(), size.as_usize());
317 let word_cost = 8 * ((size as i64 + 31) / 32);
319 evm.gas_charge(word_cost)?;
320
321 let data = evm.mem_get(offset, size)?;
322 let data: Vec<u8> = data.to_vec();
323 let init_code_hash = Int::from(keccak256(&data).as_ref());
324 let address = create2_address(&ctx.this, &salt, &init_code_hash);
325
326 let gas = sub_call_gas(evm);
327 evm.gas_charge(gas as i64)?;
328
329 let call = Call {
330 by: ctx.this,
331 to: None,
332 gas,
333 eth: value,
334 data: data.into(),
335 };
336
337 Err(EvmYield::Call(call, CallMode::Create2(address)))
338}
339
340pub fn staticcall<S: State>(
341 evm: &mut Evm,
342 ctx: &Context,
343 _: &Call,
344 state: &mut S,
345) -> EvmResult<()> {
346 let [
347 gas_arg,
348 address,
349 args_offset,
350 args_size,
351 ret_offset,
352 ret_size,
353 ] = evm.peek()?;
354 evm::mem_check_int(args_offset, args_size)?;
355 evm::mem_check_int(ret_offset, ret_size)?;
356 let (args_offset, args_size) = (args_offset.as_usize(), args_size.as_usize());
357 let (ret_offset, ret_size) = (ret_offset.as_usize(), ret_size.as_usize());
358 let address: Acc = address.to();
359 let is_precompile = is_precompile(&address);
360
361 let needs_account = address != Acc::ZERO && !is_precompile;
362 if needs_account && state.acc(&address).is_none() {
363 return Err(EvmYield::Fetch(Fetch::Account(address)));
364 };
365 if let Some(delegate) = state.auth(&address)
366 && state.acc(&address).is_none()
367 {
368 return Err(EvmYield::Fetch(Fetch::Account(delegate)));
369 }
370
371 let access_cost: i64 = if state.is_cold_acc(&address) {
373 2600
374 } else {
375 100
376 };
377 evm.warm_acc(&address);
378 evm.gas_charge(access_cost)?;
379
380 if let Some(delegate) = state.auth(&address) {
381 let access_cost: i64 = if state.is_cold_acc(&delegate) {
382 2600
383 } else {
384 100
385 };
386 evm.warm_acc(&delegate);
387 evm.gas_charge(access_cost)?;
388 };
389
390 if !is_precompile && state.acc(&address).is_none() {
392 return Err(EvmYield::Fetch(Fetch::Account(address)));
393 };
394
395 evm.mem_expand_max(&[(args_offset, args_size), (ret_offset, ret_size)])?;
396
397 let available = evm.gas_remaining().max(0) as u64;
398 let max_child = available - available / 64;
399 let gas = gas_arg.as_u64().min(max_child);
400 evm.gas_charge(gas as i64)?;
401
402 let data = evm.mem_get(args_offset, args_size)?;
403
404 let call = Call {
405 by: ctx.this,
406 to: Some(address),
407 gas,
408 eth: Int::ZERO,
409 data: data.to_vec().into(),
410 };
411
412 Err(EvmYield::Call(call, CallMode::Static(ret_offset, ret_size)))
413}
414
415pub fn revert(evm: &mut Evm, _: &Context, _: &Call, _: &mut dyn State) -> EvmResult<()> {
416 let [offset, size] = evm.peek()?;
417 evm::mem_check_int(offset, size)?;
418 let (offset, size) = (offset.as_usize(), size.as_usize());
419 let mem = evm.mem_get(offset, size)?;
420 Err(EvmYield::Revert(mem.to_vec()))
421}
422
423pub fn selfdestruct<S: State>(
424 evm: &mut Evm,
425 ctx: &Context,
426 _: &Call,
427 state: &mut S,
428) -> EvmResult<()> {
429 if ctx.is_static {
431 return Err(EvmYield::Halt(HaltReason::NonStatic));
432 }
433
434 let [beneficiary] = evm.peek()?;
435 let beneficiary: Acc = beneficiary.to();
436
437 if state.acc(&beneficiary).is_none() {
439 return Err(EvmYield::Fetch(crate::evm::Fetch::Account(beneficiary)));
440 }
441
442 evm.gas_charge(5_000)?;
443
444 if state.is_cold_acc(&beneficiary) {
446 evm.gas_charge(2600)?;
447 }
448 evm.warm_acc(&beneficiary);
449
450 let balance = state.balance(&ctx.this).unwrap_or(Int::ZERO);
451 if !balance.is_zero() && beneficiary != ctx.this {
452 let is_empty = state
454 .acc(&beneficiary)
455 .map(|a| a.value.is_zero() && a.nonce.is_zero() && a.code.0.0.is_empty())
456 .unwrap_or(true);
457 if is_empty {
458 evm.gas_charge(25000)?;
459 }
460 let add = lift(|[a, b]| a + b);
461 let to_bal = state.balance(&beneficiary).unwrap_or(Int::ZERO);
462 state.set_value(&beneficiary, add([to_bal, balance]));
463 state.set_value(&ctx.this, Int::ZERO);
464 }
465
466 if state.created().contains(&ctx.this) {
468 state.destroy(&ctx.this);
469 }
470 Err(EvmYield::Return(vec![]))
471}