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