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}