Skip to main content

stylus_proc/
lib.rs

1// Copyright 2022-2026, Offchain Labs, Inc.
2// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md
3
4//! Procedural macros for [The Stylus SDK][sdk].
5//!
6//! You can import these via
7//!
8//! ```
9//! use stylus_sdk::prelude::*;
10//! ```
11//!
12//! For a guided exploration of the features, please see the comprehensive [Feature
13//! Overview][overview].
14//!
15//! [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#calls
16//! [sdk]: https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html
17
18#![warn(missing_docs)]
19
20use proc_macro::TokenStream;
21use proc_macro_error::proc_macro_error;
22
23/// Generates a pretty error message.
24/// Note that this macro is declared before all modules so that they can use it.
25macro_rules! error {
26    ($tokens:expr, $($msg:expr),+ $(,)?) => {{
27        let error = syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+));
28        return error.to_compile_error().into();
29    }};
30    (@ $tokens:expr, $($msg:expr),+ $(,)?) => {{
31        return Err(syn::Error::new(syn::spanned::Spanned::span(&$tokens), format!($($msg),+)))
32    }};
33}
34
35mod consts;
36mod impls;
37mod imports;
38mod macros;
39mod types;
40mod utils;
41
42/// Allows a Rust `struct` to be used in persistent storage.
43///
44/// ```
45/// extern crate alloc;
46/// # use stylus_sdk::storage::{StorageAddress, StorageBool};
47/// # use stylus_proc::storage;
48/// # use stylus_sdk::prelude::*;
49/// #[storage]
50/// pub struct Contract {
51///     owner: StorageAddress,
52///     active: StorageBool,
53///     sub_struct: SubStruct,
54/// }
55///
56/// #[storage]
57/// pub struct SubStruct {
58///     number: StorageBool,
59/// }
60/// ```
61///
62/// Each field must implement [`StorageType`]. This includes other structs, which will
63/// implement the `trait` automatically when [`#[storage]`][storage] is applied.
64///
65/// One may even implement [`StorageType`] to define custom storage entries, though this is rarely
66/// necessary since the [Stylus SDK][sdk] intends to include all standard Solidity types
67/// out-of-the-box.
68///
69/// Please refer to the [SDK Feature Overview][overview] for more information on defining storage.
70///
71/// [storage]: macro@storage
72/// [`StorageType`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.StorageType.html
73/// [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#storage
74/// [sdk]: https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html
75#[proc_macro_attribute]
76#[proc_macro_error]
77pub fn storage(attr: TokenStream, input: TokenStream) -> TokenStream {
78    macros::storage(attr, input)
79}
80
81/// The types in [`#[storage]`][storage] are laid out in the EVM state trie exactly
82/// as they are in [Solidity][solidity]. This means that the fields of a `struct` definition will
83/// map to the same storage slots as they would in EVM programming languages. Hence, it is often
84/// nice to define types using Solidity syntax, which makes this guarantee easier to see.
85///
86/// ```
87/// extern crate alloc;
88/// # use stylus_sdk::prelude::*;
89/// # use stylus_proc::sol_storage;
90/// sol_storage! {
91///     pub struct Contract {
92///         address owner;                      // becomes a StorageAddress
93///         bool active;                        // becomes a StorageBool
94///         SubStruct sub_struct;
95///     }
96///
97///     pub struct SubStruct {
98///         // other solidity fields, such as
99///         mapping(address => uint) balances;  // becomes a StorageMap
100///         Delegate[] delegates;               // becomes a StorageVec
101///     }
102///     pub struct Delegate {
103///     }
104/// }
105/// ```
106///
107/// The above will expand to equivalent definitions in Rust, with each structure implementing the
108/// [`StorageType`] `trait`. Many contracts, like [the ERC 20 example][erc20], do exactly this.
109///
110/// Because the layout is identical to [Solidity's][solidity], existing Solidity smart contracts can
111/// upgrade to Rust without fear of storage slots not lining up. You simply copy-paste your type
112/// definitions.
113///
114/// Consequently, the order of fields will affect the JSON ABIs produced that explorers and tooling
115/// might use. Most developers don't need to worry about this though and can freely order their
116/// types when working on a Rust contract from scratch.
117///
118///
119/// Please refer to the [SDK Feature Overview][overview] for more information on defining storage.
120///
121/// [storage]: macro@storage
122/// [`StorageType`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.StorageType.html
123/// [solidity]: https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html
124/// [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#erase-and-deriveerase
125/// [erc20]: https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/examples/erc20/src/main.rs
126#[proc_macro]
127#[proc_macro_error]
128pub fn sol_storage(input: TokenStream) -> TokenStream {
129    macros::sol_storage(input)
130}
131
132/// Facilitates calls to other contracts.
133///
134/// This macro defines a `struct` for each of the Solidity interfaces provided.
135///
136/// ```
137/// # use stylus_proc::sol_interface;
138/// sol_interface! {
139///     interface IService {
140///         function makePayment(address user) external payable returns (string);
141///         function getConstant() external pure returns (bytes32);
142///     }
143///
144///     interface ITree {
145///         // other interface methods
146///     }
147/// }
148/// ```
149///
150/// The above will define `IService` and `ITree` for calling the methods of the two contracts.
151///
152/// For example, `IService` will have a `make_payment` method that accepts an [`Address`] and
153/// returns a [`B256`].
154///
155/// Currently only functions are supported, and any other items in the interface will cause an
156/// error. Additionally, each function must be marked `external`. Inheritance is not supported.
157///
158/// ```
159/// use alloy_primitives::Address;
160/// use stylus_sdk::{
161///     prelude::*,
162///     stylus_core::{calls::errors::*, host::*},
163/// };
164/// # use stylus_proc::sol_interface;
165///
166/// # sol_interface! {
167/// #     interface IService {
168/// #         function makePayment(address user) external payable returns (string);
169/// #     }
170/// # }
171/// # mod evm { pub fn gas_left() -> u64 { 100 } }
172/// # mod msg { pub fn value() -> alloy_primitives::U256 { 100.try_into().unwrap() } }
173/// pub fn do_call(host: &impl Host, account: IService, user: Address) -> Result<String, Error> {
174///     let config = Call::new()
175///         .gas(host.evm_gas_left() / 2) // limit to half the gas left
176///         .value(host.msg_value()); // set the callvalue
177///
178///     account.make_payment(host, config, user) // note the snake case
179/// }
180/// ```
181///
182/// Observe the casing change. [`sol_interface!`] computes the selector based on the exact name
183/// passed in, which should almost always be `camelCase`. For aesthetics, the rust functions will
184/// instead use `snake_case`.
185///
186/// Note that structs may be used, as return types for example. Trying to reference structs using
187/// the Solidity path separator (`module.MyStruct`) is supported and paths will be converted to
188/// Rust syntax (`module::MyStruct`).
189///
190/// # Reentrant calls
191///
192/// Cross-contract calls automatically [`flush`] or [`clear`] the [`StorageCache`] to safeguard
193/// state. This happens via the type system -- no special feature flags are required.
194///
195/// ```
196/// # extern crate alloc;
197/// # use stylus_sdk::prelude::*;
198/// # use stylus_proc::{entrypoint, public, sol_interface, storage};
199/// sol_interface! {
200///     interface IMethods {
201///         function pureFoo() external pure;
202///         function viewFoo() external view;
203///         function writeFoo() external;
204///     }
205/// }
206///
207/// #[entrypoint]
208/// #[storage]
209/// struct Contract {}
210/// #[public]
211/// impl Contract {
212///     pub fn call_pure(&self, methods: IMethods) -> Result<(), Vec<u8>> {
213///         let cfg = Call::new();
214///         Ok(methods.pure_foo(self.vm(), cfg)?) // `pure` methods might lie about not being `view`
215///     }
216///
217///     pub fn call_view(&self, methods: IMethods) -> Result<(), Vec<u8>> {
218///         let cfg = Call::new();
219///         Ok(methods.view_foo(self.vm(), cfg)?)
220///     }
221///
222///     pub fn call_write(&mut self, methods: IMethods) -> Result<(), Vec<u8>> {
223///         let cfg = Call::new_mutating(self);
224///         Ok(methods.write_foo(self.vm(), cfg)?)
225///     }
226/// }
227/// ```
228///
229/// Another example of making a mutable, payable call to a contract using the [`sol_interface!`]
230/// macro.
231///
232/// ```
233/// use alloy_primitives::Address;
234/// use stylus_sdk::{
235///     prelude::*,
236///     stylus_core::{calls::errors::*, host::*},
237/// };
238/// # use stylus_proc::sol_interface;
239///
240/// # sol_interface! {
241/// #     interface IService {
242/// #         function makePayment(address user) external payable returns (string);
243/// #     }
244/// # }
245/// # mod evm { pub fn gas_left() -> u64 { 100 } }
246/// # mod msg { pub fn value() -> alloy_primitives::U256 { 100.try_into().unwrap() } }
247/// pub fn do_call(
248///     host: &impl Host,
249///     account: IService, // serializes as an Address
250///     user: Address,
251/// ) -> Result<String, Error> {
252///     let config = Call::new()
253///         .gas(evm::gas_left() / 2) // limit to half the gas left
254///         .value(msg::value()); // set the callvalue
255///
256///     account.make_payment(host, config, user) // note the snake case
257/// }
258/// ```
259///
260/// Note that in the context of a [`#[public]`][public] call, the `&mut impl` argument will
261/// correctly distinguish the method as being `write` or `payable`.
262///
263/// [sol_interface]: macro@sol_interface
264/// [public]: macro@public
265/// [`TopLevelStorage`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.TopLevelStorage.html
266/// [`StorageCache`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/struct.StorageCache.html
267/// [`flush`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/struct.StorageCache.html#method.flush
268/// [`clear`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/struct.StorageCache.html#method.clear
269/// [`Address`]: https://docs.rs/alloy-primitives/latest/alloy_primitives/struct.Address.html
270/// [`B256`]: https://docs.rs/alloy-primitives/latest/alloy_primitives/aliases/type.B256.html
271/// [`Call`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/call/struct.Call.html
272#[proc_macro]
273#[proc_macro_error]
274pub fn sol_interface(input: TokenStream) -> TokenStream {
275    macros::sol_interface(input)
276}
277
278/// Some [`StorageType`] values implement [`Erase`], which provides an [`erase()`] method for
279/// clearing state. [The Stylus SDK][sdk] implements [`Erase`] for all primitives, and for vectors
280/// of primitives, but not for maps. This is because a Solidity mapping does not provide iteration,
281/// and so it's generally impossible to know which slots to clear.
282///
283/// Structs may also be [`Erase`] if all of the fields are. `#[derive(Erase)]`
284/// lets you do this automatically.
285///
286/// ```
287/// extern crate alloc;
288/// # use stylus_proc::{Erase, sol_storage};
289/// # use stylus_sdk::prelude::*;
290/// sol_storage! {
291///    #[derive(Erase)]
292///    pub struct Contract {
293///        address owner;              // can erase primitive
294///        uint256[] hashes;           // can erase vector of primitive
295///    }
296///
297///    pub struct NotErase {
298///        mapping(address => uint) balances; // can't erase a map
299///        mapping(uint => uint)[] roots;     // can't erase vector of maps
300///    }
301/// }
302/// ```
303///
304/// You can also implement [`Erase`] manually if desired. Note that the reason we care about
305/// [`Erase`] at all is that you get storage refunds when clearing state, lowering fees. There's
306/// also minor implications for storage patterns using `unsafe` Rust.
307///
308/// Please refer to the [SDK Feature Overview][overview] for more information on defining storage.
309///
310/// [`StorageType`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.StorageType.html
311/// [`Erase`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.Erase.html
312/// [`erase()`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.Erase.html#tymethod.erase
313/// [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#storage
314/// [sdk]: https://docs.rs/stylus-sdk/latest/stylus_sdk/index.html
315#[proc_macro_derive(Erase)]
316#[proc_macro_error]
317pub fn derive_erase(input: TokenStream) -> TokenStream {
318    macros::derive_erase(input)
319}
320
321/// Allows an error `enum` to be used in method signatures.
322///
323/// ```
324/// # use alloy_sol_types::sol;
325/// # use stylus_proc::{public, SolidityError};
326/// # extern crate alloc;
327/// sol! {
328///     error InsufficientBalance(address from, uint256 have, uint256 want);
329///     error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want);
330/// }
331///
332/// #[derive(SolidityError)]
333/// pub enum Erc20Error {
334///     InsufficientBalance(InsufficientBalance),
335///     InsufficientAllowance(InsufficientAllowance),
336/// }
337///
338/// # struct Contract {}
339/// #[public]
340/// impl Contract {
341///     pub fn fallible_method() -> Result<(), Erc20Error> {
342///         // code that might revert
343/// #       Ok(())
344///     }
345/// }
346/// ```
347///
348/// Under the hood, the above macro works by implementing `From<Erc20Error>` for `Vec<u8>`
349/// along with printing code for abi-export.
350#[proc_macro_derive(SolidityError)]
351#[proc_macro_error]
352pub fn derive_solidity_error(input: TokenStream) -> TokenStream {
353    macros::derive_solidity_error(input)
354}
355
356/// Defines the entrypoint, which is where Stylus execution begins.
357/// Without it the contract will fail to pass [`cargo stylus check`][check].
358/// Most commonly this macro is used to annotate the top level storage `struct`.
359///
360/// ```
361/// # extern crate alloc;
362/// # use stylus_proc::{entrypoint, public, sol_storage};
363/// # use stylus_sdk::prelude::*;
364/// sol_storage! {
365///     #[entrypoint]
366///     pub struct Contract {
367///     }
368///
369///     // only one entrypoint is allowed
370///     pub struct SubStruct {
371///     }
372/// }
373/// # #[public] impl Contract {}
374/// ```
375///
376/// The above will make the public methods of Contract the first to consider during invocation.
377/// See [`#[public]`][public] for more information on method selection.
378///
379/// # Bytes-in, bytes-out programming
380///
381/// A less common usage of [`#[entrypoint]`][entrypoint] is for low-level, bytes-in bytes-out
382/// programming. When applied to a free-standing function, a different way of writing smart
383/// contracts becomes possible, wherein the Stylus SDK's macros and storage types are entirely
384/// optional.
385///
386/// ```
387/// extern crate alloc;
388/// # use stylus_sdk::ArbResult;
389/// # use stylus_proc::entrypoint;
390/// # use stylus_sdk::prelude::*;
391/// #[entrypoint]
392/// fn entrypoint(
393///     calldata: Vec<u8>,
394///     _: alloc::boxed::Box<dyn stylus_sdk::stylus_core::Host>,
395/// ) -> ArbResult {
396///     // bytes-in, bytes-out programming
397/// #   Ok(Vec::new())
398/// }
399/// ```
400///
401/// # Reentrancy
402///
403/// If a contract calls another that then calls the first, it is said to be reentrant.
404/// Numerous exploits and hacks in Web3 are attributable to developers misusing or not fully
405/// understanding reentrant patterns. Always follow the checks-effects-interactions pattern.
406///
407/// ## Storage cache flushing
408///
409/// The SDK's primary reentrancy safety mechanism is automatic storage cache management.
410/// The high-level call functions in `stylus_sdk::call` (`call`, `delegate_call`, and
411/// `static_call`) flush the storage cache before every external call, ensuring that
412/// pending writes are persisted to storage before control is handed to another contract:
413///
414/// - `call` and `delegate_call` flush and clear (persist dirty values, then drop the in-memory
415///   cache).
416/// - `static_call` flushes without clearing (since static calls cannot modify storage, the cache
417///   remains valid).
418///
419/// This happens unconditionally for all contracts. When using `RawCall` directly, the
420/// cache is **not** flushed automatically -- if you have pending storage writes that
421/// the callee might read, call `.flush_storage_cache()` to persist dirty values without
422/// dropping the cache, or `.clear_storage_cache()` to persist and drop the cache
423/// (matching what `call` and `delegate_call` do).
424///
425/// ## The `reentrant` feature flag (deprecated)
426///
427/// The `reentrant` feature flag is **deprecated** and will be removed in a future
428/// release. Previously, contracts without this flag would automatically revert on
429/// reentrant calls via a `msg_reentrant()` check at the entrypoint. This guard is
430/// redundant — reentrancy safety is already provided by the cache flushing described
431/// above — and it indiscriminately blocks all reentrant calls, preventing use cases
432/// that require them.
433///
434/// # [`TopLevelStorage`]
435///
436/// The [`#[entrypoint]`][entrypoint] macro will automatically implement the [`TopLevelStorage`]
437/// `trait` for the annotated `struct`. The single type implementing [`TopLevelStorage`] is special
438/// in that mutable access to it represents mutable access to the entire program's state.
439/// This has implications for calls via [`sol_interface`].
440///
441/// [`TopLevelStorage`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/storage/trait.TopLevelStorage.html
442/// [`sol_interface`]: macro@sol_interface
443/// [entrypoint]: macro@entrypoint
444/// [public]: macro@public
445/// [check]: https://github.com/OffchainLabs/cargo-stylus#developing-with-stylus
446#[proc_macro_attribute]
447#[proc_macro_error]
448pub fn entrypoint(attr: TokenStream, input: TokenStream) -> TokenStream {
449    macros::entrypoint(attr, input)
450}
451
452/// Just as with storage, Stylus SDK methods are Solidity ABI-equivalent. This means that contracts
453/// written in different programming languages are fully interoperable. You can even automatically
454/// export your Rust contract as a Solidity interface so that others can add it to their Solidity
455/// projects.
456///
457/// This macro makes methods "public" so that other contracts can call them by implementing the
458/// [`Router`] trait.
459///
460/// ```
461/// # extern crate alloc;
462/// # use stylus_sdk::storage::StorageAddress;
463/// # use stylus_proc::public;
464/// # use alloy_primitives::Address;
465/// # struct Contract {
466/// #     owner: StorageAddress,
467/// # }
468/// #[public]
469/// impl Contract {
470///     // our owner method is now callable by other contracts
471///     pub fn owner(&self) -> Result<Address, Vec<u8>> {
472///         Ok(self.owner.get())
473///     }
474/// }
475///
476/// impl Contract {
477///     // our set_owner method is not
478///     pub fn set_owner(&mut self, new_owner: Address) -> Result<(), Vec<u8>> {
479///         // ...
480/// #       Ok(())
481///     }
482/// }
483/// ```
484///
485/// In is example, [`Vec<u8>`] becomes the program's revert data.
486///
487/// # [`#[payable]`][payable]
488///
489/// As in Solidity, methods may accept ETH as call value.
490///
491/// ```
492/// # extern crate alloc;
493/// # use alloy_primitives::Address;
494/// # use stylus_proc::{entrypoint, public, storage};
495/// # use stylus_sdk::prelude::*;
496/// # #[entrypoint] #[storage] struct Contract { #[borrow] erc20: Erc20, }
497/// # mod msg {
498/// #     use alloy_primitives::Address;
499/// #     pub fn sender() -> Address { Address::ZERO }
500/// #     pub fn value() -> u32 { 0 }
501/// # }
502/// #[public]
503/// impl Contract {
504///     #[payable]
505///     pub fn credit(&mut self) -> Result<(), Vec<u8>> {
506///         self.erc20.add_balance(msg::sender(), msg::value())
507///     }
508/// }
509/// # #[storage] struct Erc20;
510/// # #[public]
511/// # impl Erc20 {
512/// #     pub fn add_balance(&self, sender: Address, value: u32) -> Result<(), Vec<u8>> {
513/// #         Ok(())
514/// #     }
515/// # }
516/// ```
517///
518/// In the above, [msg::value][value] is the amount of ETH passed to the contract in wei, which may
519/// be used to pay for something depending on the contract's business logic. Note that you have to
520/// annotate the method with [`#[payable]`][payable], or else calls to it will revert. This is
521/// required as a safety measure to prevent users losing funds to methods that didn't intend to
522/// accept ether.
523///
524/// # Constructor
525///
526/// Constructors provide a standard way to deploy, activate, and initialize a stylus contract
527/// atomically. Without them, it isn’t possible to guarantee that the Stylus contract initialization
528/// code will be executed before other methods. When using Stylus constructors, cargo-stylus sends a
529/// transaction to a proxy-contract called StylusDeployer that performs all necessary steps.
530///
531/// The constructor function must be annotated with the `#[constructor]` attribute. It can have any
532/// name, but it is advisable to call it `constructor`. There must be no constructor definition or a
533/// single constructor for a contract. Like Solidity, function overloading for constructors is not
534/// supported. Constructors may be annotated with the [`#[payable]`][payable] attribute if they are
535/// supposed to receive Ether.
536///
537/// The constructor must receive the self parameter, and it can have any number of other parameters.
538/// The values for these parameters will be passed to the constructor when deploying the contract.
539/// The constructor should return no value or a result value with a unit type and a vector of bytes
540/// (`Result<(), Vec<u8>>`). If the constructor returns an error, the deployment will revert.
541///
542/// The SDK will ensure the constructor is called only once. To do so, it will wrap the constructor
543/// with a function that reads and writes to a specific slot in storage. When called, the
544/// constructor wrapper will check the contents of the specific slot and revert if it is different
545/// from zero. Then, after executing the constructor method, the wrapper will write a value to the
546/// specific slot, ensuring the next call to the constructor will revert.
547///
548/// ```
549/// # extern crate alloc;
550/// # use stylus_sdk::storage::StorageAddress;
551/// # use stylus_proc::public;
552/// # use alloy_primitives::Address;
553/// # struct Contract {
554/// #     owner: StorageAddress,
555/// # }
556/// #[public]
557/// impl Contract {
558///     #[constructor]
559///     pub fn constructor(&mut self, owner: Address) -> Result<(), Vec<u8>> {
560///         self.owner.set(owner);
561///         Ok(())
562///     }
563/// }
564/// ```
565///
566/// In the example above, the constructor receives a single parameter: the address of the contract's
567/// owner. Note that we shouldn't use `self.vm().msg_sender()` because that will return the address
568/// of StylusDeployer. Instead, we explicitly pass the address of the contract's owner.
569///
570/// # [`pure`][pure] [`view`][view], and `write`
571///
572/// For non-payable methods the [`#[public]`][public] macro can figure state mutability out for you
573/// based on the types of the arguments. Functions with `&self` will be considered `view`, those
574/// with `&mut self` will be considered `write`, and those with neither will be considered `pure`.
575/// Please note that `pure` and `view` functions may change the state of other contracts by calling
576/// into them.
577///
578/// Please refer to the [SDK Feature Overview][overview] for more information on defining methods.
579///
580/// # Exporting a Solidity interface
581///
582/// Recall that Stylus contracts are fully interoperable across all languages, including Solidity.
583/// The Stylus SDK provides tools for exporting a Solidity interface for your contract so that
584/// others can call it. This is usually done with the cargo stylus [CLI tool][cli].
585///
586/// The SDK does this automatically via a feature flag called `export-abi` that causes the
587/// [`#[public]`][public] and [`#[entrypoint]`][entrypoint] macros to generate a `main` function
588/// that prints the Solidity ABI to the console.
589///
590/// ```sh
591/// cargo run --features export-abi --target <triple>
592/// ```
593///
594/// Note that because the above actually generates a `main` function that you need to run, the
595/// target can't be `wasm32-unknown-unknown` like normal. Instead you'll need to pass in your target
596/// triple, which cargo stylus figures out for you. This `main` function is also why the following
597/// commonly appears in the `main.rs` file of Stylus contracts.
598///
599/// ```no_run
600/// #![cfg_attr(not(feature = "export-abi"), no_main)]
601/// ```
602///
603/// Here's an example output. Observe that the method names change from Rust's `snake_case` to
604/// Solidity's `camelCase`. For compatibility reasons, onchain method selectors default to
605/// `camelCase`. You can customize the ABI name used for selector computation with `#[selector(name
606/// = "...")]`.
607///
608/// Note too that you can use argument names like "address"
609/// without fear. The SDK will prepend an `_` when necessary.
610///
611/// ```solidity
612/// interface Erc20 {
613///     function name() external pure returns (string memory);
614///
615///     function balanceOf(address _address) external view returns (uint256);
616/// }
617///
618/// interface Weth is Erc20 {
619///     function mint() external payable;
620///
621///     function burn(uint256 amount) external;
622/// }
623/// ```
624///
625/// # Selector collision detection
626///
627/// The macro detects ABI selector collisions at compile time within a single `#[public]`
628/// block, whether it annotates an impl block or a trait definition. If two methods produce
629/// the same 4-byte selector (computed from the keccak256 hash of the Solidity function
630/// signature string, e.g. `transfer(uint256,address)`), compilation will fail
631/// with a descriptive error.
632///
633/// Collision checks are gated out when `contract-client-gen` is enabled, because that mode
634/// generates call stubs for external contracts where selector routing is not emitted.
635///
636/// **Not detected:** cross-block collisions (across separate `#[public]` blocks) and
637/// collisions with selectors inherited via `#[implements(...)]`. When using
638/// `#[implements]`, manually verify that your function selectors do not collide with
639/// those of the inherited trait's methods.
640///
641/// [storage]: macro@storage
642/// [sol_storage]: macro@sol_storage
643/// [entrypoint]: macro@entrypoint
644/// [public]: macro@public
645/// [overview]: https://docs.arbitrum.io/stylus/reference/rust-sdk-guide#methods
646/// [`Router`]: https://docs.rs/stylus-sdk/latest/stylus_sdk/abi/trait.Router.html
647/// [Borrow]: https://doc.rust-lang.org/std/borrow/trait.Borrow.html
648/// [BorrowMut]: https://doc.rust-lang.org/std/borrow/trait.BorrowMut.html
649/// [value]: https://docs.rs/stylus-sdk/latest/stylus_sdk/msg/fn.value.html
650/// [payable]: https://docs.alchemy.com/docs/solidity-payable-functions
651/// [view]: https://docs.soliditylang.org/en/develop/contracts.html#view-functions
652/// [pure]: https://docs.soliditylang.org/en/develop/contracts.html#pure-functions
653/// [cli]: https://github.com/OffchainLabs/cargo-stylus#exporting-solidity-abis
654/// [dfs]: https://en.wikipedia.org/wiki/Depth-first_search
655#[proc_macro_attribute]
656#[proc_macro_error]
657pub fn public(attr: TokenStream, input: TokenStream) -> TokenStream {
658    macros::public(attr, input)
659}
660
661/// Implements the AbiType for arbitrary structs, allowing them to be used in external method
662/// return types and parameters. This derive is intended to be used within the
663/// [alloy_sol_types::sol] macro.
664///
665/// ```
666/// # extern crate alloc;
667/// # use alloy_sol_types::sol;
668/// # use stylus_proc::AbiType;
669/// sol! {
670///     #[derive(AbiType)]
671///     struct Foo {
672///         uint256 bar;
673///     }
674/// }
675/// ```
676#[proc_macro_derive(AbiType)]
677#[proc_macro_error]
678pub fn derive_abi_type(input: TokenStream) -> TokenStream {
679    macros::derive_abi_type(input)
680}