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 info.buy_ink(hio::READ_ARGS_BASE_INK)?;
25 let args = info.env.args.clone();
26 info.pay_for_write(args.len() as u32)?;
27 info.write_slice(ptr, &args)?;
28 Ok(())
29}
30
31pub fn write_result<E: EvmApi>(
33 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
34 ptr: u32,
35 len: u32,
36) -> MaybeEscape {
37 let mut info = hostio!(&mut env);
38 info.buy_ink(hio::WRITE_RESULT_BASE_INK)?;
39 info.pay_for_read(len)?;
40 info.pay_for_read(len)?; let data = info.read_slice(ptr, len)?;
42 info.env.outs = data;
43 Ok(())
44}
45
46pub fn exit_early<E: EvmApi>(_env: FunctionEnvMut<'_, WasmEnv<E>>, status: u32) -> MaybeEscape {
48 Err(Escape::Exit(status))
49}
50
51pub fn storage_load_bytes32<E: EvmApi>(
53 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
54 key_ptr: u32,
55 dest_ptr: u32,
56) -> MaybeEscape {
57 let mut info = hostio!(&mut env);
58 info.buy_ink(hio::STORAGE_LOAD_BASE_INK)?;
59 let arbos_version = info.env.evm_data.arbos_version;
60 let evm_api_gas = if arbos_version < ARBOS_VERSION_STYLUS_CHARGING_FIXES {
62 Gas(crate::pricing::EVM_API_INK.0)
63 } else {
64 info.pricing().ink_to_gas(crate::pricing::EVM_API_INK)
65 };
66 info.require_gas(
67 evm_gas::COLD_SLOAD_GAS + evm_gas::STORAGE_CACHE_REQUIRED_ACCESS_GAS + evm_api_gas.0,
68 )?;
69 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
70 let (value, gas_cost) = info
71 .env
72 .evm_api
73 .get_bytes32(key, evm_api_gas)
74 .map_err(|e| Escape::Internal(e.to_string()))?;
75 info.buy_gas(gas_cost.0)?;
76 info.write_slice(dest_ptr, value.as_slice())?;
77 Ok(())
78}
79
80pub fn storage_cache_bytes32<E: EvmApi>(
82 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
83 key_ptr: u32,
84 value_ptr: u32,
85) -> MaybeEscape {
86 let mut info = hostio!(&mut env);
87 info.buy_ink(hio::STORAGE_CACHE_BASE_INK)?;
88 info.require_gas(evm_gas::SSTORE_SENTRY_GAS + evm_gas::STORAGE_CACHE_REQUIRED_ACCESS_GAS)?;
89 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
90 let value = B256::from(info.read_fixed::<32>(value_ptr)?);
91 let gas_cost = info
92 .env
93 .evm_api
94 .cache_bytes32(key, value)
95 .map_err(|e| Escape::Internal(e.to_string()))?;
96 info.buy_gas(gas_cost.0)?;
97 Ok(())
98}
99
100pub fn storage_flush_cache<E: EvmApi>(
102 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
103 clear: u32,
104) -> MaybeEscape {
105 let mut info = hostio!(&mut env);
106 info.buy_ink(hio::STORAGE_FLUSH_BASE_INK)?;
107 info.require_gas(evm_gas::SSTORE_SENTRY_GAS)?;
108 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
109 let (gas_cost, status) = info
110 .env
111 .evm_api
112 .flush_storage_cache(clear != 0, Gas(gas_left.0 + 1))
113 .map_err(|e| Escape::Internal(e.to_string()))?;
114 if info.env.evm_data.arbos_version >= ARBOS_VERSION_STYLUS_CHARGING_FIXES {
115 info.buy_gas(gas_cost.0)?;
116 }
117 if status != UserOutcomeKind::Success {
118 return Escape::logical("storage flush failed");
119 }
120 Ok(())
121}
122
123pub fn transient_load_bytes32<E: EvmApi>(
125 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
126 key_ptr: u32,
127 dest_ptr: u32,
128) -> MaybeEscape {
129 let mut info = hostio!(&mut env);
130 info.buy_ink(hio::TRANSIENT_LOAD_BASE_INK)?;
131 info.buy_gas(evm_gas::TLOAD_GAS)?;
132 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
133 let value = info
134 .env
135 .evm_api
136 .get_transient_bytes32(key)
137 .map_err(|e| Escape::Internal(e.to_string()))?;
138 info.write_slice(dest_ptr, value.as_slice())?;
139 Ok(())
140}
141
142pub fn transient_store_bytes32<E: EvmApi>(
144 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
145 key_ptr: u32,
146 value_ptr: u32,
147) -> MaybeEscape {
148 let mut info = hostio!(&mut env);
149 info.buy_ink(hio::TRANSIENT_STORE_BASE_INK)?;
150 info.buy_gas(evm_gas::TSTORE_GAS)?;
151 let key = B256::from(info.read_fixed::<32>(key_ptr)?);
152 let value = B256::from(info.read_fixed::<32>(value_ptr)?);
153 let status = info
154 .env
155 .evm_api
156 .set_transient_bytes32(key, value)
157 .map_err(|e| Escape::Internal(e.to_string()))?;
158 if status == UserOutcomeKind::Failure {
159 return Escape::logical("transient store failed");
160 }
161 Ok(())
162}
163
164pub fn call_contract<E: EvmApi>(
166 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
167 contract_ptr: u32,
168 calldata_ptr: u32,
169 calldata_len: u32,
170 value_ptr: u32,
171 gas: u64,
172 ret_len_ptr: u32,
173) -> Result<u8, Escape> {
174 let mut info = hostio!(&mut env);
175 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
176 info.pay_for_read(calldata_len)?;
177 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
179 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
180 let value = U256::from_be_bytes(info.read_fixed::<32>(value_ptr)?);
181 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
182 let gas_req = Gas(gas.min(gas_left.0));
183 let (ret_len, gas_cost, status) = info
184 .env
185 .evm_api
186 .contract_call(contract, &calldata, gas_left, gas_req, value)
187 .map_err(|e| Escape::Internal(e.to_string()))?;
188 info.buy_gas(gas_cost.0)?;
189 info.env.evm_return_data_len = ret_len;
190 info.write_u32(ret_len_ptr, ret_len)?;
191 Ok(status as u8)
192}
193
194pub fn delegate_call_contract<E: EvmApi>(
196 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
197 contract_ptr: u32,
198 calldata_ptr: u32,
199 calldata_len: u32,
200 gas: u64,
201 ret_len_ptr: u32,
202) -> Result<u8, Escape> {
203 let mut info = hostio!(&mut env);
204 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
205 info.pay_for_read(calldata_len)?;
206 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
208 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
209 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
210 let gas_req = Gas(gas.min(gas_left.0));
211 let (ret_len, gas_cost, status) = info
212 .env
213 .evm_api
214 .delegate_call(contract, &calldata, gas_left, gas_req)
215 .map_err(|e| Escape::Internal(e.to_string()))?;
216 info.buy_gas(gas_cost.0)?;
217 info.env.evm_return_data_len = ret_len;
218 info.write_u32(ret_len_ptr, ret_len)?;
219 Ok(status as u8)
220}
221
222pub fn static_call_contract<E: EvmApi>(
224 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
225 contract_ptr: u32,
226 calldata_ptr: u32,
227 calldata_len: u32,
228 gas: u64,
229 ret_len_ptr: u32,
230) -> Result<u8, Escape> {
231 let mut info = hostio!(&mut env);
232 info.buy_ink(hio::CALL_CONTRACT_BASE_INK)?;
233 info.pay_for_read(calldata_len)?;
234 info.pay_for_read(calldata_len)?; let contract = Address::from_slice(&info.read_fixed::<20>(contract_ptr)?);
236 let calldata = info.read_slice(calldata_ptr, calldata_len)?;
237 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
238 let gas_req = Gas(gas.min(gas_left.0));
239 let (ret_len, gas_cost, status) = info
240 .env
241 .evm_api
242 .static_call(contract, &calldata, gas_left, gas_req)
243 .map_err(|e| Escape::Internal(e.to_string()))?;
244 info.buy_gas(gas_cost.0)?;
245 info.env.evm_return_data_len = ret_len;
246 info.write_u32(ret_len_ptr, ret_len)?;
247 Ok(status as u8)
248}
249
250pub fn create1<E: EvmApi>(
252 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
253 code_ptr: u32,
254 code_len: u32,
255 endowment_ptr: u32,
256 contract_ptr: u32,
257 ret_len_ptr: u32,
258) -> MaybeEscape {
259 let mut info = hostio!(&mut env);
260 info.buy_ink(hio::CREATE1_BASE_INK)?;
261 info.pay_for_read(code_len)?;
262 info.pay_for_read(code_len)?; let code = info.read_slice(code_ptr, code_len)?;
264 let endowment = U256::from_be_bytes(info.read_fixed::<32>(endowment_ptr)?);
265 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
266 let (response, ret_len, gas_cost) = info
267 .env
268 .evm_api
269 .create1(code, endowment, gas_left)
270 .map_err(|e| Escape::Internal(e.to_string()))?;
271 let address = match response {
272 crate::evm_api::CreateResponse::Success(addr) => addr,
273 crate::evm_api::CreateResponse::Fail(reason) => {
274 return Err(Escape::Internal(reason));
275 }
276 };
277 info.buy_gas(gas_cost.0)?;
278 info.env.evm_return_data_len = ret_len;
279 info.write_u32(ret_len_ptr, ret_len)?;
280 info.write_slice(contract_ptr, address.as_slice())?;
281 Ok(())
282}
283
284pub fn create2<E: EvmApi>(
286 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
287 code_ptr: u32,
288 code_len: u32,
289 endowment_ptr: u32,
290 salt_ptr: u32,
291 contract_ptr: u32,
292 ret_len_ptr: u32,
293) -> MaybeEscape {
294 let mut info = hostio!(&mut env);
295 info.buy_ink(hio::CREATE2_BASE_INK)?;
296 info.pay_for_read(code_len)?;
297 info.pay_for_read(code_len)?; let code = info.read_slice(code_ptr, code_len)?;
299 let endowment = U256::from_be_bytes(info.read_fixed::<32>(endowment_ptr)?);
300 let salt = B256::from(info.read_fixed::<32>(salt_ptr)?);
301 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
302 let (response, ret_len, gas_cost) = info
303 .env
304 .evm_api
305 .create2(code, endowment, salt, gas_left)
306 .map_err(|e| Escape::Internal(e.to_string()))?;
307 let address = match response {
308 crate::evm_api::CreateResponse::Success(addr) => addr,
309 crate::evm_api::CreateResponse::Fail(reason) => {
310 return Err(Escape::Internal(reason));
311 }
312 };
313 info.buy_gas(gas_cost.0)?;
314 info.env.evm_return_data_len = ret_len;
315 info.write_u32(ret_len_ptr, ret_len)?;
316 info.write_slice(contract_ptr, address.as_slice())?;
317 Ok(())
318}
319
320pub fn read_return_data<E: EvmApi>(
322 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
323 dest_ptr: u32,
324 offset: u32,
325 size: u32,
326) -> Result<u32, Escape> {
327 let mut info = hostio!(&mut env);
328 info.buy_ink(hio::READ_RETURN_DATA_BASE_INK)?;
329 let max = info.env.evm_return_data_len.saturating_sub(offset);
330 info.pay_for_write(size.min(max))?;
331 if max == 0 {
332 return Ok(0);
333 }
334 let data = info.env.evm_api.get_return_data();
335 let offset = offset as usize;
336 let size = size as usize;
337 let available = data.len().saturating_sub(offset);
338 let copy_len = available.min(size);
339 if copy_len > 0 {
340 info.write_slice(dest_ptr, &data[offset..offset + copy_len])?;
341 }
342 Ok(copy_len as u32)
343}
344
345pub fn return_data_size<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u32, Escape> {
347 let mut info = hostio!(&mut env);
348 info.buy_ink(hio::RETURN_DATA_SIZE_BASE_INK)?;
349 Ok(info.env.evm_return_data_len)
350}
351
352pub fn emit_log<E: EvmApi>(
354 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
355 data_ptr: u32,
356 data_len: u32,
357 topics: u32,
358) -> MaybeEscape {
359 let mut info = hostio!(&mut env);
360 info.buy_ink(hio::EMIT_LOG_BASE_INK)?;
361 if topics > 4 || data_len < topics * 32 {
362 return Escape::logical("bad topic data");
363 }
364 info.pay_for_read(data_len)?;
365 info.pay_for_evm_log(topics, data_len - topics * 32)?;
366 let data = info.read_slice(data_ptr, data_len)?;
367 info.env
368 .evm_api
369 .emit_log(data, topics)
370 .map_err(|e| Escape::Internal(e.to_string()))?;
371 Ok(())
372}
373
374pub fn account_balance<E: EvmApi>(
376 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
377 addr_ptr: u32,
378 dest_ptr: u32,
379) -> MaybeEscape {
380 let mut info = hostio!(&mut env);
381 info.buy_ink(hio::ACCOUNT_BALANCE_BASE_INK)?;
382 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
383 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
384 let (balance, gas_cost) = info
385 .env
386 .evm_api
387 .account_balance(address)
388 .map_err(|e| Escape::Internal(e.to_string()))?;
389 info.buy_gas(gas_cost.0)?;
390 info.write_slice(dest_ptr, &balance.to_be_bytes::<32>())?;
391 Ok(())
392}
393
394pub fn account_code<E: EvmApi>(
396 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
397 addr_ptr: u32,
398 offset: u32,
399 size: u32,
400 dest_ptr: u32,
401) -> Result<u32, Escape> {
402 let mut info = hostio!(&mut env);
403 info.buy_ink(hio::ACCOUNT_CODE_BASE_INK)?;
404 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
405 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
406 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
407 let arbos_version = info.env.evm_data.arbos_version;
408 let (code, gas_cost) = info
409 .env
410 .evm_api
411 .account_code(arbos_version, address, gas_left)
412 .map_err(|e| Escape::Internal(e.to_string()))?;
413 info.buy_gas(gas_cost.0)?;
414 info.pay_for_write(code.len() as u32)?;
415 let offset = offset as usize;
416 let size = size as usize;
417 let available = code.len().saturating_sub(offset);
418 let copy_len = available.min(size);
419 if copy_len > 0 {
420 info.write_slice(dest_ptr, &code[offset..offset + copy_len])?;
421 }
422 Ok(copy_len as u32)
423}
424
425pub fn account_code_size<E: EvmApi>(
427 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
428 addr_ptr: u32,
429) -> Result<u32, Escape> {
430 let mut info = hostio!(&mut env);
431 info.buy_ink(hio::ACCOUNT_CODE_SIZE_BASE_INK)?;
432 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
433 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
434 let gas_left = info.ink_ready().map(|ink| info.pricing().ink_to_gas(ink))?;
435 let arbos_version = info.env.evm_data.arbos_version;
436 let (code, gas_cost) = info
437 .env
438 .evm_api
439 .account_code(arbos_version, address, gas_left)
440 .map_err(|e| Escape::Internal(e.to_string()))?;
441 info.buy_gas(gas_cost.0)?;
442 Ok(code.len() as u32)
443}
444
445pub fn account_codehash<E: EvmApi>(
447 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
448 addr_ptr: u32,
449 dest_ptr: u32,
450) -> MaybeEscape {
451 let mut info = hostio!(&mut env);
452 info.buy_ink(hio::ACCOUNT_CODE_HASH_BASE_INK)?;
453 info.require_gas(evm_gas::COLD_ACCOUNT_GAS)?;
454 let address = Address::from_slice(&info.read_fixed::<20>(addr_ptr)?);
455 let (hash, gas_cost) = info
456 .env
457 .evm_api
458 .account_codehash(address)
459 .map_err(|e| Escape::Internal(e.to_string()))?;
460 info.buy_gas(gas_cost.0)?;
461 info.write_slice(dest_ptr, hash.as_slice())?;
462 Ok(())
463}
464
465pub fn evm_gas_left<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
467 let mut info = hostio!(&mut env);
468 info.buy_ink(hio::EVM_GAS_LEFT_BASE_INK)?;
469 let ink = info.ink_ready()?;
470 Ok(info.pricing().ink_to_gas(ink).0)
471}
472
473pub fn evm_ink_left<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
475 let mut info = hostio!(&mut env);
476 info.buy_ink(hio::EVM_INK_LEFT_BASE_INK)?;
477 Ok(info.ink_ready()?.0)
478}
479
480pub fn block_basefee<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
482 let mut info = hostio!(&mut env);
483 info.buy_ink(hio::BLOCK_BASEFEE_BASE_INK)?;
484 info.write_slice(ptr, info.env.evm_data.block_basefee.as_slice())?;
485 Ok(())
486}
487
488pub fn chainid<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
490 let mut info = hostio!(&mut env);
491 info.buy_ink(hio::CHAIN_ID_BASE_INK)?;
492 Ok(info.env.evm_data.chain_id)
493}
494
495pub fn block_coinbase<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
497 let mut info = hostio!(&mut env);
498 info.buy_ink(hio::BLOCK_COINBASE_BASE_INK)?;
499 info.write_slice(ptr, info.env.evm_data.block_coinbase.as_slice())?;
500 Ok(())
501}
502
503pub fn block_gas_limit<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
505 let mut info = hostio!(&mut env);
506 info.buy_ink(hio::BLOCK_GAS_LIMIT_BASE_INK)?;
507 Ok(info.env.evm_data.block_gas_limit)
508}
509
510pub fn block_number<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
512 let mut info = hostio!(&mut env);
513 info.buy_ink(hio::BLOCK_NUMBER_BASE_INK)?;
514 Ok(info.env.evm_data.block_number)
515}
516
517pub fn block_timestamp<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u64, Escape> {
519 let mut info = hostio!(&mut env);
520 info.buy_ink(hio::BLOCK_TIMESTAMP_BASE_INK)?;
521 Ok(info.env.evm_data.block_timestamp)
522}
523
524pub fn contract_address<E: EvmApi>(
526 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
527 ptr: u32,
528) -> MaybeEscape {
529 let mut info = hostio!(&mut env);
530 info.buy_ink(hio::ADDRESS_BASE_INK)?;
531 info.write_slice(ptr, info.env.evm_data.contract_address.as_slice())?;
532 Ok(())
533}
534
535pub fn math_div<E: EvmApi>(
537 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
538 a_ptr: u32,
539 b_ptr: u32,
540) -> MaybeEscape {
541 let mut info = hostio!(&mut env);
542 info.buy_ink(hio::MATH_DIV_BASE_INK)?;
543 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
544 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
545 let result = if b.is_zero() { U256::ZERO } else { a / b };
546 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
547 Ok(())
548}
549
550pub fn math_mod<E: EvmApi>(
552 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
553 a_ptr: u32,
554 b_ptr: u32,
555) -> MaybeEscape {
556 let mut info = hostio!(&mut env);
557 info.buy_ink(hio::MATH_MOD_BASE_INK)?;
558 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
559 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
560 let result = if b.is_zero() { U256::ZERO } else { a % b };
561 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
562 Ok(())
563}
564
565pub fn math_pow<E: EvmApi>(
567 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
568 base_ptr: u32,
569 exp_ptr: u32,
570) -> MaybeEscape {
571 let mut info = hostio!(&mut env);
572 info.buy_ink(hio::MATH_POW_BASE_INK)?;
573 let base = U256::from_be_bytes(info.read_fixed::<32>(base_ptr)?);
574 let exp_bytes = info.read_fixed::<32>(exp_ptr)?;
575 info.buy_ink(crate::pricing::pow_price(&exp_bytes))?;
576 let exp = U256::from_be_bytes(exp_bytes);
577 let result = base.pow(exp);
578 info.write_slice(base_ptr, &result.to_be_bytes::<32>())?;
579 Ok(())
580}
581
582pub fn math_add_mod<E: EvmApi>(
584 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
585 a_ptr: u32,
586 b_ptr: u32,
587 mod_ptr: u32,
588) -> MaybeEscape {
589 let mut info = hostio!(&mut env);
590 info.buy_ink(hio::MATH_ADD_MOD_BASE_INK)?;
591 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
592 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
593 let modulus = U256::from_be_bytes(info.read_fixed::<32>(mod_ptr)?);
594 let result = if modulus.is_zero() {
595 U256::ZERO
596 } else {
597 a.add_mod(b, modulus)
598 };
599 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
600 Ok(())
601}
602
603pub fn math_mul_mod<E: EvmApi>(
605 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
606 a_ptr: u32,
607 b_ptr: u32,
608 mod_ptr: u32,
609) -> MaybeEscape {
610 let mut info = hostio!(&mut env);
611 info.buy_ink(hio::MATH_MUL_MOD_BASE_INK)?;
612 let a = U256::from_be_bytes(info.read_fixed::<32>(a_ptr)?);
613 let b = U256::from_be_bytes(info.read_fixed::<32>(b_ptr)?);
614 let modulus = U256::from_be_bytes(info.read_fixed::<32>(mod_ptr)?);
615 let result = if modulus.is_zero() {
616 U256::ZERO
617 } else {
618 a.mul_mod(b, modulus)
619 };
620 info.write_slice(a_ptr, &result.to_be_bytes::<32>())?;
621 Ok(())
622}
623
624pub fn msg_reentrant<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u32, Escape> {
626 let mut info = hostio!(&mut env);
627 info.buy_ink(hio::MSG_REENTRANT_BASE_INK)?;
628 Ok(info.env.evm_data.reentrant)
629}
630
631pub fn msg_sender<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
633 let mut info = hostio!(&mut env);
634 info.buy_ink(hio::MSG_SENDER_BASE_INK)?;
635 info.write_slice(ptr, info.env.evm_data.msg_sender.as_slice())?;
636 Ok(())
637}
638
639pub fn msg_value<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
641 let mut info = hostio!(&mut env);
642 info.buy_ink(hio::MSG_VALUE_BASE_INK)?;
643 info.write_slice(ptr, info.env.evm_data.msg_value.as_slice())?;
644 Ok(())
645}
646
647pub fn tx_gas_price<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
649 let mut info = hostio!(&mut env);
650 info.buy_ink(hio::TX_GAS_PRICE_BASE_INK)?;
651 info.write_slice(ptr, info.env.evm_data.tx_gas_price.as_slice())?;
652 Ok(())
653}
654
655pub fn tx_ink_price<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> Result<u32, Escape> {
657 let mut info = hostio!(&mut env);
658 info.buy_ink(hio::TX_INK_PRICE_BASE_INK)?;
659 Ok(info.pricing().ink_price)
660}
661
662pub fn tx_origin<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>, ptr: u32) -> MaybeEscape {
664 let mut info = hostio!(&mut env);
665 info.buy_ink(hio::TX_ORIGIN_BASE_INK)?;
666 info.write_slice(ptr, info.env.evm_data.tx_origin.as_slice())?;
667 Ok(())
668}
669
670pub fn pay_for_memory_grow<E: EvmApi>(
672 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
673 pages: u16,
674) -> MaybeEscape {
675 let mut info = hostio!(&mut env);
676 if pages == 0 {
677 info.buy_ink(hio::PAY_FOR_MEMORY_GROW_BASE_INK)?;
678 return Ok(());
679 }
680 let gas_cost = info
681 .env
682 .evm_api
683 .add_pages(pages)
684 .map_err(|e| Escape::Internal(e.to_string()))?;
685 info.buy_gas(gas_cost.0)?;
686 Ok(())
687}
688
689pub fn native_keccak256<E: EvmApi>(
691 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
692 input_ptr: u32,
693 input_len: u32,
694 output_ptr: u32,
695) -> MaybeEscape {
696 let mut info = hostio!(&mut env);
697 info.pay_for_keccak(input_len)?;
698 let data = info.read_slice(input_ptr, input_len)?;
699 let hash = alloy_primitives::keccak256(&data);
700 info.write_slice(output_ptr, hash.as_slice())?;
701 Ok(())
702}
703
704pub fn console_log_text<E: EvmApi>(
708 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
709 ptr: u32,
710 len: u32,
711) -> MaybeEscape {
712 let info = hostio!(&mut env);
713 let text = info.read_slice(ptr, len)?;
714 if let Ok(s) = std::str::from_utf8(&text) {
715 tracing::debug!(target: "stylus", "{s}");
716 }
717 Ok(())
718}
719
720pub fn console_log<E: EvmApi, T: std::fmt::Display>(
722 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
723 value: T,
724) -> MaybeEscape {
725 let _info = hostio!(&mut env);
726 tracing::debug!(target: "stylus", "{value}");
727 Ok(())
728}
729
730pub fn console_tee<E: EvmApi, T: Copy + std::fmt::Display>(
732 mut env: FunctionEnvMut<'_, WasmEnv<E>>,
733 value: T,
734) -> Result<T, Escape> {
735 let _info = hostio!(&mut env);
736 tracing::debug!(target: "stylus", "{value}");
737 Ok(value)
738}
739
740pub fn null_host<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
742 let _info = hostio!(&mut env);
743 Ok(())
744}
745
746pub fn start_benchmark<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
748 let _info = hostio!(&mut env);
749 Ok(())
750}
751
752pub fn end_benchmark<E: EvmApi>(mut env: FunctionEnvMut<'_, WasmEnv<E>>) -> MaybeEscape {
754 let _info = hostio!(&mut env);
755 Ok(())
756}