stylus_core/
host.rs

1// Copyright 2024-2025, Offchain Labs, Inc.
2// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md
3
4//! Defines host environment methods Stylus SDK contracts have access to.
5extern crate alloc;
6
7use alloc::vec::Vec;
8use alloy_primitives::{Address, B256, U256};
9use alloy_sol_types::abi::token::WordToken;
10use alloy_sol_types::{SolEvent, TopicList};
11use dyn_clone::DynClone;
12
13/// The host trait defines methods a Stylus contract can use to interact
14/// with a host environment, such as the EVM. It is a composition
15/// of traits with different access to host values and modifications.
16/// Stylus contracts in the SDK have access to a host via the HostAccessor trait for safe access
17/// to hostios without the need for global invocations. The host trait may be implemented
18/// by test frameworks as an easier way of mocking hostio invocations for testing
19/// Stylus contracts.
20pub trait Host:
21    CryptographyAccess
22    + CalldataAccess
23    + UnsafeDeploymentAccess
24    + StorageAccess
25    + UnsafeCallAccess
26    + BlockAccess
27    + ChainAccess
28    + AccountAccess
29    + MemoryAccess
30    + MessageAccess
31    + MeteringAccess
32    + RawLogAccess
33    + DynClone
34{
35}
36
37// Enables cloning of a boxed, host trait object.
38dyn_clone::clone_trait_object!(Host);
39
40/// Defines a trait that allows a Stylus contract to access its host safely.
41pub trait HostAccess {
42    /// Host type returned by [`Self::vm()`].
43    type Host: Host;
44
45    /// Provides access to the parametrized host of a contract, giving access
46    /// to all the desired hostios from the user.
47    fn vm(&self) -> &Self::Host;
48}
49
50/// Defines a trait that can deny access to a contract router method if a message value is
51/// passed in while the method is non-payable.
52pub trait ValueDenier {
53    fn deny_value(&self, method_name: &str) -> Result<(), Vec<u8>>;
54}
55
56/// Defines a trait that guards whether the constructor was already called.
57pub trait ConstructorGuard {
58    fn check_constructor_slot(&self) -> Result<(), Vec<u8>>;
59}
60
61/// Provides access to native cryptography extensions provided by
62/// a Stylus contract host, such as keccak256.
63pub trait CryptographyAccess {
64    /// Efficiently computes the [`keccak256`] hash of the given preimage.
65    /// The semantics are equivalent to that of the EVM's [`SHA3`] opcode.
66    ///
67    /// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3
68    /// [`SHA3`]: https://www.evm.codes/#20
69    fn native_keccak256(&self, input: &[u8]) -> B256;
70}
71
72/// Provides access to host methods relating to the accessing the calldata
73/// of a Stylus contract transaction.
74pub trait CalldataAccess {
75    /// Reads the program calldata. The semantics are equivalent to that of the EVM's
76    /// [`CALLDATA_COPY`] opcode when requesting the entirety of the current call's calldata.
77    ///
78    /// [`CALLDATA_COPY`]: https://www.evm.codes/#37
79    fn read_args(&self, len: usize) -> Vec<u8>;
80    /// Copies the bytes of the last EVM call or deployment return result. Does not revert if out of
81    /// bounds, but rather copies the overlapping portion. The semantics are otherwise equivalent
82    /// to that of the EVM's [`RETURN_DATA_COPY`] opcode.
83    ///
84    /// Returns the number of bytes written.
85    ///
86    /// [`RETURN_DATA_COPY`]: https://www.evm.codes/#3e
87    fn read_return_data(&self, offset: usize, size: Option<usize>) -> Vec<u8>;
88    /// Returns the length of the last EVM call or deployment return result, or `0` if neither have
89    /// happened during the program's execution. The semantics are equivalent to that of the EVM's
90    /// [`RETURN_DATA_SIZE`] opcode.
91    ///
92    /// [`RETURN_DATA_SIZE`]: https://www.evm.codes/#3d
93    fn return_data_size(&self) -> usize;
94    /// Writes the final return data. If not called before the program exists, the return data will
95    /// be 0 bytes long. Note that this hostio does not cause the program to exit, which happens
96    /// naturally when `user_entrypoint` returns.
97    fn write_result(&self, data: &[u8]);
98}
99
100/// Provides access to programmatic creation of contracts via the host environment's CREATE
101/// and CREATE2 opcodes in the EVM.
102///
103/// # Safety
104/// These methods should only be used in advanced cases when lowest-level access
105/// to create1 and create2 opcodes is needed. Using the methods by themselves will not protect
106/// against reentrancy safety, storage aliasing, or cache flushing. For safe contract deployment,
107/// utilize a [`RawDeploy`] struct instead.
108pub unsafe trait UnsafeDeploymentAccess {
109    /// Deploys a new contract using the init code provided, which the EVM executes to construct
110    /// the code of the newly deployed contract. The init code must be written in EVM bytecode, but
111    /// the code it deploys can be that of a Stylus program. The code returned will be treated as
112    /// WASM if it begins with the EOF-inspired header `0xEFF000`. Otherwise the code will be
113    /// interpreted as that of a traditional EVM-style contract. See [`Deploying Stylus Programs`]
114    /// for more information on writing init code.
115    ///
116    /// On success, this hostio returns the address of the newly created account whose address is
117    /// a function of the sender and nonce. On failure the address will be `0`, `return_data_len`
118    /// will store the length of the revert data, the bytes of which can be read via the
119    /// `read_return_data` hostio. The semantics are equivalent to that of the EVM's [`CREATE`]
120    /// opcode, which notably includes the exact address returned.
121    ///
122    /// [`Deploying Stylus Programs`]: https://docs.arbitrum.io/stylus/quickstart
123    /// [`CREATE`]: https://www.evm.codes/#f0
124    ///
125    /// # Safety
126    /// This method should only be used in advanced cases when lowest-level access to create1 is required.
127    /// Safe usage needs to consider reentrancy, storage aliasing, and cache flushing.
128    /// Utilize a [`RawDeploy`] struct instead for safety.
129    unsafe fn create1(
130        &self,
131        code: *const u8,
132        code_len: usize,
133        endowment: *const u8,
134        contract: *mut u8,
135        revert_data_len: *mut usize,
136    );
137    /// Deploys a new contract using the init code provided, which the EVM executes to construct
138    /// the code of the newly deployed contract. The init code must be written in EVM bytecode, but
139    /// the code it deploys can be that of a Stylus program. The code returned will be treated as
140    /// WASM if it begins with the EOF-inspired header `0xEFF000`. Otherwise the code will be
141    /// interpreted as that of a traditional EVM-style contract. See [`Deploying Stylus Programs`]
142    /// for more information on writing init code.
143    ///
144    /// On success, this hostio returns the address of the newly created account whose address is a
145    /// function of the sender, salt, and init code. On failure the address will be `0`,
146    /// `return_data_len` will store the length of the revert data, the bytes of which can be read
147    /// via the `read_return_data` hostio. The semantics are equivalent to that of the EVM's
148    /// `[CREATE2`] opcode, which notably includes the exact address returned.
149    ///
150    /// [`Deploying Stylus Programs`]: https://docs.arbitrum.io/stylus/quickstart
151    /// [`CREATE2`]: https://www.evm.codes/#f5
152    ///
153    /// # Safety
154    /// This method should only be used in advanced cases when lowest-level access to create2 is required.
155    /// Safe usage needs to consider reentrancy, storage aliasing, and cache flushing.
156    /// Utilize a [`RawDeploy`] struct instead for safety.
157    unsafe fn create2(
158        &self,
159        code: *const u8,
160        code_len: usize,
161        endowment: *const u8,
162        salt: *const u8,
163        contract: *mut u8,
164        revert_data_len: *mut usize,
165    );
166}
167
168/// Provides access to storage access and mutation via host methods.
169pub trait StorageAccess {
170    /// Reads a 32-byte value from permanent storage. Stylus's storage format is identical to
171    /// that of the EVM. This means that, under the hood, this hostio is accessing the 32-byte
172    /// value stored in the EVM state trie at offset `key`, which will be `0` when not previously
173    /// set. The semantics, then, are equivalent to that of the EVM's [`SLOAD`] opcode.
174    ///
175    /// Note: the Stylus VM implements storage caching. This means that repeated calls to the same key
176    /// will cost less than in the EVM.
177    ///
178    /// [`SLOAD`]: https://www.evm.codes/#54
179    fn storage_load_bytes32(&self, key: U256) -> B256;
180    /// Writes a 32-byte value to the permanent storage cache. Stylus's storage format is identical to that
181    /// of the EVM. This means that, under the hood, this hostio represents storing a 32-byte value into
182    /// the EVM state trie at offset `key`. Refunds are tabulated exactly as in the EVM. The semantics, then,
183    /// are equivalent to that of the EVM's [`SSTORE`] opcode.
184    ///
185    /// Note: because the value is cached, one must call `flush_cache` to persist it.
186    ///
187    /// [`SSTORE`]: https://www.evm.codes/#55
188    ///
189    /// # Safety
190    /// May alias storage.
191    unsafe fn storage_cache_bytes32(&self, key: U256, value: B256);
192    /// Persists any dirty values in the storage cache to the EVM state trie, dropping the cache entirely if requested.
193    /// Analogous to repeated invocations of [`SSTORE`].
194    ///
195    /// [`SSTORE`]: https://www.evm.codes/#55
196    fn flush_cache(&self, clear: bool);
197}
198
199/// Provides access to calling other contracts using host semantics.
200///
201/// # Safety
202/// These methods should only be used in advanced cases when lowest-level access
203/// to call, static_call, and delegate_call methods is required. Using the methods by themselves will not protect
204/// against reentrancy safety, storage aliasing, or cache flushing. For safe contract calls,
205/// utilize a [`RawCall`] struct instead.
206pub unsafe trait UnsafeCallAccess {
207    /// Calls the contract at the given address with options for passing value and to limit the
208    /// amount of gas supplied. The return status indicates whether the call succeeded, and is
209    /// nonzero on failure.
210    ///
211    /// In both cases `return_data_len` will store the length of the result, the bytes of which can
212    /// be read via the `read_return_data` hostio. The bytes are not returned directly so that the
213    /// programmer can potentially save gas by choosing which subset of the return result they'd
214    /// like to copy.
215    ///
216    /// The semantics are equivalent to that of the EVM's [`CALL`] opcode, including callvalue
217    /// stipends and the 63/64 gas rule. This means that supplying the `u64::MAX` gas can be used
218    /// to send as much as possible.
219    ///
220    /// [`CALL`]: https://www.evm.codes/#f1
221    ///
222    /// # Safety
223    /// This method should only be used in advanced cases when lowest-level access to calls is required.
224    /// Safe usage needs to consider reentrancy, storage aliasing, and cache flushing.
225    /// Utilize a [`RawCall`] struct instead for safety.
226    unsafe fn call_contract(
227        &self,
228        to: *const u8,
229        data: *const u8,
230        data_len: usize,
231        value: *const u8,
232        gas: u64,
233        outs_len: &mut usize,
234    ) -> u8;
235    /// Static calls the contract at the given address, with the option to limit the amount of gas
236    /// supplied. The return status indicates whether the call succeeded, and is nonzero on
237    /// failure.
238    ///
239    /// In both cases `return_data_len` will store the length of the result, the bytes of which can
240    /// be read via the `read_return_data` hostio. The bytes are not returned directly so that the
241    /// programmer can potentially save gas by choosing which subset of the return result they'd
242    /// like to copy.
243    ///
244    /// The semantics are equivalent to that of the EVM's [`STATIC_CALL`] opcode, including the
245    /// 63/64 gas rule. This means that supplying `u64::MAX` gas can be used to send as much as
246    /// possible.
247    ///
248    /// [`STATIC_CALL`]: https://www.evm.codes/#FA
249    ///
250    /// # Safety
251    /// This method should only be used in advanced cases when lowest-level access to calls is required.
252    /// Safe usage needs to consider reentrancy, storage aliasing, and cache flushing.
253    /// Utilize a [`RawCall`] struct instead for safety.
254    unsafe fn static_call_contract(
255        &self,
256        to: *const u8,
257        data: *const u8,
258        data_len: usize,
259        gas: u64,
260        outs_len: &mut usize,
261    ) -> u8;
262    /// Delegate calls the contract at the given address, with the option to limit the amount of
263    /// gas supplied. The return status indicates whether the call succeeded, and is nonzero on
264    /// failure.
265    ///
266    /// In both cases `return_data_len` will store the length of the result, the bytes of which
267    /// can be read via the `read_return_data` hostio. The bytes are not returned directly so that
268    /// the programmer can potentially save gas by choosing which subset of the return result
269    /// they'd like to copy.
270    ///
271    /// The semantics are equivalent to that of the EVM's [`DELEGATE_CALL`] opcode, including the
272    /// 63/64 gas rule. This means that supplying `u64::MAX` gas can be used to send as much as
273    /// possible.
274    ///
275    /// [`DELEGATE_CALL`]: https://www.evm.codes/#F4
276    ///
277    /// # Safety
278    /// This method should only be used in advanced cases when lowest-level access to calls is required.
279    /// Safe usage needs to consider reentrancy, storage aliasing, and cache flushing.
280    /// Utilize a [`RawCall`] struct instead for safety.
281    unsafe fn delegate_call_contract(
282        &self,
283        to: *const u8,
284        data: *const u8,
285        data_len: usize,
286        gas: u64,
287        outs_len: &mut usize,
288    ) -> u8;
289}
290
291/// Provides access to host methods relating to the block a transaction
292/// to a Stylus contract is included in.
293pub trait BlockAccess {
294    /// Gets the basefee of the current block. The semantics are equivalent to that of the EVM's
295    /// [`BASEFEE`] opcode.
296    ///
297    /// [`BASEFEE`]: https://www.evm.codes/#48
298    fn block_basefee(&self) -> U256;
299    /// Gets the coinbase of the current block, which on Arbitrum chains is the L1 batch poster's
300    /// address. This differs from Ethereum where the validator including the transaction
301    /// determines the coinbase.
302    fn block_coinbase(&self) -> Address;
303    /// Gets a bounded estimate of the L1 block number at which the Sequencer sequenced the
304    /// transaction. See [`Block Numbers and Time`] for more information on how this value is
305    /// determined.
306    ///
307    /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time
308    fn block_number(&self) -> u64;
309    /// Gets a bounded estimate of the Unix timestamp at which the Sequencer sequenced the
310    /// transaction. See [`Block Numbers and Time`] for more information on how this value is
311    /// determined.
312    ///
313    /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time
314    fn block_timestamp(&self) -> u64;
315    /// Gets the gas limit of the current block. The semantics are equivalent to that of the EVM's
316    /// [`GAS_LIMIT`] opcode. Note that as of the time of this writing, `evm.codes` incorrectly
317    /// implies that the opcode returns the gas limit of the current transaction.  When in doubt,
318    /// consult [`The Ethereum Yellow Paper`].
319    ///
320    /// [`GAS_LIMIT`]: https://www.evm.codes/#45
321    /// [`The Ethereum Yellow Paper`]: https://ethereum.github.io/yellowpaper/paper.pdf
322    fn block_gas_limit(&self) -> u64;
323}
324
325/// Provides access to the chain details of the host environment.
326pub trait ChainAccess {
327    /// Gets the unique chain identifier of the Arbitrum chain. The semantics are equivalent to
328    /// that of the EVM's [`CHAIN_ID`] opcode.
329    ///
330    /// [`CHAIN_ID`]: https://www.evm.codes/#46
331    fn chain_id(&self) -> u64;
332}
333
334/// Provides access to account details of addresses of the host environment.
335pub trait AccountAccess {
336    /// Gets the ETH balance in wei of the account at the given address.
337    /// The semantics are equivalent to that of the EVM's [`BALANCE`] opcode.
338    ///
339    /// [`BALANCE`]: https://www.evm.codes/#31
340    fn balance(&self, account: Address) -> U256;
341    /// Gets the address of the current program. The semantics are equivalent to that of the EVM's
342    /// [`ADDRESS`] opcode.
343    ///
344    /// [`ADDRESS`]: https://www.evm.codes/#30
345    fn contract_address(&self) -> Address;
346    /// Gets a subset of the code from the account at the given address. The semantics are identical to that
347    /// of the EVM's [`EXT_CODE_COPY`] opcode, aside from one small detail: the write to the buffer `dest` will
348    /// stop after the last byte is written. This is unlike the EVM, which right pads with zeros in this scenario.
349    /// The return value is the number of bytes written, which allows the caller to detect if this has occurred.
350    ///
351    /// [`EXT_CODE_COPY`]: https://www.evm.codes/#3C
352    fn code(&self, account: Address) -> Vec<u8>;
353    /// Gets the size of the code in bytes at the given address. The semantics are equivalent
354    /// to that of the EVM's [`EXT_CODESIZE`].
355    ///
356    /// [`EXT_CODESIZE`]: https://www.evm.codes/#3B
357    fn code_size(&self, account: Address) -> usize;
358    /// Gets the code hash of the account at the given address. The semantics are equivalent
359    /// to that of the EVM's [`EXT_CODEHASH`] opcode. Note that the code hash of an account without
360    /// code will be the empty hash
361    /// `keccak("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`.
362    ///
363    /// [`EXT_CODEHASH`]: https://www.evm.codes/#3F
364    fn code_hash(&self, account: Address) -> B256;
365}
366
367/// Provides the ability to pay for memory growth of a Stylus contract.
368pub trait MemoryAccess {
369    /// The `entrypoint!` macro handles importing this hostio, which is required if the
370    /// program's memory grows. Otherwise compilation through the `ArbWasm` precompile will revert.
371    /// Internally the Stylus VM forces calls to this hostio whenever new WASM pages are allocated.
372    /// Calls made voluntarily will unproductively consume gas.
373    fn pay_for_memory_grow(&self, pages: u16);
374}
375
376/// Provides access to transaction details of a Stylus contract.
377pub trait MessageAccess {
378    /// Gets the address of the account that called the program. For normal L2-to-L2 transactions
379    /// the semantics are equivalent to that of the EVM's [`CALLER`] opcode, including in cases
380    /// arising from [`DELEGATE_CALL`].
381    ///
382    /// For L1-to-L2 retryable ticket transactions, the top-level sender's address will be aliased.
383    /// See [`Retryable Ticket Address Aliasing`] for more information on how this works.
384    ///
385    /// [`CALLER`]: https://www.evm.codes/#33
386    /// [`DELEGATE_CALL`]: https://www.evm.codes/#f4
387    /// [`Retryable Ticket Address Aliasing`]: https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing
388    fn msg_sender(&self) -> Address;
389    /// Whether the current call is reentrant.
390    fn msg_reentrant(&self) -> bool;
391    /// Get the ETH value in wei sent to the program. The semantics are equivalent to that of the
392    /// EVM's [`CALLVALUE`] opcode.
393    ///
394    /// [`CALLVALUE`]: https://www.evm.codes/#34
395    fn msg_value(&self) -> U256;
396    /// Gets the top-level sender of the transaction. The semantics are equivalent to that of the
397    /// EVM's [`ORIGIN`] opcode.
398    ///
399    /// [`ORIGIN`]: https://www.evm.codes/#32
400    fn tx_origin(&self) -> Address;
401}
402
403/// Provides access to metering values such as EVM gas and Stylus ink used and remaining,
404/// as well as details of their prices based on the host environment.
405pub trait MeteringAccess {
406    /// Gets the amount of gas left after paying for the cost of this hostio. The semantics are
407    /// equivalent to that of the EVM's [`GAS`] opcode.
408    ///
409    /// [`GAS`]: https://www.evm.codes/#5a
410    fn evm_gas_left(&self) -> u64;
411    /// Gets the amount of ink remaining after paying for the cost of this hostio. The semantics
412    /// are equivalent to that of the EVM's [`GAS`] opcode, except the units are in ink. See
413    /// [`Ink and Gas`] for more information on Stylus's compute pricing.
414    ///
415    /// [`GAS`]: https://www.evm.codes/#5a
416    /// [`Ink and Gas`]: https://docs.arbitrum.io/stylus/concepts/gas-metering
417    fn evm_ink_left(&self) -> u64;
418    /// Gets the gas price in wei per gas, which on Arbitrum chains equals the basefee. The
419    /// semantics are equivalent to that of the EVM's [`GAS_PRICE`] opcode.
420    ///
421    /// [`GAS_PRICE`]: https://www.evm.codes/#3A
422    fn tx_gas_price(&self) -> U256;
423    /// Gets the price of ink in evm gas basis points. See [`Ink and Gas`] for more information on
424    /// Stylus's compute-pricing model. Arbitrum enforces a minimum gas price floor, so tx ink
425    /// price should always return a value greater than zero.
426    ///
427    /// [`Ink and Gas`]: https://docs.arbitrum.io/stylus/concepts/gas-metering
428    fn tx_ink_price(&self) -> u32;
429
430    /// Computes the units of gas per a specified amount of ink.
431    fn ink_to_gas(&self, ink: u64) -> u64 {
432        let price = self.tx_ink_price();
433        if price == 0 {
434            return 0;
435        }
436        ink / price as u64
437    }
438
439    /// Computes the units of ink per a specified amount of gas.
440    fn gas_to_ink(&self, gas: u64) -> u64 {
441        gas.saturating_mul(self.tx_ink_price().into())
442    }
443}
444
445/// Provides access to the ability to emit logs from a Stylus contract.
446pub trait RawLogAccess {
447    /// Emits an EVM log with the given number of topics and data, the first bytes of which should
448    /// be the 32-byte-aligned topic data. The semantics are equivalent to that of the EVM's
449    /// [`LOG0`], [`LOG1`], [`LOG2`], [`LOG3`], and [`LOG4`] opcodes based on the number of topics
450    /// specified. Requesting more than `4` topics will induce a revert.
451    ///
452    /// [`LOG0`]: https://www.evm.codes/#a0
453    /// [`LOG1`]: https://www.evm.codes/#a1
454    /// [`LOG2`]: https://www.evm.codes/#a2
455    /// [`LOG3`]: https://www.evm.codes/#a3
456    /// [`LOG4`]: https://www.evm.codes/#a4
457    fn emit_log(&self, input: &[u8], num_topics: usize);
458    /// Emits a raw log from topics and data.
459    fn raw_log(&self, topics: &[B256], data: &[u8]) -> Result<(), &'static str>;
460}
461
462/// Provides access to the ability to emit typed logs from a Stylus contract.
463pub trait LogAccess: RawLogAccess {
464    /// Emits a typed alloy log.
465    ///
466    /// ```rust,ignore
467    /// sol! {
468    ///    event Transfer(
469    ///        address indexed from,
470    ///        address indexed to,
471    ///        uint256 indexed token_id
472    ///    );
473    /// }
474    ///
475    /// self.vm().log(Transfer { from, to, token_id });
476    /// ```
477    fn log<T: SolEvent>(&self, event: T) {
478        // According to the alloy docs, encode_topics_raw fails only if the array is too small
479
480        let mut topics = [WordToken::default(); 4];
481        event.encode_topics_raw(&mut topics).unwrap();
482
483        let count = T::TopicList::COUNT;
484        let mut bytes = Vec::with_capacity(32 * count);
485        for topic in &topics[..count] {
486            bytes.extend_from_slice(topic.as_slice());
487        }
488        event.encode_data_to(&mut bytes);
489        self.emit_log(&bytes, count)
490    }
491}
492
493impl<T: RawLogAccess> LogAccess for T {}