$npx -y skills add OpenZeppelin/openzeppelin-skills --skill setup-stylus-contractsSet up a Stylus smart contract project with OpenZeppelin Contracts for Stylus on Arbitrum. Use when users need to: (1) install Rust toolchain and WASM target for Stylus, (2) create a new Cargo Stylus project, (3) add OpenZeppelin Stylus dependencies to Cargo.toml, or (4) understa
| 1 | # Stylus Setup |
| 2 | |
| 3 | ## Rust & Cargo Stylus Setup |
| 4 | |
| 5 | Install the Rust toolchain and WASM target: |
| 6 | |
| 7 | ```bash |
| 8 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh |
| 9 | rustup target add wasm32-unknown-unknown |
| 10 | ``` |
| 11 | |
| 12 | Install the Cargo Stylus CLI: |
| 13 | |
| 14 | ```bash |
| 15 | cargo install --force cargo-stylus |
| 16 | ``` |
| 17 | |
| 18 | Create a new Stylus project: |
| 19 | |
| 20 | ```bash |
| 21 | cargo stylus new my_project |
| 22 | ``` |
| 23 | |
| 24 | > A Rust nightly toolchain is required. The project should include a `rust-toolchain.toml` specifying the nightly channel, `rust-src` component, and `wasm32-unknown-unknown` target. Check the [rust-contracts-stylus repo](https://github.com/OpenZeppelin/rust-contracts-stylus) for the current recommended nightly date. |
| 25 | |
| 26 | ## Adding OpenZeppelin Dependencies |
| 27 | |
| 28 | Look up the current version from [crates.io/crates/openzeppelin-stylus](https://crates.io/crates/openzeppelin-stylus) before adding. Add to `Cargo.toml`: |
| 29 | |
| 30 | ```toml |
| 31 | [dependencies] |
| 32 | openzeppelin-stylus = "=<VERSION>" |
| 33 | ``` |
| 34 | |
| 35 | Enable the `export-abi` feature flag for ABI generation: |
| 36 | |
| 37 | ```toml |
| 38 | [features] |
| 39 | export-abi = ["openzeppelin-stylus/export-abi"] |
| 40 | ``` |
| 41 | |
| 42 | The crate must be compiled as both a library and a cdylib: |
| 43 | |
| 44 | ```toml |
| 45 | [lib] |
| 46 | crate-type = ["lib", "cdylib"] |
| 47 | ``` |
| 48 | |
| 49 | ## Import Conventions |
| 50 | |
| 51 | Imports use `openzeppelin_stylus` (underscores) as the crate root: |
| 52 | |
| 53 | ```rust |
| 54 | use openzeppelin_stylus::token::erc20::{Erc20, IErc20}; |
| 55 | use openzeppelin_stylus::access::ownable::{Ownable, IOwnable}; |
| 56 | use openzeppelin_stylus::utils::pausable::{Pausable, IPausable}; |
| 57 | use openzeppelin_stylus::utils::introspection::erc165::IErc165; |
| 58 | ``` |
| 59 | |
| 60 | Contracts use `#[storage]` and `#[entrypoint]` on the main struct, embedding OpenZeppelin components as fields: |
| 61 | |
| 62 | ```rust |
| 63 | #[entrypoint] |
| 64 | #[storage] |
| 65 | struct MyToken { |
| 66 | erc20: Erc20, |
| 67 | ownable: Ownable, |
| 68 | } |
| 69 | ``` |
| 70 | |
| 71 | Public methods are exposed with `#[public]` and `#[implements(...)]`. The canonical pattern uses an empty impl block for dispatch registration, plus separate trait impl blocks: |
| 72 | |
| 73 | ```rust |
| 74 | #[public] |
| 75 | #[implements(IErc20<Error = erc20::Error>, IOwnable<Error = ownable::Error>)] |
| 76 | impl MyToken {} |
| 77 | |
| 78 | #[public] |
| 79 | impl IErc20 for MyToken { |
| 80 | type Error = erc20::Error; |
| 81 | // delegate to self.erc20 ... |
| 82 | } |
| 83 | ``` |
| 84 | |
| 85 | Top-level modules: `access`, `finance`, `proxy`, `token`, `utils`. |
| 86 | |
| 87 | ## Build & Deploy Basics |
| 88 | |
| 89 | Validate the contract compiles to valid Stylus WASM: |
| 90 | |
| 91 | ```bash |
| 92 | cargo stylus check |
| 93 | ``` |
| 94 | |
| 95 | Export the Solidity ABI: |
| 96 | |
| 97 | ```bash |
| 98 | cargo stylus export-abi |
| 99 | ``` |
| 100 | |
| 101 | Deploy to an Arbitrum Stylus endpoint: |
| 102 | |
| 103 | ```bash |
| 104 | cargo stylus deploy --endpoint="<RPC_URL>" --private-key-path="<KEY_FILE>" |
| 105 | ``` |