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