1use alloy_primitives::{Address, B256, U256};
2use arb_chainspec::arbos_version::ARBOS_VERSION_STYLUS_CHARGING_FIXES;
3use wasmer::FunctionEnvMut;
4
5use crate::{
6 env::WasmEnv,
7 error::{MaybeEscape, StylusError},
8 evm_api::{EvmApi, UserOutcomeKind},
9 ink::Gas,
10 meter::{GasMeteredMachine, MeteredMachine},
11 pricing::{evm_gas, hostio as hio},
12};
13
14macro_rules! hostio {
15 ($env:expr_2021) => {
16 WasmEnv::program($env)?
17 };
18}
19
20pub fn read_args<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
22 let mut info = hostio!(&mut env);
23 let trace_on = crate::trace::is_active();
24 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
25 info.buy_ink(hio::READ_ARGS_BASE_INK)?;
26 let args = info.env.args.clone();
27 info.pay_for_write(args.len() as u32)?;
28 info.write_slice(ptr, &args)?;
29 if trace_on {
30 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
31 crate::trace::record(
32 "read_args",
33 Default::default(),
34 alloy_primitives::Bytes::copy_from_slice(&args),
35 start_ink,
36 end_ink,
37 None,
38 );
39 }
40 Ok(())
41}
42
43pub fn write_result<E: EvmApi>(
45 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
46 ptr: u32,
47 len: u32,
48) -> MaybeEscape {
49 let mut info = hostio!(&mut env);
50 let trace_on = crate::trace::is_active();
51 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
52 info.buy_ink(hio::WRITE_RESULT_BASE_INK)?;
53 info.pay_for_read(len)?;
54 info.pay_for_read(len)?; let data = info.read_slice(ptr, len)?;
56 info.env.outs = data.clone();
57 if trace_on {
58 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
59 crate::trace::record(
60 "write_result",
61 alloy_primitives::Bytes::from(data),
62 Default::default(),
63 start_ink,
64 end_ink,
65 None,
66 );
67 }
68 Ok(())
69}
70
71pub fn exit_early<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, status: u32) -> MaybeEscape {
73 if crate::trace::is_active() {
74 crate::trace::record(
75 "exit_early",
76 alloy_primitives::Bytes::copy_from_slice(&status.to_be_bytes()),
77 Default::default(),
78 0,
79 0,
80 None,
81 );
82 }
83 let _info = hostio!(&mut env);
84 Err(StylusError::Exit(status))
85}
86
87pub fn storage_load_bytes32<E: EvmApi>(
89 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
90 key_ptr: u32,
91 dest_ptr: u32,
92) -> MaybeEscape {
93 let mut info = hostio!(&mut env);
94 let trace_on = crate::trace::is_active();
95 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
96 info.buy_ink(hio::STORAGE_LOAD_BASE_INK)?;
97 let arbos_version = info.env.evm_data.arbos_version;
98 let evm_api_gas = if arbos_version < ARBOS_VERSION_STYLUS_CHARGING_FIXES {
100 Gas(crate::pricing::EVM_API_INK.0)
101 } else {
102 info.pricing().ink_to_gas(crate::pricing::EVM_API_INK)
103 };
104 info.require_gas(
105 evm_gas::COLD_SLOAD_GAS + evm_gas::STORAGE_CACHE_REQUIRED_ACCESS_GAS + evm_api_gas.0,
106 )?;
107 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
108 let (value, gas_cost) = info
109 .env
110 .evm_api
111 .get_bytes32(key, evm_api_gas)
112 .map_err(|e| StylusError::Internal(e.to_string()))?;
113 info.buy_gas(gas_cost.0)?;
114 info.write_slice(dest_ptr, value.as_slice())?;
115 if trace_on {
116 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
117 crate::trace::record(
118 "storage_load_bytes32",
119 alloy_primitives::Bytes::copy_from_slice(key.as_slice()),
120 alloy_primitives::Bytes::copy_from_slice(value.as_slice()),
121 start_ink,
122 end_ink,
123 None,
124 );
125 }
126 Ok(())
127}
128
129pub fn storage_cache_bytes32<E: EvmApi>(
131 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
132 key_ptr: u32,
133 value_ptr: u32,
134) -> MaybeEscape {
135 let mut info = hostio!(&mut env);
136 let trace_on = crate::trace::is_active();
137 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
138 info.buy_ink(hio::STORAGE_CACHE_BASE_INK)?;
139 info.require_gas(evm_gas::SSTORE_SENTRY_GAS + evm_gas::STORAGE_CACHE_REQUIRED_ACCESS_GAS)?;
140 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
141 let value = B256::from(info.read_fixed::<32>(value_ptr)?);
142 let gas_cost = info
143 .env
144 .evm_api
145 .cache_bytes32(key, value)
146 .map_err(|e| StylusError::Internal(e.to_string()))?;
147 info.buy_gas(gas_cost.0)?;
148 if trace_on {
149 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
150 let mut args = Vec::with_capacity(64);
151 args.extend_from_slice(key.as_slice());
152 args.extend_from_slice(value.as_slice());
153 crate::trace::record(
154 "storage_cache_bytes32",
155 alloy_primitives::Bytes::from(args),
156 Default::default(),
157 start_ink,
158 end_ink,
159 None,
160 );
161 }
162 Ok(())
163}
164
165pub fn storage_flush_cache<E: EvmApi>(
167 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
168 clear: u32,
169) -> MaybeEscape {
170 crate::trace::record_leaf(
171 "storage_flush_cache",
172 Default::default(),
173 Default::default(),
174 );
175 let mut info = hostio!(&mut env);
176 info.buy_ink(hio::STORAGE_FLUSH_BASE_INK)?;
177 info.require_gas(evm_gas::SSTORE_SENTRY_GAS)?;
178 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
179 let (gas_cost, status) = info
180 .env
181 .evm_api
182 .flush_storage_cache(clear != 0, Gas(gas_left.0 + 1))
183 .map_err(|e| StylusError::Internal(e.to_string()))?;
184
185 match status {
190 UserOutcomeKind::Success => {
191 if info.env.evm_data.arbos_version >= ARBOS_VERSION_STYLUS_CHARGING_FIXES {
192 info.buy_gas(gas_cost.0)?;
193 }
194 Ok(())
195 }
196 UserOutcomeKind::Failure => StylusError::logical("storage flush failed"),
197 _ => {
198 if info.env.evm_data.arbos_version >= ARBOS_VERSION_STYLUS_CHARGING_FIXES {
199 info.buy_gas(gas_cost.0)?;
200 }
201 StylusError::logical("storage flush failed")
202 }
203 }
204}
205
206pub fn transient_load_bytes32<E: EvmApi>(
208 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
209 key_ptr: u32,
210 dest_ptr: u32,
211) -> MaybeEscape {
212 let mut info = hostio!(&mut env);
213 let trace_on = crate::trace::is_active();
214 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
215 info.buy_ink(hio::TRANSIENT_LOAD_BASE_INK)?;
216 info.buy_gas(evm_gas::TLOAD_GAS)?;
217 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
218 let value = info
219 .env
220 .evm_api
221 .get_transient_bytes32(key)
222 .map_err(|e| StylusError::Internal(e.to_string()))?;
223 info.write_slice(dest_ptr, value.as_slice())?;
224 if trace_on {
225 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
226 crate::trace::record(
227 "transient_load_bytes32",
228 alloy_primitives::Bytes::copy_from_slice(key.as_slice()),
229 alloy_primitives::Bytes::copy_from_slice(value.as_slice()),
230 start_ink,
231 end_ink,
232 None,
233 );
234 }
235 Ok(())
236}
237
238pub fn transient_store_bytes32<E: EvmApi>(
240 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
241 key_ptr: u32,
242 value_ptr: u32,
243) -> MaybeEscape {
244 let mut info = hostio!(&mut env);
245 let trace_on = crate::trace::is_active();
246 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
247 info.buy_ink(hio::TRANSIENT_STORE_BASE_INK)?;
248 info.buy_gas(evm_gas::TSTORE_GAS)?;
249 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
250 let value = B256::from(info.read_fixed::<32>(value_ptr)?);
251 let status = info
252 .env
253 .evm_api
254 .set_transient_bytes32(key, value)
255 .map_err(|e| StylusError::Internal(e.to_string()))?;
256 if status == UserOutcomeKind::Failure {
257 return StylusError::logical("transient store failed");
258 }
259 if trace_on {
260 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
261 let mut args = Vec::with_capacity(64);
262 args.extend_from_slice(key.as_slice());
263 args.extend_from_slice(value.as_slice());
264 crate::trace::record(
265 "transient_store_bytes32",
266 alloy_primitives::Bytes::from(args),
267 Default::default(),
268 start_ink,
269 end_ink,
270 None,
271 );
272 }
273 Ok(())
274}
275
276pub fn call_contract<E: EvmApi>(
278 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
279 contract_ptr: u32,
280 calldata_ptr: u32,
281 calldata_len: u32,
282 value_ptr: u32,
283 gas: u64,
284 ret_len_ptr: u32,
285) -> Result<u8, StylusError> {
286 let mut info = hostio!(&mut env);
287 let trace_on = crate::trace::is_active();
288 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
289 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
290 info.pay_for_read(calldata_len)?;
291 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
293 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
294 let value = U256::from_be_bytes(info.read_fixed::<32>(value_ptr)?);
295 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
296 let gas_req = Gas(gas.min(gas_left.0));
297 if trace_on {
298 crate::trace::enter_subcall();
299 }
300 let pages_in = (info.env.pages_open, info.env.pages_ever);
301 let result = info
302 .env
303 .evm_api
304 .contract_call(contract, &calldata, gas_left, gas_req, value, pages_in);
305 let steps = if trace_on {
306 crate::trace::exit_subcall()
307 } else {
308 Vec::new()
309 };
310 let (ret_len, gas_cost, status, pages_out) =
311 result.map_err(|e| StylusError::Internal(e.to_string()))?;
312 info.env.pages_open = pages_out.0;
313 info.env.pages_ever = pages_out.1;
314 info.buy_gas(gas_cost.0)?;
315 info.env.evm_return_data_len = ret_len;
316 info.write_u32(ret_len_ptr, ret_len)?;
317 if trace_on {
318 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
319 let mut args = Vec::with_capacity(20 + 32 + 8 + calldata.len());
320 args.extend_from_slice(contract.as_slice());
321 args.extend_from_slice(&value.to_be_bytes::<32>());
322 args.extend_from_slice(&gas.to_be_bytes());
323 args.extend_from_slice(&calldata);
324 crate::trace::record_with_steps(
325 "call_contract",
326 alloy_primitives::Bytes::from(args),
327 alloy_primitives::Bytes::from(vec![status as u8]),
328 start_ink,
329 end_ink,
330 Some(contract),
331 steps,
332 );
333 }
334 Ok(status as u8)
335}
336
337pub fn delegate_call_contract<E: EvmApi>(
339 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
340 contract_ptr: u32,
341 calldata_ptr: u32,
342 calldata_len: u32,
343 gas: u64,
344 ret_len_ptr: u32,
345) -> Result<u8, StylusError> {
346 let mut info = hostio!(&mut env);
347 let trace_on = crate::trace::is_active();
348 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
349 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
350 info.pay_for_read(calldata_len)?;
351 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
353 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
354 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
355 let gas_req = Gas(gas.min(gas_left.0));
356 if trace_on {
357 crate::trace::enter_subcall();
358 }
359 let pages_in = (info.env.pages_open, info.env.pages_ever);
360 let result = info
361 .env
362 .evm_api
363 .delegate_call(contract, &calldata, gas_left, gas_req, pages_in);
364 let steps = if trace_on {
365 crate::trace::exit_subcall()
366 } else {
367 Vec::new()
368 };
369 let (ret_len, gas_cost, status, pages_out) =
370 result.map_err(|e| StylusError::Internal(e.to_string()))?;
371 info.env.pages_open = pages_out.0;
372 info.env.pages_ever = pages_out.1;
373 info.buy_gas(gas_cost.0)?;
374 info.env.evm_return_data_len = ret_len;
375 info.write_u32(ret_len_ptr, ret_len)?;
376 if trace_on {
377 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
378 let mut args = Vec::with_capacity(20 + 8 + calldata.len());
379 args.extend_from_slice(contract.as_slice());
380 args.extend_from_slice(&gas.to_be_bytes());
381 args.extend_from_slice(&calldata);
382 crate::trace::record_with_steps(
383 "delegate_call_contract",
384 alloy_primitives::Bytes::from(args),
385 alloy_primitives::Bytes::from(vec![status as u8]),
386 start_ink,
387 end_ink,
388 Some(contract),
389 steps,
390 );
391 }
392 Ok(status as u8)
393}
394
395pub fn static_call_contract<E: EvmApi>(
397 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
398 contract_ptr: u32,
399 calldata_ptr: u32,
400 calldata_len: u32,
401 gas: u64,
402 ret_len_ptr: u32,
403) -> Result<u8, StylusError> {
404 let mut info = hostio!(&mut env);
405 let trace_on = crate::trace::is_active();
406 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
407 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
408 info.pay_for_read(calldata_len)?;
409 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
411 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
412 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
413 let gas_req = Gas(gas.min(gas_left.0));
414 if trace_on {
415 crate::trace::enter_subcall();
416 }
417 let pages_in = (info.env.pages_open, info.env.pages_ever);
418 let result = info
419 .env
420 .evm_api
421 .static_call(contract, &calldata, gas_left, gas_req, pages_in);
422 let steps = if trace_on {
423 crate::trace::exit_subcall()
424 } else {
425 Vec::new()
426 };
427 let (ret_len, gas_cost, status, pages_out) =
428 result.map_err(|e| StylusError::Internal(e.to_string()))?;
429 info.env.pages_open = pages_out.0;
430 info.env.pages_ever = pages_out.1;
431 info.buy_gas(gas_cost.0)?;
432 info.env.evm_return_data_len = ret_len;
433 info.write_u32(ret_len_ptr, ret_len)?;
434 if trace_on {
435 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
436 let mut args = Vec::with_capacity(20 + 8 + calldata.len());
437 args.extend_from_slice(contract.as_slice());
438 args.extend_from_slice(&gas.to_be_bytes());
439 args.extend_from_slice(&calldata);
440 crate::trace::record_with_steps(
441 "static_call_contract",
442 alloy_primitives::Bytes::from(args),
443 alloy_primitives::Bytes::from(vec![status as u8]),
444 start_ink,
445 end_ink,
446 Some(contract),
447 steps,
448 );
449 }
450 Ok(status as u8)
451}
452
453pub fn create1<E: EvmApi>(
455 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
456 code_ptr: u32,
457 code_len: u32,
458 endowment_ptr: u32,
459 contract_ptr: u32,
460 ret_len_ptr: u32,
461) -> MaybeEscape {
462 let mut info = hostio!(&mut env);
463 let trace_on = crate::trace::is_active();
464 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
465 info.buy_ink(hio::CREATE1_BASE_INK)?;
466 info.pay_for_read(code_len)?;
467 info.pay_for_read(code_len)?; let code = info.read_slice(code_ptr, code_len)?;
469 let endowment = U256::from_be_bytes(info.read_fixed::<32>(endowment_ptr)?);
470 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
471 if trace_on {
472 crate::trace::enter_subcall();
473 }
474 let pages_in = (info.env.pages_open, info.env.pages_ever);
475 let result = info
476 .env
477 .evm_api
478 .create1(code.clone(), endowment, gas_left, pages_in);
479 let steps = if trace_on {
480 crate::trace::exit_subcall()
481 } else {
482 Vec::new()
483 };
484 let (response, ret_len, gas_cost, pages_out) =
485 result.map_err(|e| StylusError::Internal(e.to_string()))?;
486 info.env.pages_open = pages_out.0;
487 info.env.pages_ever = pages_out.1;
488 let address = match response {
489 crate::evm_api::CreateResponse::Success(addr) => addr,
490 crate::evm_api::CreateResponse::Fail(reason) => return Err(StylusError::Internal(reason)),
491 };
492 info.buy_gas(gas_cost.0)?;
493 info.env.evm_return_data_len = ret_len;
494 info.write_u32(ret_len_ptr, ret_len)?;
495 info.write_slice(contract_ptr, address.as_slice())?;
496 if trace_on {
497 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
498 let mut args = Vec::with_capacity(32 + code.len());
499 args.extend_from_slice(&endowment.to_be_bytes::<32>());
500 args.extend_from_slice(&code);
501 crate::trace::record_with_steps(
502 "create1",
503 alloy_primitives::Bytes::from(args),
504 alloy_primitives::Bytes::copy_from_slice(address.as_slice()),
505 start_ink,
506 end_ink,
507 Some(address),
508 steps,
509 );
510 }
511 Ok(())
512}
513
514pub fn create2<E: EvmApi>(
516 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
517 code_ptr: u32,
518 code_len: u32,
519 endowment_ptr: u32,
520 salt_ptr: u32,
521 contract_ptr: u32,
522 ret_len_ptr: u32,
523) -> MaybeEscape {
524 let mut info = hostio!(&mut env);
525 let trace_on = crate::trace::is_active();
526 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
527 info.buy_ink(hio::CREATE2_BASE_INK)?;
528 info.pay_for_read(code_len)?;
529 info.pay_for_read(code_len)?; let code = info.read_slice(code_ptr, code_len)?;
531 let endowment = U256::from_be_bytes(info.read_fixed::<32>(endowment_ptr)?);
532 let salt = B256::from(info.read_fixed::<32>(salt_ptr)?);
533 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
534 if trace_on {
535 crate::trace::enter_subcall();
536 }
537 let pages_in = (info.env.pages_open, info.env.pages_ever);
538 let result = info
539 .env
540 .evm_api
541 .create2(code.clone(), endowment, salt, gas_left, pages_in);
542 let steps = if trace_on {
543 crate::trace::exit_subcall()
544 } else {
545 Vec::new()
546 };
547 let (response, ret_len, gas_cost, pages_out) =
548 result.map_err(|e| StylusError::Internal(e.to_string()))?;
549 info.env.pages_open = pages_out.0;
550 info.env.pages_ever = pages_out.1;
551 let address = match response {
552 crate::evm_api::CreateResponse::Success(addr) => addr,
553 crate::evm_api::CreateResponse::Fail(reason) => return Err(StylusError::Internal(reason)),
554 };
555 info.buy_gas(gas_cost.0)?;
556 info.env.evm_return_data_len = ret_len;
557 info.write_u32(ret_len_ptr, ret_len)?;
558 info.write_slice(contract_ptr, address.as_slice())?;
559 if trace_on {
560 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
561 let mut args = Vec::with_capacity(64 + code.len());
562 args.extend_from_slice(&endowment.to_be_bytes::<32>());
563 args.extend_from_slice(salt.as_slice());
564 args.extend_from_slice(&code);
565 crate::trace::record_with_steps(
566 "create2",
567 alloy_primitives::Bytes::from(args),
568 alloy_primitives::Bytes::copy_from_slice(address.as_slice()),
569 start_ink,
570 end_ink,
571 Some(address),
572 steps,
573 );
574 }
575 Ok(())
576}
577
578pub fn read_return_data<E: EvmApi>(
580 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
581 dest_ptr: u32,
582 offset: u32,
583 size: u32,
584) -> Result<u32, StylusError> {
585 let mut info = hostio!(&mut env);
586 let trace_on = crate::trace::is_active();
587 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
588 info.buy_ink(hio::READ_RETURN_DATA_BASE_INK)?;
589 let max = info.env.evm_return_data_len.saturating_sub(offset);
590 info.pay_for_write(size.min(max))?;
591 if max == 0 {
592 if trace_on {
593 let mut args = Vec::with_capacity(8);
594 args.extend_from_slice(&offset.to_be_bytes());
595 args.extend_from_slice(&size.to_be_bytes());
596 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
597 crate::trace::record(
598 "read_return_data",
599 alloy_primitives::Bytes::from(args),
600 Default::default(),
601 start_ink,
602 end_ink,
603 None,
604 );
605 }
606 return Ok(0);
607 }
608 let data = info.env.evm_api.get_return_data();
609 let offset_us = offset as usize;
610 let size_us = size as usize;
611 let available = data.len().saturating_sub(offset_us);
612 let copy_len = available.min(size_us);
613 let copied = if copy_len > 0 {
614 let slice = &data[offset_us..offset_us + copy_len];
615 info.write_slice(dest_ptr, slice)?;
616 slice.to_vec()
617 } else {
618 Vec::new()
619 };
620 if trace_on {
621 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
622 let mut args = Vec::with_capacity(8);
623 args.extend_from_slice(&offset.to_be_bytes());
624 args.extend_from_slice(&size.to_be_bytes());
625 crate::trace::record(
626 "read_return_data",
627 alloy_primitives::Bytes::from(args),
628 alloy_primitives::Bytes::from(copied),
629 start_ink,
630 end_ink,
631 None,
632 );
633 }
634 Ok(copy_len as u32)
635}
636
637pub fn return_data_size<E: EvmApi>(
639 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
640) -> Result<u32, StylusError> {
641 let mut info = hostio!(&mut env);
642 let trace_on = crate::trace::is_active();
643 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
644 info.buy_ink(hio::RETURN_DATA_SIZE_BASE_INK)?;
645 let size = info.env.evm_return_data_len;
646 if trace_on {
647 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
648 crate::trace::record(
649 "return_data_size",
650 Default::default(),
651 alloy_primitives::Bytes::copy_from_slice(&size.to_be_bytes()),
652 start_ink,
653 end_ink,
654 None,
655 );
656 }
657 Ok(size)
658}
659
660pub fn emit_log<E: EvmApi>(
662 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
663 data_ptr: u32,
664 data_len: u32,
665 topics: u32,
666) -> MaybeEscape {
667 let mut info = hostio!(&mut env);
668 let trace_on = crate::trace::is_active();
669 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
670 info.buy_ink(hio::EMIT_LOG_BASE_INK)?;
671 if topics > 4 || data_len < topics * 32 {
672 return StylusError::logical("bad topic data");
673 }
674 info.pay_for_read(data_len)?;
675 info.pay_for_evm_log(topics, data_len - topics * 32)?;
676 let data = info.read_slice(data_ptr, data_len)?;
677 info.env
678 .evm_api
679 .emit_log(data.clone(), topics)
680 .map_err(|e| StylusError::Internal(e.to_string()))?;
681 if trace_on {
682 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
683 let mut args = Vec::with_capacity(4 + data.len());
684 args.extend_from_slice(&topics.to_be_bytes());
685 args.extend_from_slice(&data);
686 crate::trace::record(
687 "emit_log",
688 alloy_primitives::Bytes::from(args),
689 Default::default(),
690 start_ink,
691 end_ink,
692 None,
693 );
694 }
695 Ok(())
696}
697
698pub fn account_balance<E: EvmApi>(
700 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
701 addr_ptr: u32,
702 dest_ptr: u32,
703) -> MaybeEscape {
704 let mut info = hostio!(&mut env);
705 let trace_on = crate::trace::is_active();
706 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
707 info.buy_ink(hio::ACCOUNT_BALANCE_BASE_INK)?;
708 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
709 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
710 let (balance, gas_cost) = info
711 .env
712 .evm_api
713 .account_balance(address)
714 .map_err(|e| StylusError::Internal(e.to_string()))?;
715 info.buy_gas(gas_cost.0)?;
716 info.write_slice(dest_ptr, &balance.to_be_bytes::<32>())?;
717 if trace_on {
718 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
719 crate::trace::record(
720 "account_balance",
721 alloy_primitives::Bytes::copy_from_slice(address.as_slice()),
722 alloy_primitives::Bytes::copy_from_slice(&balance.to_be_bytes::<32>()),
723 start_ink,
724 end_ink,
725 Some(address),
726 );
727 }
728 Ok(())
729}
730
731pub fn account_code<E: EvmApi>(
733 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
734 addr_ptr: u32,
735 offset: u32,
736 size: u32,
737 dest_ptr: u32,
738) -> Result<u32, StylusError> {
739 let mut info = hostio!(&mut env);
740 let trace_on = crate::trace::is_active();
741 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
742 info.buy_ink(hio::ACCOUNT_CODE_BASE_INK)?;
743 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
744 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
745 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
746 let arbos_version = info.env.evm_data.arbos_version;
747 let (code, gas_cost) = info
748 .env
749 .evm_api
750 .account_code(arbos_version, address, gas_left)
751 .map_err(|e| StylusError::Internal(e.to_string()))?;
752 info.buy_gas(gas_cost.0)?;
753 info.pay_for_write(code.len() as u32)?;
754 let offset_usize = offset as usize;
755 let size_usize = size as usize;
756 let available = code.len().saturating_sub(offset_usize);
757 let copy_len = available.min(size_usize);
758 if copy_len > 0 {
759 info.write_slice(dest_ptr, &code[offset_usize..offset_usize + copy_len])?;
760 }
761 if trace_on {
762 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
763 let mut args = Vec::with_capacity(28);
764 args.extend_from_slice(address.as_slice());
765 args.extend_from_slice(&offset.to_be_bytes());
766 args.extend_from_slice(&size.to_be_bytes());
767 crate::trace::record(
768 "account_code",
769 alloy_primitives::Bytes::from(args),
770 alloy_primitives::Bytes::copy_from_slice(&code[..code.len().min(copy_len)]),
771 start_ink,
772 end_ink,
773 Some(address),
774 );
775 }
776 Ok(copy_len as u32)
777}
778
779pub fn account_code_size<E: EvmApi>(
781 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
782 addr_ptr: u32,
783) -> Result<u32, StylusError> {
784 let mut info = hostio!(&mut env);
785 let trace_on = crate::trace::is_active();
786 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
787 info.buy_ink(hio::ACCOUNT_CODE_SIZE_BASE_INK)?;
788 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
789 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
790 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
791 let arbos_version = info.env.evm_data.arbos_version;
792 let (code, gas_cost) = info
793 .env
794 .evm_api
795 .account_code(arbos_version, address, gas_left)
796 .map_err(|e| StylusError::Internal(e.to_string()))?;
797 info.buy_gas(gas_cost.0)?;
798 let len = code.len() as u32;
799 if trace_on {
800 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
801 crate::trace::record(
802 "account_code_size",
803 alloy_primitives::Bytes::copy_from_slice(address.as_slice()),
804 alloy_primitives::Bytes::copy_from_slice(&len.to_be_bytes()),
805 start_ink,
806 end_ink,
807 Some(address),
808 );
809 }
810 Ok(len)
811}
812
813pub fn account_codehash<E: EvmApi>(
815 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
816 addr_ptr: u32,
817 dest_ptr: u32,
818) -> MaybeEscape {
819 let mut info = hostio!(&mut env);
820 let trace_on = crate::trace::is_active();
821 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
822 info.buy_ink(hio::ACCOUNT_CODE_HASH_BASE_INK)?;
823 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
824 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
825 let (hash, gas_cost) = info
826 .env
827 .evm_api
828 .account_codehash(address)
829 .map_err(|e| StylusError::Internal(e.to_string()))?;
830 info.buy_gas(gas_cost.0)?;
831 info.write_slice(dest_ptr, hash.as_slice())?;
832 if trace_on {
833 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
834 crate::trace::record(
835 "account_codehash",
836 alloy_primitives::Bytes::copy_from_slice(address.as_slice()),
837 alloy_primitives::Bytes::copy_from_slice(hash.as_slice()),
838 start_ink,
839 end_ink,
840 Some(address),
841 );
842 }
843 Ok(())
844}
845
846pub fn evm_gas_left<E: EvmApi>(
848 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
849) -> Result<u64, StylusError> {
850 let mut info = hostio!(&mut env);
851 let trace_on = crate::trace::is_active();
852 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
853 info.buy_ink(hio::EVM_GAS_LEFT_BASE_INK)?;
854 let ink = info.ink_ready()?;
855 let gas = info.pricing().ink_to_gas(ink).0;
856 if trace_on {
857 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
858 crate::trace::record(
859 "evm_gas_left",
860 Default::default(),
861 alloy_primitives::Bytes::copy_from_slice(&gas.to_be_bytes()),
862 start_ink,
863 end_ink,
864 None,
865 );
866 }
867 Ok(gas)
868}
869
870pub fn evm_ink_left<E: EvmApi>(
872 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
873) -> Result<u64, StylusError> {
874 let mut info = hostio!(&mut env);
875 let trace_on = crate::trace::is_active();
876 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
877 info.buy_ink(hio::EVM_INK_LEFT_BASE_INK)?;
878 let ink = info.ink_ready()?.0;
879 if trace_on {
880 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
881 crate::trace::record(
882 "evm_ink_left",
883 Default::default(),
884 alloy_primitives::Bytes::copy_from_slice(&ink.to_be_bytes()),
885 start_ink,
886 end_ink,
887 None,
888 );
889 }
890 Ok(ink)
891}
892
893pub fn block_basefee<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
895 let mut info = hostio!(&mut env);
896 let trace_on = crate::trace::is_active();
897 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
898 info.buy_ink(hio::BLOCK_BASEFEE_BASE_INK)?;
899 let basefee = info.env.evm_data.block_basefee;
900 info.write_slice(ptr, basefee.as_slice())?;
901 if trace_on {
902 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
903 crate::trace::record(
904 "block_basefee",
905 Default::default(),
906 alloy_primitives::Bytes::copy_from_slice(basefee.as_slice()),
907 start_ink,
908 end_ink,
909 None,
910 );
911 }
912 Ok(())
913}
914
915pub fn chainid<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, StylusError> {
917 let mut info = hostio!(&mut env);
918 let trace_on = crate::trace::is_active();
919 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
920 info.buy_ink(hio::CHAIN_ID_BASE_INK)?;
921 let id = info.env.evm_data.chain_id;
922 if trace_on {
923 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
924 crate::trace::record(
925 "chainid",
926 Default::default(),
927 alloy_primitives::Bytes::copy_from_slice(&id.to_be_bytes()),
928 start_ink,
929 end_ink,
930 None,
931 );
932 }
933 Ok(id)
934}
935
936pub fn block_coinbase<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
938 let mut info = hostio!(&mut env);
939 let trace_on = crate::trace::is_active();
940 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
941 info.buy_ink(hio::BLOCK_COINBASE_BASE_INK)?;
942 let coinbase = info.env.evm_data.block_coinbase;
943 info.write_slice(ptr, coinbase.as_slice())?;
944 if trace_on {
945 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
946 crate::trace::record(
947 "block_coinbase",
948 Default::default(),
949 alloy_primitives::Bytes::copy_from_slice(coinbase.as_slice()),
950 start_ink,
951 end_ink,
952 None,
953 );
954 }
955 Ok(())
956}
957
958pub fn block_gas_limit<E: EvmApi>(
960 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
961) -> Result<u64, StylusError> {
962 let mut info = hostio!(&mut env);
963 let trace_on = crate::trace::is_active();
964 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
965 info.buy_ink(hio::BLOCK_GAS_LIMIT_BASE_INK)?;
966 let limit = info.env.evm_data.block_gas_limit;
967 if trace_on {
968 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
969 crate::trace::record(
970 "block_gas_limit",
971 Default::default(),
972 alloy_primitives::Bytes::copy_from_slice(&limit.to_be_bytes()),
973 start_ink,
974 end_ink,
975 None,
976 );
977 }
978 Ok(limit)
979}
980
981pub fn block_number<E: EvmApi>(
983 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
984) -> Result<u64, StylusError> {
985 let mut info = hostio!(&mut env);
986 let trace_on = crate::trace::is_active();
987 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
988 info.buy_ink(hio::BLOCK_NUMBER_BASE_INK)?;
989 let n = info.env.evm_data.block_number;
990 if trace_on {
991 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
992 crate::trace::record(
993 "block_number",
994 Default::default(),
995 alloy_primitives::Bytes::copy_from_slice(&n.to_be_bytes()),
996 start_ink,
997 end_ink,
998 None,
999 );
1000 }
1001 Ok(n)
1002}
1003
1004pub fn block_timestamp<E: EvmApi>(
1006 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1007) -> Result<u64, StylusError> {
1008 let mut info = hostio!(&mut env);
1009 let trace_on = crate::trace::is_active();
1010 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1011 info.buy_ink(hio::BLOCK_TIMESTAMP_BASE_INK)?;
1012 let ts = info.env.evm_data.block_timestamp;
1013 if trace_on {
1014 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1015 crate::trace::record(
1016 "block_timestamp",
1017 Default::default(),
1018 alloy_primitives::Bytes::copy_from_slice(&ts.to_be_bytes()),
1019 start_ink,
1020 end_ink,
1021 None,
1022 );
1023 }
1024 Ok(ts)
1025}
1026
1027pub fn contract_address<E: EvmApi>(
1029 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1030 ptr: u32,
1031) -> MaybeEscape {
1032 let mut info = hostio!(&mut env);
1033 let trace_on = crate::trace::is_active();
1034 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1035 info.buy_ink(hio::ADDRESS_BASE_INK)?;
1036 let addr = info.env.evm_data.contract_address;
1037 info.write_slice(ptr, addr.as_slice())?;
1038 if trace_on {
1039 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1040 crate::trace::record(
1041 "contract_address",
1042 Default::default(),
1043 alloy_primitives::Bytes::copy_from_slice(addr.as_slice()),
1044 start_ink,
1045 end_ink,
1046 Some(addr),
1047 );
1048 }
1049 Ok(())
1050}
1051
1052pub fn math_div<E: EvmApi>(
1054 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1055 a_ptr: u32,
1056 b_ptr: u32,
1057) -> MaybeEscape {
1058 let mut info = hostio!(&mut env);
1059 let trace_on = crate::trace::is_active();
1060 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1061 info.buy_ink(hio::MATH_DIV_BASE_INK)?;
1062 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
1063 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
1064 let result = if b.is_zero() { U256::ZERO } else { a / b };
1065 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
1066 if trace_on {
1067 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1068 let mut args = Vec::with_capacity(64);
1069 args.extend_from_slice(&a.to_be_bytes::<32>());
1070 args.extend_from_slice(&b.to_be_bytes::<32>());
1071 crate::trace::record(
1072 "math_div",
1073 alloy_primitives::Bytes::from(args),
1074 alloy_primitives::Bytes::copy_from_slice(&result.to_be_bytes::<32>()),
1075 start_ink,
1076 end_ink,
1077 None,
1078 );
1079 }
1080 Ok(())
1081}
1082
1083pub fn math_mod<E: EvmApi>(
1085 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1086 a_ptr: u32,
1087 b_ptr: u32,
1088) -> MaybeEscape {
1089 let mut info = hostio!(&mut env);
1090 let trace_on = crate::trace::is_active();
1091 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1092 info.buy_ink(hio::MATH_MOD_BASE_INK)?;
1093 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
1094 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
1095 let result = if b.is_zero() { U256::ZERO } else { a % b };
1096 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
1097 if trace_on {
1098 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1099 let mut args = Vec::with_capacity(64);
1100 args.extend_from_slice(&a.to_be_bytes::<32>());
1101 args.extend_from_slice(&b.to_be_bytes::<32>());
1102 crate::trace::record(
1103 "math_mod",
1104 alloy_primitives::Bytes::from(args),
1105 alloy_primitives::Bytes::copy_from_slice(&result.to_be_bytes::<32>()),
1106 start_ink,
1107 end_ink,
1108 None,
1109 );
1110 }
1111 Ok(())
1112}
1113
1114pub fn math_pow<E: EvmApi>(
1116 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1117 base_ptr: u32,
1118 exp_ptr: u32,
1119) -> MaybeEscape {
1120 let mut info = hostio!(&mut env);
1121 let trace_on = crate::trace::is_active();
1122 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1123 info.buy_ink(hio::MATH_POW_BASE_INK)?;
1124 let base = U256::from_be_bytes(info.read_fixed::<32>(base_ptr)?);
1125 let exp_bytes = info.read_fixed::<32>(exp_ptr)?;
1126 info.buy_ink(crate::pricing::pow_price(&exp_bytes))?;
1127 let exp = U256::from_be_bytes(exp_bytes);
1128 let result = base.pow(exp);
1129 info.write_slice(base_ptr, &result.to_be_bytes::<32>())?;
1130 if trace_on {
1131 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1132 let mut args = Vec::with_capacity(64);
1133 args.extend_from_slice(&base.to_be_bytes::<32>());
1134 args.extend_from_slice(&exp.to_be_bytes::<32>());
1135 crate::trace::record(
1136 "math_pow",
1137 alloy_primitives::Bytes::from(args),
1138 alloy_primitives::Bytes::copy_from_slice(&result.to_be_bytes::<32>()),
1139 start_ink,
1140 end_ink,
1141 None,
1142 );
1143 }
1144 Ok(())
1145}
1146
1147pub fn math_add_mod<E: EvmApi>(
1149 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1150 a_ptr: u32,
1151 b_ptr: u32,
1152 mod_ptr: u32,
1153) -> MaybeEscape {
1154 let mut info = hostio!(&mut env);
1155 let trace_on = crate::trace::is_active();
1156 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1157 info.buy_ink(hio::MATH_ADD_MOD_BASE_INK)?;
1158 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
1159 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
1160 let modulus = U256::from_be_bytes(info.read_fixed::<32>(mod_ptr)?);
1161 let result = if modulus.is_zero() {
1162 U256::ZERO
1163 } else {
1164 a.add_mod(b, modulus)
1165 };
1166 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
1167 if trace_on {
1168 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1169 let mut args = Vec::with_capacity(96);
1170 args.extend_from_slice(&a.to_be_bytes::<32>());
1171 args.extend_from_slice(&b.to_be_bytes::<32>());
1172 args.extend_from_slice(&modulus.to_be_bytes::<32>());
1173 crate::trace::record(
1174 "math_add_mod",
1175 alloy_primitives::Bytes::from(args),
1176 alloy_primitives::Bytes::copy_from_slice(&result.to_be_bytes::<32>()),
1177 start_ink,
1178 end_ink,
1179 None,
1180 );
1181 }
1182 Ok(())
1183}
1184
1185pub fn math_mul_mod<E: EvmApi>(
1187 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1188 a_ptr: u32,
1189 b_ptr: u32,
1190 mod_ptr: u32,
1191) -> MaybeEscape {
1192 let mut info = hostio!(&mut env);
1193 let trace_on = crate::trace::is_active();
1194 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1195 info.buy_ink(hio::MATH_MUL_MOD_BASE_INK)?;
1196 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
1197 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
1198 let modulus = U256::from_be_bytes(info.read_fixed::<32>(mod_ptr)?);
1199 let result = if modulus.is_zero() {
1200 U256::ZERO
1201 } else {
1202 a.mul_mod(b, modulus)
1203 };
1204 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
1205 if trace_on {
1206 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1207 let mut args = Vec::with_capacity(96);
1208 args.extend_from_slice(&a.to_be_bytes::<32>());
1209 args.extend_from_slice(&b.to_be_bytes::<32>());
1210 args.extend_from_slice(&modulus.to_be_bytes::<32>());
1211 crate::trace::record(
1212 "math_mul_mod",
1213 alloy_primitives::Bytes::from(args),
1214 alloy_primitives::Bytes::copy_from_slice(&result.to_be_bytes::<32>()),
1215 start_ink,
1216 end_ink,
1217 None,
1218 );
1219 }
1220 Ok(())
1221}
1222
1223pub fn msg_reentrant<E: EvmApi>(
1225 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1226) -> Result<u32, StylusError> {
1227 let mut info = hostio!(&mut env);
1228 let trace_on = crate::trace::is_active();
1229 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1230 info.buy_ink(hio::MSG_REENTRANT_BASE_INK)?;
1231 let r = info.env.evm_data.reentrant;
1232 if trace_on {
1233 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1234 crate::trace::record(
1235 "msg_reentrant",
1236 Default::default(),
1237 alloy_primitives::Bytes::copy_from_slice(&r.to_be_bytes()),
1238 start_ink,
1239 end_ink,
1240 None,
1241 );
1242 }
1243 Ok(r)
1244}
1245
1246pub fn msg_sender<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
1248 let mut info = hostio!(&mut env);
1249 let trace_on = crate::trace::is_active();
1250 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1251 info.buy_ink(hio::MSG_SENDER_BASE_INK)?;
1252 let sender = info.env.evm_data.msg_sender;
1253 info.write_slice(ptr, sender.as_slice())?;
1254 if trace_on {
1255 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1256 crate::trace::record(
1257 "msg_sender",
1258 Default::default(),
1259 alloy_primitives::Bytes::copy_from_slice(sender.as_slice()),
1260 start_ink,
1261 end_ink,
1262 Some(sender),
1263 );
1264 }
1265 Ok(())
1266}
1267
1268pub fn msg_value<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
1270 let mut info = hostio!(&mut env);
1271 let trace_on = crate::trace::is_active();
1272 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1273 info.buy_ink(hio::MSG_VALUE_BASE_INK)?;
1274 let v = info.env.evm_data.msg_value;
1275 info.write_slice(ptr, v.as_slice())?;
1276 if trace_on {
1277 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1278 crate::trace::record(
1279 "msg_value",
1280 Default::default(),
1281 alloy_primitives::Bytes::copy_from_slice(v.as_slice()),
1282 start_ink,
1283 end_ink,
1284 None,
1285 );
1286 }
1287 Ok(())
1288}
1289
1290pub fn tx_gas_price<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
1292 let mut info = hostio!(&mut env);
1293 let trace_on = crate::trace::is_active();
1294 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1295 info.buy_ink(hio::TX_GAS_PRICE_BASE_INK)?;
1296 let p_ = info.env.evm_data.tx_gas_price;
1297 info.write_slice(ptr, p_.as_slice())?;
1298 if trace_on {
1299 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1300 crate::trace::record(
1301 "tx_gas_price",
1302 Default::default(),
1303 alloy_primitives::Bytes::copy_from_slice(p_.as_slice()),
1304 start_ink,
1305 end_ink,
1306 None,
1307 );
1308 }
1309 Ok(())
1310}
1311
1312pub fn tx_ink_price<E: EvmApi>(
1314 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1315) -> Result<u32, StylusError> {
1316 let mut info = hostio!(&mut env);
1317 let trace_on = crate::trace::is_active();
1318 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1319 info.buy_ink(hio::TX_INK_PRICE_BASE_INK)?;
1320 let price = info.pricing().ink_price;
1321 if trace_on {
1322 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1323 crate::trace::record(
1324 "tx_ink_price",
1325 Default::default(),
1326 alloy_primitives::Bytes::copy_from_slice(&price.to_be_bytes()),
1327 start_ink,
1328 end_ink,
1329 None,
1330 );
1331 }
1332 Ok(price)
1333}
1334
1335pub fn tx_origin<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
1337 let mut info = hostio!(&mut env);
1338 let trace_on = crate::trace::is_active();
1339 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1340 info.buy_ink(hio::TX_ORIGIN_BASE_INK)?;
1341 let origin = info.env.evm_data.tx_origin;
1342 info.write_slice(ptr, origin.as_slice())?;
1343 if trace_on {
1344 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1345 crate::trace::record(
1346 "tx_origin",
1347 Default::default(),
1348 alloy_primitives::Bytes::copy_from_slice(origin.as_slice()),
1349 start_ink,
1350 end_ink,
1351 Some(origin),
1352 );
1353 }
1354 Ok(())
1355}
1356
1357pub fn pay_for_memory_grow<E: EvmApi>(
1359 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1360 pages: u32,
1361) -> MaybeEscape {
1362 crate::trace::record_leaf(
1363 "pay_for_memory_grow",
1364 Default::default(),
1365 Default::default(),
1366 );
1367 let mut info = hostio!(&mut env);
1368 if crate::env::pay_for_memory_grow_overflows(info.env.evm_data.arbos_version, pages) {
1369 info.buy_gas(u64::MAX)?;
1370 }
1371 let pages = pages as u16;
1372 if pages == 0 {
1373 info.buy_ink(hio::PAY_FOR_MEMORY_GROW_BASE_INK)?;
1374 return Ok(());
1375 }
1376 let gas_cost = info.env.add_pages_charge(pages);
1377 info.buy_gas(gas_cost)?;
1378 Ok(())
1379}
1380
1381pub fn native_keccak256<E: EvmApi>(
1383 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1384 input_ptr: u32,
1385 input_len: u32,
1386 output_ptr: u32,
1387) -> MaybeEscape {
1388 let mut info = hostio!(&mut env);
1389 let trace_on = crate::trace::is_active();
1390 let start_ink = if trace_on { info.ink_ready()?.0 } else { 0 };
1391 info.pay_for_keccak(input_len)?;
1392 let data = info.read_slice(input_ptr, input_len)?;
1393 let hash = alloy_primitives::keccak256(&data);
1394 info.write_slice(output_ptr, hash.as_slice())?;
1395 if trace_on {
1396 let end_ink = info.ink_ready().map(|i| i.0).unwrap_or(0);
1397 crate::trace::record(
1398 "native_keccak256",
1399 alloy_primitives::Bytes::from(data),
1400 alloy_primitives::Bytes::copy_from_slice(hash.as_slice()),
1401 start_ink,
1402 end_ink,
1403 None,
1404 );
1405 }
1406 Ok(())
1407}
1408
1409pub fn console_log_text<E: EvmApi>(
1413 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1414 ptr: u32,
1415 len: u32,
1416) -> MaybeEscape {
1417 crate::trace::record_leaf("console_log_text", Default::default(), Default::default());
1418 let info = hostio!(&mut env);
1419 let text = info.read_slice(ptr, len)?;
1420 if let Ok(s) = std::str::from_utf8(&text) {
1421 tracing::debug!(target: "stylus", "{s}");
1422 }
1423 Ok(())
1424}
1425
1426pub fn console_log<E: EvmApi, T: std::fmt::Display>(
1428 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1429 value: T,
1430) -> MaybeEscape {
1431 crate::trace::record_leaf("console_log", Default::default(), Default::default());
1432 let _info = hostio!(&mut env);
1433 tracing::debug!(target: "stylus", "{value}");
1434 Ok(())
1435}
1436
1437pub fn console_tee<E: EvmApi, T: Copy + std::fmt::Display>(
1439 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
1440 value: T,
1441) -> Result<T, StylusError> {
1442 crate::trace::record_leaf("console_tee", Default::default(), Default::default());
1443 let _info = hostio!(&mut env);
1444 tracing::debug!(target: "stylus", "{value}");
1445 Ok(value)
1446}
1447
1448pub fn null_host<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
1450 crate::trace::record_leaf("null_host", Default::default(), Default::default());
1451 let _info = hostio!(&mut env);
1452 Ok(())
1453}
1454
1455pub fn start_benchmark<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
1457 crate::trace::record_leaf("start_benchmark", Default::default(), Default::default());
1458 let _info = hostio!(&mut env);
1459 Ok(())
1460}
1461
1462pub fn end_benchmark<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
1464 crate::trace::record_leaf("end_benchmark", Default::default(), Default::default());
1465 let _info = hostio!(&mut env);
1466 Ok(())
1467}