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}