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