"
+ }
+ }
+ }
+}
+```
+
+## Verifying your Timeboost setup
+
+There are multiple ways to confirm that Timeboost is correctly enabled on your chain:
+
+### Periodic startup logs
+
+When you start your sequencer with Timeboost enabled, you'll see periodic logs indicating the start of new express lane auction rounds:
+
+```shell
+New express lane auction round
+```
+
+This log indicates that the Timeboost mechanism is active and running normally.
+
+### Transaction processing confirmation
+
+After finishing a bid request, look for messages in your sequencer logs such as:
+
+```shell
+AuctionResolved: New express lane controller assigned round
+```
+
+This message confirms that your sequencer is processing express queue transactions from the express lane controller, and that Timeboost is functioning correctly.
+
+### User interaction verification
+
+Users can interact with Timeboost by submitting bids through the `auctioneer_submitBid` endpoint of your auctioneer service.
+For detailed instructions on how users can interact with Timeboost, see [How to Use Timeboost](https://docs.arbitrum.io/how-arbitrum-works/timeboost/how-to-use-timeboost).
+
+A successful bid submission will trigger the auction resolution process and generate the corresponding logs mentioned above.
diff --git a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx
index 8ec44c8492..699a81a7c1 100644
--- a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx
+++ b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/04-stake-and-validator-configurations.mdx
@@ -1,22 +1,26 @@
---
-title: 'Bond and validator configurations'
-description: 'Learn how to configure your Arbitrum chain with a custom bond and validator configurations'
-author: pete-vielhaber
-sme: Jason-W123
+title: 'Stake and validator configurations'
+description: 'Learn how to configure your Arbitrum chain with a custom stake and validator configurations'
+author:
+sme:
content_type: how-to
+tags: ['hide-from-search']
unlisted: true
---
-Arbitrum chains are customizable Layer 3 (L3) chains that settle
-to an Arbitrum Layer 2 (L2) chain, such as Arbitrum One. They support
- validator
- configurations to ensure chain security through bonding and
- assertion
- challenges. Validators post assertions about the chain's state on the parent L2 chain and can
- challenge
- incorrect assertions. Arbitrum chains can be permissioned, meaning validators must be allowlisted.
-For chains that use that elect to use the BoLD protocol, permissionless validation is an option (BoLD
-also supports permissioned validation).
+- **Base Stake**: Minimum bond required for validators.
+- **Stake Token**: Token used for staking.
+- **Loser Stake Escrow**: Address for escrowed funds from losing validators.
+ Arbitrum chains are customizable Layer 3 (L3) chains that settle
+ to an Arbitrum Layer 2 (L2) chain, such as Arbitrum One. They support
+ validator
+ configurations to ensure chain security through bonding and
+ assertion
+ challenges. Validators post assertions about the chain's state on the parent L2 chain and can
+ challenge
+ incorrect assertions. Arbitrum chains can be permissioned, meaning validators must be allowlisted.
+ For chains that use that elect to use the BoLD protocol, permissionless validation is an option (BoLD
+ also supports permissioned validation).
Bonding is required for active validation, where validators place bond funds to participate. If a validator loses a challenge (e.g., due to a faulty assertion), their bond is escrowed or burned. Configurations such as the `stakeToken`, `baseStake`, and `loserStakeEscrow` are configurable during chain deployment or post-deployment via contract calls.
diff --git a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx
index 67eaeab929..f531d4b465 100644
--- a/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx
+++ b/docs/launch-arbitrum-chain/02-configure-your-chain/common/validation-and-security/13-arbos-upgrade.mdx
@@ -16,7 +16,7 @@ The specific upgrade requirements for each ArbOS release are located under each
#### Step 1: Update Nitro on nodes and validators
-Refer to the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx) to identify the specific [Nitro release](https://github.com/OffchainLabs/nitro/releases/) that supports the ArbOS version that you're upgrading to. For example, if your upgrade targets ArbOS 51, you'd use Nitro `v3.9.3` (Docker image: `offchainlabs/nitro-node:v3.9.3-8bc5554`) or higher. This is the version of the Nitro stack that needs to be running on each of your Arbitrum chain's nodes. A list of [all Nitro releases can be found on Github](https://github.com/OffchainLabs/nitro/releases).
+Refer to the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx) to identify the specific [Nitro release](https://github.com/OffchainLabs/nitro/releases/) that supports the ArbOS version that you're upgrading to. For example, if your upgrade targets ArbOS 50, you'd use Nitro `v3.9.0` (Docker image: `offchainlabs/nitro-node:v3.9.0-cca645a`) or higher. This is the version of the Nitro stack that needs to be running on each of your Arbitrum chain's nodes. A list of [all Nitro releases can be found on Github](https://github.com/OffchainLabs/nitro/releases).
Begin by upgrading your validator node(s) to the specified Nitro version, then update each remaining Arbitrum chain node to match this version.
@@ -26,7 +26,7 @@ Note that upgrading your node version _must occur_ before the deadline establish
While every ArbOS upgrade will require an update to the WASM module root, not every ArbOS upgrade will require an upgrade to the chain's `nitro-contracts` version.
-If necessary, as defined in the release notes for each ArbOS release ([example of ArbOS 51](/run-arbitrum-node/arbos-releases/arbos51.mdx)), you may need to deploy new versions of some (or all) of the Nitro contracts to the parent chain of your Arbitrum chain. These contracts include the rollup logic, bridging logic, fraud-proof contracts, and interfaces for interacting with Nitro precompiles. To verify the current version of your Nitro contracts, follow [these instructions](https://github.com/OffchainLabs/orbit-actions/blob/main/README.md#check-version-and-upgrade-path) while replacing the inbox contract address and network name with that of your Arbitrum chain. This information will allow you to find the correct upgrade path for your Nitro contracts.
+If necessary, as defined in the release notes for each ArbOS release ([example of ArbOS 50](/run-arbitrum-node/arbos-releases/arbos50.mdx)), you may need to deploy new versions of some (or all) of the Nitro contracts to the parent chain of your Arbitrum chain. These contracts include the rollup logic, bridging logic, fraud-proof contracts, and interfaces for interacting with Nitro precompiles. To verify the current version of your Nitro contracts, follow [these instructions](https://github.com/OffchainLabs/orbit-actions/blob/main/README.md#check-version-and-upgrade-path) while replacing the inbox contract address and network name with that of your Arbitrum chain. This information will allow you to find the correct upgrade path for your Nitro contracts.
To update the WASM module root and deploy your chain's Nitro contracts to the parent chain for the most recent ArbOS release, you will need the following inputs (obtained from the [requirements for the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx)):
@@ -35,7 +35,7 @@ To update the WASM module root and deploy your chain's Nitro contracts to the pa
Once you have the WASM module root and have identified the required `nitro-contracts` version for the target ArbOS release, if any, [please follow the instructions in this guide](https://github.com/OffchainLabs/orbit-actions?tab=readme-ov-file#nitro-contracts-upgrades) for specific actions based on the `nitro-contracts` version you are deploying. Note that each ArbOS release will require performing this step with a different WASM module root and may require a different version of `nitro-contracts`. The guide linked above will be kept updated with the instructions for each specific ArbOS release.
-The `WASM module root` is a 32-byte hash created from the Merkelized Go replay binary and its dependencies. When ArbOS is upgraded, a new WASM module root is generated due to modifications in the State Transition Function (STF). This new WASM module root must be set in the rollup contract on the parent chain. For example, the WASM module root for ArbOS 51 Dia is `0x8a7513bf7bb3e3db04b0d982d0e973bcf57bf8b88aef7c6d03dba3a81a56a499`.
+The `WASM module root` is a 32-byte hash created from the Merkelized Go replay binary and its dependencies. When ArbOS is upgraded, a new WASM module root is generated due to modifications in the State Transition Function (STF). This new WASM module root must be set in the rollup contract on the parent chain. For example, the WASM module root for ArbOS 50 Dia is `0x2c54f6e9e378ba320ed9c713a1d9f067a572b1437e4f1c40b1a915d3066c04f2`.
To set the WASM module root manually (i.e., not using the above guide), use the `Rollup proxy` contract's [`setWasmModuleRoot`](https://github.com/OffchainLabs/nitro-contracts/blob/38a70a5e14f8b52478eb5db08e7551a82ced14fe/src/rollup/RollupAdminLogic.sol#L321) method. Note that the `upgrade executor` contract on the parent chain is the designated owner of the Rollup contract, so the **chain owner account** needs to initiate a call to the `upgrade executor` contract in order to perform the upgrade. This call should include the correct calldata for setting the new WASM module root.
@@ -49,7 +49,7 @@ WASM module roots are backward compatible, so upgrading them before an ArbOS ver
To schedule an ArbOS version upgrade for your Arbitrum chain, [follow this guide](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/arbos-upgrades/at-timestamp). In addition to the upgrade action contract address and the account address for the chain owner account, you will need the following inputs:
-1. **`newVersion`**: Specify the ArbOS version you wish to upgrade to (e.g., `51`).
+1. **`newVersion`**: Specify the ArbOS version you wish to upgrade to (e.g., `50`).
2. **`timestamp`**: Set the exact UNIX timestamp at which you want your Arbitrum chain (Orbit) to transition to the new ArbOS version.
If you would prefer to do this manually, simply call the [`scheduleArbOSUpgrade`](https://github.com/OffchainLabs/nitro-precompile-interfaces/blob/fe4121240ca1ee2cbf07d67d0e6c38015d94e704/ArbOwner.sol#L116) function on the `ArbOwner` precompile of the Arbitrum chain(s) you're upgrading. Because this is an administrative action (similar to upgrading your Wasm module root), the **chain owner account** must call the target chain's `upgrade executor` contract with the appropriate calldata in order to invoke the `scheduleArbOSUpgrade` function of the ArbOwner precompile. This will schedule the ArbOS upgrade using the specified version and timestamp.
@@ -64,14 +64,14 @@ To upgrade immediately (without scheduling), set the timestamp to `0`.
You can obtain the current ArbOS version of your chain by calling `ArbSys.ArbOSVersion()`. Keep in mind that this function adds `55` to the current ArbOS version. For example, if your chain is running on ArbOS 10, calling this function will return `65`.
-When scheduling the ArbOS upgrade through `ArbOwner.scheduleArbOSUpgrade` you must use the actual ArbOS version you're upgrading to. For example, if you're upgrading to ArbOS 51, you will pass `51` when calling this function.
+When scheduling the ArbOS upgrade through `ArbOwner.scheduleArbOSUpgrade` you must use the actual ArbOS version you're upgrading to. For example, if you're upgrading to ArbOS 50, you will pass `50` when calling this function.
#### Step 4: Enable ArbOS specific configurations or feature flags (not always required)
-For some ArbOS upgrades, such as [ArbOS 51 Dia](/run-arbitrum-node/arbos-releases/arbos51.mdx), there may be additional requirements or steps that need to be satisfied to ensure your Arbitrum chain can use all of the new features and improvements made available in that particular ArbOS release.
+For some ArbOS upgrades, such as [ArbOS 50 Dia](/run-arbitrum-node/arbos-releases/arbos50.mdx), there may be additional requirements or steps that need to be satisfied to ensure your Arbitrum chain can use all of the new features and improvements made available in that particular ArbOS release.
-If there are additional requirements for the targeted ArbOS release you're attempting to upgrade to; the additional requirements will be listed on the reference pages for [the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx#list-of-available-arbos-releases). For example, the additional requirements for Arbitrum chains upgrading to ArbOS 51 can be found [here on the ArbOS 51 docs](/run-arbitrum-node/arbos-releases/arbos51.mdx).
+If there are additional requirements for the targeted ArbOS release you're attempting to upgrade to; the additional requirements will be listed on the reference pages for [the targeted ArbOS release](/run-arbitrum-node/arbos-releases/01-overview.mdx#list-of-available-arbos-releases). For example, the additional requirements for Arbitrum chains upgrading to ArbOS 50 can be found [here on the ArbOS 50 docs](/run-arbitrum-node/arbos-releases/arbos50.mdx).
Congratulations! You've upgraded your Arbitrum chain(s) to the specified ArbOS version.
diff --git a/docs/launch-arbitrum-chain/02-start-your-journey.mdx b/docs/launch-arbitrum-chain/02-start-your-journey.mdx
new file mode 100644
index 0000000000..7efc94581b
--- /dev/null
+++ b/docs/launch-arbitrum-chain/02-start-your-journey.mdx
@@ -0,0 +1,12 @@
+---
+title: 'Start your journey into Arbitrum chains'
+sidebar_label: 'Arbitrum chain: get started'
+description: 'Learn how to get started with Arbitrum chains'
+sidebar_position: '2'
+author: 'anegg0'
+sme: 'mahsamoosavi'
+editor: 'anegg0'
+target_audience: 'Prospective chain owners, and operators'
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/03-arbitrum-license.mdx b/docs/launch-arbitrum-chain/03-arbitrum-license.mdx
new file mode 100644
index 0000000000..b727357f1a
--- /dev/null
+++ b/docs/launch-arbitrum-chain/03-arbitrum-license.mdx
@@ -0,0 +1,12 @@
+---
+title: 'The Arbitrum chain license'
+sidebar_label: 'Arbitrum chain license'
+description: ''
+sidebar_position: 2
+author:
+sme:
+editor: anegg0
+target_audience:
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx
new file mode 100644
index 0000000000..598237adc9
--- /dev/null
+++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/01-bridging.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Bridging'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx
new file mode 100644
index 0000000000..649e63f45f
--- /dev/null
+++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/02-monitoring.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Monitoring'
+description: 'Learn how to configure your Arbitrum chain with a custom bond and validator configurations'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx
new file mode 100644
index 0000000000..272aab0202
--- /dev/null
+++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/01-decentralization-security.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Decentralization and Security'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx
new file mode 100644
index 0000000000..eca5d9a361
--- /dev/null
+++ b/docs/launch-arbitrum-chain/04-maintain-your-chain/04-guidance/02-guidance-on-altda.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Guidance on AltDA'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx b/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx
new file mode 100644
index 0000000000..34c95d1a99
--- /dev/null
+++ b/docs/launch-arbitrum-chain/06-third-party-integrations/03-integrations.mdx
@@ -0,0 +1,15 @@
+---
+title: 'Integrations'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
+
+## Compatible third-party integrations
+
+## LayerZero OFT
+
+## xERC-20 Gateway
diff --git a/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx b/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx
new file mode 100644
index 0000000000..142b208a4a
--- /dev/null
+++ b/docs/launch-arbitrum-chain/07-arbitrum-node-runners/arbitrum-chain-node-providers.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Arbitrum chain Node Providers'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+robots: noindex
+---
diff --git a/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx b/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx
new file mode 100644
index 0000000000..7c7c6b0fe6
--- /dev/null
+++ b/docs/launch-arbitrum-chain/08-ecosystem-support/01-arbitrum-chain-portal.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Arbitrum chains Portal'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx b/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx
new file mode 100644
index 0000000000..416349caf7
--- /dev/null
+++ b/docs/launch-arbitrum-chain/08-ecosystem-support/03-get-listed-arbitrum-chain-platforms.mdx
@@ -0,0 +1,9 @@
+---
+title: 'Get listed on Arbitrum platforms'
+description: 'PLACEHOLDER'
+author:
+sme:
+content_type: how-to
+tags: ['hide-from-search']
+unlisted: true
+---
diff --git a/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx b/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx
new file mode 100644
index 0000000000..01cd8daf02
--- /dev/null
+++ b/docs/launch-arbitrum-chain/arbitrum-chain-supported-parent-chains.mdx
@@ -0,0 +1,60 @@
+---
+title: 'Supported parent chains'
+sidebar_label: 'Supported parent chains'
+description: 'List of officially supported parent chains for Arbitrum chains'
+sidebar_position: '2'
+author: 'mahsamoosavi'
+sme: 'mahsamoosavi'
+editor: 'anegg0'
+target_audience: 'Arbitrum chains owners and operators'
+---
+
+This page lists the parent chains that are officially supported for Arbitrum chains, including mainnets, testnets, and options for local development. While the Arbitrum chain SDK provides functionality to enable custom parent chains, support is limited to the chains explicitly listed on this page. Developers are welcome to use the Arbitrum chain SDK to configure and deploy custom parent chains; however, such setups, including deploying the required creator and template contracts, are beyond the scope of our official support.
+
+:::caution Supported Chains Only
+
+Please note that we cannot guarantee compatibility or offer assistance for custom configurations outside of the supported chains detailed below.
+
+:::
+
+#### Supported chains
+
+
+
+- **Ethereum Mainnet**
+ **Chain ID:** `1`
+
+- **Arbitrum One**
+ **Chain ID:** `42161`
+
+- **Arbitrum Nova**
+ **Chain ID:** `42170`
+
+- **Base**
+ **Chain ID:** `8453`
+
+- **Sepolia**
+ **Chain ID:** `11155111`
+
+- **Arbitrum Sepolia**
+ **Chain ID:** `421613`
+
+- **Base Sepolia**
+ **Chain ID:** `84531`
+
+- **Nitro Testnode L1**
+ **Chain ID:** `1337`
+
+- **Nitro Testnode L2**
+ **Chain ID:** `412346`
+
+- **Nitro Testnode L3**
+ **Chain ID:** `333333`
+
+
+
+### Adding custom parent chains
+
+Although Arbitrum chains primarily supports a predefined set of chains, it provides developers with the flexibility to add custom parent chains through [the `registerCustomParentChain` function of the Arbitrum (Orbit) chain SDK](https://github.com/OffchainLabs/arbitrum-orbit-sdk/blob/729facd4d50156d7a84cf1204552c900eb86655c/src/chains.ts#L102). This capability enables integration with chains beyond the officially supported list, offering opportunities for customization and expanding the Arbtrium ecosystem.
+
+However, adding a custom chain requires deploying essential contracts, such as the creator contract and template contract, on the target chain. These contracts are fundamental for ensuring the proper functioning of the Arbitrum chain framework on the custom chain.
diff --git a/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md b/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md
new file mode 100644
index 0000000000..3b33fd052a
--- /dev/null
+++ b/docs/launch-arbitrum-chain/partials/_arbitrum-chain-public-preview-banner-partial.md
@@ -0,0 +1,7 @@
+:::info PUBLIC PREVIEW, MAINNET READY
+
+Arbitrum chains are now [Mainnet ready](/launch-arbitrum-chain/concepts/public-preview-expectations.mdx#arbitrum-chains-are-mainnet-ready-but-deploy-to-testnet-first)! Note that Arbitrum is still a **[public preview](/launch-arbitrum-chain/concepts/public-preview-expectations.mdx)** capability - the Arbitrum product and its supporting documentation may change significantly as we capture feedback from readers like you.
+
+To provide feedback, click the _Request an update_ button at the top of this document, [join the Arbitrum Discord](https://discord.gg/arbitrum), or reach out to our team directly by completing [this form](http://bit.ly/3yy6EUK).
+
+:::
diff --git a/docs/node-running/sequencer-content-map.mdx b/docs/node-running/sequencer-content-map.mdx
deleted file mode 100644
index cd4465ec67..0000000000
--- a/docs/node-running/sequencer-content-map.mdx
+++ /dev/null
@@ -1,38 +0,0 @@
----
-id: sequencer-content-map
-title: Sequencer
-sidebar_label: Sequencer
----
-
-import Card from '@site/src/components/Cards/Card';
-
-# Sequencer
-
-Keep your node in sync with the sequencer.
-
-
-
-
-
-
diff --git a/docs/partials/_macos-verify-tx-issue-banner-partial.mdx b/docs/partials/_macos-verify-tx-issue-banner-partial.mdx
new file mode 100644
index 0000000000..11df1da02c
--- /dev/null
+++ b/docs/partials/_macos-verify-tx-issue-banner-partial.mdx
@@ -0,0 +1,13 @@
+---
+partial_type: banner
+title: 'MacOS Verification Issue Banner'
+description: 'Warning banner for MacOS transaction verification limitations'
+author: anegg0
+last_reviewed: 2025-01-15
+---
+
+:::caution MacOS Users
+
+This feature is currently unavailable to MacOS users. We are working on a solution and will update this page when this limitation is resolved.
+
+:::
diff --git a/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx b/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx
index d191c83b9b..8ebb26465b 100644
--- a/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx
+++ b/docs/partials/_reference-arbitrum-rpc-endpoints-partial.mdx
@@ -12,7 +12,7 @@ last_reviewed: 2025-01-15
- Unlike the RPC Urls, the Sequencer endpoints only support `eth_sendRawTransaction` and `eth_sendRawTransactionConditional` calls.
- Arbitrum public RPCs do not provide Websocket support.
-- View the [faucets](/stylus/reference/testnet-information.md#faucets) for testnet Sepolia tokens on L2.
+- View the [faucets](docs/for-devs/dev-tools-and-resources/chain-info.mdx) for testnet Sepolia tokens on L2.
:::
diff --git a/docs/partials/_under-construction-banner-partial.mdx b/docs/partials/_under-construction-banner-partial.mdx
new file mode 100644
index 0000000000..1a30c582f3
--- /dev/null
+++ b/docs/partials/_under-construction-banner-partial.mdx
@@ -0,0 +1,15 @@
+---
+partial_type: banner
+title: 'Under Construction Banner'
+description: 'Banner indicating document is under construction'
+author: anegg0
+last_reviewed: 2025-01-15
+---
+
+:::caution UNDER CONSTRUCTION
+
+This document is currently under construction.
+
+If you're waiting for this content to be completed, click the `Request an update` button at the top of this page to let us know. We incorporate these requests into our content prioritization discussions. **Thank you!**
+
+:::
diff --git a/docs/run-arbitrum-node/arbos-releases/arbos32.mdx b/docs/run-arbitrum-node/arbos-releases/arbos32.mdx
index a4233c77fd..5ac1f9fb2d 100644
--- a/docs/run-arbitrum-node/arbos-releases/arbos32.mdx
+++ b/docs/run-arbitrum-node/arbos-releases/arbos32.mdx
@@ -42,8 +42,7 @@ ArbOS 32 Bianca brings many features, improvements, and bug fixes to Arbitrum ch
It is strongly recommended that teams upgrading to ArbOS 32 also spend the time following the instructions described below to deploy and enable the Stylus Cache Manager. Even if your team does not intend to build with Stylus in the immediate term, enabling the Cache Manager ensures that future usage of Arbitrum Stylus on your chain is smooth and provides a consistent UX with the developer experience of building with Arbitrum Stylus on Arbitrum One.
:::
-Specific to Stylus and ArbOS 32 "Bianca", we have developed a caching strategy that stores frequently accessed contracts in memory to reduce the costs and time associated with contract execution from repeated initializations. Check out the [Stylus caching strategy docs](../../stylus/how-tos/caching-contracts.mdx) to learn more.
-
+Specific to Stylus and ArbOS 32 "Bianca", we have developed a caching strategy that stores frequently accessed contracts in memory to reduce the costs and time associated with contract execution from repeated initializations. Check out the [Stylus caching strategy docs](../../stylus/guides/caching-contracts.mdx) to learn more.
In order to take advantage of this caching strategy, an additional step is required to deploy and enable it's use on your Arbitrum chain.
### Additional requirement for Arbitrum chains who wish to enable Fast Withdrawals
diff --git a/docs/stylus/advanced/_category_.yml b/docs/stylus/advanced/_category_.yml
new file mode 100644
index 0000000000..2765a35f98
--- /dev/null
+++ b/docs/stylus/advanced/_category_.yml
@@ -0,0 +1,5 @@
+label: 'Advanced'
+position: 8
+link:
+ type: generated-index
+ description: Deep dives into Stylus internals, advanced patterns, and optimization techniques.
diff --git a/docs/stylus/advanced/hostio-exports.mdx b/docs/stylus/advanced/hostio-exports.mdx
new file mode 100644
index 0000000000..80210af745
--- /dev/null
+++ b/docs/stylus/advanced/hostio-exports.mdx
@@ -0,0 +1,878 @@
+---
+title: 'Hostio exports'
+description: 'Learn about low-level hostio functions for direct VM access in Stylus smart contracts'
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers who need to understand how to use hostio exports with Stylus.
+displayed_sidebar: buildStylusSidebar
+---
+
+Hostio (Host I/O) exports are low-level functions that provide direct access to the Stylus VM runtime. These functions are WebAssembly imports that allow Stylus programs to interact with the blockchain environment, similar to how EVM opcodes work in Solidity.
+
+## Overview
+
+Hostio functions are the foundational layer that powers all Stylus smart contract operations. While most developers will use the higher-level SDK abstractions, understanding hostio functions is valuable for:
+
+- Performance optimization through direct VM access
+- Implementing custom low-level operations
+- Understanding gas costs and execution flow
+- Debugging and troubleshooting contract behavior
+
+:::info
+Most developers should use the high-level SDK wrappers instead of calling hostio functions directly. The SDK provides safe, ergonomic interfaces that handle memory management and error checking automatically.
+:::
+
+## How hostio works
+
+Hostio functions are WebAssembly imports defined in the `vm_hooks` module. When a Stylus program is compiled to WASM, these functions are linked at runtime by the Arbitrum VM:
+
+```rust
+#[link(wasm_import_module = "vm_hooks")]
+extern "C" {
+ pub fn msg_sender(sender: *mut u8);
+ pub fn block_number() -> u64;
+ // ... more functions
+}
+```
+
+During execution, calls to these functions are intercepted by the Stylus VM, which implements the actual functionality using the underlying ArbOS infrastructure.
+
+## Function categories
+
+Hostio functions are organized into several categories based on their purpose.
+
+### Account operations
+
+Query information about accounts on the blockchain.
+
+#### `account_balance`
+
+Gets the ETH balance of an account in wei. Equivalent to EVM's `BALANCE` opcode.
+
+```rust
+pub fn account_balance(address: *const u8, dest: *mut u8);
+```
+
+**Parameters:**
+
+- `address`: Pointer to 20-byte address
+- `dest`: Pointer to write 32-byte balance value
+
+**Usage:**
+
+```rust
+use stylus_sdk::alloy_primitives::{Address, U256};
+
+unsafe {
+ let addr = Address::from([0x11; 20]);
+ let mut balance_bytes = [0u8; 32];
+ hostio::account_balance(addr.as_ptr(), balance_bytes.as_mut_ptr());
+ let balance = U256::from_be_bytes(balance_bytes);
+}
+```
+
+#### `account_code`
+
+Gets a subset of code from an account. Equivalent to EVM's `EXTCODECOPY` opcode.
+
+```rust
+pub fn account_code(
+ address: *const u8,
+ offset: usize,
+ size: usize,
+ dest: *mut u8
+) -> usize;
+```
+
+**Returns:** Number of bytes actually written
+
+#### `account_code_size`
+
+Gets the size of code at an address. Equivalent to EVM's `EXTCODESIZE` opcode.
+
+```rust
+pub fn account_code_size(address: *const u8) -> usize;
+```
+
+#### `account_codehash`
+
+Gets the code hash of an account. Equivalent to EVM's `EXTCODEHASH` opcode.
+
+```rust
+pub fn account_codehash(address: *const u8, dest: *mut u8);
+```
+
+:::note
+Empty accounts return the keccak256 hash of empty bytes: `c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`
+:::
+
+### Storage operations
+
+Interact with persistent contract storage.
+
+#### `storage_load_bytes32`
+
+Reads a 32-byte value from storage. Equivalent to EVM's `SLOAD` opcode.
+
+```rust
+pub fn storage_load_bytes32(key: *const u8, dest: *mut u8);
+```
+
+**Parameters:**
+
+- `key`: Pointer to 32-byte storage key
+- `dest`: Pointer to write 32-byte value
+
+#### `storage_cache_bytes32`
+
+Writes a 32-byte value to the storage cache. Equivalent to EVM's `SSTORE` opcode.
+
+```rust
+pub fn storage_cache_bytes32(key: *const u8, value: *const u8);
+```
+
+:::warning
+Values are cached and must be persisted using `storage_flush_cache` before they're permanently written to storage.
+:::
+
+#### `storage_flush_cache`
+
+Persists cached storage values to the EVM state trie. Equivalent to multiple `SSTORE` operations.
+
+```rust
+pub fn storage_flush_cache(clear: bool);
+```
+
+**Parameters:**
+
+- `clear`: Whether to drop the cache entirely after flushing
+
+**Storage caching benefits:**
+
+The Stylus VM implements storage caching for improved performance:
+
+- Repeated reads of the same key cost less gas
+- Writes are batched for efficiency
+- Cache is automatically managed by the SDK
+
+### Block information
+
+Access information about the current block.
+
+#### `block_basefee`
+
+Gets the basefee of the current block. Equivalent to EVM's `BASEFEE` opcode.
+
+```rust
+pub fn block_basefee(basefee: *mut u8);
+```
+
+#### `block_chainid`
+
+Gets the chain identifier. Equivalent to EVM's `CHAINID` opcode.
+
+```rust
+pub fn chainid() -> u64;
+```
+
+#### `block_coinbase`
+
+Gets the coinbase (block producer address). On Arbitrum, this is the L1 batch poster's address.
+
+```rust
+pub fn block_coinbase(coinbase: *mut u8);
+```
+
+#### `block_gas_limit`
+
+Gets the gas limit of the current block. Equivalent to EVM's `GASLIMIT` opcode.
+
+```rust
+pub fn block_gas_limit() -> u64;
+```
+
+#### `block_number`
+
+Gets a bounded estimate of the L1 block number when the transaction was sequenced.
+
+```rust
+pub fn block_number() -> u64;
+```
+
+:::info
+See [Arbitrum block numbers and time](https://developer.arbitrum.io/time) for more information on how block numbers work on Arbitrum chains.
+:::
+
+#### `block_timestamp`
+
+Gets a bounded estimate of the Unix timestamp when the transaction was sequenced.
+
+```rust
+pub fn block_timestamp() -> u64;
+```
+
+### Transaction and message context
+
+Access information about the current transaction and call context.
+
+#### `msg_sender`
+
+Gets the address of the caller. Equivalent to EVM's `CALLER` opcode.
+
+```rust
+pub fn msg_sender(sender: *mut u8);
+```
+
+**Parameters:**
+
+- `sender`: Pointer to write 20-byte address
+
+:::note
+For L1-to-L2 retryable ticket transactions, addresses are aliased. See [address aliasing documentation](https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing).
+:::
+
+#### `msg_value`
+
+Gets the ETH value sent with the call in wei. Equivalent to EVM's `CALLVALUE` opcode.
+
+```rust
+pub fn msg_value(value: *mut u8);
+```
+
+#### `msg_reentrant`
+
+Checks if the current call is reentrant.
+
+```rust
+pub fn msg_reentrant() -> bool;
+```
+
+#### `tx_gas_price`
+
+Gets the gas price in wei per gas. On Arbitrum, this equals the basefee. Equivalent to EVM's `GASPRICE` opcode.
+
+```rust
+pub fn tx_gas_price(gas_price: *mut u8);
+```
+
+#### `tx_origin`
+
+Gets the top-level sender of the transaction. Equivalent to EVM's `ORIGIN` opcode.
+
+```rust
+pub fn tx_origin(origin: *mut u8);
+```
+
+#### `tx_ink_price`
+
+Gets the price of ink in EVM gas basis points. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for more information.
+
+```rust
+pub fn tx_ink_price() -> u32;
+```
+
+### Contract calls
+
+Make calls to other contracts.
+
+#### `call_contract`
+
+Calls another contract with optional value and gas limit. Equivalent to EVM's `CALL` opcode.
+
+```rust
+pub fn call_contract(
+ contract: *const u8,
+ calldata: *const u8,
+ calldata_len: usize,
+ value: *const u8,
+ gas: u64,
+ return_data_len: *mut usize
+) -> u8;
+```
+
+**Parameters:**
+
+- `contract`: Pointer to 20-byte contract address
+- `calldata`: Pointer to calldata bytes
+- `calldata_len`: Length of calldata
+- `value`: Pointer to 32-byte value in wei (use 0 for no value)
+- `gas`: Gas to supply (use `u64::MAX` for all available gas)
+- `return_data_len`: Pointer to store length of return data
+
+**Returns:** `0` on success, non-zero on failure
+
+**Gas rules:**
+
+- Follows the 63/64 rule (at most 63/64 of available gas is forwarded)
+- Includes callvalue stipend when value is sent
+
+**Usage:**
+
+```rust
+use stylus_sdk::call::RawCall;
+
+unsafe {
+ let result = RawCall::new(self.vm())
+ .gas(100_000)
+ .value(U256::from(1_000_000))
+ .call(contract_address, &calldata)?;
+}
+```
+
+#### `delegate_call_contract`
+
+Delegate calls another contract. Equivalent to EVM's `DELEGATECALL` opcode.
+
+```rust
+pub fn delegate_call_contract(
+ contract: *const u8,
+ calldata: *const u8,
+ calldata_len: usize,
+ gas: u64,
+ return_data_len: *mut usize
+) -> u8;
+```
+
+:::warning
+Delegate calls execute code in the context of the current contract. Be extremely careful when delegate calling to untrusted contracts as they have full access to your storage.
+:::
+
+#### `static_call_contract`
+
+Static calls another contract (read-only). Equivalent to EVM's `STATICCALL` opcode.
+
+```rust
+pub fn static_call_contract(
+ contract: *const u8,
+ calldata: *const u8,
+ calldata_len: usize,
+ gas: u64,
+ return_data_len: *mut usize
+) -> u8;
+```
+
+### Contract deployment
+
+Deploy new contracts.
+
+#### `create1`
+
+Deploys a contract using CREATE. Equivalent to EVM's `CREATE` opcode.
+
+```rust
+pub fn create1(
+ code: *const u8,
+ code_len: usize,
+ endowment: *const u8,
+ contract: *mut u8,
+ revert_data_len: *mut usize
+);
+```
+
+**Parameters:**
+
+- `code`: Pointer to initialization code (EVM bytecode)
+- `code_len`: Length of initialization code
+- `endowment`: Pointer to 32-byte value to send
+- `contract`: Pointer to write deployed contract address (20 bytes)
+- `revert_data_len`: Pointer to store revert data length on failure
+
+**Deployment rules:**
+
+- Init code must be EVM bytecode
+- Deployed code can be Stylus (WASM) if it starts with `0xEFF000` header
+- Address is determined by sender and nonce
+- On failure, address will be zero
+
+#### `create2`
+
+Deploys a contract using CREATE2. Equivalent to EVM's `CREATE2` opcode.
+
+```rust
+pub fn create2(
+ code: *const u8,
+ code_len: usize,
+ endowment: *const u8,
+ salt: *const u8,
+ contract: *mut u8,
+ revert_data_len: *mut usize
+);
+```
+
+**Parameters:**
+
+- `salt`: Pointer to 32-byte salt value
+
+**Address calculation:**
+
+- Address is deterministic based on sender, salt, and init code hash
+- Allows for pre-computed addresses
+
+### Events and logging
+
+Emit events to the blockchain.
+
+#### `emit_log`
+
+Emits an EVM log with topics and data. Equivalent to EVM's `LOG0`-`LOG4` opcodes.
+
+```rust
+pub fn emit_log(data: *const u8, len: usize, topics: usize);
+```
+
+**Parameters:**
+
+- `data`: Pointer to event data (first bytes should be 32-byte aligned topics)
+- `len`: Total length of data including topics
+- `topics`: Number of topics (0-4)
+
+:::warning
+Requesting more than 4 topics will cause a revert.
+:::
+
+**Higher-level usage:**
+
+```rust
+sol! {
+ event Transfer(address indexed from, address indexed to, uint256 value);
+}
+
+// Emit using the SDK
+self.vm().log(Transfer {
+ from: sender,
+ to: recipient,
+ value: amount,
+});
+```
+
+### Gas and ink metering
+
+Monitor execution costs.
+
+#### `evm_gas_left`
+
+Gets the amount of gas remaining. Equivalent to EVM's `GAS` opcode.
+
+```rust
+pub fn evm_gas_left() -> u64;
+```
+
+#### `evm_ink_left`
+
+Gets the amount of ink remaining. Stylus-specific metering unit.
+
+```rust
+pub fn evm_ink_left() -> u64;
+```
+
+:::info
+Ink is Stylus's compute pricing unit. See [Ink and Gas](https://docs.arbitrum.io/stylus/concepts/gas-metering) for conversion between ink and gas.
+:::
+
+#### `pay_for_memory_grow`
+
+Pays for WASM memory growth. Automatically called when allocating new pages.
+
+```rust
+pub fn pay_for_memory_grow(pages: u16);
+```
+
+:::note
+The `entrypoint!` macro handles importing this hostio. Manual calls will unproductively consume gas.
+:::
+
+### Cryptography
+
+Cryptographic operations.
+
+#### `native_keccak256`
+
+Efficiently computes keccak256 hash. Equivalent to EVM's `SHA3` opcode.
+
+```rust
+pub fn native_keccak256(bytes: *const u8, len: usize, output: *mut u8);
+```
+
+**Parameters:**
+
+- `bytes`: Pointer to input data
+- `len`: Length of input data
+- `output`: Pointer to write 32-byte hash
+
+**Higher-level usage:**
+
+```rust
+use stylus_sdk::crypto::keccak;
+
+let hash = keccak(b"hello world");
+```
+
+### Calldata operations
+
+Read and write calldata and return data.
+
+#### `read_args`
+
+Reads the program calldata. Equivalent to EVM's `CALLDATACOPY` opcode.
+
+```rust
+pub fn read_args(dest: *mut u8);
+```
+
+:::note
+This reads the entirety of the call's calldata.
+:::
+
+#### `read_return_data`
+
+Copies bytes from the last call or deployment return result. Equivalent to EVM's `RETURNDATACOPY` opcode.
+
+```rust
+pub fn read_return_data(dest: *mut u8, offset: usize, size: usize) -> usize;
+```
+
+**Parameters:**
+
+- `dest`: Destination buffer
+- `offset`: Offset in return data to start copying from
+- `size`: Number of bytes to copy
+
+**Returns:** Number of bytes actually written
+
+**Behavior:**
+
+- Does not revert if out of bounds
+- Copies overlapping portion only
+
+#### `return_data_size`
+
+Gets the length of the last return result. Equivalent to EVM's `RETURNDATASIZE` opcode.
+
+```rust
+pub fn return_data_size() -> usize;
+```
+
+#### `write_result`
+
+Writes the final return data for the current call.
+
+```rust
+pub fn write_result(data: *const u8, len: usize);
+```
+
+**Behavior:**
+
+- Does not cause program to exit
+- If not called, return data will be empty
+- Program exits naturally when entrypoint returns
+
+#### `contract_address`
+
+Gets the address of the current program. Equivalent to EVM's `ADDRESS` opcode.
+
+```rust
+pub fn contract_address(address: *mut u8);
+```
+
+### Debug and console
+
+Debug-only functions for development.
+
+#### `log_txt`
+
+Prints UTF-8 text to console. Only available in debug mode.
+
+```rust
+pub fn log_txt(text: *const u8, len: usize);
+```
+
+#### `log_i32` / `log_i64`
+
+Prints integers to console. Only available in debug mode.
+
+```rust
+pub fn log_i32(value: i32);
+pub fn log_i64(value: i64);
+```
+
+#### `log_f32` / `log_f64`
+
+Prints floating-point numbers to console. Only available in debug mode with floating point enabled.
+
+```rust
+pub fn log_f32(value: f32);
+pub fn log_f64(value: f64);
+```
+
+**Higher-level usage:**
+
+```rust
+use stylus_sdk::console;
+
+console!("Value: {}", value); // Prints in debug mode, no-op in production
+```
+
+## Safety considerations
+
+All hostio functions are marked `unsafe` because they:
+
+1. **Operate on raw pointers**: Require correct memory management
+2. **Lack bounds checking**: Can cause undefined behavior if pointers are invalid
+3. **Have side effects**: Can modify contract state or make external calls
+4. **May revert**: Some operations can cause the transaction to revert
+
+### Safe usage patterns
+
+**Always validate inputs:**
+
+```rust
+// Bad: unchecked pointer usage
+unsafe {
+ hostio::msg_sender(ptr); // ptr might be invalid
+}
+
+// Good: use safe wrappers
+let sender = self.vm().msg_sender();
+```
+
+**Use SDK wrappers:**
+
+```rust
+// Bad: direct hostio call
+unsafe {
+ let mut balance = [0u8; 32];
+ hostio::account_balance(addr.as_ptr(), balance.as_mut_ptr());
+}
+
+// Good: use SDK wrapper
+use stylus_sdk::evm;
+let balance = evm::balance(addr);
+```
+
+**Handle return values:**
+
+```rust
+// Check return status from calls
+let status = unsafe {
+ hostio::call_contract(
+ contract.as_ptr(),
+ calldata.as_ptr(),
+ calldata.len(),
+ value.as_ptr(),
+ gas,
+ &mut return_len,
+ )
+};
+
+if status != 0 {
+ // Handle call failure
+}
+```
+
+## Higher-level wrappers
+
+The Stylus SDK provides safe, ergonomic wrappers around hostio functions:
+
+### Storage operations
+
+```rust
+// Instead of direct hostio:
+unsafe {
+ hostio::storage_load_bytes32(key.as_ptr(), dest.as_mut_ptr());
+}
+
+// Use storage types:
+use stylus_sdk::storage::StorageU256;
+
+#[storage]
+pub struct Contract {
+ value: StorageU256,
+}
+
+let value = self.value.get(); // Safe, ergonomic
+```
+
+### Contract calls
+
+```rust
+// Instead of direct hostio:
+unsafe {
+ hostio::call_contract(/* many parameters */);
+}
+
+// Use RawCall or typed interfaces:
+use stylus_sdk::call::RawCall;
+
+let result = unsafe {
+ RawCall::new(self.vm())
+ .gas(100_000)
+ .call(contract, &calldata)?
+};
+```
+
+### VM context
+
+```rust
+// Instead of direct hostio:
+unsafe {
+ let mut sender = [0u8; 20];
+ hostio::msg_sender(sender.as_mut_ptr());
+}
+
+// Use VM accessor:
+let sender = self.vm().msg_sender();
+let value = self.vm().msg_value();
+let timestamp = self.vm().block_timestamp();
+```
+
+## Feature flags
+
+Hostio behavior changes based on feature flags:
+
+### `export-abi`
+
+When enabled, hostio functions are stubbed and return `unimplemented!()`. Used for ABI generation.
+
+### `stylus-test`
+
+When enabled, hostio functions panic with an error message. Use `TestVM` for testing instead.
+
+### `debug`
+
+When enabled, console logging functions become available. In production, console functions are no-ops.
+
+## Performance considerations
+
+### Direct hostio vs SDK wrappers
+
+- **Direct hostio**: Slightly lower overhead, requires manual memory management
+- **SDK wrappers**: Minimal overhead (often zero-cost abstractions), much safer
+
+**Recommendation:** Use SDK wrappers unless profiling shows a specific performance bottleneck.
+
+### Storage caching
+
+The Stylus VM automatically caches storage operations:
+
+```rust
+// First read: full SLOAD cost
+let value1 = storage.value.get();
+
+// Subsequent reads: reduced cost from cache
+let value2 = storage.value.get();
+
+// Writes are cached until flush
+storage.value.set(new_value); // Cached
+
+// Cache is flushed automatically at call boundaries
+```
+
+### Gas vs ink
+
+Stylus uses "ink" for fine-grained gas metering:
+
+- **Ink**: WASM execution cost in Stylus-specific units
+- **Gas**: Standard EVM gas units
+- Conversion happens automatically
+
+Most developers don't need to think about ink vs gas distinction.
+
+## Common patterns
+
+### Check-effects-interactions pattern
+
+```rust
+#[public]
+impl MyContract {
+ pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), Vec> {
+ // Checks
+ let sender = self.vm().msg_sender();
+ let balance = self.balances.get(sender);
+ if balance < amount {
+ return Err(b"Insufficient balance".to_vec());
+ }
+
+ // Effects
+ self.balances.setter(sender).set(balance - amount);
+ self.balances.setter(to).set(self.balances.get(to) + amount);
+
+ // Interactions (if any)
+ Ok(())
+ }
+}
+```
+
+### Efficient event logging
+
+```rust
+sol! {
+ event DataUpdated(bytes32 indexed key, uint256 value);
+}
+
+// SDK handles hostio::emit_log internally
+self.vm().log(DataUpdated {
+ key: key_hash,
+ value: new_value,
+});
+```
+
+### Gas-limited external calls
+
+```rust
+use stylus_sdk::call::RawCall;
+
+// Limit gas to prevent griefing
+let result = unsafe {
+ RawCall::new(self.vm())
+ .gas(50_000) // Fixed gas limit
+ .call(untrusted_contract, &calldata)
+};
+
+match result {
+ Ok(data) => { /* process return data */ },
+ Err(_) => { /* handle failure gracefully */ },
+}
+```
+
+## Testing with hostio
+
+Hostio functions are not available in the test environment. Use `TestVM` instead:
+
+```rust
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use stylus_sdk::testing::*;
+
+ #[test]
+ fn test_function() {
+ let vm = TestVM::default();
+ let mut contract = MyContract::from(&vm);
+
+ // VM functions work in tests
+ let sender = vm.msg_sender(); // Works
+
+ // Direct hostio would panic
+ // unsafe { hostio::msg_sender(...) } // Would panic
+ }
+}
+```
+
+## Resources
+
+- [Stylus VM specification](https://github.com/OffchainLabs/stylus)
+- [EVM opcodes reference](https://www.evm.codes/)
+- [Arbitrum block numbers and time](https://developer.arbitrum.io/time)
+- [Ink and gas metering](https://docs.arbitrum.io/stylus/concepts/gas-metering)
+- [stylus-sdk-rs source](https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/stylus-sdk/src/hostio.rs)
+
+## Best practices
+
+1. **Use SDK wrappers**: Prefer high-level abstractions over direct hostio calls
+2. **Validate inputs**: Always check pointers and sizes before unsafe operations
+3. **Handle errors**: Check return values from call operations
+4. **Test thoroughly**: Use `TestVM` for comprehensive testing
+5. **Profile first**: Only optimize to direct hostio if profiling shows it's necessary
+6. **Document unsafe code**: Always document why `unsafe` is necessary
+7. **Minimize unsafe blocks**: Keep `unsafe` blocks as small as possible
diff --git a/docs/stylus/advanced/minimal-entrypoint-contracts.mdx b/docs/stylus/advanced/minimal-entrypoint-contracts.mdx
new file mode 100644
index 0000000000..2168b20094
--- /dev/null
+++ b/docs/stylus/advanced/minimal-entrypoint-contracts.mdx
@@ -0,0 +1,644 @@
+---
+title: 'Minimal entrypoint contracts'
+description: 'Understanding low-level Stylus contract entrypoints and the Router trait'
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers who need to understand how to build a minimal entrypoint contract without high-level macros.
+displayed_sidebar: buildStylusSidebar
+---
+
+This guide explains the low-level mechanics of Stylus contract entrypoints, helping you understand what happens behind the `#[entrypoint]` and `#[public]` macros. This knowledge is useful for advanced use cases, debugging, and building custom contract frameworks.
+
+## Overview
+
+A Stylus contract at its core consists of:
+
+1. **`user_entrypoint` function**: The WASM export that Stylus calls
+2. **Router implementation**: Routes function selectors to method implementations
+3. **TopLevelStorage trait**: Marks the contract's root storage type
+4. **ArbResult type**: Represents success/failure with encoded return data
+
+## Understanding `ArbResult`
+
+`ArbResult` is the fundamental return type for Stylus contract methods:
+
+```rust
+pub type ArbResult = Result, Vec>;
+```
+
+- `Ok(Vec)` - Success with ABI-encoded return data
+- `Err(Vec)` - Revert with ABI-encoded error data
+
+**Example:**
+
+```rust
+use stylus_sdk::ArbResult;
+
+// Success with no return data
+fn no_return() -> ArbResult {
+ Ok(Vec::new())
+}
+
+// Success with encoded data
+fn return_value() -> ArbResult {
+ let value: u32 = 42;
+ Ok(value.to_le_bytes().to_vec())
+}
+
+// Revert with error data
+fn revert_with_error() -> ArbResult {
+ Err(b"InsufficientBalance".to_vec())
+}
+```
+
+## The `user_entrypoint` Function
+
+The `user_entrypoint` function is the WASM export that Stylus calls when a transaction invokes the contract. The `#[entrypoint]` macro generates this function automatically.
+
+### Generated Structure
+
+When you use `#[entrypoint]`, the macro generates:
+
+```rust
+#[no_mangle]
+pub extern "C" fn user_entrypoint(len: usize) -> usize {
+ let host = stylus_sdk::host::VM {
+ host: stylus_sdk::host::WasmVM{}
+ };
+
+ // Reentrancy check (unless reentrant feature enabled)
+ if host.msg_reentrant() {
+ return 1; // revert
+ }
+
+ // Ensure pay_for_memory_grow is referenced
+ // (costs 8700 ink, less than 1 gas)
+ host.pay_for_memory_grow(0);
+
+ // Read calldata
+ let input = host.read_args(len);
+
+ // Call the router
+ let (data, status) = match router_entrypoint::(input, host.clone()) {
+ Ok(data) => (data, 0), // Success
+ Err(data) => (data, 1), // Revert
+ };
+
+ // Persist storage changes
+ host.flush_cache(false);
+
+ // Write return data
+ host.write_result(&data);
+
+ status
+}
+```
+
+### Key Points
+
+- **Signature**: `extern "C" fn user_entrypoint(len: usize) -> usize`
+- **Input**: `len` is the length of calldata to read
+- **Output**: `0` for success, `1` for revert
+- **Side effects**: Reads calldata, writes return data, flushes storage cache
+
+## The `Router` Trait
+
+The `Router` trait defines how function calls are dispatched to method implementations.
+
+### Trait Definition
+
+```rust
+pub trait Router
+where
+ S: TopLevelStorage + BorrowMut + ValueDenier,
+ I: ?Sized,
+{
+ type Storage;
+
+ /// Route a function call by selector
+ fn route(storage: &mut S, selector: u32, input: &[u8]) -> Option;
+
+ /// Handle receive (plain ETH transfers, no calldata)
+ fn receive(storage: &mut S) -> Option>>;
+
+ /// Handle fallback (unknown selectors or no receive)
+ fn fallback(storage: &mut S, calldata: &[u8]) -> Option;
+
+ /// Handle constructor
+ fn constructor(storage: &mut S, calldata: &[u8]) -> Option;
+}
+```
+
+### Routing Logic
+
+The `router_entrypoint` function implements the routing logic:
+
+```rust
+pub fn router_entrypoint(input: Vec, host: VM) -> ArbResult
+where
+ R: Router,
+ S: StorageType + TopLevelStorage + BorrowMut + ValueDenier,
+{
+ let mut storage = unsafe { S::new(U256::ZERO, 0, host) };
+
+ // No calldata - try receive, then fallback
+ if input.is_empty() {
+ if let Some(res) = R::receive(&mut storage) {
+ return res.map(|_| Vec::new());
+ }
+ if let Some(res) = R::fallback(&mut storage, &[]) {
+ return res;
+ }
+ return Err(Vec::new()); // No receive or fallback
+ }
+
+ // Extract selector (first 4 bytes)
+ if input.len() >= 4 {
+ let selector = u32::from_be_bytes(input[..4].try_into().unwrap());
+
+ // Check for constructor
+ if selector == CONSTRUCTOR_SELECTOR {
+ if let Some(res) = R::constructor(&mut storage, &input[4..]) {
+ return res;
+ }
+ }
+ // Try to route to a method
+ else if let Some(res) = R::route(&mut storage, selector, &input[4..]) {
+ return res;
+ }
+ }
+
+ // Try fallback
+ if let Some(res) = R::fallback(&mut storage, &input) {
+ return res;
+ }
+
+ Err(Vec::new()) // Unknown selector and no fallback
+}
+```
+
+## The `TopLevelStorage` Trait
+
+The `TopLevelStorage` trait marks types that represent the contract's root storage.
+
+### Trait Definition
+
+```rust
+pub unsafe trait TopLevelStorage {}
+```
+
+### Purpose
+
+- Prevents storage aliasing during reentrancy
+- Lifetime tracks all EVM state changes during contract invocation
+- Must hold a reference when making external calls
+- Automatically implemented by `#[entrypoint]`
+
+### Safety
+
+The trait is `unsafe` because:
+
+- Type must truly be top-level to prevent storage aliasing
+- Incorrectly implementing this trait can lead to undefined behavior
+
+## Building a Minimal Contract
+
+Here's a minimal contract without using the high-level macros:
+
+### Step 1: Define Storage
+
+```rust
+#![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
+extern crate alloc;
+
+use alloc::vec::Vec;
+use stylus_sdk::{
+ abi::{Router, ArbResult},
+ storage::StorageType,
+ host::VM,
+ alloy_primitives::U256,
+};
+
+// Mark as top-level storage (normally done by #[entrypoint])
+pub struct MyContract;
+
+unsafe impl stylus_core::storage::TopLevelStorage for MyContract {}
+
+impl StorageType for MyContract {
+ type Wraps<'a> = &'a Self where Self: 'a;
+ type WrapsMut<'a> = &'a mut Self where Self: 'a;
+
+ unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self {
+ MyContract
+ }
+
+ fn load<'s>(self) -> Self::Wraps<'s> {
+ &self
+ }
+
+ fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
+ &mut self
+ }
+}
+```
+
+### Step 2: Implement Router
+
+```rust
+impl Router for MyContract {
+ type Storage = MyContract;
+
+ fn route(_storage: &mut MyContract, selector: u32, _input: &[u8]) -> Option {
+ // Simple example: one method with selector 0x12345678
+ match selector {
+ 0x12345678 => Some(Ok(Vec::new())),
+ _ => None, // Unknown selector
+ }
+ }
+
+ fn receive(_storage: &mut MyContract) -> Option>> {
+ None // No receive function
+ }
+
+ fn fallback(_storage: &mut MyContract, _calldata: &[u8]) -> Option {
+ None // No fallback function
+ }
+
+ fn constructor(_storage: &mut MyContract, _calldata: &[u8]) -> Option {
+ None // No constructor
+ }
+}
+```
+
+### Step 3: Define Entrypoint
+
+```rust
+#[no_mangle]
+pub extern "C" fn user_entrypoint(len: usize) -> usize {
+ let host = VM { host: stylus_sdk::host::WasmVM{} };
+
+ // Reentrancy check
+ if host.msg_reentrant() {
+ return 1;
+ }
+
+ // Reference pay_for_memory_grow
+ host.pay_for_memory_grow(0);
+
+ // Read input
+ let input = host.read_args(len);
+
+ // Route the call
+ let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) {
+ Ok(data) => (data, 0),
+ Err(data) => (data, 1),
+ };
+
+ // Flush storage
+ host.flush_cache(false);
+
+ // Write result
+ host.write_result(&data);
+
+ status
+}
+```
+
+## Function Selectors
+
+Function selectors are 4-byte identifiers computed from the function signature.
+
+### Computing Selectors
+
+```rust
+use stylus_sdk::function_selector;
+
+// Manual computation
+const MY_FUNCTION: [u8; 4] = function_selector!("myFunction");
+
+// With parameters
+const TRANSFER: [u8; 4] = function_selector!("transfer", Address, U256);
+
+// Constructor selector
+const CONSTRUCTOR_SELECTOR: u32 =
+ u32::from_be_bytes(function_selector!("constructor"));
+```
+
+### Using in Router
+
+```rust
+impl Router for MyContract {
+ type Storage = MyContract;
+
+ fn route(_storage: &mut MyContract, selector: u32, input: &[u8]) -> Option {
+ const GET_VALUE: u32 = u32::from_be_bytes(function_selector!("getValue"));
+ const SET_VALUE: u32 = u32::from_be_bytes(function_selector!("setValue", U256));
+
+ match selector {
+ GET_VALUE => {
+ // Return encoded U256 value
+ let value = U256::from(42);
+ Some(Ok(value.to_be_bytes::<32>().to_vec()))
+ }
+ SET_VALUE => {
+ // Decode input and set value
+ if input.len() >= 32 {
+ // Process set_value logic
+ Some(Ok(Vec::new()))
+ } else {
+ Some(Err(Vec::new()))
+ }
+ }
+ _ => None,
+ }
+ }
+
+ fn receive(_storage: &mut MyContract) -> Option>> {
+ None
+ }
+
+ fn fallback(_storage: &mut MyContract, _calldata: &[u8]) -> Option {
+ None
+ }
+
+ fn constructor(_storage: &mut MyContract, _calldata: &[u8]) -> Option {
+ None
+ }
+}
+```
+
+## Implementing Special Functions
+
+### Receive Function
+
+Handles plain ETH transfers (no calldata):
+
+```rust
+fn receive(storage: &mut MyContract) -> Option>> {
+ // Access msg_value via storage.vm().msg_value()
+ // Must return Ok(()) for success
+ Some(Ok(()))
+}
+```
+
+### Fallback Function
+
+Handles unknown selectors or when no receive is defined:
+
+```rust
+fn fallback(storage: &mut MyContract, calldata: &[u8]) -> Option {
+ // Can access full calldata
+ // Return Some to handle, None to revert
+ Some(Ok(Vec::new()))
+}
+```
+
+### Constructor
+
+Called once during deployment with `CONSTRUCTOR_SELECTOR`:
+
+```rust
+fn constructor(storage: &mut MyContract, calldata: &[u8]) -> Option {
+ // Initialize contract state
+ // calldata contains constructor parameters
+ Some(Ok(Vec::new()))
+}
+```
+
+## Complete Minimal Example
+
+Here's a complete working minimal contract:
+
+```rust
+#![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
+extern crate alloc;
+
+use alloc::vec::Vec;
+use core::borrow::BorrowMut;
+use stylus_sdk::{
+ abi::Router,
+ alloy_primitives::U256,
+ host::VM,
+ storage::StorageType,
+ ArbResult,
+ function_selector,
+};
+use stylus_core::{storage::TopLevelStorage, ValueDenier};
+
+// Contract storage
+pub struct MinimalContract;
+
+// Mark as top-level storage
+unsafe impl TopLevelStorage for MinimalContract {}
+
+// Implement StorageType
+impl StorageType for MinimalContract {
+ type Wraps<'a> = &'a Self where Self: 'a;
+ type WrapsMut<'a> = &'a mut Self where Self: 'a;
+
+ unsafe fn new(_slot: U256, _offset: u8, _host: VM) -> Self {
+ MinimalContract
+ }
+
+ fn load<'s>(self) -> Self::Wraps<'s> {
+ &self
+ }
+
+ fn load_mut<'s>(self) -> Self::WrapsMut<'s> {
+ &mut self
+ }
+}
+
+// Implement ValueDenier (for non-payable check)
+impl ValueDenier for MinimalContract {
+ fn deny_value(&self, _method_name: &str) -> Result<(), Vec> {
+ Ok(()) // Allow all for simplicity
+ }
+}
+
+// Implement BorrowMut
+impl BorrowMut for MinimalContract {
+ fn borrow_mut(&mut self) -> &mut MinimalContract {
+ self
+ }
+}
+
+// Implement Router
+impl Router for MinimalContract {
+ type Storage = MinimalContract;
+
+ fn route(_storage: &mut MinimalContract, selector: u32, _input: &[u8]) -> Option {
+ const HELLO: u32 = u32::from_be_bytes(function_selector!("hello"));
+
+ match selector {
+ HELLO => Some(Ok(Vec::new())),
+ _ => None,
+ }
+ }
+
+ fn receive(_storage: &mut MinimalContract) -> Option>> {
+ None
+ }
+
+ fn fallback(_storage: &mut MinimalContract, _calldata: &[u8]) -> Option {
+ Some(Ok(Vec::new())) // Accept all unknown calls
+ }
+
+ fn constructor(_storage: &mut MinimalContract, _calldata: &[u8]) -> Option {
+ Some(Ok(Vec::new()))
+ }
+}
+
+// Define user_entrypoint
+#[no_mangle]
+pub extern "C" fn user_entrypoint(len: usize) -> usize {
+ let host = VM { host: stylus_sdk::host::WasmVM{} };
+
+ if host.msg_reentrant() {
+ return 1;
+ }
+
+ host.pay_for_memory_grow(0);
+
+ let input = host.read_args(len);
+ let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) {
+ Ok(data) => (data, 0),
+ Err(data) => (data, 1),
+ };
+
+ host.flush_cache(false);
+ host.write_result(&data);
+ status
+}
+```
+
+## Why Use High-Level Macros?
+
+While minimal contracts are educational, the `#[entrypoint]` and `#[public]` macros provide:
+
+1. **Automatic selector generation** from method names
+2. **Type-safe parameter encoding/decoding** using Alloy types
+3. **Solidity ABI export** for interoperability
+4. **Storage trait implementations** with caching
+5. **Error handling** with `Result` types
+6. **Payable checks** for ETH-receiving functions
+7. **Reentrancy protection** by default
+
+**Recommended approach:**
+
+```rust
+// Use macros for production contracts
+#[storage]
+#[entrypoint]
+pub struct MyContract {
+ value: StorageU256,
+}
+
+#[public]
+impl MyContract {
+ pub fn get_value(&self) -> U256 {
+ self.value.get()
+ }
+
+ pub fn set_value(&mut self, value: U256) {
+ self.value.set(value);
+ }
+}
+```
+
+This generates all the low-level code automatically while providing a clean, type-safe interface.
+
+## Advanced Use Cases
+
+### Custom Routing Logic
+
+Implement custom routing for multi-contract systems:
+
+```rust
+impl Router for MultiContract {
+ type Storage = MultiContract;
+
+ fn route(storage: &mut MultiContract, selector: u32, input: &[u8]) -> Option {
+ // Route to different modules based on selector range
+ match selector {
+ 0x00000000..=0x0fffffff => ModuleA::route(storage, selector, input),
+ 0x10000000..=0x1fffffff => ModuleB::route(storage, selector, input),
+ _ => None,
+ }
+ }
+
+ // ... other methods
+}
+```
+
+### Custom Entrypoint Logic
+
+Add custom logic before/after routing:
+
+```rust
+#[no_mangle]
+pub extern "C" fn user_entrypoint(len: usize) -> usize {
+ let host = VM { host: stylus_sdk::host::WasmVM{} };
+
+ // Custom pre-processing
+ let start_gas = host.evm_gas_left();
+
+ // Standard entrypoint logic
+ if host.msg_reentrant() {
+ return 1;
+ }
+
+ host.pay_for_memory_grow(0);
+ let input = host.read_args(len);
+
+ let (data, status) = match stylus_sdk::abi::router_entrypoint::(input, host.clone()) {
+ Ok(data) => (data, 0),
+ Err(data) => (data, 1),
+ };
+
+ // Custom post-processing
+ let gas_used = start_gas - host.evm_gas_left();
+ // Log or handle gas usage
+
+ host.flush_cache(false);
+ host.write_result(&data);
+ status
+}
+```
+
+## Debugging Tips
+
+### Enable Debug Mode
+
+```rust
+#[cfg(feature = "debug")]
+use stylus_sdk::console;
+
+fn route(storage: &mut MyContract, selector: u32, input: &[u8]) -> Option {
+ #[cfg(feature = "debug")]
+ console!("Selector: {:08x}", selector);
+
+ // Routing logic...
+}
+```
+
+### Check Selector Computation
+
+```rust
+#[test]
+fn test_selectors() {
+ use stylus_sdk::function_selector;
+
+ let hello = u32::from_be_bytes(function_selector!("hello"));
+ assert_eq!(hello, 0x19ff1d21);
+
+ // Compare with Solidity: bytes4(keccak256("hello()"))
+}
+```
+
+## See Also
+
+- [Contracts](../fundamentals/contracts.mdx) - High-level contract development
+- [Global Variables](../fundamentals/global-variables-and-functions.mdx) - VM context methods
+- [Storage Types](../fundamentals/data-types/storage.mdx) - Persistent storage
diff --git a/docs/stylus/recommended-libraries.md b/docs/stylus/advanced/recommended-libraries.mdx
similarity index 96%
rename from docs/stylus/recommended-libraries.md
rename to docs/stylus/advanced/recommended-libraries.mdx
index 11f5485db1..0d7886f509 100644
--- a/docs/stylus/recommended-libraries.md
+++ b/docs/stylus/advanced/recommended-libraries.mdx
@@ -1,11 +1,9 @@
---
id: recommended-libraries
-title: Recommended Libraries
-sidebar_label: Use Rust Crates
+title: Recommended libraries (Rust crates)
+sidebar_label: Recommended libraries
---
-# Recommended libraries
-
## Using public Rust crates
Rust provides a package registry at [crates.io](https://crates.io/), which lets developers conveniently access a plethora of open source libraries to utilize as dependencies in their code. Stylus Rust contracts can take advantage of these crates to simplify their development workflow.
diff --git a/docs/stylus/advanced/rust-to-solidity-differences.mdx b/docs/stylus/advanced/rust-to-solidity-differences.mdx
new file mode 100644
index 0000000000..18f813863e
--- /dev/null
+++ b/docs/stylus/advanced/rust-to-solidity-differences.mdx
@@ -0,0 +1,745 @@
+---
+title: 'Rust to Solidity differences'
+sidebar_label: 'Rust to Solidity'
+description: 'Language and syntax differences between Rust Stylus and Solidity smart contracts'
+user_story: 'As a Solidity developer, I want to understand how to write contracts in Rust'
+content_type: concept
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers who need to compare Stylus with Solidity.
+displayed_sidebar: buildStylusSidebar
+---
+
+Stylus introduces a new paradigm for writing smart contracts on Arbitrum using Rust and other WebAssembly-compatible languages. While Stylus contracts maintain full interoperability with Solidity contracts, there are important differences in how you structure and write code. This guide helps Solidity developers understand these differences.
+
+## Language and syntax
+
+### Contract structure
+
+**Solidity:**
+
+```solidity
+contract MyContract {
+ uint256 private value;
+ address public owner;
+
+ constructor(uint256 initialValue) {
+ value = initialValue;
+ owner = msg.sender;
+ }
+
+ function setValue(uint256 newValue) public {
+ value = newValue;
+ }
+}
+```
+
+**Stylus (Rust):**
+
+```rust
+use stylus_sdk::prelude::*;
+use stylus_sdk::alloy_primitives::{Address, U256};
+
+#[storage]
+#[entrypoint]
+pub struct MyContract {
+ value: StorageU256,
+ owner: StorageAddress,
+}
+
+#[public]
+impl MyContract {
+ #[constructor]
+ pub fn constructor(&mut self, initial_value: U256) {
+ self.value.set(initial_value);
+ self.owner.set(self.vm().msg_sender());
+ }
+
+ pub fn set_value(&mut self, new_value: U256) {
+ self.value.set(new_value);
+ }
+}
+```
+
+### Key structural differences
+
+1. **Attributes over keywords**: Stylus uses Rust attributes (`#[storage]`, `#[entrypoint]`, `#[public]`) instead of Solidity keywords
+2. **Explicit storage types**: Storage variables use special types like `StorageU256`, `StorageAddress`
+3. **Getter/setter pattern**: Storage access requires explicit `.get()` and `.set()` calls
+4. **Module system**: Rust uses `mod` and `use` for imports instead of `import`
+
+## Function visibility and state mutability
+
+### Visibility
+
+**Solidity:**
+
+```solidity
+function publicFunc() public {}
+function externalFunc() external {}
+function internalFunc() internal {}
+function privateFunc() private {}
+```
+
+**Stylus:**
+
+```rust
+#[public]
+impl MyContract {
+ // Public external functions
+ pub fn public_func(&self) {}
+
+ // Internal functions (not in #[public] block)
+ fn internal_func(&self) {}
+
+ // Private functions
+ fn private_func(&self) {}
+}
+```
+
+In Stylus:
+
+- Functions in `#[public]` blocks are externally callable
+- Regular `pub fn` outside `#[public]` blocks are internal
+- Non-pub functions are private to the module
+
+### State mutability
+
+**Solidity:**
+
+```solidity
+function viewFunc() public view returns (uint256) {}
+function pureFunc() public pure returns (uint256) {}
+function payableFunc() public payable {}
+```
+
+**Stylus:**
+
+```rust
+#[public]
+impl MyContract {
+ // View function (immutable reference)
+ pub fn view_func(&self) -> U256 {
+ self.value.get()
+ }
+
+ // Pure function (no self reference)
+ pub fn pure_func(a: U256, b: U256) -> U256 {
+ a + b
+ }
+
+ // Payable function
+ #[payable]
+ pub fn payable_func(&mut self) {
+ // Can receive Ether
+ }
+
+ // Write function (mutable reference)
+ pub fn write_func(&mut self) {
+ self.value.set(U256::from(42));
+ }
+}
+```
+
+State mutability in Stylus is determined by:
+
+- `&self` → View (read-only)
+- `&mut self` → Write (can modify storage)
+- No `self` → Pure (no storage access)
+- `#[payable]` → Can receive Ether
+
+## Constructors
+
+**Solidity:**
+
+```solidity
+constructor(uint256 initialValue) {
+ value = initialValue;
+}
+```
+
+**Stylus:**
+
+```rust
+#[public]
+impl MyContract {
+ #[constructor]
+ pub fn constructor(&mut self, initial_value: U256) {
+ self.value.set(initial_value);
+ }
+}
+```
+
+Key differences:
+
+- Use `#[constructor]` attribute
+- Constructor name is always `constructor`
+- Can be marked `#[payable]` if needed
+- Called only once during deployment
+- Each contract struct can have only one constructor
+
+## Modifiers
+
+Solidity modifiers don't exist in Stylus. Instead, use regular Rust patterns.
+
+**Solidity:**
+
+```solidity
+modifier onlyOwner() {
+ require(msg.sender == owner, "Not owner");
+ _;
+}
+
+function sensitiveFunction() public onlyOwner {
+ // Function logic
+}
+```
+
+**Stylus:**
+
+```rust
+impl MyContract {
+ fn only_owner(&self) -> Result<(), Vec> {
+ if self.owner.get() != self.vm().msg_sender() {
+ return Err(b"Not owner".to_vec());
+ }
+ Ok(())
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn sensitive_function(&mut self) -> Result<(), Vec> {
+ self.only_owner()?;
+ // Function logic
+ Ok(())
+ }
+}
+```
+
+Or using custom errors:
+
+```rust
+sol! {
+ error Unauthorized();
+}
+
+#[derive(SolidityError)]
+pub enum MyErrors {
+ Unauthorized(Unauthorized),
+}
+
+impl MyContract {
+ fn only_owner(&self) -> Result<(), MyErrors> {
+ if self.owner.get() != self.vm().msg_sender() {
+ return Err(MyErrors::Unauthorized(Unauthorized {}));
+ }
+ Ok(())
+ }
+}
+```
+
+## Fallback and receive functions
+
+**Solidity:**
+
+```solidity
+receive() external payable {
+ // Handle plain Ether transfers
+}
+
+fallback() external payable {
+ // Handle unmatched calls
+}
+```
+
+**Stylus:**
+
+```rust
+#[public]
+impl MyContract {
+ #[receive]
+ #[payable]
+ pub fn receive(&mut self) -> Result<(), Vec> {
+ // Handle plain Ether transfers
+ Ok(())
+ }
+
+ #[fallback]
+ #[payable]
+ pub fn fallback(&mut self, calldata: &[u8]) -> ArbResult {
+ // Handle unmatched calls
+ Ok(Vec::new())
+ }
+}
+```
+
+Key differences:
+
+- Use `#[receive]` and `#[fallback]` attributes
+- Receive function takes no parameters
+- Fallback function receives calldata as a parameter
+- Both return `Result` types
+
+## Events
+
+**Solidity:**
+
+```solidity
+event Transfer(address indexed from, address indexed to, uint256 value);
+
+function transfer() public {
+ emit Transfer(msg.sender, recipient, amount);
+}
+```
+
+**Stylus:**
+
+```rust
+sol! {
+ event Transfer(address indexed from, address indexed to, uint256 value);
+}
+
+#[public]
+impl MyContract {
+ pub fn transfer(&mut self, recipient: Address, amount: U256) {
+ self.vm().log(Transfer {
+ from: self.vm().msg_sender(),
+ to: recipient,
+ value: amount,
+ });
+ }
+}
+```
+
+Key differences:
+
+- Define events in `sol!` macro
+- Emit using `self.vm().log()`
+- Up to 3 parameters can be indexed
+- Can also use `raw_log()` for custom logging
+
+## Error handling
+
+**Solidity:**
+
+```solidity
+error InsufficientBalance(uint256 requested, uint256 available);
+
+function withdraw(uint256 amount) public {
+ if (balance < amount) {
+ revert InsufficientBalance(amount, balance);
+ }
+}
+```
+
+**Stylus:**
+
+```rust
+sol! {
+ error InsufficientBalance(uint256 requested, uint256 available);
+}
+
+#[derive(SolidityError)]
+pub enum MyErrors {
+ InsufficientBalance(InsufficientBalance),
+}
+
+#[public]
+impl MyContract {
+ pub fn withdraw(&mut self, amount: U256) -> Result<(), MyErrors> {
+ let balance = self.balance.get();
+ if balance < amount {
+ return Err(MyErrors::InsufficientBalance(InsufficientBalance {
+ requested: amount,
+ available: balance,
+ }));
+ }
+ Ok(())
+ }
+}
+```
+
+Key differences:
+
+- Define errors in `sol!` macro
+- Create error enum with `#[derive(SolidityError)]`
+- Return `Result`
+- Use Rust's `?` operator for error propagation
+
+## Inheritance
+
+**Solidity:**
+
+```solidity
+contract Base {
+ function baseFoo() public virtual {}
+}
+
+contract Derived is Base {
+ function baseFoo() public override {}
+}
+```
+
+**Stylus:**
+
+```rust
+#[public]
+trait IBase {
+ fn base_foo(&self);
+}
+
+#[storage]
+struct Base {}
+
+#[public]
+impl IBase for Base {
+ fn base_foo(&self) {
+ // Implementation
+ }
+}
+
+#[storage]
+#[entrypoint]
+struct Derived {
+ base: Base,
+}
+
+#[public]
+#[implements(IBase)]
+impl Derived {}
+
+#[public]
+impl IBase for Derived {
+ fn base_foo(&self) {
+ // Override implementation
+ }
+}
+```
+
+Key differences:
+
+- Use Rust traits for interfaces
+- Composition through storage fields
+- Use `#[implements()]` to expose inherited interfaces
+- No `virtual` or `override` keywords
+
+## Storage
+
+### Storage slots
+
+**Solidity:**
+
+```solidity
+uint256 public value;
+mapping(address => uint256) public balances;
+uint256[] public items;
+```
+
+**Stylus:**
+
+```rust
+#[storage]
+pub struct MyContract {
+ value: StorageU256,
+ balances: StorageMap,
+ items: StorageVec,
+}
+```
+
+### Storage access
+
+**Solidity:**
+
+```solidity
+value = 42;
+uint256 x = value;
+balances[user] = 100;
+```
+
+**Stylus:**
+
+```rust
+self.value.set(U256::from(42));
+let x = self.value.get();
+self.balances.setter(user).set(U256::from(100));
+```
+
+Key differences:
+
+- Explicit storage types (`Storage*`)
+- Must use `.get()` and `.set()`
+- Maps use `.setter()` for write access
+- Storage layout is compatible with Solidity
+
+## Constants and immutables
+
+**Solidity:**
+
+```solidity
+uint256 public constant MAX_SUPPLY = 1000000;
+address public immutable OWNER;
+
+constructor() {
+ OWNER = msg.sender;
+}
+```
+
+**Stylus:**
+
+```rust
+const MAX_SUPPLY: u64 = 1000000;
+
+#[storage]
+#[entrypoint]
+pub struct MyContract {
+ owner: StorageAddress, // Set in constructor
+}
+
+#[public]
+impl MyContract {
+ #[constructor]
+ pub fn constructor(&mut self) {
+ self.owner.set(self.vm().msg_sender());
+ }
+}
+```
+
+Key differences:
+
+- Use Rust `const` for constants
+- No direct equivalent to `immutable` (use storage set once in constructor)
+- Constants can be defined outside structs
+
+## Type system
+
+### Integer types
+
+| Solidity | Stylus (Rust) | Notes |
+| -------------------- | ---------------------- | ------------------------------------- |
+| `uint8` to `uint256` | `u8` to `u128`, `U256` | Native Rust types or Alloy primitives |
+| `int8` to `int256` | `i8` to `i128`, `I256` | Signed integers |
+| `address` | `Address` | 20-byte addresses |
+| `bytes` | `Bytes` | Dynamic bytes |
+| `bytesN` | `FixedBytes` | Fixed-size bytes |
+| `string` | `String` | UTF-8 strings |
+
+### Arrays and mappings
+
+| Solidity | Stylus (Rust) | Notes |
+| ----------------------------- | ------------------------------------------------------------ | -------------- |
+| `uint256[]` | `Vec` (memory)
`StorageVec` (storage) | Dynamic arrays |
+| `uint256[5]` | `[U256; 5]` | Fixed arrays |
+| `mapping(address => uint256)` | `StorageMap` | Key-value maps |
+
+## Global variables and functions
+
+### Block and transaction properties
+
+| Solidity | Stylus (Rust) |
+| ----------------- | ----------------------------- |
+| `msg.sender` | `self.vm().msg_sender()` |
+| `msg.value` | `self.vm().msg_value()` |
+| `msg.data` | Access through calldata |
+| `tx.origin` | `self.vm().tx_origin()` |
+| `tx.gasprice` | `self.vm().tx_gas_price()` |
+| `block.number` | `self.vm().block_number()` |
+| `block.timestamp` | `self.vm().block_timestamp()` |
+| `block.basefee` | `self.vm().block_basefee()` |
+| `block.coinbase` | `self.vm().block_coinbase()` |
+
+### Cryptographic functions
+
+| Solidity | Stylus (Rust) |
+| ----------------- | ---------------------------------- |
+| `keccak256(data)` | `self.vm().native_keccak256(data)` |
+| `sha256(data)` | Use external crate |
+| `ecrecover(...)` | Use `crypto::recover()` |
+
+## External calls
+
+**Solidity:**
+
+```solidity
+(bool success, bytes memory data) = address.call{value: amount}(data);
+```
+
+**Stylus:**
+
+```rust
+use stylus_sdk::call::RawCall;
+
+let result = unsafe {
+ RawCall::new(self.vm())
+ .value(amount)
+ .call(address, &data)
+};
+```
+
+Key differences:
+
+- Use `RawCall` for raw calls
+- Calls are `unsafe` in Rust
+- Use type-safe interfaces when possible via `sol_interface!`
+
+## Contract deployment
+
+**Solidity:**
+
+```solidity
+new MyContract{value: amount}(arg1, arg2);
+```
+
+**Stylus:**
+
+```rust
+use stylus_sdk::deploy::RawDeploy;
+
+let contract_address = unsafe {
+ RawDeploy::new(self.vm())
+ .value(amount)
+ .deploy(&bytecode, salt)?
+};
+```
+
+## Assembly
+
+**Solidity:**
+
+```solidity
+assembly {
+ let x := mload(0x40)
+ sstore(0, x)
+}
+```
+
+**Stylus:**
+
+Stylus does not support inline assembly. Instead:
+
+- Use hostio functions for low-level operations
+- Use Rust's `unsafe` blocks when necessary
+- Direct memory manipulation through safe Rust APIs
+
+## Features not in Stylus
+
+1. **No inline assembly**: Use hostio or safe Rust instead
+2. **No `selfdestruct`**: Deprecated in Ethereum, not available in Stylus
+3. **No `delegatecall` from storage**: Available but requires careful use
+4. **No modifier syntax**: Use regular functions
+5. **No multiple inheritance complexity**: Use trait-based composition
+
+## Features unique to Stylus
+
+1. **Rust's type system**: Strong compile-time guarantees
+2. **Zero-cost abstractions**: No overhead for safe code patterns
+3. **Cargo ecosystem**: Access to thousands of Rust crates
+4. **Memory safety**: Rust's borrow checker prevents common bugs
+5. **Better performance**: Wasm execution can be more efficient
+6. **Testing framework**: Use Rust's built-in testing with `TestVM`
+
+## Memory and gas costs
+
+### Memory management
+
+- **Solidity**: Automatic memory management with gas costs for allocation
+- **Stylus**: Manual control with Rust's ownership system, more efficient memory usage
+
+### Gas efficiency
+
+Stylus programs typically use less gas than equivalent Solidity:
+
+- More efficient Wasm execution
+- Better compiler optimizations
+- Fine-grained control over allocations
+
+## Development workflow
+
+### Compilation
+
+**Solidity:**
+
+```shell
+solc --bin --abi MyContract.sol
+```
+
+**Stylus:**
+
+```shell
+cargo stylus build
+```
+
+### Testing
+
+**Solidity:**
+
+```javascript
+// Hardhat or Foundry tests
+```
+
+**Stylus:**
+
+```rust
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use stylus_sdk::testing::*;
+
+ #[test]
+ fn test_function() {
+ let vm = TestVM::default();
+ let mut contract = MyContract::from(&vm);
+ // Test logic
+ }
+}
+```
+
+### Deployment
+
+Both use similar deployment processes but Stylus requires an activation step for new programs.
+
+## Interoperability
+
+Stylus and Solidity contracts can fully interact:
+
+- Stylus can call Solidity contracts
+- Solidity can call Stylus contracts
+- Same ABI encoding/decoding
+- Share storage layout compatibility
+
+Example calling Solidity from Stylus:
+
+```rust
+sol_interface! {
+ interface IToken {
+ function transfer(address to, uint256 amount) external returns (bool);
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn call_token(&self, token: Address, recipient: Address, amount: U256) -> Result> {
+ let token_contract = IToken::new(token);
+ let result = token_contract.transfer(self.vm(), recipient, amount)?;
+ Ok(result)
+ }
+}
+```
+
+## Best practices for transitioning
+
+1. **Think in Rust patterns**: Don't translate Solidity directly, use idiomatic Rust
+2. **Leverage the type system**: Use Rust's types to prevent bugs at compile time
+3. **Use composition over inheritance**: Prefer traits and composition
+4. **Handle errors explicitly**: Use `Result` types and the `?` operator
+5. **Write tests in Rust**: Take advantage of `TestVM` for unit testing
+6. **Read existing examples**: Study the stylus-sdk-rs examples directory
+7. **Start small**: Convert simple contracts first to learn the patterns
+
+## Resources
+
+- [Stylus SDK Documentation](https://docs.arbitrum.io/stylus/reference/stylus-sdk)
+- [stylus-sdk-rs Repository](https://github.com/OffchainLabs/stylus-sdk-rs)
+- [Example Contracts](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/examples)
+- [Rust Book](https://doc.rust-lang.org/book/) for learning Rust
diff --git a/docs/stylus/best-practices/_category_.yml b/docs/stylus/best-practices/_category_.yml
new file mode 100644
index 0000000000..401b1ad018
--- /dev/null
+++ b/docs/stylus/best-practices/_category_.yml
@@ -0,0 +1,5 @@
+label: 'Best practices'
+position: 6
+link:
+ type: generated-index
+ description: Security, performance, and gas optimization patterns for production Stylus contracts.
diff --git a/docs/stylus/best-practices/gas-optimization.mdx b/docs/stylus/best-practices/gas-optimization.mdx
new file mode 100644
index 0000000000..e8c35dd56b
--- /dev/null
+++ b/docs/stylus/best-practices/gas-optimization.mdx
@@ -0,0 +1,536 @@
+---
+title: 'Gas optimization best practices'
+sidebar_label: 'Gas optimization'
+description: 'Techniques and patterns for optimizing gas costs in Stylus smart contracts'
+user_story: 'As a developer, I want to minimize gas costs for my Stylus contracts while maintaining functionality'
+content_type: concept
+author: offchainlabs
+sme: offchainlabs
+sidebar_position: 2
+---
+
+Stylus contracts offer significant gas savings compared to Solidity (10-100x for compute-heavy operations), but following optimization best practices can reduce costs even further.
+
+## Why Stylus is cheaper
+
+
+
+_Figure: Stylus WASM executes natively, avoiding EVM interpretation overhead._
+
+### Performance comparison
+
+| Operation | Solidity (EVM) | Stylus (WASM) | Savings |
+| ---------------------- | -------------- | ------------- | ----------- |
+| Keccak256 hashing | ~30 gas/byte | ~3 gas/byte | **10x** |
+| Signature verification | ~3,000 gas | ~300 gas | **10x** |
+| Memory operations | ~3 gas/word | ~0.3 gas/word | **10x** |
+| Compute-heavy loops | High | Very low | **50-100x** |
+| Storage operations | Same | Same | **1x** |
+
+**Key insight**: Storage operations cost the same in Stylus and Solidity. Optimize by reducing storage access and maximizing compute efficiency.
+
+## Storage optimization
+
+### 1. Minimize storage reads
+
+```rust
+// ❌ Bad: Multiple storage reads
+pub fn calculate_bad(&self, iterations: u32) -> U256 {
+ let mut result = U256::ZERO;
+ for i in 0..iterations {
+ // Reads from storage every iteration!
+ result += self.multiplier.get();
+ }
+ result
+}
+
+// ✅ Good: Cache storage value
+pub fn calculate_good(&self, iterations: u32) -> U256 {
+ // Read once, use many times
+ let multiplier = self.multiplier.get();
+
+ let mut result = U256::ZERO;
+ for i in 0..iterations {
+ result += multiplier;
+ }
+ result
+}
+```
+
+**Gas impact**: Each storage read costs ~100 gas. The optimized version can save thousands of gas for large loops.
+
+### 2. Batch storage writes
+
+```rust
+// ❌ Bad: Multiple separate writes
+pub fn update_user_bad(&mut self, addr: Address, amount: U256, active: bool) {
+ self.balances.setter(addr).set(amount);
+ self.last_update.setter(addr).set(block::timestamp());
+ self.is_active.setter(addr).set(active);
+}
+
+// ✅ Good: Combine into struct
+sol_storage! {
+ pub struct UserData {
+ U256 balance;
+ U256 last_update;
+ bool is_active;
+ }
+
+ pub struct OptimizedContract {
+ StorageMap users;
+ }
+}
+
+pub fn update_user_good(&mut self, addr: Address, amount: U256, active: bool) {
+ let mut user = self.users.setter(addr);
+ user.balance.set(amount);
+ user.last_update.set(block::timestamp());
+ user.is_active.set(active);
+ // Single storage slot update instead of three
+}
+```
+
+### 3. Use appropriate data types
+
+```rust
+// ❌ Bad: Oversized types
+sol_storage! {
+ pub struct Wasteful {
+ StorageU256 tiny_counter; // Only needs u8
+ StorageU256 timestamp; // Only needs u64
+ StorageU256 percentage; // Only needs u16
+ }
+}
+
+// ✅ Good: Right-sized types
+sol_storage! {
+ pub struct Efficient {
+ StorageU8 tiny_counter; // Saves 31 bytes
+ StorageU64 timestamp; // Saves 24 bytes
+ StorageU16 percentage; // Saves 30 bytes
+ }
+}
+```
+
+**Note**: While smaller types save storage space, they don't reduce gas for individual storage operations. The benefit comes from packing multiple small values in one slot (if your storage layout supports it).
+
+### 4. Delete unused storage
+
+```rust
+pub fn cleanup(&mut self, addr: Address) -> Result<(), Vec> {
+ let balance = self.balances.get(addr);
+
+ ensure!(balance == U256::ZERO, "Balance not zero");
+
+ // ✅ Deleting storage refunds gas
+ self.balances.delete(addr);
+ self.metadata.delete(addr);
+
+ Ok(())
+}
+```
+
+**Gas refund**: Deleting storage refunds up to 15,000 gas per slot cleared.
+
+## Memory optimization
+
+### 1. Avoid unnecessary clones
+
+```rust
+use alloy_primitives::Bytes;
+
+// ❌ Bad: Unnecessary cloning
+pub fn process_data_bad(&self, data: Bytes) -> Bytes {
+ let copy = data.clone(); // Expensive memory allocation
+ copy
+}
+
+// ✅ Good: Use references
+pub fn process_data_good(&self, data: &Bytes) -> &Bytes {
+ data // No clone needed
+}
+
+// ✅ Good: Move when possible
+pub fn consume_data(mut data: Bytes) -> Bytes {
+ data.extend_from_slice(&[1, 2, 3]);
+ data // Ownership moved, no clone
+}
+```
+
+### 2. Use iterators efficiently
+
+```rust
+// ❌ Bad: Collect into vector unnecessarily
+pub fn sum_bad(&self, values: Vec) -> U256 {
+ let filtered: Vec = values
+ .iter()
+ .filter(|v| **v > U256::ZERO)
+ .copied()
+ .collect(); // Allocates new vector
+
+ filtered.iter().sum()
+}
+
+// ✅ Good: Chain iterators
+pub fn sum_good(&self, values: Vec) -> U256 {
+ values
+ .iter()
+ .filter(|v| **v > U256::ZERO)
+ .sum() // No intermediate allocation
+}
+```
+
+### 3. Reuse allocations
+
+```rust
+// ✅ Reuse buffers for repeated operations
+pub fn process_batch(&mut self, items: Vec) -> Vec {
+ let mut buffer = Vec::with_capacity(items.len());
+
+ for item in items {
+ buffer.clear(); // Reuse allocation
+ buffer.extend_from_slice(&item);
+ // Process buffer...
+ }
+
+ buffer
+}
+```
+
+## Computation optimization
+
+### 1. Use Stylus for compute-heavy operations
+
+```rust
+// ✅ Stylus excels at complex computation
+pub fn verify_merkle_proof(
+ &self,
+ leaf: [u8; 32],
+ proof: Vec<[u8; 32]>,
+ root: [u8; 32]
+) -> bool {
+ let mut computed_hash = leaf;
+
+ // This loop is 10-50x cheaper in Stylus than Solidity
+ for proof_element in proof {
+ computed_hash = if computed_hash <= proof_element {
+ keccak256(&[computed_hash, proof_element].concat())
+ } else {
+ keccak256(&[proof_element, computed_hash].concat())
+ };
+ }
+
+ computed_hash == root
+}
+```
+
+**Why it's faster**: Native WASM execution vs. EVM interpretation makes loops dramatically cheaper.
+
+### 2. Optimize hot paths
+
+```rust
+// ✅ Optimize frequently-called functions
+#[inline(always)]
+pub fn is_valid_amount(&self, amount: U256) -> bool {
+ amount > U256::ZERO && amount <= self.max_amount.get()
+}
+
+// Use in hot path
+pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), Vec> {
+ ensure!(self.is_valid_amount(amount), "Invalid amount");
+ // Transfer logic...
+ Ok(())
+}
+```
+
+### 3. Avoid redundant checks
+
+```rust
+// ❌ Bad: Redundant zero check
+pub fn add_to_balance_bad(&mut self, addr: Address, amount: U256) -> Result<(), Vec> {
+ ensure!(amount > U256::ZERO, "Amount must be positive");
+
+ let current = self.balances.get(addr);
+ ensure!(current + amount > current, "Overflow"); // Redundant if amount > 0
+
+ self.balances.setter(addr).add_assign(amount);
+ Ok(())
+}
+
+// ✅ Good: Single overflow check covers both
+pub fn add_to_balance_good(&mut self, addr: Address, amount: U256) -> Result<(), Vec> {
+ let current = self.balances.get(addr);
+
+ let new_balance = current
+ .checked_add(amount)
+ .ok_or("Overflow or invalid amount")?;
+
+ self.balances.setter(addr).set(new_balance);
+ Ok(())
+}
+```
+
+## Function call optimization
+
+### 1. Minimize cross-contract calls
+
+```rust
+// ❌ Bad: Multiple external calls
+pub fn get_price_bad(&self, token: Address) -> Result> {
+ let oracle = IOracle::new(self.oracle_address.get());
+
+ let price = oracle.get_price(self, token)?;
+ let decimals = oracle.get_decimals(self, token)?; // Second call
+ let timestamp = oracle.get_timestamp(self, token)?; // Third call
+
+ Ok(price)
+}
+
+// ✅ Good: Batch external calls
+pub fn get_price_good(&self, token: Address) -> Result> {
+ let oracle = IOracle::new(self.oracle_address.get());
+
+ // Single call returns all data
+ oracle.get_price_data(self, token)
+}
+```
+
+**Gas impact**: Each external call has overhead. Batching reduces cost significantly.
+
+### 2. Use internal functions
+
+```rust
+// ✅ Extract common logic to internal functions
+impl MyContract {
+ // Internal helper (no ABI encoding overhead)
+ fn internal_validate(&self, addr: Address, amount: U256) -> Result<(), Vec> {
+ ensure!(!addr.is_zero(), "Invalid address");
+ ensure!(amount > U256::ZERO, "Invalid amount");
+ Ok(())
+ }
+
+ // Public functions use internal helper
+ #[external]
+ pub fn deposit(&mut self, amount: U256) -> Result<(), Vec> {
+ self.internal_validate(msg::sender(), amount)?;
+ // Deposit logic...
+ Ok(())
+ }
+
+ #[external]
+ pub fn withdraw(&mut self, amount: U256) -> Result<(), Vec> {
+ self.internal_validate(msg::sender(), amount)?;
+ // Withdraw logic...
+ Ok(())
+ }
+}
+```
+
+## Event optimization
+
+### 1. Use indexed parameters wisely
+
+```rust
+sol! {
+ // ✅ Index frequently-queried fields (max 3 indexed)
+ event Transfer(
+ address indexed from,
+ address indexed to,
+ uint256 value // Not indexed - saves gas
+ );
+
+ // ❌ Bad: Too many indexed parameters
+ event TooManyIndexed(
+ address indexed from,
+ address indexed to,
+ uint256 indexed amount, // Expensive to index
+ uint256 indexed timestamp // 4th indexed param - not allowed!
+ );
+}
+```
+
+**Gas impact**: Each indexed parameter costs ~375 additional gas. Only index fields you'll search by.
+
+### 2. Batch events when possible
+
+```rust
+// ✅ Emit single event for batch operation
+sol! {
+ event BatchTransfer(
+ address indexed from,
+ address[] to,
+ uint256[] amounts
+ );
+}
+
+pub fn batch_transfer(
+ &mut self,
+ recipients: Vec,
+ amounts: Vec
+) -> Result<(), Vec> {
+ // Process transfers...
+
+ // Single event instead of N events
+ evm::log(BatchTransfer {
+ from: msg::sender(),
+ to: recipients,
+ amounts,
+ });
+
+ Ok(())
+}
+```
+
+## Binary size optimization
+
+Smaller WASM binaries cost less to deploy and activate.
+
+### 1. Optimize compilation flags
+
+```toml
+# Cargo.toml
+[profile.release]
+opt-level = "z" # Optimize for size
+lto = true # Link-time optimization
+codegen-units = 1 # Better optimization
+strip = true # Remove debug symbols
+panic = "abort" # Smaller panic handling
+```
+
+### 2. Avoid large dependencies
+
+```rust
+// ❌ Bad: Heavy dependency for simple task
+use fancy_math_library::complex_sqrt; // Adds 50KB to binary
+
+pub fn calculate(&self, value: U256) -> U256 {
+ complex_sqrt(value) // Using 1% of library
+}
+
+// ✅ Good: Implement simple operations yourself
+pub fn simple_sqrt(&self, value: U256) -> U256 {
+ // Custom implementation adds minimal binary size
+ // Newton's method or similar
+}
+```
+
+### 3. Use cargo stylus optimization
+
+```shell
+# Check binary size
+cargo stylus check
+
+# Optimize with wasm-opt
+cargo stylus deploy --optimize
+
+# Maximum optimization (slower build, smaller binary)
+cargo stylus deploy --optimize-level 3
+```
+
+## Gas measurement
+
+### 1. Profile your contracts
+
+```rust
+#[cfg(test)]
+mod gas_tests {
+ use super::*;
+
+ #[test]
+ fn benchmark_transfer() {
+ let vm = TestVM::default();
+ let mut contract = Token::from(&vm);
+
+ // Measure gas for operation
+ let gas_before = vm.gas_left();
+ contract.transfer(recipient, amount).unwrap();
+ let gas_used = gas_before - vm.gas_left();
+
+ println!("Transfer gas used: {}", gas_used);
+ assert!(gas_used < 50000, "Transfer too expensive");
+ }
+}
+```
+
+### 2. Compare implementations
+
+```rust
+#[cfg(test)]
+mod optimization_tests {
+ #[test]
+ fn compare_storage_patterns() {
+ // Test pattern A
+ let gas_a = measure_pattern_a();
+
+ // Test pattern B
+ let gas_b = measure_pattern_b();
+
+ println!("Pattern A: {} gas", gas_a);
+ println!("Pattern B: {} gas", gas_b);
+ println!("Savings: {}%", (gas_a - gas_b) * 100 / gas_a);
+ }
+}
+```
+
+## Optimization checklist
+
+Before deploying, verify you've:
+
+- [ ] Minimized storage reads and writes
+- [ ] Cached frequently-accessed storage values
+- [ ] Used appropriate data types
+- [ ] Deleted unused storage for gas refunds
+- [ ] Avoided unnecessary clones and allocations
+- [ ] Optimized hot code paths
+- [ ] Minimized cross-contract calls
+- [ ] Used indexed events sparingly
+- [ ] Optimized WASM binary size
+- [ ] Profiled gas usage for critical functions
+- [ ] Compared against Solidity baseline (if porting)
+
+## Common optimizations summary
+
+| Pattern | Gas Savings | Complexity |
+| ------------------------- | ------------------------------ | ---------- |
+| Cache storage reads | High (100+ gas per read saved) | Low |
+| Delete unused storage | Medium (15,000 gas refund) | Low |
+| Batch storage writes | Medium (varies) | Medium |
+| Use iterators vs. collect | Low-Medium | Low |
+| Minimize external calls | High | Medium |
+| Optimize binary size | High (deployment only) | Medium |
+| Right-size data types | Low-Medium | Low |
+
+## Advanced optimization
+
+### Custom memory allocators
+
+For advanced users, custom allocators can reduce memory overhead:
+
+```rust
+#[global_allocator]
+static ALLOCATOR: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
+```
+
+**Warning**: Only use if you understand the trade-offs.
+
+### Assembly optimization
+
+For critical paths, you can use WASM intrinsics:
+
+```rust
+use core::arch::wasm32::*;
+
+// ✅ Advanced: Use WASM intrinsics for critical operations
+pub fn optimized_hash(&self, data: &[u8]) -> [u8; 32] {
+ // WASM-optimized hashing
+ // Only use if you're an advanced developer
+}
+```
+
+## Next steps
+
+- Apply [security best practices](/stylus/best-practices/security)
+- Study [deployment optimization](/stylus/guides/optimizing-binaries)
+- Learn about [caching strategies](/stylus/guides/caching-contracts)
+- Review [debugging techniques](/stylus/cli-tools/debugging-tx)
diff --git a/docs/stylus/best-practices/security.mdx b/docs/stylus/best-practices/security.mdx
new file mode 100644
index 0000000000..a3e320835a
--- /dev/null
+++ b/docs/stylus/best-practices/security.mdx
@@ -0,0 +1,499 @@
+---
+title: 'Security best practices'
+sidebar_label: 'Security'
+description: 'Essential security patterns and guidelines for writing secure Stylus smart contracts'
+user_story: 'As a developer, I want to write secure Stylus contracts that protect user funds and prevent exploits'
+content_type: concept
+author: offchainlabs
+sme: offchainlabs
+sidebar_position: 1
+---
+
+Writing secure smart contracts is critical - vulnerabilities can lead to loss of funds and user trust. This guide covers essential security patterns for Stylus development.
+
+## Core security principles
+
+### 1. Input validation
+
+Always validate external inputs before using them in your contract logic.
+
+```rust
+use stylus_sdk::{prelude::*, msg};
+use alloy_primitives::{Address, U256};
+
+#[external]
+impl MyContract {
+ // ❌ Bad: No validation
+ pub fn transfer_bad(&mut self, recipient: Address, amount: U256) -> Result<(), Vec> {
+ let sender = msg::sender();
+ self.balances.setter(sender).sub_assign(amount);
+ self.balances.setter(recipient).add_assign(amount);
+ Ok(())
+ }
+
+ // ✅ Good: Proper validation
+ pub fn transfer_good(&mut self, recipient: Address, amount: U256) -> Result<(), Vec> {
+ // Validate inputs
+ ensure!(!recipient.is_zero(), "Invalid recipient");
+ ensure!(amount > U256::ZERO, "Amount must be positive");
+
+ let sender = msg::sender();
+ let sender_balance = self.balances.get(sender);
+
+ // Check sufficient balance
+ ensure!(sender_balance >= amount, "Insufficient balance");
+
+ // Safe arithmetic
+ self.balances.setter(sender).sub_assign(amount);
+ self.balances.setter(recipient).add_assign(amount);
+
+ Ok(())
+ }
+}
+```
+
+### 2. Access control
+
+Implement proper authorization checks for privileged operations.
+
+```rust
+use stylus_sdk::{prelude::*, msg, storage::StorageAddress};
+
+sol_storage! {
+ pub struct Ownable {
+ StorageAddress owner;
+ }
+}
+
+#[external]
+impl Ownable {
+ // Initialize owner in constructor-like pattern
+ pub fn init(&mut self) -> Result<(), Vec> {
+ let owner = self.owner.get();
+ ensure!(owner.is_zero(), "Already initialized");
+ self.owner.set(msg::sender());
+ Ok(())
+ }
+
+ // Modifier pattern for owner-only functions
+ fn only_owner(&self) -> Result<(), Vec> {
+ ensure!(msg::sender() == self.owner.get(), "Not authorized");
+ Ok(())
+ }
+
+ pub fn sensitive_operation(&mut self) -> Result<(), Vec> {
+ self.only_owner()?;
+ // Perform privileged operation
+ Ok(())
+ }
+
+ pub fn transfer_ownership(&mut self, new_owner: Address) -> Result<(), Vec> {
+ self.only_owner()?;
+ ensure!(!new_owner.is_zero(), "Invalid new owner");
+ self.owner.set(new_owner);
+ Ok(())
+ }
+}
+```
+
+### 3. Reentrancy protection
+
+Protect against reentrancy attacks using the checks-effects-interactions pattern.
+
+```rust
+use stylus_sdk::{prelude::*, call::transfer_eth, msg};
+
+sol_storage! {
+ pub struct Vault {
+ StorageMap balances;
+ StorageBool locked; // Reentrancy guard
+ }
+}
+
+#[external]
+impl Vault {
+ // ❌ Bad: Vulnerable to reentrancy
+ pub fn withdraw_bad(&mut self, amount: U256) -> Result<(), Vec> {
+ let sender = msg::sender();
+ let balance = self.balances.get(sender);
+
+ ensure!(balance >= amount, "Insufficient balance");
+
+ // DANGER: External call before state update
+ transfer_eth(sender, amount)?;
+
+ // State updated after external call - vulnerable!
+ self.balances.setter(sender).sub_assign(amount);
+ Ok(())
+ }
+
+ // ✅ Good: Checks-Effects-Interactions pattern
+ pub fn withdraw_good(&mut self, amount: U256) -> Result<(), Vec> {
+ // Check: Reentrancy guard
+ ensure!(!self.locked.get(), "Reentrancy detected");
+ self.locked.set(true);
+
+ let sender = msg::sender();
+ let balance = self.balances.get(sender);
+
+ // Check: Validate conditions
+ ensure!(balance >= amount, "Insufficient balance");
+
+ // Effect: Update state BEFORE external call
+ self.balances.setter(sender).sub_assign(amount);
+
+ // Interaction: External call last
+ let result = transfer_eth(sender, amount);
+
+ // Release lock
+ self.locked.set(false);
+
+ result
+ }
+}
+```
+
+### 4. Safe arithmetic
+
+While Rust prevents overflows in debug mode, use explicit checks for production.
+
+```rust
+use alloy_primitives::U256;
+
+#[external]
+impl SafeMath {
+ // ✅ Use checked arithmetic
+ pub fn safe_add(&self, a: U256, b: U256) -> Result> {
+ a.checked_add(b).ok_or("Arithmetic overflow".into())
+ }
+
+ pub fn safe_mul(&self, a: U256, b: U256) -> Result> {
+ a.checked_mul(b).ok_or("Arithmetic overflow".into())
+ }
+
+ // ✅ Validate before operations
+ pub fn calculate_fee(&self, amount: U256, basis_points: U256) -> Result> {
+ ensure!(basis_points <= U256::from(10000), "Invalid fee");
+
+ amount
+ .checked_mul(basis_points)
+ .and_then(|v| v.checked_div(U256::from(10000)))
+ .ok_or("Fee calculation failed".into())
+ }
+}
+```
+
+## Common vulnerabilities
+
+### Integer overflow/underflow
+
+**Risk**: Arithmetic operations that exceed type limits can cause unexpected behavior.
+
+**Prevention**:
+
+```rust
+// ✅ Use checked operations
+let result = value.checked_add(amount).ok_or("Overflow")?;
+
+// ✅ Or use saturating operations when appropriate
+let capped_value = value.saturating_add(amount);
+```
+
+### Unchecked external calls
+
+**Risk**: Failed external calls may be silently ignored.
+
+**Prevention**:
+
+```rust
+// ❌ Bad: Ignoring call result
+let _ = external_contract.call(data);
+
+// ✅ Good: Handle all results
+external_contract.call(data).map_err(|e| "External call failed")?;
+```
+
+### Front-running
+
+**Risk**: Transactions visible in mempool can be exploited by miners or bots.
+
+**Prevention**:
+
+```rust
+// ✅ Use commit-reveal pattern for sensitive operations
+sol_storage! {
+ pub struct CommitReveal {
+ StorageMap commits;
+ StorageMap reveal_times;
+ }
+}
+
+impl CommitReveal {
+ pub fn commit(&mut self, commitment: [u8; 32]) -> Result<(), Vec> {
+ let sender = msg::sender();
+ self.commits.setter(sender).set(commitment);
+ self.reveal_times.setter(sender).set(block::timestamp() + 100);
+ Ok(())
+ }
+
+ pub fn reveal(&mut self, value: U256, salt: [u8; 32]) -> Result<(), Vec> {
+ let sender = msg::sender();
+
+ // Verify commit period passed
+ ensure!(
+ block::timestamp() >= self.reveal_times.get(sender),
+ "Too early to reveal"
+ );
+
+ // Verify commitment
+ let expected = keccak256(&[&value.to_be_bytes(), &salt].concat());
+ ensure!(
+ expected == self.commits.get(sender),
+ "Invalid reveal"
+ );
+
+ // Process reveal...
+ Ok(())
+ }
+}
+```
+
+### Denial of Service (DoS)
+
+**Risk**: Unbounded loops or operations that can be griefed.
+
+**Prevention**:
+
+```rust
+// ❌ Bad: Unbounded loop
+pub fn distribute_rewards_bad(&mut self, recipients: Vec) -> Result<(), Vec> {
+ for recipient in recipients {
+ // Could run out of gas with too many recipients
+ self.send_reward(recipient)?;
+ }
+ Ok(())
+}
+
+// ✅ Good: Paginated or pull-based pattern
+pub fn distribute_rewards_good(
+ &mut self,
+ start_index: U256,
+ count: U256
+) -> Result<(), Vec> {
+ ensure!(count <= U256::from(50), "Batch too large");
+
+ let end = start_index + count;
+ for i in start_index..end {
+ let recipient = self.recipients.get(i);
+ if !recipient.is_zero() {
+ self.send_reward(recipient)?;
+ }
+ }
+ Ok(())
+}
+
+// ✅ Better: Pull-based (users claim their own rewards)
+pub fn claim_reward(&mut self) -> Result<(), Vec> {
+ let sender = msg::sender();
+ let reward = self.pending_rewards.get(sender);
+
+ ensure!(reward > U256::ZERO, "No rewards");
+
+ self.pending_rewards.setter(sender).set(U256::ZERO);
+ transfer_eth(sender, reward)?;
+
+ Ok(())
+}
+```
+
+## Storage security
+
+### Visibility and access patterns
+
+```rust
+sol_storage! {
+ pub struct SecureVault {
+ // Public read, controlled write
+ StorageU256 public_total;
+
+ // Private storage - not visible off-chain without knowing slot
+ StorageMap private_balances;
+
+ // Owner-controlled
+ StorageAddress owner;
+ }
+}
+
+#[external]
+impl SecureVault {
+ // ✅ Expose only what's necessary
+ pub fn get_total(&self) -> U256 {
+ self.public_total.get()
+ }
+
+ // ✅ Don't expose internal mappings directly
+ pub fn get_balance(&self, account: Address) -> Result> {
+ ensure!(msg::sender() == account || msg::sender() == self.owner.get(), "Unauthorized");
+ Ok(self.private_balances.get(account))
+ }
+}
+```
+
+### Prevent storage collisions
+
+```rust
+// ✅ Use unique storage namespace for upgradeable contracts
+sol_storage! {
+ pub struct MyContract {
+ // Prefix with contract name to avoid collisions
+ #[borrow]
+ MyContractStorage storage;
+ }
+}
+
+sol_storage! {
+ pub struct MyContractStorage {
+ StorageU256 value;
+ StorageMap balances;
+ }
+}
+```
+
+## Error handling
+
+### Informative error messages
+
+```rust
+#[derive(SolidityError)]
+pub enum MyError {
+ InsufficientBalance(U256),
+ Unauthorized(Address),
+ InvalidAmount(U256),
+ TransferFailed(Address, U256),
+}
+
+#[external]
+impl MyContract {
+ pub fn transfer(&mut self, to: Address, amount: U256) -> Result<(), MyError> {
+ let sender = msg::sender();
+ let balance = self.balances.get(sender);
+
+ if balance < amount {
+ return Err(MyError::InsufficientBalance(balance));
+ }
+
+ if to.is_zero() {
+ return Err(MyError::InvalidAmount(amount));
+ }
+
+ // Transfer logic...
+ Ok(())
+ }
+}
+```
+
+### Fail securely
+
+```rust
+// ✅ Fail closed, not open
+pub fn privileged_function(&mut self) -> Result<(), Vec> {
+ // Default to denying access
+ let is_authorized = self.check_authorization(msg::sender());
+
+ // Explicit check required to proceed
+ ensure!(is_authorized, "Access denied");
+
+ // Privileged operation
+ Ok(())
+}
+```
+
+## Testing for security
+
+### Write comprehensive tests
+
+```rust
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use stylus_sdk::testing::*;
+
+ #[test]
+ fn test_reentrancy_protection() {
+ let vm = TestVM::default();
+ let mut contract = Vault::from(&vm);
+
+ // Setup
+ contract.deposit(U256::from(100)).unwrap();
+
+ // Attempt reentrancy
+ let result = contract.withdraw(U256::from(50));
+ assert!(result.is_ok());
+
+ // Second withdrawal should fail if still locked
+ let result2 = contract.withdraw(U256::from(50));
+ assert!(result2.is_err());
+ }
+
+ #[test]
+ fn test_access_control() {
+ let vm = TestVM::default();
+ let mut contract = Ownable::from(&vm);
+
+ // Initialize owner
+ contract.init().unwrap();
+
+ // Non-owner should be rejected
+ vm.set_caller(address!("0x0000000000000000000000000000000000000001"));
+ assert!(contract.sensitive_operation().is_err());
+
+ // Owner should succeed
+ vm.set_caller(/* original owner */);
+ assert!(contract.sensitive_operation().is_ok());
+ }
+
+ #[test]
+ fn test_arithmetic_safety() {
+ let vm = TestVM::default();
+ let contract = SafeMath::from(&vm);
+
+ // Test overflow
+ let max = U256::MAX;
+ let result = contract.safe_add(max, U256::from(1));
+ assert!(result.is_err());
+
+ // Test valid operation
+ let result = contract.safe_add(U256::from(1), U256::from(2));
+ assert_eq!(result.unwrap(), U256::from(3));
+ }
+}
+```
+
+## Security checklist
+
+Before deploying your contract, verify:
+
+- [ ] All external inputs are validated
+- [ ] Access control is implemented for privileged functions
+- [ ] Reentrancy guards protect state-changing functions
+- [ ] Arithmetic operations use checked methods
+- [ ] External call results are handled
+- [ ] Error messages don't leak sensitive information
+- [ ] Storage visibility is appropriate
+- [ ] No unbounded loops or arrays
+- [ ] Critical functions have comprehensive tests
+- [ ] Code has been reviewed by another developer
+- [ ] Consider professional audit for high-value contracts
+
+## Additional resources
+
+- [Stylus Security Audit](https://docs.arbitrum.io/stylus/reference/audit-reports)
+- [Rust Security Guidelines](https://anssi-fr.github.io/rust-guide/)
+- [Smart Contract Security Best Practices](https://consensys.github.io/smart-contract-best-practices/)
+- [OWASP Smart Contract Top 10](https://owasp.org/www-project-smart-contract-top-10/)
+
+## Next steps
+
+- Review [gas optimization best practices](/stylus/best-practices/gas-optimization)
+- Study [error handling patterns](/stylus/fundamentals/contracts#error-handling)
+- Explore [testing strategies](/stylus/fundamentals/testing-contracts)
diff --git a/docs/stylus/cli-tools-overview.md b/docs/stylus/cli-tools-overview.md
deleted file mode 100644
index ba9675b99d..0000000000
--- a/docs/stylus/cli-tools-overview.md
+++ /dev/null
@@ -1,43 +0,0 @@
----
-id: cli-tools-overview
-title: CLI Tools (cargo-stylus)
-sidebar_label: CLI tools overview
----
-
-The CLI tools provided for Stylus, specifically the `cargo-stylus` tool, are designed to help developers manage, compile, and optimize their Stylus contracts efficiently. This overview provides a summary of the tools available and how to use them effectively.
-
-## Available tools
-
-### 1. Optimize WASM binaries
-
-The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible.
-
-- **[Optimize WASM binaries](/stylus/how-tos/optimizing-binaries.mdx):** Learn how to optimize your WASM binaries for performance and size.
-
-### 2. Debug Stylus transactions
-
-Gain insights into your Stylus contracts by debugging transactions.
-
-- **[Debug Stylus transactions](/stylus/how-tos/debugging-tx.mdx):** A guide to debugging transactions, helping you identify and fix issues.
-
-### 3. Verify contracts
-
-Ensure that your Stylus contracts are correctly verified.
-
-- **[Verify contracts](/stylus/how-tos/verifying-contracts.mdx):** Step-by-step instructions on how to verify your contracts using `cargo-stylus`.
-
-## Source code repository
-
-The source code for `cargo-stylus` is available on GitHub. Explore the code, contribute, or use it as a reference.
-
-- **[cargo-stylus repository](https://github.com/OffchainLabs/stylus):** Visit the GitHub repository for more information.
-
-## Additional resources
-
-For more advanced usage and detailed guides, refer to the following resources:
-
-- **[Optimize WASM binaries](/stylus/how-tos/optimizing-binaries.mdx)**
-- **[Troubleshooting](/stylus/troubleshooting-building-stylus.md)**
-- **[Run a Stylus dev node](/run-arbitrum-node/03-run-local-full-chain-simulation.mdx)**
-
-This overview page serves as the starting point for mastering the CLI tools available for Stylus development. From optimizing your contracts to debugging and verifying them, the `cargo-stylus` toolset is integral to a smooth development experience.
diff --git a/docs/stylus/cli-tools/_category_.yml b/docs/stylus/cli-tools/_category_.yml
new file mode 100644
index 0000000000..5a13bee5be
--- /dev/null
+++ b/docs/stylus/cli-tools/_category_.yml
@@ -0,0 +1,5 @@
+label: 'CLI tools'
+position: 4
+link:
+ type: generated-index
+ description: cargo-stylus command-line tools for building, deploying, and verifying contracts.
diff --git a/docs/stylus/cli-tools/check-and-deploy.mdx b/docs/stylus/cli-tools/check-and-deploy.mdx
new file mode 100644
index 0000000000..b4c419ba60
--- /dev/null
+++ b/docs/stylus/cli-tools/check-and-deploy.mdx
@@ -0,0 +1,779 @@
+---
+title: 'Check and deploy'
+description: 'Check and deploy Stylus contracts'
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers who need to understand how to check and deploy Stylus contracts.
+displayed_sidebar: buildStylusSidebar
+---
+
+This guide explains how to validate and deploy Stylus smart contracts using the `cargo stylus` CLI tool. The process involves two main steps: checking that your contract is valid, and deploying it to an Arbitrum chain.
+
+## Prerequisites
+
+Before checking or deploying contracts, ensure you have:
+
+1. **Rust toolchain** installed (see [rustup.rs](https://rustup.rs))
+2. **WebAssembly target** added:
+ ```shell
+ rustup target add wasm32-unknown-unknown
+ ```
+3. **cargo-stylus CLI** installed:
+ ```shell
+ cargo install cargo-stylus
+ ```
+4. **RPC endpoint** for the target chain (see [testnet information](https://docs.arbitrum.io/stylus/reference/testnet-information))
+5. **Funded account** with ETH for gas and activation fees
+
+## Overview: The Two-Step Process
+
+Deploying a Stylus contract involves two distinct steps:
+
+1. **Deployment**: Upload the compressed WASM bytecode to the chain, assigning it an address
+2. **Activation**: Trigger onchain compilation to native code and cache it for fast execution
+
+The `cargo stylus check` command validates your contract before deployment, and `cargo stylus deploy` handles both steps automatically.
+
+## Checking Contracts
+
+The `cargo stylus check` command validates that your contract can be deployed and activated without actually sending a transaction.
+
+### What check does
+
+1. **Compiles** your Rust code to WASM with `wasm32-unknown-unknown` target
+2. **Compresses** the WASM using brotli compression
+3. **Validates** the WASM structure:
+ - Required exports (`user_entrypoint`)
+ - Allowed imports (only `vm_hooks`)
+ - Memory constraints
+ - Size limits (24KB compressed)
+4. **Simulates activation** using `eth_call` to verify onchain compatibility
+5. **Estimates data fee** required for activation
+
+### Basic usage
+
+```shell
+# Check the current project against Arbitrum Sepolia (default)
+cargo stylus check
+```
+
+### Common options
+
+```shell
+# Check against a specific network
+cargo stylus check \
+ --endpoint="https://arb1.arbitrum.io/rpc"
+
+# Check a specific WASM file
+cargo stylus check \
+ --wasm-file=./target/wasm32-unknown-unknown/release/my_contract.wasm
+
+# Check with a specific contract address
+cargo stylus check \
+ --contract-address=0x1234567890123456789012345678901234567890
+```
+
+### Success output
+
+When your contract passes validation:
+
+```shell
+Finished release [optimized] target(s) in 1.88s
+Reading WASM file at target/wasm32-unknown-unknown/release/my_contract.wasm
+Compressed WASM size: 3 KB
+Contract succeeded Stylus onchain activation checks with Stylus version: 1
+wasm data fee: 0.0001 ETH (originally 0.00008 ETH with 20% bump)
+```
+
+### Failure output
+
+If validation fails, you'll see detailed error information:
+
+```shell
+Reading WASM file at target/wasm32-unknown-unknown/release/bad_contract.wasm
+Compressed WASM size: 55 B
+Stylus checks failed: contract predeployment check failed
+
+Caused by:
+ binary exports reserved symbol stylus_ink_left
+
+Location:
+ prover/src/binary.rs:493:9
+```
+
+Common validation errors include:
+
+- **Missing entrypoint**: Contract lacks `#[entrypoint]` attribute
+- **Invalid exports**: Contract exports reserved symbols
+- **Size limit exceeded**: Compressed WASM exceeds 24KB
+- **Invalid imports**: Contract imports functions outside `vm_hooks`
+- **Memory violations**: Incorrect memory handling or growth
+
+## Deploying Contracts
+
+The `cargo stylus deploy` command compiles, deploys, and activates your contract in a single operation.
+
+### What deploy does
+
+1. **Compiles and checks** the contract (same as `cargo stylus check`)
+2. **Deploys bytecode**: Sends transaction to upload compressed WASM to the chain
+3. **Activates contract**: Calls `activateProgram` precompile to compile to native code
+4. **Verifies success**: Confirms both transactions completed successfully
+
+### Basic deployment
+
+```shell
+# Deploy to Arbitrum Sepolia (default testnet)
+cargo stylus deploy \
+ --private-key-path=./key.txt
+```
+
+### Deployment with gas estimation
+
+Before deploying, estimate the gas required:
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --estimate-gas
+```
+
+Output:
+
+```shell
+Compressed WASM size: 3 KB
+Deploying contract to address 0x457b1ba688e9854bdbed2f473f7510c476a3da09
+Estimated gas: 12756792
+wasm data fee: 0.0001 ETH
+```
+
+### Full deployment
+
+Once estimation looks correct, deploy for real:
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt
+```
+
+Output shows both transactions:
+
+```shell
+Compressed WASM size: 3 KB
+Deploying contract to address 0x457b1ba688e9854bdbed2f473f7510c476a3da09
+Estimated gas: 12756792
+Submitting tx...
+Confirmed tx 0x42db...7311, gas used 11657164
+
+Activating contract at address 0x457b1ba688e9854bdbed2f473f7510c476a3da09
+Estimated gas: 14251759
+Submitting tx...
+Confirmed tx 0x0bdb...3307, gas used 14204908
+```
+
+### Deployment options
+
+#### Network selection
+
+```shell
+# Deploy to Arbitrum One (mainnet)
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt
+
+# Deploy to Arbitrum Sepolia (testnet)
+cargo stylus deploy \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \
+ --private-key-path=./key.txt
+```
+
+#### Private key management
+
+```shell
+# From file (recommended)
+cargo stylus deploy \
+ --private-key-path=./key.txt
+
+# From environment (WARNING: exposes key to shell history)
+cargo stylus deploy \
+ --private-key=$PRIVATE_KEY
+
+# From keystore file
+cargo stylus deploy \
+ --keystore-path=./keystore.json \
+ --keystore-password-path=./password.txt
+```
+
+#### Gas price control
+
+```shell
+# Set custom gas price (in gwei)
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --max-fee-per-gas-gwei=0.05
+```
+
+#### Deploy without activation
+
+For advanced use cases, deploy bytecode without immediate activation:
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --no-activate
+```
+
+Later, activate separately:
+
+```shell
+cargo stylus activate \
+ --address=0x457b1ba688e9854bdbed2f473f7510c476a3da09 \
+ --private-key-path=./key.txt
+```
+
+#### Constructor arguments
+
+Deploy contracts with constructor arguments:
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --constructor-args "Hello" "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" 42
+```
+
+Send ETH to payable constructor:
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --constructor-value=0.1 \
+ --constructor-args "InitialValue"
+```
+
+#### Reproducible builds
+
+For contract verification, use Docker-based reproducible builds:
+
+```shell
+# Default: uses Docker for reproducibility
+cargo stylus deploy \
+ --private-key-path=./key.txt
+
+# Specify cargo-stylus version
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --cargo-stylus-version=0.5.0
+```
+
+Skip Docker for local development (non-reproducible):
+
+```shell
+cargo stylus deploy \
+ --private-key-path=./key.txt \
+ --no-verify
+```
+
+## Understanding Activation
+
+Activation is the process of compiling WASM to native machine code onchain.
+
+### Why activation is required
+
+- **Performance**: Native code executes 10-100x faster than interpreted WASM
+- **Validation**: Ensures WASM is well-formed and follows all constraints
+- **Caching**: Compiled code is cached for all future contract calls
+
+### Activation process
+
+1. **Decompress**: Brotli-compressed WASM is decompressed
+2. **Validate**: WASM structure is checked for correctness
+3. **Compile**: WASM is compiled to native machine code
+4. **Cache**: Compiled code is stored in the activation cache
+5. **Charge fee**: Data fee based on WASM size is charged
+
+### Data fee calculation
+
+The data fee depends on the size of your WASM:
+
+```rust
+// From activation.rs
+pub async fn data_fee(
+ code: impl Into,
+ address: Address,
+ config: &ActivationConfig,
+ provider: &impl Provider,
+) -> Result {
+ let result = arbwasm
+ .activateProgram(address)
+ .call()
+ .await?;
+
+ let data_fee = result.dataFee;
+ let bump = config.data_fee_bump_percent; // Default 20%
+ let adjusted = bump_data_fee(data_fee, bump);
+
+ Ok(adjusted)
+}
+```
+
+By default, the fee is bumped by 20% to account for gas price fluctuations.
+
+### Activation errors
+
+Common activation failures:
+
+#### Missing entrypoint
+
+```
+Error: Contract could not be activated as it is missing an entrypoint.
+Please ensure that your contract has an #[entrypoint] defined on your main struct
+```
+
+**Solution**: Add `#[entrypoint]` to your main storage struct:
+
+```rust
+#[entrypoint]
+#[storage]
+pub struct MyContract {
+ // ...
+}
+```
+
+#### Insufficient funds
+
+```
+Error: not enough funds in account 0x... to pay for data fee
+balance 0.0001 ETH < 0.0005 ETH
+```
+
+**Solution**: Fund your account with more ETH. Get testnet ETH from faucets:
+
+- [Arbitrum Sepolia faucet](https://faucet.quicknode.com/arbitrum/sepolia)
+
+#### Invalid WASM
+
+```
+Error: contract activation failed: failed to parse contract
+Caused by: binary exports reserved symbol stylus_ink_left
+```
+
+**Solution**: Ensure you're using the latest `stylus-sdk` version and following SDK conventions.
+
+## Deployment Workflows
+
+### Development workflow
+
+For rapid iteration during development:
+
+```shell
+# 1. Check frequently during development
+cargo stylus check
+
+# 2. Deploy to testnet with no-verify for speed
+cargo stylus deploy \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \
+ --private-key-path=./key.txt \
+ --no-verify
+
+# 3. Test the deployed contract
+# (use your testing framework)
+
+# 4. Iterate and redeploy as needed
+```
+
+### Production workflow
+
+For production deployments:
+
+```shell
+# 1. Final check against mainnet
+cargo stylus check \
+ --endpoint="https://arb1.arbitrum.io/rpc"
+
+# 2. Estimate costs
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt \
+ --estimate-gas
+
+# 3. Deploy with reproducible build (for verification)
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt \
+ --cargo-stylus-version=0.5.0
+
+# 4. Verify the deployed contract
+cargo stylus verify \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --deployment-tx=0x...
+```
+
+### Multi-contract deployment
+
+Deploy multiple contracts from a workspace:
+
+```shell
+# Check all contracts in workspace
+cargo stylus check
+
+# Deploy specific contract
+cargo stylus deploy \
+ --contract=my-token \
+ --private-key-path=./key.txt
+
+# Deploy all contracts (must have no-arg constructors)
+cargo stylus deploy \
+ --private-key-path=./key.txt
+```
+
+## Checking Existing Deployments
+
+### Check if contract is activated
+
+```rust
+// From check.rs
+let codehash = processed.codehash();
+if Contract::exists(codehash, &provider).await? {
+ return Ok(ContractStatus::Active {
+ code: processed.code,
+ });
+}
+```
+
+Use `cargo stylus check` with `--contract-address` to verify an existing deployment:
+
+```shell
+cargo stylus check \
+ --contract-address=0x457b1ba688e9854bdbed2f473f7510c476a3da09
+```
+
+### Re-activation
+
+If a contract is already deployed but not activated, activate it:
+
+```shell
+cargo stylus activate \
+ --address=0x457b1ba688e9854bdbed2f473f7510c476a3da09 \
+ --private-key-path=./key.txt
+```
+
+## Best Practices
+
+### 1. Always check before deploying
+
+```shell
+# ✅ Good: Check first
+cargo stylus check
+cargo stylus deploy --private-key-path=./key.txt
+
+# ❌ Bad: Deploy without checking
+cargo stylus deploy --private-key-path=./key.txt
+```
+
+### 2. Use gas estimation
+
+```shell
+# ✅ Good: Estimate first
+cargo stylus deploy --private-key-path=./key.txt --estimate-gas
+# Review the output, then deploy for real
+cargo stylus deploy --private-key-path=./key.txt
+
+# ❌ Bad: Deploy without estimation
+cargo stylus deploy --private-key-path=./key.txt
+```
+
+### 3. Secure private key handling
+
+```shell
+# ✅ Good: Use key file
+echo $PRIVATE_KEY > /tmp/key.txt
+chmod 600 /tmp/key.txt
+cargo stylus deploy --private-key-path=/tmp/key.txt
+rm /tmp/key.txt
+
+# ⚠️ Risky: Expose key in command line
+cargo stylus deploy --private-key=$PRIVATE_KEY
+```
+
+### 4. Test on testnet first
+
+```shell
+# ✅ Good: Test on Sepolia first
+cargo stylus deploy \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \
+ --private-key-path=./key.txt
+
+# After testing succeeds, deploy to mainnet
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt
+
+# ❌ Bad: Deploy directly to mainnet
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt
+```
+
+### 5. Use reproducible builds for verification
+
+```shell
+# ✅ Good: Reproducible build for mainnet
+cargo stylus deploy \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --private-key-path=./key.txt \
+ --cargo-stylus-version=0.5.0
+
+# Then verify on Arbiscan
+cargo stylus verify \
+ --endpoint="https://arb1.arbitrum.io/rpc" \
+ --deployment-tx=0x...
+
+# ⚠️ OK for development: Skip Docker
+cargo stylus deploy \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \
+ --private-key-path=./key.txt \
+ --no-verify
+```
+
+### 6. Monitor contract size
+
+```shell
+# Check compressed size
+cargo stylus check
+
+# If size is close to 24KB limit:
+# - Use #[no_std]
+# - Remove unused dependencies
+# - Enable aggressive optimizations
+# - Strip debug symbols
+```
+
+### 7. Verify data fee is reasonable
+
+```shell
+# Check data fee before deploying
+cargo stylus deploy --private-key-path=./key.txt --estimate-gas
+
+# Output shows:
+# wasm data fee: 0.0001 ETH (originally 0.00008 ETH with 20% bump)
+
+# If fee seems high:
+# - Optimize WASM size
+# - Check network congestion
+# - Verify contract correctness
+```
+
+## Troubleshooting
+
+### Size limit errors
+
+**Error**: Compressed WASM exceeds 24KB
+
+**Solutions**:
+
+1. Use `#[no_std]` to eliminate standard library:
+
+ ```rust
+ #![no_std]
+ extern crate alloc;
+ ```
+
+2. Remove unused dependencies from `Cargo.toml`:
+
+ ```toml
+ [dependencies]
+ stylus-sdk = "0.5"
+ # Remove unnecessary crates
+ ```
+
+3. Enable size optimizations in `Cargo.toml`:
+
+ ```toml
+ [profile.release]
+ opt-level = "z"
+ lto = true
+ strip = true
+ ```
+
+4. Use `wasm-opt` for additional optimization:
+ ```shell
+ wasm-opt -Oz -o optimized.wasm input.wasm
+ cargo stylus deploy --wasm-file=optimized.wasm --private-key-path=./key.txt
+ ```
+
+### Activation failures
+
+**Error**: Transaction reverted during activation
+
+**Solutions**:
+
+1. Verify entrypoint exists:
+
+ ```rust
+ #[entrypoint]
+ #[storage]
+ pub struct MyContract { /* ... */ }
+ ```
+
+2. Check WASM validity:
+
+ ```shell
+ cargo stylus check --wasm-file=./target/wasm32-unknown-unknown/release/my_contract.wasm
+ ```
+
+3. Ensure sufficient funds:
+
+ ```shell
+ # Check balance
+ cast balance $YOUR_ADDRESS --rpc-url $RPC_URL
+
+ # Get testnet ETH if needed
+ # Visit faucet.quicknode.com/arbitrum/sepolia
+ ```
+
+### RPC errors
+
+**Error**: Connection timeout or RPC error
+
+**Solutions**:
+
+1. Verify endpoint URL:
+
+ ```shell
+ curl -X POST $RPC_URL \
+ -H "Content-Type: application/json" \
+ -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
+ ```
+
+2. Try alternative endpoints:
+
+ ```shell
+ # Arbitrum One
+ --endpoint="https://arb1.arbitrum.io/rpc"
+ --endpoint="https://arbitrum-one.publicnode.com"
+
+ # Arbitrum Sepolia
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc"
+ --endpoint="https://arbitrum-sepolia.blockpi.network/v1/rpc/public"
+ ```
+
+3. Check network status:
+ - [Arbitrum Status](https://arbiscan.io/)
+ - [Chainlist](https://chainlist.org/)
+
+### Build errors
+
+**Error**: Compilation fails
+
+**Solutions**:
+
+1. Update dependencies:
+
+ ```shell
+ cargo update
+ ```
+
+2. Clear build cache:
+
+ ```shell
+ cargo clean
+ ```
+
+3. Verify Rust toolchain:
+
+ ```shell
+ rustup update
+ rustup target add wasm32-unknown-unknown
+ ```
+
+4. Check SDK version compatibility:
+ ```toml
+ [dependencies]
+ stylus-sdk = "0.5" # Use latest stable version
+ ```
+
+## Non-Rust WASM Deployment
+
+Deploy WASM from any language (C, C++, etc.):
+
+```shell
+# Deploy raw WASM file
+cargo stylus deploy \
+ --wasm-file=./my_contract.wasm \
+ --private-key-path=./key.txt
+
+# Deploy WebAssembly Text (WAT) file
+cargo stylus deploy \
+ --wasm-file=./my_contract.wat \
+ --private-key-path=./key.txt
+```
+
+Example WAT file structure:
+
+```wasm
+(module
+ (memory 0 0)
+ (export "memory" (memory 0))
+ (func (export "user_entrypoint") (param $args_len i32) (result i32)
+ (i32.const 0)
+ ))
+```
+
+## Command Reference
+
+### cargo stylus check
+
+**Syntax**:
+
+```shell
+cargo stylus check [OPTIONS]
+```
+
+**Common options**:
+
+- `--endpoint=`: RPC endpoint (default: Arbitrum Sepolia)
+- `--wasm-file=`: Check specific WASM file
+- `--contract-address=`: Target contract address
+
+### cargo stylus deploy
+
+**Syntax**:
+
+```shell
+cargo stylus deploy [OPTIONS]
+```
+
+**Common options**:
+
+- `--endpoint=`: RPC endpoint
+- `--private-key-path=`: Private key file
+- `--estimate-gas`: Only estimate gas
+- `--no-activate`: Deploy without activation
+- `--no-verify`: Skip Docker reproducible build
+- `--constructor-args `: Constructor arguments
+- `--constructor-value=`: ETH sent to constructor
+- `--max-fee-per-gas-gwei=`: Custom gas price
+
+### cargo stylus activate
+
+**Syntax**:
+
+```shell
+cargo stylus activate --address= [OPTIONS]
+```
+
+**Options**:
+
+- `--address=`: Deployed contract address (required)
+- `--private-key-path=`: Private key file
+- `--estimate-gas`: Only estimate gas
+
+## Resources
+
+- [Stylus quickstart guide](https://docs.arbitrum.io/stylus/stylus-quickstart)
+- [Cargo Stylus repository](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus)
+- [Testnet information](https://docs.arbitrum.io/stylus/reference/testnet-information)
+- [Contract verification guide](https://docs.arbitrum.io/stylus/guides/verifying-contracts)
+- [Optimizing WASM size](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus/blob/main/OPTIMIZING_BINARIES.md)
+- [Valid WASM requirements](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus/blob/main/VALID_WASM.md)
diff --git a/docs/stylus/cli-tools/commands-reference.mdx b/docs/stylus/cli-tools/commands-reference.mdx
new file mode 100644
index 0000000000..7f4539c50c
--- /dev/null
+++ b/docs/stylus/cli-tools/commands-reference.mdx
@@ -0,0 +1,258 @@
+---
+title: 'cargo-stylus command reference'
+sidebar_label: 'Commands reference'
+description: 'Complete reference for all cargo-stylus CLI commands'
+user_story: 'As a Stylus developer, I want a complete reference of all CLI commands'
+content_type: reference
+author: offchainlabs
+sme: offchainlabs
+sidebar_position: 5
+---
+
+Complete reference for all `cargo-stylus` commands.
+
+## new
+
+Create a new Stylus project.
+
+**Usage:**
+
+```shell
+cargo stylus new
+```
+
+**Options:**
+
+- `--minimal` - Create minimal project structure
+
+**Example:**
+
+```shell
+cargo stylus new my-stylus-contract
+```
+
+## init
+
+Initialize a Stylus project in the current directory.
+
+**Usage:**
+
+```shell
+cargo stylus init
+```
+
+**Options:**
+
+- `--minimal` - Create minimal project structure
+
+## check
+
+Verify a contract compiles to valid WASM and passes onchain activation checks.
+
+**Usage:**
+
+```shell
+cargo stylus check
+```
+
+**Options:**
+
+- `--endpoint ` - RPC endpoint (default: http://localhost:8547)
+- `--wasm-file ` - WASM file to check (defaults to any found in current directory)
+- `--contract-address ` - Optional deployment address
+
+**Example:**
+
+```shell
+cargo stylus check --endpoint https://sepolia-rollup.arbitrum.io/rpc
+```
+
+## deploy
+
+Deploy a Stylus contract.
+
+**Usage:**
+
+```shell
+cargo stylus deploy \
+ --endpoint \
+ --private-key
+```
+
+**Options:**
+
+- `--endpoint ` - RPC endpoint (required)
+- `--private-key ` - Private key for deployment (required)
+- `--contract-address ` - Specific deployment address (defaults to random)
+- `--estimate-gas` - Estimate gas only, don't deploy
+- `--no-verify` - Skip reproducible container verification
+- `--wasm-file ` - WASM file to deploy
+- `--max-fee-per-gas-gwei ` - Optional max fee per gas
+
+**Example:**
+
+```shell
+cargo stylus deploy \
+ --endpoint https://sepolia-rollup.arbitrum.io/rpc \
+ --private-key $PRIVATE_KEY \
+ --estimate-gas
+```
+
+## activate
+
+Activate an already deployed contract.
+
+**Usage:**
+
+```shell
+cargo stylus activate \
+ --endpoint \
+ --private-key \
+ --address
+```
+
+**Options:**
+
+- `--endpoint ` - RPC endpoint (required)
+- `--private-key ` - Private key for activation (required)
+- `--address ` - Contract address to activate (required)
+- `--data-fee-bump-percent ` - Percent to bump estimated fee (default: 20%)
+- `--estimate-gas` - Only estimate gas without sending transaction
+
+## verify
+
+Verify the deployment of a Stylus contract.
+
+**Usage:**
+
+```shell
+cargo stylus verify \
+ --deployment-tx
+```
+
+**Options:**
+
+- `--endpoint ` - RPC endpoint
+- `--deployment-tx ` - Deployment transaction hash (required)
+- `--no-verify` - Skip reproducible container
+- `--cargo-stylus-version ` - Version for Docker image
+
+**Example:**
+
+```shell
+cargo stylus verify --deployment-tx 0xd4...85
+```
+
+## cache
+
+Manage contract caching using the Stylus CacheManager.
+
+**Usage:**
+
+```shell
+# Place a bid on a contract
+cargo stylus cache bid --address
+
+# Check contract cache status
+cargo stylus cache status --address
+
+# Get suggested minimum bid
+cargo stylus cache suggest-bid --address
+```
+
+**Options:**
+
+- `--address ` - Contract address (required)
+- `--endpoint ` - RPC endpoint
+
+## export-abi
+
+Export a Solidity ABI interface.
+
+**Usage:**
+
+```shell
+cargo stylus export-abi
+```
+
+**Options:**
+
+- `--output ` - Output file (defaults to stdout)
+- `--json` - Write JSON ABI using solc format
+
+**Example:**
+
+```shell
+cargo stylus export-abi > IMyContract.sol
+cargo stylus export-abi --json > abi.json
+```
+
+## cgen
+
+Generate C code bindings for a Stylus contract.
+
+**Usage:**
+
+```shell
+cargo stylus cgen \
+ --input \
+ --out-dir
+```
+
+**Options:**
+
+- `--input ` - Input file path (required)
+- `--out-dir ` - Output directory path (required)
+
+## replay
+
+Replay a transaction in GDB for debugging.
+
+**Usage:**
+
+```shell
+cargo stylus replay --tx
+```
+
+**Options:**
+
+- `--tx ` - Transaction hash to replay (required)
+- `--endpoint ` - RPC endpoint
+- `--project ` - Project path (default: current directory)
+- `--use-native-tracer` - Use native tracer instead of JavaScript
+- `--stable-rust` - Use stable Rust (note: nightly needed to expand macros)
+
+## trace
+
+Trace a transaction.
+
+**Usage:**
+
+```shell
+cargo stylus trace --tx
+```
+
+**Options:**
+
+- `--tx ` - Transaction hash (required)
+- `--endpoint ` - RPC endpoint
+- `--project ` - Project path
+- `--use-native-tracer` - Use native tracer
+
+---
+
+## Common options
+
+These options are available across multiple commands:
+
+| Option | Description |
+| ------------------------ | --------------------------------------------- |
+| `--endpoint ` | RPC endpoint URL |
+| `--private-key ` | Private key for transactions |
+| `--estimate-gas` | Estimate gas without sending transaction |
+| `--no-verify` | Skip reproducible container verification |
+| `--cargo-stylus-version` | Specify cargo-stylus version for Docker image |
+
+---
+
+For detailed usage examples, see the [CLI tools guides](/stylus/cli-tools/overview).
diff --git a/docs/stylus/how-tos/debugging-tx.mdx b/docs/stylus/cli-tools/debugging-tx.mdx
similarity index 96%
rename from docs/stylus/how-tos/debugging-tx.mdx
rename to docs/stylus/cli-tools/debugging-tx.mdx
index c47cb69116..aac7157564 100644
--- a/docs/stylus/how-tos/debugging-tx.mdx
+++ b/docs/stylus/cli-tools/debugging-tx.mdx
@@ -7,14 +7,14 @@ sme: mahsamoosavi
target_audience: 'Developers deploying smart contracts using Stylus'
content_type: how-to
sidebar_position: 2
-displayed_sidebar: buildAppsSidebar
+displayed_sidebar: buildStylusSidebar
---
Debugging smart contracts can be challenging, especially when dealing with complex transactions. The `cargo-stylus` crate simplifies the debugging process by allowing developers to replay Stylus transactions. This tool leverages GDB to provide an interactive debugging experience, enabling developers to set breakpoints, inspect state changes, and trace the execution flow step-by-step. This capability is crucial for identifying and resolving issues, ensuring that smart contracts function correctly and efficiently.
### Overview
-Cargo Stylus is a tool designed to simplify the development and debugging process for smart contracts written in Rust for the Stylus execution environment. One of its powerful features is the `cargo stylus` subcommand, which provides essential functionalities for developers:
+Cargo Stylus simplifies the development and debugging of Rust smart contracts for the Stylus execution environment. One of its powerful features is the `cargo stylus` subcommand, which provides essential functionalities for developers:
1. **Trace transactions**: Perform trace calls against Stylus transactions using Ethereum nodes' `debug_traceTransaction` RPC. This feature enables developers to analyze the execution flow and state changes of their transactions in a detailed manner.
2. **Debugging with GDB or LLDB**: Replay and debug the execution of a Stylus transaction using a debugger. This allows developers to set breakpoints, inspect variables, and step through the transaction execution line by line, providing an in-depth understanding of the transaction's behavior.
diff --git a/docs/stylus/cli-tools/overview.mdx b/docs/stylus/cli-tools/overview.mdx
new file mode 100644
index 0000000000..bcd8207d4b
--- /dev/null
+++ b/docs/stylus/cli-tools/overview.mdx
@@ -0,0 +1,131 @@
+---
+id: 'overview'
+title: 'Using Stylus CLI'
+description: 'Get started with Stylus CLI, a Rust toolkit for developing Stylus contracts'
+author: 'anegg0'
+sme: 'anegg0'
+sidebar_position: 2
+target_audience: Developers writing Stylus contracts in Rust using Stylus
+displayed_sidebar: buildStylusSidebar
+---
+
+This guide will get you started using [cargo stylus](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus), a CLI toolkit to help developers manage, compile, deploy, and optimize their Stylus contracts efficiently.
+
+This overview will help you discover and learn how to uses cargo stylus tools.
+
+### Installing cargo stylus
+
+Cargo stylus is a plugin to the standard cargo tool for developing Rust programs.
+
+#### Prerequisites
+
+
+Rust toolchain
+
+Follow the instructions on [Rust Lang's installation page](https://www.rust-lang.org/tools/install) to install a complete Rust toolchain (v1.81 or newer) on your system. After installation, ensure you can access the programs `rustup`, `rustc`, and `cargo` from your preferred terminal application.
+
+
+
+
+Docker
+
+We will use the testnet, and some `cargo stylus` commands will require Docker to operate.
+
+You can download Docker from [Docker's website](https://www.docker.com/products/docker-desktop).
+
+
+
+
+Foundry's Cast
+
+[Foundry's Cast](https://book.getfoundry.sh/cast/) is a command-line tool for interacting with your EVM contracts.
+
+
+
+
+Nitro devnode
+
+Stylus is available on Arbitrum Sepolia, but we'll use Nitro devnode, which has a pre-funded wallet, saving us the effort of wallet provisioning or running out of tokens to send transactions.
+
+```shell title="Install your devnode"
+git clone https://github.com/OffchainLabs/nitro-devnode.git
+cd nitro-devnode
+```
+
+```shell title="Launch your devnode"
+./run-dev-node.sh
+```
+
+
+
+#### Installation
+
+In your terminal, run:
+
+```shell
+cargo install --force cargo-stylus
+```
+
+Add WASM ([WebAssembly](https://webassembly.org/)) as a build target for the specific Rust toolchain you are using. The below example sets your default Rust toolchain to 1.80 as well as adding the WASM build target:
+
+```shell
+rustup default 1.80
+rustup target add wasm32-unknown-unknown --toolchain 1.80
+```
+
+You can verify the cargo stylus installation by running `cargo stylus -V` in your terminal, returning something like:`stylus 0.5.6`
+
+### Using cargo stylus
+
+#### Cargo Stylus Commands Reference
+
+| Command | Description | Arguments | Options | Example Usage |
+| ------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
+| `new` | Create a new Stylus project | • `name`: Project name (required) | • `--minimal`: Create a minimal contract | `cargo stylus new ` |
+| `init` | Initialize a Stylus project in current directory | | • `--minimal`: Create a minimal contract | `cargo stylus init --minimal` |
+| `export-abi` | Export a Solidity ABI | | • `--output`: Output file (defaults to stdout)
• `--json`: Write JSON ABI using `solc` | `cargo stylus export-abi --json` |
+| `activate` | Activate an already deployed contract | • `--address`: Contract address to activate | • `--data-fee-bump-percent`: Percent to bump estimated fee (default 20%)
• `--estimate-gas`: Only estimate gas without sending transaction | `cargo stylus activate --address ` |
+| `cache` | Cache contract using Stylus CacheManager | • `bid`: Place bid on contract
• `status`: Check contract status
• `suggest-bid`: Get suggested minimum bid | | `cargo stylus cache bid --address ` |
+| `check` | Check a contract | | • `--wasm-file`: WASM file to check
• `--contract-address`: Deployment address | |
+| `deploy` | Deploy a contract | • `--contract-address `: Where to deploy and activate the contract (defaults to a random address) | • `--estimate-gas`: Only perform estimation
• `--no-verify`: Skip reproducible container
• `--cargo-stylus-version`: Version for Docker image
• `--source-files-for-project-hash `: Path to source files to include in the project hash
• `--max-fee-per-gas-gwei `: Optional max fee per gas in `gwei` units
• `--wasm-file `: The WASM file to check (defaults to any found in the current directory) | `cargo stylus deploy --endpoint='http://localhost:8547' --private-key="" --estimate-gas` |
+| `verify` | Verify contract deployment | • `--deployment-tx`: Hash of deployment transaction | • `--no-verify`: Skip reproducible container
• `--cargo-stylus-version`: Version for Docker image | |
+| `cgen` | Generate C code bindings | • `--input`: Input file path
• `--out_dir`: Output directory path | | |
+| `replay` | Replay transaction in GDB | • `-t, --tx `: Transaction to replay | • `-p, --project `: Project path (default: `.`)
• `-u, --use-native-tracer`: Use the native tracer instead of the JavaScript one (may not be available in the node)
• `-s, --stable-rust`: Use stable Rust (note that nightly is needed to expand macros) | `cargo stylus replay --tx ` |
+| `trace` | Trace a transaction | • `--tx`: Transaction hash | • `--endpoint`: RPC endpoint
• `--project`: Project path
• `--use-native-tracer`: Use native tracer | |
+
+##### Common options
+
+These options are available across multiple commands:
+
+| Option | Description |
+| ------------------------------- | ------------------------------------------------------ |
+| --endpoint | Arbitrum RPC endpoint (default: http://localhost:8547) |
+| --verbose | Print debug info |
+| --source-files-for-project-hash | Paths to source files for project hash |
+| --max-fee-per-gas-gwei | Optional max fee per gas in `gwei` |
+
+##### Authentication options
+
+Available for commands involving transactions:
+
+| Option | Description |
+| ------------------------ | ---------------------------------------------------- |
+| --private-key-path | Path to file containing hex-encoded private key |
+| --private-key | Private key as hex string (exposes to shell history) |
+| --keystore-path | Path to Ethereum wallet keystore file |
+| --keystore-password-path | Keystore password file path |
+
+#### How-tos
+
+| Topic | Description |
+| --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
+| [Learn how to optimize WASM binaries](/stylus/guides/optimizing-binaries) | The `cargo-stylus` tool allows you to optimize WebAssembly (WASM) binaries, ensuring that your contracts are as efficient as possible. |
+| [Debug Stylus transactions](/stylus/cli-tools/debugging-tx) | A guide to debugging transactions, helping you identify and fix issues. Gain insights into your Stylus contracts by debugging transactions. |
+| [Verify contracts](/stylus/cli-tools/verify-contracts) | Ensure that your Stylus contracts are correctly verified. Step-by-step instructions on how to verify your contracts using `cargo-stylus`. |
+| [Run a Stylus dev node](/run-arbitrum-node/run-local-full-chain-simulation) | Learn how to run a local Arbitrum dev node to test your Stylus contracts. |
+
+#### Additional resources
+
+#### [Troubleshooting](/stylus/troubleshooting-building-stylus): solve the most common issues.
+
+#### [cargo-stylus repository](https://github.com/OffchainLabs/cargo-stylus): consult cargo stylus' source code.
diff --git a/docs/stylus/cli-tools/verify-contracts.mdx b/docs/stylus/cli-tools/verify-contracts.mdx
new file mode 100644
index 0000000000..fafd7d2f85
--- /dev/null
+++ b/docs/stylus/cli-tools/verify-contracts.mdx
@@ -0,0 +1,270 @@
+---
+title: 'How to verify Stylus contracts'
+sidebar_label: 'Verify contracts'
+description: 'Learn how to verify Stylus contracts locally and on Arbiscan'
+user_story: 'As a Stylus developer, I want to verify my contracts to ensure reproducibility and transparency'
+content_type: how-to
+author: rauljordan, mahsamoosavi
+sme: rauljordan, mahsamoosavi
+target_audience: 'Developers deploying smart contracts using Stylus'
+sidebar_position: 2
+displayed_sidebar: buildStylusSidebar
+---
+
+import ImageZoom from '@site/src/components/ImageZoom';
+
+Stylus contracts can be verified in two ways: locally using `cargo stylus`, or on [Arbiscan](https://arbiscan.io/), Arbitrum's block explorer. This guide covers both methods.
+
+:::info
+Stylus contract verification on Arbiscan is only supported for contracts deployed using `cargo-stylus` 0.5.0 or higher.
+:::
+
+## Overview: Why verify contracts?
+
+Contract verification ensures that:
+
+- Your deployment is reproducible by anyone running the same environment
+- Users can independently verify that deployed bytecode matches published source code
+- The contract source code is publicly available on block explorers
+- Trust and transparency are established with your users
+
+## Local verification
+
+Local verification uses the `cargo stylus` tool to verify contracts against your local codebase.
+
+### Goals
+
+- Ensure Stylus contract deployments are reproducible by anyone on the same architecture
+- Sandbox the reproducible environment and standardize it
+- Guarantee that programs reproducibly deployed with cargo stylus version ≥ 0.4.2 are verifiable
+
+### Opting out
+
+By default, `cargo stylus deploy` is reproducible as it runs in a Docker container. You can opt out by specifying `--no-verify`:
+
+```shell
+cargo stylus deploy --no-verify
+```
+
+### How it works
+
+When you deploy a contract, the deployment transaction's `data` field contains your compressed contract code. The `cargo stylus` tool compresses and encodes your contract's WASM in a standardized, reproducible way.
+
+Anyone with the same codebase can rebuild your contract and verify the output matches the onchain data. This is similar to [Solidity contract verification](https://docs.sourcify.dev/docs/how-to-verify/#how-does-verification-work) but for WASM-based contracts.
+
+### Verification flow
+
+```
+cargo stylus deploy
+ ↓
+Compressed WASM bytecode sent to chain
+ ↓
+cargo stylus verify
+ ↓
+Rebuilds WASM and compares with onchain code
+ ↓
+Reports: ✓ Verified or ✗ Mismatch
+```
+
+### Prerequisites for local verification
+
+1. Docker installed and running
+2. The exact same codebase used for deployment
+3. `cargo-stylus` CLI installed (version ≥ 0.4.2)
+
+### Running local verification
+
+Use the `cargo stylus verify` command with your deployment transaction hash:
+
+```shell
+cargo stylus verify --deployment-tx 0xd4...85
+```
+
+**Example output (successful verification):**
+
+```
+Reading deployment tx from chain...
+Wasm data hash: 0xf80a...
+Program succeeded Stylus onchain activation checks.
+Connecting to Docker...
+Reproducing wasm binary from local code... (This may take a while)
+Finished release build in X.XXs
+
+INFO: contract code matches the onchain code ✓
+```
+
+**Example output (mismatch):**
+
+```
+Reading deployment tx from chain...
+ERROR: contract code does not match the onchain code ✗
+```
+
+### Troubleshooting local verification
+
+**Error: Docker not running**
+
+```
+Error: Cannot connect to Docker daemon
+```
+
+Solution: Start Docker Desktop or Docker daemon
+
+**Error: Mismatch**
+
+If verification fails, common causes:
+
+- Different Rust toolchain version
+- Different dependency versions
+- Modified source code
+- Different build flags
+
+Ensure you're using the exact same codebase and Rust version as the deployment.
+
+---
+
+## Arbiscan verification
+
+Arbiscan provides a web-based interface for contract verification, making your source code publicly visible on the block explorer.
+
+### View verified contracts
+
+You can browse verified Stylus contracts on:
+
+- [Verified Stylus Contracts on Arbitrum One](https://arbiscan.io/contractsVerified?filter=stylus)
+- [Verified Stylus Contracts on Arbitrum Sepolia](https://sepolia.arbiscan.io/contractsVerified?filter=stylus)
+
+Example: [English Auction Stylus contract](https://sepolia.arbiscan.io/address/0xe85a046fd3ea22ceeb3caef3a0d38123eecbe3ca) (verified on Arbitrum Sepolia)
+
+### Step 1: Navigate to the verification page
+
+You have two options:
+
+**Option A: Direct link**
+Visit [Arbiscan Verify Contract](https://arbiscan.io/verifyContract) directly if you have the contract address ready.
+
+**Option B: From the contract page**
+
+1. Go to your contract's page on Arbiscan
+2. Click the "Contract" tab
+3. Click the "Verify and Publish" link
+
+
+
+### Step 2: Enter contract details
+
+On the verification page, provide:
+
+1. **Contract address**: The deployed contract address (e.g., `0xe85a...3ca`)
+2. **Compiler type**: Select **"Stylus (Rust)"** from the dropdown
+
+
+
+3. **cargo-stylus version**: Select the version you used for deployment (must be ≥ 0.5.0)
+
+
+
+Then click **Continue**.
+
+### Step 3: Submit source code
+
+You can submit your source code in two ways:
+
+#### Option A: Single Rust file
+
+If your contract is a single `.rs` file:
+
+1. Click **"Rust File Upload"**
+2. Upload your `.rs` file
+
+#### Option B: Standard JSON input
+
+For multi-file projects:
+
+1. Click **"Standard JSON Input"**
+2. Generate the JSON using:
+
+```shell
+cargo stylus verify --json
+```
+
+This generates a `project.json` file containing all sources and metadata.
+
+3. Upload the `project.json` file
+
+
+
+Click **Verify and Publish** to complete verification.
+
+### Step 4: Verification result
+
+If successful, you'll see:
+
+- ✅ Verification successful message
+- Your source code is now publicly visible on Arbiscan
+- The "Contract" tab shows your Rust source code
+
+
+
+### Handling previously verified contracts
+
+If your contract was already verified:
+
+1. Arbiscan will detect this automatically
+2. It will display: "Contract Source Code Already Verified"
+3. You can view the existing verification in the Contract tab
+
+
+
+### Troubleshooting Arbiscan verification
+
+**Error: cargo-stylus version too old**
+
+Arbiscan requires cargo-stylus ≥ 0.5.0. Update your toolchain:
+
+```shell
+cargo install cargo-stylus --force
+```
+
+**Error: Verification failed**
+
+Common causes:
+
+- Wrong cargo-stylus version selected
+- Source code doesn't match deployed bytecode
+- Missing dependencies in JSON file
+
+Solution: Ensure you're using the exact source code from deployment and the correct cargo-stylus version.
+
+**Error: Contract not found**
+
+Ensure:
+
+- The contract address is correct
+- The contract is deployed on the selected network (Arbitrum One vs Sepolia)
+- The deployment transaction is confirmed
+
+---
+
+## Which verification method should I use?
+
+| Method | When to use | Benefits |
+| ------------------------- | --------------------------------------- | --------------------------------------------------------------------- |
+| **Local verification** | Quick verification during development | Fast, no external dependencies, proves reproducibility |
+| **Arbiscan verification** | Publishing verified contracts for users | Public source code visibility, block explorer integration, user trust |
+
+**Best practice**: Use both methods:
+
+1. Verify locally after deployment to ensure reproducibility
+2. Verify on Arbiscan to publish source code for your users
+
+---
+
+## Next steps
+
+- [Learn about deployment](./check-and-deploy.mdx)
+- [Explore CLI tools](./overview.mdx)
+- [View verified examples](https://github.com/OffchainLabs/stylus-by-example)
diff --git a/docs/stylus/concepts/activation.mdx b/docs/stylus/concepts/activation.mdx
new file mode 100644
index 0000000000..812565fdf0
--- /dev/null
+++ b/docs/stylus/concepts/activation.mdx
@@ -0,0 +1,778 @@
+---
+title: 'Activation'
+description: 'Understanding Stylus contract deployment and activation'
+user_story: 'As a developer, I want to understand the two-step deployment and activation process'
+content_type: concept
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers deploying Stylus contracts to Arbitrum chains.
+displayed_sidebar: buildStylusSidebar
+---
+
+Stylus contracts undergo a two-step process to become executable on Arbitrum chains: **deployment** and **activation**. This guide explains both steps, the distinction between them, and how to manage the activation process.
+
+## Overview
+
+Unlike traditional EVM contracts that become immediately executable after deployment, Stylus contracts require an additional activation step:
+
+1. **Deployment**: Stores the compressed WASM bytecode onchain at a contract address
+2. **Activation**: Converts the bytecode into an executable Stylus program by registering it with the ArbWasm precompile
+
+**Why two steps?**
+
+- **Gas optimization**: Activation involves one-time processing and caching that would be expensive to repeat on every call
+- **Code reuse**: Multiple contracts can share the same activated codehash, reducing activation costs
+- **Version management**: Allows the chain to track which Stylus protocol version a contract targets
+
+## Deployment vs Activation
+
+| Aspect | Deployment | Activation |
+| --------------------- | ----------------------------- | ------------------------------ |
+| **Purpose** | Store compressed WASM onchain | Register program with ArbWasm |
+| **Transaction count** | 1 transaction | 1 transaction (separate) |
+| **Cost type** | Standard EVM deployment gas | Data fee (WASM-specific cost) |
+| **When required** | Always - stores the code | Always - makes code executable |
+| **Reversible** | No | No (but can expire) |
+| **Who can call** | Anyone with funds | Anyone (after deployment) |
+| **Can be skipped** | No | No (unless already activated) |
+
+### Contract States
+
+A Stylus contract can be in one of these states:
+
+```rust
+pub enum ContractStatus {
+ /// Contract already exists onchain and is activated
+ Active { code: Vec },
+
+ /// Contract is deployed but not yet activated
+ /// Ready to activate with the given data fee
+ Ready { code: Vec, fee: U256 },
+}
+```
+
+## The Activation Process
+
+### Step 1: Build and Process WASM
+
+Before deployment, your Rust contract is compiled and processed:
+
+```bash
+cargo stylus check
+```
+
+This performs:
+
+1. **Compile Rust to WASM**: Using `wasm32-unknown-unknown` target
+2. **Process WASM binary**:
+ - Remove dangling references
+ - Add project hash metadata
+ - Strip unnecessary custom sections
+3. **Brotli compression**: Maximum compression (level 11)
+4. **Add EOF prefix**: `EFF00000` (identifies Stylus programs)
+5. **Size validation**: Compressed code must be ≤ 24KB
+
+**WASM Processing Pipeline**:
+
+
+
+_Figure 1: WASM binary processing pipeline showing transformation from raw binary to deployment-ready compressed code._
+
+### Step 2: Deploy the Contract
+
+Deployment creates a transaction that stores your processed WASM onchain:
+
+```bash
+cargo stylus deploy \
+ --private-key-path=key.txt \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc"
+```
+
+**What happens during deployment:**
+
+1. **Generate deployment bytecode**: Create EVM initcode with embedded compressed WASM
+2. **Estimate gas**: Calculate deployment transaction gas cost
+3. **Send deployment transaction**: To Stylus deployer contract
+4. **Extract contract address**: From transaction receipt
+
+**Deployment Bytecode Structure**:
+
+```
+EVM Initcode Prelude (43 bytes):
+┌─────────────────────────────────────┐
+│ 0x7f PUSH32 │ Push code length
+│ 0x80 DUP1 │ Duplicate length
+│ 0x60 PUSH1 │ Push prelude length
+│ 0x60 PUSH1 0x00 │ Push 0
+│ 0x39 CODECOPY │ Copy code to memory
+│ 0x60 PUSH1 0x00 │ Push 0
+│ 0xf3 RETURN │ Return code
+│ 0x00 │ Stylus version
+└─────────────────────────────────────┘
+ ↓
+
+```
+
+### Step 3: Calculate Activation Fee
+
+Before activating, the data fee must be calculated:
+
+```rust
+// Simulated via state overrides (no transaction sent)
+let data_fee = calculate_activation_fee(contract_address);
+
+// Apply bump percentage for safety (default: 20%)
+let final_fee = data_fee * (1 + bump_percent / 100);
+```
+
+**Data fee calculation**:
+
+- Uses state override simulation to estimate fee
+- No actual transaction sent during estimation
+- Configurable bump percentage protects against variance (default: 20%)
+- Fee is paid in ETH when activating
+
+### Step 4: Activate the Contract
+
+Activation registers your contract with the ArbWasm precompile:
+
+```bash
+# Automatic activation (default)
+cargo stylus deploy --private-key-path=key.txt
+
+# Or manual activation
+cargo stylus activate \
+ --address=0x1234... \
+ --private-key-path=key.txt
+```
+
+**What happens during activation:**
+
+1. **Call ArbWasm precompile**: At address `0x0000000000000000000000000000000000000071`
+2. **Send activation transaction**:
+ ```solidity
+ ArbWasm.activateProgram{value: dataFee}(contractAddress)
+ ```
+3. **ArbWasm processes the code**:
+ - Validates WASM format
+ - Checks against protocol version
+ - Stores activation metadata
+ - Emits `ProgramActivated` event
+4. **Returns activation info**:
+ ```solidity
+ returns (uint16 version, uint256 actualDataFee)
+ ```
+
+## Using cargo-stylus
+
+The `cargo-stylus` CLI tool simplifies the deployment and activation workflow.
+
+### Basic Deployment (Automatic Activation)
+
+By default, `cargo stylus deploy` handles both steps:
+
+```bash
+cargo stylus deploy \
+ --private-key-path=wallet.txt \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc"
+```
+
+**Output**:
+
+```
+Building contract...
+Compressing WASM...
+Deploying contract to 0x1234567890abcdef...
+Deployment transaction: 0xabcd...
+Contract deployed at: 0x1234567890abcdef
+Activating contract...
+Activation transaction: 0xef12...
+Contract activated successfully!
+```
+
+### Deploy Without Activation
+
+To deploy but skip activation:
+
+```bash
+cargo stylus deploy \
+ --private-key-path=wallet.txt \
+ --no-activate
+```
+
+This is useful when:
+
+- You want to inspect the contract before activating
+- Someone else will handle activation
+- You're testing deployment workflows
+
+### Manual Activation
+
+Activate a previously deployed contract:
+
+```bash
+cargo stylus activate \
+ --address=0x1234567890abcdef \
+ --private-key-path=wallet.txt \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc"
+```
+
+### Check Contract Status
+
+Before deploying, check if a contract with the same code is already activated:
+
+```bash
+cargo stylus check \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc"
+```
+
+**Possible outcomes**:
+
+1. **Code already activated**: You can reuse the existing deployment
+2. **Ready to activate**: Shows estimated data fee
+3. **Validation errors**: Displays issues that must be fixed
+
+## Deployment with Constructors
+
+If your contract has a constructor, provide arguments during deployment:
+
+```rust
+#[public]
+impl MyContract {
+ #[constructor]
+ pub fn constructor(&mut self, initial_value: U256, owner: Address) {
+ self.value.set(initial_value);
+ self.owner.set(owner);
+ }
+}
+```
+
+**Deploy with constructor arguments**:
+
+```bash
+cargo stylus deploy \
+ --private-key-path=wallet.txt \
+ --constructor-args 42 0x1234567890abcdef1234567890abcdef12345678
+```
+
+**With payable constructor**:
+
+```rust
+#[constructor]
+#[payable]
+pub fn constructor(&mut self) {
+ let value = self.vm().msg_value();
+ self.initial_balance.set(value);
+}
+```
+
+```bash
+cargo stylus deploy \
+ --private-key-path=wallet.txt \
+ --constructor-value=1000000000000000000 # 1 ETH in wei
+```
+
+## The ArbWasm Precompile
+
+Activation is handled by the ArbWasm precompile at address `0x0000000000000000000000000000000000000071`.
+
+### Key Functions
+
+#### activateProgram
+
+Activates a deployed Stylus contract:
+
+```solidity
+function activateProgram(
+ address program
+) external payable returns (uint16 version, uint256 dataFee);
+```
+
+**Parameters**:
+
+- `program`: Contract address containing WASM bytecode
+
+**Payment**:
+
+- Must send `value` equal to the calculated data fee (in wei)
+
+**Returns**:
+
+- `version`: Stylus protocol version the program was activated against
+- `dataFee`: Actual fee paid for activation
+
+**Example (via cast)**:
+
+```bash
+cast send 0x0000000000000000000000000000000000000071 \
+ "activateProgram(address)" \
+ 0x1234567890abcdef \
+ --value 100000000000000000 \
+ --private-key=$PRIVATE_KEY
+```
+
+#### codehashVersion
+
+Check if a codehash is activated and get its version:
+
+```solidity
+function codehashVersion(bytes32 codehash) external view returns (uint16 version);
+```
+
+**Reverts if**:
+
+- Code is not activated
+- Program needs upgrade
+- Program has expired
+
+#### programTimeLeft
+
+Get remaining time before a program expires:
+
+```solidity
+function programTimeLeft(address program) external view returns (uint64 timeLeft);
+```
+
+Returns seconds until expiration (default: ~1 year from activation).
+
+#### codehashKeepalive
+
+Extend a program's expiration time:
+
+```solidity
+function codehashKeepalive(bytes32 codehash) external payable returns (uint64 expirySeconds);
+```
+
+Resets the expiration timer, preventing program deactivation.
+
+### ArbWasm Errors
+
+Activation can fail with these errors:
+
+```solidity
+error ProgramNotWasm();
+// The deployed bytecode is not valid WASM
+
+error ProgramNotActivated();
+// Contract exists but hasn't been activated
+
+error ProgramNeedsUpgrade(uint16 version, uint16 stylusVersion);
+// Program version incompatible with current Stylus version
+
+error ProgramExpired(uint64 ageInSeconds);
+// Program has expired and must be reactivated
+
+error ProgramInsufficientValue(uint256 have, uint256 want);
+// Sent data fee is less than required
+```
+
+## Gas and Fee Optimization
+
+### Estimating Costs
+
+Get cost estimates before deploying:
+
+```bash
+# Estimate deployment gas
+cargo stylus deploy --estimate-gas
+
+# Check activation fee
+cargo stylus check # Shows estimated data fee
+```
+
+### Fee Bump Configuration
+
+Protect against fee variance with configurable bump percentage:
+
+```bash
+# Default: 20% bump
+cargo stylus deploy --private-key-path=wallet.txt
+
+# Custom bump percentage
+# (Note: Use programmatically via stylus-tools library)
+```
+
+**In code** (using stylus-tools):
+
+```rust
+use stylus_tools::core::activation::ActivationConfig;
+
+let config = ActivationConfig {
+ data_fee_bump_percent: 25, // 25% safety margin
+};
+```
+
+### Code Reuse Optimization
+
+If your contract's codehash matches an already-activated contract:
+
+```bash
+cargo stylus check
+```
+
+**Output if already activated**:
+
+```
+Checking contract...
+✓ Contract with this codehash is already activated!
+Version: 1
+No activation needed - you can deploy without activating.
+```
+
+You can deploy the contract normally, and it will automatically use the existing activation.
+
+### Contract Caching
+
+After activation, contracts can be cached for cheaper calls:
+
+```solidity
+// ArbWasmCache precompile (0x0000000000000000000000000000000000000072)
+function cacheProgram(address program) external payable returns (uint256);
+```
+
+**Benefits**:
+
+- Reduces gas costs for subsequent contract calls
+- One-time caching fee
+- Shared across all contracts with same codehash
+
+## Advanced Activation Patterns
+
+### Multi-Contract Deployment
+
+When deploying multiple instances of the same contract:
+
+```bash
+# First deployment: full deploy + activate
+cargo stylus deploy --private-key-path=wallet.txt
+# Contract 1: 0xaaaa... (activated)
+
+# Subsequent deployments: deploy only (reuses activation)
+cargo stylus deploy --private-key-path=wallet.txt --no-activate
+# Contract 2: 0xbbbb... (uses existing activation)
+
+cargo stylus deploy --private-key-path=wallet.txt --no-activate
+# Contract 3: 0xcccc... (uses existing activation)
+```
+
+All three contracts share the same codehash and activation, saving on data fees.
+
+### Programmatic Deployment
+
+Using the stylus-tools library directly:
+
+```rust
+use stylus_tools::core::{
+ deployment::{deploy, DeploymentConfig},
+ activation::{activate_contract, ActivationConfig, data_fee},
+ check::{check_contract, ContractStatus},
+};
+use alloy::providers::{Provider, WalletProvider};
+
+async fn deploy_and_activate(
+ provider: &impl Provider + WalletProvider,
+) -> Result> {
+ let contract = /* build contract */;
+
+ // Step 1: Check if already activated
+ let config = CheckConfig::default();
+ match check_contract(&contract, None, &config, provider).await? {
+ ContractStatus::Active { .. } => {
+ println!("Already activated!");
+ // Deploy without activation
+ }
+ ContractStatus::Ready { code, fee } => {
+ println!("Ready to activate. Data fee: {}", fee);
+ // Continue with deployment + activation
+ }
+ }
+
+ // Step 2: Deploy
+ let deploy_config = DeploymentConfig {
+ no_activate: false,
+ ..Default::default()
+ };
+
+ deploy(&contract, &deploy_config, provider).await?;
+
+ // Contract address returned from deployment
+ Ok(contract_address)
+}
+```
+
+### Custom Deployer Contracts
+
+Use a custom deployer contract instead of the default:
+
+```bash
+cargo stylus deploy \
+ --private-key-path=wallet.txt \
+ --deployer-address=0x... \
+ --deployer-salt=0x0000000000000000000000000000000000000000000000000000000000000001
+```
+
+This is useful for:
+
+- CREATE2 deterministic addresses
+- Custom deployment logic
+- Factory patterns
+
+## Contract Lifecycle
+
+### Activation Lifecycle
+
+```
+Deployed → Activated → [Active] → [Keepalive] → [Expired]
+ ↑ ↓
+ └──────────┘
+ (Periodic keepalive)
+```
+
+### Expiration and Keepalive
+
+Programs automatically expire after ~1 year (configurable by chain):
+
+```solidity
+// Check time remaining
+uint64 timeLeft = ArbWasm.programTimeLeft(contractAddress);
+
+// Extend expiration
+ArbWasm.codehashKeepalive{value: keepaliveFee}(codehash);
+```
+
+**Why expiration?**
+
+- Prevents abandoned contracts from consuming ArbOS resources
+- Encourages active maintenance
+- Allows protocol upgrades
+
+**Keepalive strategy**:
+
+- Monitor `programTimeLeft()` periodically
+- Call `codehashKeepalive()` before expiration
+- Automated scripts can handle this
+
+### Reactivation After Expiry
+
+If a program expires:
+
+```bash
+# Reactivate the existing deployment
+cargo stylus activate --address=0x...
+```
+
+The contract code remains onchain; only the activation state was cleared.
+
+## Troubleshooting
+
+### Common Activation Errors
+
+#### "Program not activated"
+
+**Cause**: Trying to call a deployed but not activated contract
+
+**Solution**:
+
+```bash
+cargo stylus activate --address=0x...
+```
+
+#### "Insufficient value"
+
+**Cause**: Data fee sent is less than required
+
+**Solution**:
+
+- Check current data fee: `cargo stylus check`
+- Increase fee bump percentage
+- Ensure sufficient ETH balance
+
+#### "Program not WASM"
+
+**Cause**: Deployed bytecode is not valid Stylus WASM
+
+**Solution**:
+
+- Verify you deployed the correct contract
+- Rebuild and redeploy: `cargo stylus deploy`
+
+#### "Program needs upgrade"
+
+**Cause**: Contract was activated against an old Stylus version
+
+**Solution**:
+
+- Recompile with latest SDK
+- Redeploy and reactivate
+
+#### "Program expired"
+
+**Cause**: Contract hasn't been kept alive and expired
+
+**Solution**:
+
+```bash
+# Reactivate the contract
+cargo stylus activate --address=0x...
+```
+
+### Debugging Activation
+
+Enable verbose output:
+
+```bash
+# Check detailed status
+cargo stylus check --verbose
+
+# Deploy with verbose logging
+RUST_LOG=debug cargo stylus deploy --private-key-path=wallet.txt
+```
+
+### Verifying Activation Status
+
+Check if a contract is activated:
+
+```bash
+# Via cargo-stylus
+cargo stylus check --address=0x...
+
+# Via cast (calling ArbWasm)
+cast call 0x0000000000000000000000000000000000000071 \
+ "codehashVersion(bytes32)" \
+ $(cast keccak $(cast code 0x...))
+```
+
+## Best Practices
+
+### 1. Always Check Before Deploying
+
+```bash
+cargo stylus check
+```
+
+This prevents deploying duplicate code and wasting gas.
+
+### 2. Use Automatic Activation
+
+Unless you have specific reasons to split deployment and activation, use the default behavior:
+
+```bash
+cargo stylus deploy # Deploys AND activates
+```
+
+### 3. Test on Testnet First
+
+Deploy to Arbitrum Sepolia before mainnet:
+
+```bash
+cargo stylus deploy \
+ --endpoint="https://sepolia-rollup.arbitrum.io/rpc" \
+ --private-key-path=testnet-key.txt
+```
+
+### 4. Monitor Contract Expiration
+
+Set up monitoring for production contracts:
+
+```solidity
+uint64 timeLeft = ArbWasm.programTimeLeft(contractAddress);
+if (timeLeft < 30 days) {
+ // Send alert or trigger keepalive
+}
+```
+
+### 5. Document Activation Details
+
+Track activation information:
+
+- Contract address
+- Activation transaction hash
+- Stylus version
+- Data fee paid
+- Activation timestamp
+
+### 6. Keep SDK Updated
+
+Use the latest Stylus SDK version:
+
+```toml
+[dependencies]
+stylus-sdk = "0.10.0-beta.1"
+```
+
+Older versions may become incompatible with chain upgrades.
+
+### 7. Handle Constructor Arguments Carefully
+
+Type-check constructor arguments:
+
+```bash
+# Incorrect (will fail)
+cargo stylus deploy --constructor-args "hello" 123
+
+# Correct (matches constructor signature)
+cargo stylus deploy --constructor-args 0x1234... 42
+```
+
+## Complete Example
+
+Here's a full deployment workflow:
+
+```bash
+# 1. Create new Stylus project
+cargo stylus new my-token
+cd my-token
+
+# 2. Build and verify locally
+cargo build --release --target wasm32-unknown-unknown
+cargo stylus check
+
+# 3. Test on Arbitrum Sepolia
+export SEPOLIA_ENDPOINT="https://sepolia-rollup.arbitrum.io/rpc"
+export PRIVATE_KEY_PATH="./sepolia-key.txt"
+
+cargo stylus deploy \
+ --endpoint=$SEPOLIA_ENDPOINT \
+ --private-key-path=$PRIVATE_KEY_PATH \
+ --constructor-args "MyToken" "MTK" 18
+
+# 4. Verify deployment
+cargo stylus check \
+ --endpoint=$SEPOLIA_ENDPOINT \
+ --address=0x... # Address from step 3
+
+# 5. Deploy to mainnet
+export MAINNET_ENDPOINT="https://arb1.arbitrum.io/rpc"
+export MAINNET_KEY_PATH="./mainnet-key.txt"
+
+cargo stylus deploy \
+ --endpoint=$MAINNET_ENDPOINT \
+ --private-key-path=$MAINNET_KEY_PATH \
+ --constructor-args "MyToken" "MTK" 18
+
+# 6. Cache the contract (optional, for gas optimization)
+cast send 0x0000000000000000000000000000000000000072 \
+ "cacheProgram(address)" \
+ 0x... # Your contract address \
+ --value 10000000000000000 \
+ --rpc-url=$MAINNET_ENDPOINT \
+ --private-key=$MAINNET_PRIVATE_KEY
+```
+
+## Summary
+
+- **Two-step process**: Deployment stores code, activation makes it executable
+- **cargo-stylus handles both**: Use `deploy` for automatic activation
+- **Data fee required**: Activation costs ETH (separate from deployment gas)
+- **Code reuse**: Identical contracts share activation, saving costs
+- **Expiration**: Programs expire after ~1 year without keepalive
+- **ArbWasm precompile**: All activation goes through address `0x71`
+- **Check first**: Use `cargo stylus check` to avoid duplicate activations
+
+## See Also
+
+- [Contracts](../fundamentals/contracts.mdx) - Writing Stylus contracts
+- [Global Variables and Functions](../fundamentals/global-variables-and-functions.mdx) - VM interface methods
+
+
diff --git a/docs/stylus/concepts/gas-metering.mdx b/docs/stylus/concepts/gas-metering.mdx
index 9354556719..99574f34a5 100644
--- a/docs/stylus/concepts/gas-metering.mdx
+++ b/docs/stylus/concepts/gas-metering.mdx
@@ -5,7 +5,7 @@ author: rachel-bousfield
sme: rachel-bousfield
target_audience: 'Developers deploying smart contracts using Stylus.'
sidebar_position: 3
-displayed_sidebar: buildAppsSidebar
+displayed_sidebar: buildStylusSidebar
---
**Gas and ink** are the pricing primitives that are used to determine the cost of handling specific opcodes and host I/Os on Stylus. For an overview of specific opcode and host I/O costs, see [Gas and ink costs](/stylus/reference/opcode-hostio-pricing).
@@ -76,4 +76,4 @@ However, developers optimizing contracts may choose to measure performance in in
### See also
- [Gas and ink costs](/stylus/reference/opcode-hostio-pricing): Detailed costs per opcode and host I/O
-- [Caching strategy](/stylus/how-tos/caching-contracts): Description of the Stylus caching strategy and the `CacheManager` contract
+- [Caching strategy](/stylus/guides/caching-contracts): Description of the Stylus caching strategy and the `CacheManager` contract
diff --git a/docs/stylus/concepts/public-preview-expectations.mdx b/docs/stylus/concepts/public-preview-expectations.mdx
new file mode 100644
index 0000000000..3ff00bc060
--- /dev/null
+++ b/docs/stylus/concepts/public-preview-expectations.mdx
@@ -0,0 +1,42 @@
+---
+title: 'Public preview: what to expect'
+description: 'Stylus is currently tagged as a `release-candidate` supported by *public preview* documentation. This concept document explains what this means, and what to expect.'
+author: symbolpunk
+sidebar_position: 10
+---
+
+Stylus is currently tagged as a `release-candidate` supported by _public preview_ documentation. This concept document explains what "public preview" means, what to expect from public preview capabilities, and how to engage with our team as you tinker.
+
+### How products are developed at Offchain Labs
+
+Offchain Labs builds products in a way that aligns loosely with the spirit of "building in public". We like to release things **early and often** so that we can capture feedback and iterate in service of your needs, as empirically as possible.
+
+To do this, some of our product offerings are documented with **public preview** disclaimers that look like this:
+
+This banner's purpose is to set expectations while inviting readers like you to express your needs so that we can incorporate them into the way that we iterate on product.
+
+### What to expect when using public preview offerings
+
+As you tinker and provide feedback, we'll be listening. Sometimes, we'll learn something non-obvious that will result in a significant change. More commonly, you'll experience incremental improvements to the developer experience as the offering grows out of its **public preview** status, towards **stable** status.
+
+Public preview offerings are evolving rapidly, so don't expect the degree of release notes discipline that you'd expect from a stable offering. Keep your eyes open for notifications regarding patch, minor, and major changes, along with corresponding relnotes that highlight breaking changes and new capabilities.
+
+### How to provide feedback
+
+Our product team primarily uses three feedback channels while iterating on public preview capabilities:
+
+1. **Docs**: Click on the **Request an update** button located in the top-right corner of any document to provide feedback on the docs and/or developer experience. This will lead you to a prefilled Github issue that members of our product team periodically review.
+2. **Discord**: [Join the Arbitrum Discord](https://discord.gg/arbitrum) to engage with members of the Arbitrum community and product team.
+3. **Google form**: Complete [this form](http://bit.ly/3yy6EUK) to ask for support.
+
+### What to expect when providing feedback
+
+Our ability to respond to feedback is determined by our ever-evolving capacity and priorities. We can't guarantee responses to all feedback submissions, but our small-but-mighty team is listening, and we'll try our best to acknowledge and respond to your feedback. No guarantees though!
+
+:::info
+[Our small-but-mighty team is hiring](https://jobs.lever.co/offchainlabs).
+:::
+
+### Thank you!
+
+Thanks for helping us build things that meet your needs! We're excited to engage with OGs and newcomers alike; please don't hesitate to reach out.
diff --git a/docs/stylus/concepts/vm-differences.mdx b/docs/stylus/concepts/vm-differences.mdx
new file mode 100644
index 0000000000..dd5008f529
--- /dev/null
+++ b/docs/stylus/concepts/vm-differences.mdx
@@ -0,0 +1,641 @@
+---
+title: 'VM and execution differences'
+sidebar_label: 'VM differences'
+description: 'Understand the differences between EVM and WASM execution models in Stylus'
+user_story: 'As a developer, I want to understand how WASM execution differs from EVM'
+content_type: concept
+author: chrisco
+sme: chrisco
+sidebar_position: 4
+target_audience: Developers who need to understand how Stylus works with EVM differences.
+displayed_sidebar: buildStylusSidebar
+---
+
+Arbitrum Nitro supports two execution environments: the traditional Ethereum Virtual Machine (EVM) for Solidity contracts and a WebAssembly (WASM) VM for Stylus contracts. While both environments are fully interoperable and share the same state, they differ significantly in their execution models, performance characteristics, and developer experience.
+
+## Execution model
+
+### EVM: Stack-based architecture
+
+The EVM uses a stack-based execution model:
+
+- **Operations**: Work with values on a stack (PUSH, POP, ADD, etc.)
+- **Opcodes**: 256 predefined opcodes with fixed gas costs
+- **Memory**: Linear, byte-addressable memory that grows dynamically
+- **Storage**: 256-bit word-based key-value store
+- **Call depth**: Limited to 1024 levels
+
+**Example EVM execution:**
+
+```
+PUSH1 0x02 // Push 2 onto stack
+PUSH1 0x03 // Push 3 onto stack
+ADD // Pop 2 values, push sum (5)
+```
+
+### WASM: Register-based architecture
+
+The Stylus WASM VM uses a register-based execution model:
+
+- **Operations**: Work with virtual registers and local variables
+- **Instructions**: Thousands of WASM instructions with fine-grained metering
+- **Memory**: Linear memory with explicit grow operations
+- **Storage**: Same 256-bit storage as EVM (shared state)
+- **Call depth**: Same 1024 limit for compatibility
+
+**Example WASM execution:**
+
+```wasm
+(local.get 0) ;; Read from local variable 0
+(local.get 1) ;; Read from local variable 1
+(i32.add) ;; Add and store result
+(local.set 2) ;; Store in local variable 2
+```
+
+## Memory model
+
+### EVM memory
+
+- **Dynamic expansion**: Memory grows in 32-byte chunks
+- **Gas cost**: Quadratic growth (memory expansion gets expensive)
+- **Access pattern**: Byte-level addressing
+- **Limit**: Practical limit around 15 MB due to gas costs
+
+### WASM memory
+
+- **Page-based**: Memory grows in 64 KB pages (WASM standard)
+- **Gas cost**: Linear cost per page through `pay_for_memory_grow`
+- **Access pattern**: Direct memory load/store instructions
+- **Limit**: Can grow much larger efficiently
+
+**Memory growth in Stylus:**
+
+```rust
+// The entrypoint macro automatically handles pay_for_memory_grow
+#[entrypoint]
+pub struct MyContract {
+ // Large data structures are more practical in WASM
+ data: StorageVec,
+}
+
+// Nitro automatically inserts pay_for_memory_grow calls
+// when allocating new pages
+let large_vector = vec![0u8; 100_000]; // Efficient in WASM
+```
+
+:::note
+The Stylus SDK's `entrypoint!` macro includes a no-op call to `pay_for_memory_grow` to ensure the function is referenced. Nitro then automatically inserts actual calls when memory allocation occurs.
+:::
+
+## Gas metering: Ink and gas
+
+### EVM gas metering
+
+- **Unit**: Gas (standard Ethereum unit)
+- **Granularity**: Per opcode (e.g., ADD = 3 gas, SSTORE = 20,000 gas)
+- **Measurement**: Coarse-grained
+- **Refunds**: Available for storage deletions
+
+### Stylus ink metering
+
+Stylus introduces "ink" as a fine-grained metering unit:
+
+- **Unit**: Ink (Stylus-specific, converted to gas)
+- **Granularity**: Per WASM instruction (more fine-grained)
+- **Measurement**: Precise tracking of WASM execution costs
+- **Conversion**: Ink → Gas conversion happens automatically
+
+**Ink to gas conversion:**
+
+```rust
+// Check remaining ink
+let ink_left = evm_ink_left();
+
+// Check remaining gas
+let gas_left = evm_gas_left();
+
+// Get ink price (in gas basis points)
+let ink_price = tx_ink_price();
+
+// Conversion formula:
+// gas = ink * ink_price / 10000
+```
+
+**Why ink?**
+
+1. **Precision**: WASM instructions have varying costs that don't map cleanly to EVM gas
+2. **Efficiency**: Fine-grained metering allows for more accurate pricing
+3. **Performance**: Enables cheaper execution for compute-heavy operations
+4. **Flexibility**: Ink prices can be adjusted without changing contract code
+
+**Gas cost comparison:**
+
+| Operation | EVM Gas | Stylus Gas | Improvement |
+| ------------------- | --------- | ---------- | --------------- |
+| Basic arithmetic | 3-5 | ~1-2 | 2-3x cheaper |
+| Memory operations | Variable | Efficient | 10-100x cheaper |
+| Complex computation | Expensive | Cheap | 10-100x cheaper |
+| Storage operations | Same | Same | Equal |
+| External calls | Same | Same | Equal |
+
+## Instruction sets
+
+### EVM opcodes
+
+- **Count**: ~140 opcodes
+- **Categories**: Arithmetic, logic, storage, flow control, system
+- **Size**: 1 byte per opcode
+- **Examples**:
+ - `ADD`, `MUL`, `SUB`, `DIV` (arithmetic)
+ - `SLOAD`, `SSTORE` (storage)
+ - `CALL`, `DELEGATECALL` (calls)
+ - `SHA3` (hashing)
+
+### WASM instructions
+
+- **Count**: Hundreds of instructions
+- **Categories**: Numeric, memory, control flow, function calls
+- **Size**: Variable encoding (1-5 bytes)
+- **Examples**:
+ - `i32.add`, `i64.mul`, `f64.div` (numeric)
+ - `memory.grow`, `memory.size` (memory)
+ - `call`, `call_indirect` (functions)
+ - Hostio imports (system operations)
+
+**WASM advantages:**
+
+- More expressive instruction set
+- Better compiler optimization targets
+- Efficient handling of complex data structures
+- Native support for 32-bit and 64-bit operations
+
+## Size limits
+
+### EVM contracts
+
+- **Maximum size**: 24,576 bytes (24 KB) of deployed bytecode
+- **Limit reason**: Block gas limit and deployment costs
+- **Workaround**: Contract splitting, proxies
+
+### Stylus contracts
+
+- **Initial limit**: 24 KB (same as EVM for compatibility)
+- **Compressed size**: Can be larger before compression
+- **Future**: Limit may be increased as WASM tooling improves
+- **Practical size**: Stylus programs are often smaller due to efficient compilation
+
+**Size optimization:**
+
+```rust
+// Stylus contracts benefit from:
+// 1. Rust's zero-cost abstractions
+// 2. Dead code elimination by wasm-opt
+// 3. Efficient WASM encoding
+
+#[no_std] // Opt out of standard library for smaller binaries
+extern crate alloc;
+
+// Only the code actually used is included
+use stylus_sdk::prelude::*;
+```
+
+## Storage model
+
+Both EVM and WASM contracts use the **same storage system**:
+
+- **Format**: 256-bit key-value store
+- **Compatibility**: EVM and WASM contracts can share storage
+- **Costs**: SLOAD and SSTORE costs are identical
+- **Caching**: Stylus VM implements storage caching for efficiency
+
+### Storage caching in Stylus
+
+```rust
+use stylus_sdk::prelude::*;
+
+#[storage]
+pub struct Counter {
+ count: StorageU256,
+}
+
+#[public]
+impl Counter {
+ pub fn increment(&mut self) {
+ // First read: full SLOAD cost
+ let current = self.count.get();
+
+ // Write is cached
+ self.count.set(current + U256::from(1));
+
+ // Additional reads in same call are cheaper (cached)
+ let new_value = self.count.get();
+
+ // Cache is automatically flushed at call boundary
+ }
+}
+```
+
+**Cache benefits:**
+
+1. **Reduced gas costs**: Repeated reads are cheaper
+2. **Better performance**: Fewer state trie accesses
+3. **Automatic management**: SDK handles cache flushing
+4. **Compatibility**: Refund logic matches EVM exactly
+
+## Performance characteristics
+
+### Compute operations
+
+| Category | EVM | Stylus WASM | Winner |
+| ------------------- | --------- | ----------- | ------------ |
+| Integer arithmetic | Moderate | Fast | WASM (10x+) |
+| Loops | Expensive | Cheap | WASM (100x+) |
+| Memory copying | Expensive | Cheap | WASM (10x+) |
+| Hashing (keccak256) | Native | Native | Equal |
+| Cryptography | Limited | Efficient | WASM |
+| String operations | Expensive | Cheap | WASM (100x+) |
+
+### Storage operations
+
+| Operation | EVM | Stylus WASM | Winner |
+| --------------- | ---------- | ----------- | ------ |
+| SLOAD | 2,100 gas | 2,100 gas | Equal |
+| SSTORE (new) | 20,000 gas | 20,000 gas | Equal |
+| SSTORE (update) | 5,000 gas | 5,000 gas | Equal |
+| Storage refunds | Standard | Standard | Equal |
+| Cached reads | No | Yes | WASM |
+
+### External interactions
+
+| Operation | EVM | Stylus WASM | Winner |
+| -------------------- | ------------- | ------------- | ------ |
+| Contract calls | ~700 gas base | ~700 gas base | Equal |
+| Cross-language calls | N/A | Efficient | WASM |
+| Event emission | Same cost | Same cost | Equal |
+| Value transfers | Same cost | Same cost | Equal |
+
+## Call semantics
+
+### Interoperability
+
+Both environments support seamless interoperability:
+
+```rust
+// Stylus calling Solidity
+sol_interface! {
+ interface IERC20 {
+ function transfer(address to, uint256 amount) external returns (bool);
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn call_evm_contract(&self, token: Address) -> Result> {
+ let erc20 = IERC20::new(token);
+ let result = erc20.transfer(self.vm(), recipient, amount)?;
+ Ok(result)
+ }
+}
+```
+
+```solidity
+// Solidity calling Stylus
+interface IStylusContract {
+ function computeHash(bytes calldata data) external view returns (bytes32);
+}
+
+contract EvmContract {
+ function useStylus(address stylusAddr, bytes calldata data) public view returns (bytes32) {
+ return IStylusContract(stylusAddr).computeHash(data);
+ }
+}
+```
+
+### Call costs
+
+- **Same call overhead**: Both directions have similar base costs
+- **ABI encoding**: Identical for both
+- **Gas forwarding**: Follows 63/64 rule in both cases
+- **Return data**: Handled consistently
+
+## Contract lifecycle
+
+### Deployment
+
+**EVM contracts:**
+
+1. Submit init code (constructor bytecode)
+2. EVM executes init code
+3. Returns runtime bytecode
+4. Bytecode stored onchain
+
+**Stylus contracts:**
+
+1. Compile Rust → WASM
+2. Submit WASM code
+3. **Activation step**: One-time compilation to native code
+4. Activated programs cached for efficiency
+5. WASM code stored onchain
+
+**Activation benefits:**
+
+```shell
+# Deploy and activate a Stylus program
+cargo stylus deploy --private-key $PRIVATE_KEY
+
+# Activation happens once
+# Subsequent calls use cached native code
+```
+
+- **One-time cost**: Pay activation gas once
+- **Future savings**: All executions use optimized native code
+- **Upgradeability**: Re-activation needed for upgrades
+
+### Execution flow
+
+**EVM contracts:**
+
+```
+Transaction → EVM → Opcode interpretation → State changes
+```
+
+**Stylus contracts:**
+
+```
+Transaction → WASM VM → Native code execution → State changes
+ ↓
+ Hostio calls for state access
+```
+
+## Developer experience
+
+### EVM development
+
+**Languages**: Solidity, Vyper, Huff
+
+**Tools**:
+
+- Hardhat, Foundry for testing
+- Remix for quick development
+- Ethers.js/Web3.js for interaction
+
+**Debugging**:
+
+- Revert messages
+- Events for tracing
+- Stack traces limited
+
+### Stylus development
+
+**Languages**: Rust, C, C++ (any WASM-compatible language)
+
+**Tools**:
+
+- `cargo stylus` for deployment
+- Standard Rust tooling (cargo, rustc)
+- `TestVM` for unit testing
+- Rust analyzer for IDE support
+
+**Debugging**:
+
+- Full Rust error messages
+- Compile-time safety checks
+- `console!` macro for debug builds
+- Stack traces in development
+
+**Development comparison:**
+
+| Aspect | EVM | Stylus | Notes |
+| --------------- | -------------- | ------------------- | ------------------------------------- |
+| Type safety | Runtime | Compile-time | Rust catches errors before deployment |
+| Memory safety | Manual | Automatic | Rust's borrow checker |
+| Testing | External tools | Built-in Rust tests | `#[test]` functions work natively |
+| Iteration speed | Slower | Faster | No need to redeploy for tests |
+| Learning curve | Moderate | Steeper | Rust has more concepts |
+| Maturity | Very mature | Growing | Solidity has more resources |
+
+## Feature compatibility
+
+### Supported features
+
+Both EVM and Stylus support:
+
+✅ Contract calls and delegate calls
+✅ Value transfers
+✅ Event emission
+✅ Storage operations
+✅ Block and transaction properties
+✅ Cryptographic functions (keccak256)
+✅ Contract creation (CREATE, CREATE2)
+✅ Revert and error handling
+✅ Reentrancy guards
+
+### EVM-specific features not in WASM
+
+❌ Inline assembly (use hostio or Rust instead)
+❌ `selfdestruct` (deprecated in Ethereum anyway)
+❌ Solidity modifiers (use Rust functions)
+❌ Multiple inheritance (use traits and composition)
+
+### Stylus-specific features not in EVM
+
+✅ Access to Rust ecosystem (crates)
+✅ Efficient memory management
+✅ Zero-cost abstractions
+✅ Compile-time guarantees
+✅ Native testing support
+✅ Better optimization opportunities
+
+## State sharing
+
+EVM and WASM contracts share the same blockchain state:
+
+```rust
+// Stylus contract can read EVM contract storage
+#[storage]
+pub struct Bridge {
+ evm_contract: StorageAddress,
+}
+
+#[public]
+impl Bridge {
+ pub fn read_evm_storage(&self, key: U256) -> U256 {
+ // Both VMs use the same storage layout
+ // Can read storage written by EVM contracts
+ storage_load_bytes32(key)
+ }
+}
+```
+
+**Shared state:**
+
+- Account balances
+- Contract storage
+- Contract code
+- Transaction history
+- Block data
+
+## Gas economics
+
+### Cost structure
+
+**EVM contract execution:**
+
+```
+Total cost = Base transaction cost (21,000 gas)
+ + Input data cost (~16 gas/byte)
+ + Execution cost (opcode gas)
+ + Storage cost (SLOAD/SSTORE)
+```
+
+**Stylus contract execution:**
+
+```
+Total cost = Base transaction cost (21,000 gas)
+ + Input data cost (~16 gas/byte)
+ + Execution cost (ink → gas conversion)
+ + Storage cost (same as EVM)
+```
+
+### When to use each
+
+**Use EVM (Solidity) when:**
+
+- Quick prototyping needed
+- Simple contracts with minimal computation
+- Team expertise in Solidity
+- Extensive storage operations (cost is equal)
+- Maximum ecosystem compatibility
+
+**Use Stylus (Rust) when:**
+
+- Compute-intensive operations
+- Complex algorithms or data structures
+- Need for memory safety guarantees
+- Existing Rust codebase to port
+- Optimizing for gas efficiency
+- Cryptographic operations
+- String/byte manipulation
+
+## Best practices
+
+### For EVM contracts
+
+1. **Minimize storage operations**: Use memory when possible
+2. **Optimize loops**: Keep iterations minimal
+3. **Pack storage**: Use smaller types when possible
+4. **Avoid complex math**: Basic operations only
+5. **Use libraries**: Leverage audited code
+
+### For Stylus contracts
+
+1. **Leverage Rust's safety**: Let the compiler catch bugs
+2. **Use iterators**: More efficient than manual loops
+3. **Profile before optimizing**: Use cargo-stylus tools
+4. **Test thoroughly**: Use Rust's built-in test framework
+5. **Consider binary size**: Use `#[no_std]` if needed
+6. **Batch operations**: Take advantage of cheap compute
+
+### Hybrid approach
+
+Many projects can benefit from both:
+
+```rust
+// Compute-heavy logic in Stylus
+#[public]
+impl ComputeEngine {
+ pub fn complex_calculation(&self, data: Vec) -> Vec {
+ // Efficient loops and data processing
+ data.iter()
+ .map(|x| expensive_computation(*x))
+ .collect()
+ }
+}
+```
+
+```solidity
+// Coordination and state management in Solidity
+contract Coordinator {
+ IComputeEngine public engine; // Stylus contract
+
+ function process(uint256[] calldata data) public {
+ uint256[] memory results = engine.complex_calculation(data);
+ // Store results, emit events, etc.
+ }
+}
+```
+
+## Migration considerations
+
+### From Solidity to Stylus
+
+**What stays the same:**
+
+- Contract addresses
+- Storage layout
+- ABIs and interfaces
+- Gas for storage operations
+- Event signatures
+
+**What changes:**
+
+- Programming language (Solidity → Rust)
+- Execution engine (EVM → WASM)
+- Gas costs for compute (usually cheaper)
+- Development workflow
+- Testing approach
+
+**Migration strategy:**
+
+1. Start with compute-heavy functions
+2. Maintain same ABI for compatibility
+3. Test extensively with existing contracts
+4. Monitor gas costs in production
+5. Gradually migrate more functionality
+
+## Future developments
+
+### EVM evolution
+
+- EIP improvements
+- New opcodes
+- Gas repricing
+- EOF (EVM Object Format)
+
+### Stylus evolution
+
+- Support for more languages
+- SIMD instructions
+- Floating point operations
+- Larger contract size limits
+- Further gas optimizations
+- Enhanced debugging tools
+
+## Resources
+
+- [Stylus documentation](https://docs.arbitrum.io/stylus)
+- [Ink and gas metering](https://docs.arbitrum.io/stylus/concepts/gas-metering)
+- [WASM specification](https://webassembly.github.io/spec/)
+- [EVM opcodes reference](https://www.evm.codes/)
+- [Stylus SDK repository](https://github.com/OffchainLabs/stylus-sdk-rs)
+
+## Summary
+
+The WASM VM in Arbitrum Nitro represents a significant evolution in smart contract execution:
+
+**Key advantages of WASM:**
+
+- 10-100x cheaper for compute operations
+- More expressive programming languages
+- Better memory management
+- Compile-time safety guarantees
+- Access to mature language ecosystems
+
+**Key advantages of EVM:**
+
+- Mature tooling and ecosystem
+- Familiar to existing developers
+- No activation cost
+- Decades of collective knowledge
+
+Both execution environments coexist harmoniously on Arbitrum, allowing developers to choose the best tool for each use case while maintaining full interoperability.
diff --git a/docs/stylus/concepts/webassembly.mdx b/docs/stylus/concepts/webassembly.mdx
new file mode 100644
index 0000000000..c1ead592ad
--- /dev/null
+++ b/docs/stylus/concepts/webassembly.mdx
@@ -0,0 +1,412 @@
+---
+title: 'WebAssembly in Nitro'
+description: 'Understanding WebAssembly compilation, deployment, and execution in Arbitrum Nitro'
+author: chrisco
+sme: chrisco
+sidebar_position: 1
+target_audience: Developers who need to understand how Stylus works with WebAssembly.
+displayed_sidebar: buildStylusSidebar
+---
+
+WebAssembly (WASM) is a binary instruction format that enables high-performance execution of programs in the Nitro virtual machine. This guide explains how WASM works in the context of Arbitrum Nitro and Stylus smart contract development.
+
+## What is WebAssembly?
+
+WebAssembly is a portable, size-efficient binary format designed for safe execution at near-native speeds. Key characteristics include:
+
+- **Binary format**: Compact representation that's faster to parse than text-based formats
+- **Stack-based VM**: Simple execution model with operand stack
+- **Sandboxed execution**: Memory-safe by design with explicit bounds checking
+- **Language-agnostic**: Can be targeted by many programming languages (Rust, C, C++, etc.)
+
+## Why WebAssembly in Nitro?
+
+Nitro uses WebAssembly as its execution environment for several reasons:
+
+1. **Performance**: WASM compiles to native machine code for fast execution
+2. **Security**: Sandboxed environment prevents unauthorized access
+3. **Portability**: Same bytecode runs identically across all nodes
+4. **Language flexibility**: Developers can use Rust, C, C++, or any language that compiles to WASM
+5. **Determinism**: Guaranteed identical execution across all validators
+
+
+
+_Figure: WebAssembly execution pipeline in Arbitrum Nitro, from source code to native execution with access to blockchain state._
+
+## WASM compilation target
+
+Stylus contracts are compiled to the `wasm32-unknown-unknown` target, which means:
+
+- **32-bit addressing**: Uses 32-bit pointers and memory addresses
+- **Unknown OS**: No operating system dependencies
+- **Unknown environment**: Minimal runtime assumptions (no std by default)
+
+The `.cargo/config.toml` file in Stylus projects configures the WASM target:
+
+```toml
+[target.wasm32-unknown-unknown]
+rustflags = [
+ "-C", "link-arg=-zstack-size=32768", # 32KB stack
+ "-C", "target-feature=-reference-types", # Disable reference types
+ "-C", "target-feature=+bulk-memory", # Enable bulk memory operations
+]
+```
+
+### Compilation flags
+
+- **Stack size**: Limited to 32KB to ensure bounded memory usage
+- **Bulk memory**: Enables efficient `memory.copy` and `memory.fill` operations
+- **No reference types**: Keeps the WASM simpler and more compatible
+
+## WASM binary structure
+
+A Stylus WASM module consists of several sections:
+
+
+
+_Figure: WASM module structure showing the main sections including exports, imports (hostio functions), memory, and code._
+
+### Exports
+
+Every Stylus contract exports a `user_entrypoint` function:
+
+```rust
+#[no_mangle]
+pub extern "C" fn user_entrypoint(len: usize) -> usize {
+ // Entry point for all contract calls
+ // len: size of calldata in bytes
+ // returns: size of output data in bytes
+}
+```
+
+This function is automatically generated by the `#[entrypoint]` macro and serves as the single entry point for all contract interactions.
+
+### Imports
+
+WASM modules import low-level functions from the `vm_hooks` module:
+
+```rust
+// Example hostio imports
+extern "C" {
+ fn storage_load_bytes32(key: *const u8, dest: *mut u8);
+ fn storage_store_bytes32(key: *const u8, value: *const u8);
+ fn msg_sender(sender: *mut u8);
+ fn block_timestamp() -> u64;
+ // ... and many more
+}
+```
+
+These imported functions (called "hostio" functions) provide access to blockchain state and functionality.
+
+### Memory
+
+WASM modules use linear memory, which is:
+
+- **Contiguous**: Single continuous address space starting at 0
+- **Growable**: Can expand at runtime (in 64KB pages)
+- **Isolated**: Each contract has its own memory space
+
+Memory growth is explicitly metered:
+
+```rust
+// Exported function that must exist
+#[no_mangle]
+pub extern "C" fn pay_for_memory_grow(pages: u16) {
+ // Called before memory.grow to charge for new pages
+ // Each page is 64KB
+}
+```
+
+### Custom sections
+
+WASM supports custom sections for metadata:
+
+```rust
+// Example: Add version information
+#[link_section = ".custom.stylus-version"]
+static VERSION: [u8; 5] = *b"0.1.0";
+```
+
+Custom sections can store:
+
+- Contract version
+- Source code hashes
+- Compiler metadata
+- ABI information
+
+## Compression and deployment
+
+Before deployment, Stylus contracts undergo compression:
+
+### Brotli compression
+
+```rust
+// From stylus-tools/src/utils/wasm.rs
+pub fn brotli_compress(wasm: impl Read, compression_level: u32) -> io::Result> {
+ let mut compressed = Vec::new();
+ let mut encoder = brotli::CompressorWriter::new(&mut compressed, 4096, compression_level, 22);
+ io::copy(&mut wasm, &mut encoder)?;
+ encoder.flush()?;
+ Ok(compressed)
+}
+```
+
+Brotli compression typically reduces WASM size by 50-70%.
+
+### 0xEFF000 prefix
+
+Compressed WASM is prefixed with `0xEFF000` to identify it as a Stylus program:
+
+```rust
+pub fn add_prefix(compressed_wasm: impl IntoIterator- , prefix: &str) -> Vec {
+ let prefix_bytes = hex::decode(prefix.strip_prefix("0x").unwrap_or(prefix)).unwrap();
+ prefix_bytes.into_iter().chain(compressed_wasm).collect()
+}
+```
+
+This prefix allows the Nitro VM to distinguish Stylus contracts from EVM bytecode.
+
+## Contract activation
+
+After deployment, contracts must be **activated** before execution:
+
+### Activation process
+
+1. **Initial deployment**: Contract code is stored onchain (compressed)
+2. **Activation call**: Special transaction invokes `activateProgram`
+3. **Decompression**: Brotli-compressed WASM is decompressed
+4. **Validation**: WASM is checked for:
+ - Valid structure
+ - Required exports (`user_entrypoint`)
+ - Allowed imports (only `vm_hooks`)
+ - Memory constraints
+5. **Compilation**: WASM is compiled to native machine code
+6. **Caching**: Compiled code is cached for future executions
+
+### One-time cost
+
+Activation incurs a one-time gas cost but provides benefits:
+
+- **Fast execution**: Native code runs 10-100x faster than interpreted
+- **Persistent cache**: Compilation happens once, benefits all future calls
+- **Optimizations**: Native compiler applies target-specific optimizations
+
+### Verification
+
+The activation process checks for the `pay_for_memory_grow` function to verify correct entrypoint setup:
+
+```rust
+// From activation.rs
+if !wasm::has_entrypoint(&wasm)? {
+ bail!("WASM is missing the entrypoint export");
+}
+```
+
+## Development workflow
+
+### 1. Write Rust code
+
+```rust
+use stylus_sdk::{alloy_primitives::U256, prelude::*};
+
+#[entrypoint]
+#[storage]
+pub struct Counter {
+ count: StorageU256,
+}
+
+#[public]
+impl Counter {
+ pub fn increment(&mut self) {
+ let count = self.count.get() + U256::from(1);
+ self.count.set(count);
+ }
+}
+```
+
+### 2. Compile to WASM
+
+```bash
+cargo stylus build
+```
+
+This runs:
+
+```bash
+cargo build \
+ --lib \
+ --locked \
+ --release \
+ --target wasm32-unknown-unknown \
+ --target-dir target/wasm32-unknown-unknown/release
+```
+
+### 3. Optimize (optional)
+
+```bash
+wasm-opt target/wasm32-unknown-unknown/release/my_contract.wasm \
+ -O3 \
+ --strip-debug \
+ -o optimized.wasm
+```
+
+Optimization can reduce size by an additional 10-30%.
+
+### 4. Deploy and activate
+
+```bash
+# Deploy compressed WASM
+cargo stylus deploy --private-key=$PRIVATE_KEY
+
+# Activation happens automatically
+```
+
+## Size limitations
+
+Nitro imposes limits on WASM contract size:
+
+| Limit | Value | Reason |
+| --------------------- | ---------------------- | --------------------------------- |
+| **Uncompressed size** | ~3-4 MB | Memory and processing constraints |
+| **Compressed size** | 24 KB (initial) | Ethereum transaction size limit |
+| **Compressed size** | 128 KB (with EIP-4844) | Larger blob transactions |
+
+To stay within limits:
+
+- Use `#[no_std]` to avoid standard library bloat
+- Strip debug symbols with `--strip-debug`
+- Enable aggressive optimization (`-O3`)
+- Minimize dependencies
+- Use compact data structures
+
+## Memory model
+
+
+
+_Figure: WASM linear memory model showing the fixed 32KB stack and growable heap organized in 64KB pages._
+
+### Linear memory layout
+
+```
+0x00000000 ┌─────────────────┐
+ │ Stack │ 32 KB fixed size
+0x00008000 ├─────────────────┤
+ │ Heap/Data │ Grows upward
+ │ │
+ │ (Available) │
+ │ │
+0xFFFFFFFF └─────────────────┘
+```
+
+### Memory operations
+
+```rust
+// Bulk memory operations (enabled by target config)
+unsafe {
+ // Fast memory copy
+ core::ptr::copy_nonoverlapping(src, dst, len);
+
+ // Fast memory fill
+ core::ptr::write_bytes(ptr, value, len);
+}
+```
+
+The `bulk-memory` feature flag enables efficient WASM instructions like `memory.copy` and `memory.fill`.
+
+## Advanced: WASM instructions
+
+Stylus uses WASM MVP (Minimum Viable Product) instructions plus bulk-memory operations:
+
+### Arithmetic
+
+- `i32.add`, `i32.sub`, `i32.mul`, `i32.div_s`, `i32.div_u`
+- `i64.add`, `i64.sub`, `i64.mul`, `i64.div_s`, `i64.div_u`
+
+### Memory access
+
+- `i32.load`, `i32.store` (32-bit load/store)
+- `i64.load`, `i64.store` (64-bit load/store)
+- `memory.grow` (expand memory)
+- `memory.copy` (bulk copy, requires flag)
+- `memory.fill` (bulk fill, requires flag)
+
+### Control flow
+
+- `call`, `call_indirect` (function calls)
+- `if`, `else`, `block`, `loop` (structured control flow)
+- `br`, `br_if` (branching)
+
+### Not supported
+
+- ❌ Floating point operations (f32, f64)
+- ❌ SIMD operations
+- ❌ Reference types
+- ❌ Multiple memories
+- ❌ Threads
+
+## Best practices
+
+### 1. Minimize binary size
+
+```rust
+// Use #[no_std] when possible
+#![no_std]
+extern crate alloc;
+
+// Avoid large dependencies
+// Prefer: alloy-primitives
+// Avoid: serde_json, regex (unless necessary)
+```
+
+### 2. Optimize memory usage
+
+```rust
+// Stack allocate when possible
+let small_buffer = [0u8; 32];
+
+// Heap allocate only when necessary
+let large_buffer = vec![0u8; 1024];
+```
+
+### 3. Profile before optimizing
+
+```bash
+# Check binary size
+ls -lh target/wasm32-unknown-unknown/release/*.wasm
+
+# Analyze with twiggy
+cargo install twiggy
+twiggy top target/wasm32-unknown-unknown/release/my_contract.wasm
+```
+
+### 4. Test locally
+
+```bash
+# Use cargo-stylus for local testing
+cargo stylus check
+cargo stylus export-abi
+```
+
+### 5. Validate before deployment
+
+```rust
+// Ensure entrypoint exists
+#[entrypoint]
+#[storage]
+pub struct MyContract { /* ... */ }
+
+// Verify required exports
+#[no_mangle]
+pub extern "C" fn pay_for_memory_grow(pages: u16) {
+ // Generated automatically by SDK
+}
+```
+
+## Resources
+
+- [WebAssembly specification](https://webassembly.github.io/spec/)
+- [Rust WASM target documentation](https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-unknown.html)
+- [Stylus SDK repository](https://github.com/OffchainLabs/stylus-sdk-rs)
+- [Cargo Stylus CLI tool](https://github.com/OffchainLabs/stylus-sdk-rs/tree/main/cargo-stylus)
+- [WASM binary toolkit (wabt)](https://github.com/WebAssembly/wabt)
+- [Binaryen optimization tools](https://github.com/WebAssembly/binaryen)
diff --git a/docs/stylus/fundamentals/_category_.yml b/docs/stylus/fundamentals/_category_.yml
new file mode 100644
index 0000000000..09fabd7c58
--- /dev/null
+++ b/docs/stylus/fundamentals/_category_.yml
@@ -0,0 +1,6 @@
+label: 'Fundamentals'
+position: 2
+collapsed: false
+link:
+ type: generated-index
+ description: Essential Stylus SDK concepts, prerequisites, and core development skills.
diff --git a/docs/stylus/fundamentals/choose-your-path.mdx b/docs/stylus/fundamentals/choose-your-path.mdx
new file mode 100644
index 0000000000..ec7ec8dfe0
--- /dev/null
+++ b/docs/stylus/fundamentals/choose-your-path.mdx
@@ -0,0 +1,261 @@
+---
+title: 'Choose your learning path'
+sidebar_label: 'Choose your path'
+description: 'Find the right Stylus learning path based on your background and goals'
+user_story: 'As a developer new to Stylus, I want to find the most efficient learning path based on my background'
+content_type: concept
+author: offchainlabs
+sme: offchainlabs
+sidebar_position: 0
+---
+
+# Choose your learning path
+
+Not sure where to start with Stylus? This guide helps you find the optimal learning path based on your background and goals.
+
+## Quick path selector
+
+
+Path 1: New to Rust
+
+**Best for**: Developers experienced with other languages but new to Rust.
+
+### Prerequisites
+
+- Basic programming knowledge
+- Familiarity with concepts like variables, functions, and control flow
+- Optional: Smart contract development experience
+
+### Recommended journey
+
+1. **Learn Rust basics** (1-2 weeks)
+
+ - Work through [The Rust Book](https://doc.rust-lang.org/book/) chapters 1-10
+ - Focus on: ownership, borrowing, structs, enums, error handling
+ - Practice with [Rustlings](https://github.com/rust-lang/rustlings)
+
+2. **Start with Stylus** (2-3 days)
+
+ - Complete the [Quickstart](/stylus/quickstart)
+ - Understand [project structure](/stylus/fundamentals/project-structure)
+ - Deploy your first contract
+
+3. **Build fundamentals** (1 week)
+
+ - Study [data types](/stylus/fundamentals/data-types/primitives)
+ - Learn [storage patterns](/stylus/fundamentals/data-types/storage)
+ - Practice [writing tests](/stylus/fundamentals/testing-contracts)
+
+4. **Explore advanced topics** (ongoing)
+ - Review [best practices](/stylus/best-practices/security)
+ - Study [gas optimization](/stylus/best-practices/gas-optimization)
+ - Build real projects
+
+### Key resources
+
+- [The Rust Book](https://doc.rust-lang.org/book/)
+- [Stylus Quickstart](/stylus/quickstart)
+- [Stylus by Example](https://stylus-by-example.org/)
+
+---
+
+
+
+
+Path 2: Solidity developer
+
+**Best for**: Experienced Solidity developers transitioning to Stylus.
+
+### Prerequisites
+
+- Strong Solidity knowledge
+- Understanding of EVM and smart contract security
+- No Rust experience required
+
+### Recommended journey
+
+1. **Understand the differences** (1 day)
+
+ - Read [Rust to Solidity differences](/stylus/advanced/rust-to-solidity-differences)
+ - Review [VM differences](/stylus/concepts/vm-differences)
+ - Understand [type conversions](/stylus/fundamentals/data-types/conversions-between-types)
+
+2. **Quick start** (1-2 days)
+
+ - Complete the [Quickstart](/stylus/quickstart)
+ - Compare with familiar Solidity patterns
+ - Deploy a simple contract
+
+3. **Learn Rust patterns** (3-5 days)
+
+ - Study [ownership and borrowing](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)
+ - Learn [error handling](/stylus/fundamentals/data-types/primitives)
+ - Understand [storage macros](/stylus/fundamentals/data-types/storage)
+
+4. **Port existing contracts** (ongoing)
+ - Start with simple contracts
+ - Apply [security best practices](/stylus/best-practices/security)
+ - Optimize for [gas efficiency](/stylus/best-practices/gas-optimization)
+
+### Common migration patterns
+
+| Solidity Pattern | Stylus Equivalent | Guide |
+| ----------------------------- | --------------------------- | ----------------------------------------------------------------------- |
+| `mapping(address => uint)` | `StorageMap` | [Storage types](/stylus/fundamentals/data-types/storage) |
+| `require(condition, "error")` | `ensure!(condition, Error)` | [Error handling](/stylus/fundamentals/contracts#error-handling) |
+| `msg.sender` | `msg::sender()` | [Global functions](/stylus/fundamentals/global-variables-and-functions) |
+| `constructor` | `impl` with initialization | [Contracts](/stylus/fundamentals/contracts) |
+| Events | Derive `Erase` trait | See Stylus SDK documentation |
+
+### Key resources
+
+- [Rust to Solidity Differences](/stylus/advanced/rust-to-solidity-differences)
+- [Stylus by Example](https://stylus-by-example.org/)
+- [Solidity to Rust Cheat Sheet](https://docs.arbitrum.io/stylus)
+
+---
+
+
+
+
+Path 3: Smart contract developer (non-Solidity)
+
+**Best for**: Developers experienced with other smart contract platforms (Move, Cairo, Clarity, etc.).
+
+### Prerequisites
+
+- Smart contract development experience
+- Understanding of blockchain concepts
+- Basic programming knowledge
+
+### Recommended journey
+
+1. **Jump right in** (1-2 days)
+
+ - Complete the [Quickstart](/stylus/quickstart)
+ - Explore [project structure](/stylus/fundamentals/project-structure)
+ - Deploy and interact with a contract
+
+2. **Understand the execution model** (2-3 days)
+
+ - Study [WebAssembly concepts](/stylus/concepts/webassembly)
+ - Review [VM differences](/stylus/concepts/vm-differences)
+ - Learn about [activation](/stylus/concepts/activation)
+
+3. **Master Rust for smart contracts** (1 week)
+
+ - Focus on [storage patterns](/stylus/fundamentals/data-types/storage)
+ - Learn [contract structure](/stylus/fundamentals/contracts)
+ - Understand [testing approaches](/stylus/fundamentals/testing-contracts)
+
+4. **Advanced development** (ongoing)
+ - Apply platform-specific optimizations
+ - Implement cross-contract calls
+ - Build production applications
+
+### Key resources
+
+- [Quickstart](/stylus/quickstart)
+- [WebAssembly Concepts](/stylus/concepts/webassembly)
+- [Advanced Topics](/stylus/advanced/rust-to-solidity-differences)
+
+---
+
+
+
+
+Path 4: Rust developer new to Web3
+
+**Best for**: Experienced Rust developers entering blockchain development.
+
+### Prerequisites
+
+- Strong Rust knowledge
+- Understanding of ownership, traits, and async programming
+- No blockchain experience required
+
+### Recommended journey
+
+1. **Learn blockchain basics** (2-3 days)
+
+ - Understand [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/)
+ - Learn about [gas and transactions](https://ethereum.org/en/developers/docs/gas/)
+ - Study [EVM basics](https://ethereum.org/en/developers/docs/evm/)
+
+2. **Quick start with Stylus** (1 day)
+
+ - Complete the [Quickstart](/stylus/quickstart)
+ - Your Rust skills transfer directly!
+ - Deploy your first contract
+
+3. **Web3-specific patterns** (3-5 days)
+
+ - Learn [storage patterns](/stylus/fundamentals/data-types/storage)
+ - Understand [global variables](/stylus/fundamentals/global-variables-and-functions)
+ - Study [security considerations](/stylus/best-practices/security)
+
+4. **Build applications** (ongoing)
+ - Start with simple DeFi primitives
+ - Implement events and logs
+ - Optimize for [gas efficiency](/stylus/best-practices/gas-optimization)
+
+### Rust skills that transfer well
+
+- **Ownership model**: Natural fit for secure contract design
+- **Type safety**: Prevents common smart contract bugs
+- **Error handling**: `Result` maps perfectly to contract errors
+- **Testing**: Your testing skills apply directly
+- **Performance optimization**: Critical for gas efficiency
+
+### Key resources
+
+- [Ethereum Smart Contracts](https://ethereum.org/en/developers/docs/smart-contracts/)
+- [Stylus Quickstart](/stylus/quickstart)
+- [Storage Patterns](/stylus/fundamentals/data-types/storage)
+
+---
+
+
+
+## General learning resources
+
+Regardless of your path, these resources are valuable:
+
+### Official documentation
+
+- [Stylus Rust SDK Docs](https://docs.rs/stylus-sdk/latest/stylus_sdk/)
+- [Cargo Stylus CLI](https://github.com/OffchainLabs/cargo-stylus)
+- [Stylus by Example](https://stylus-by-example.org/)
+
+### Community and support
+
+- [Arbitrum Discord](https://discord.gg/arbitrum)
+- [Developer Forums](https://forum.arbitrum.foundation/)
+- [GitHub Discussions](https://github.com/OffchainLabs/stylus-sdk-rs/discussions)
+
+### Practice projects
+
+1. **Token contract**: Implement ERC-20 token standard
+2. **NFT contract**: Build ERC-721 compatible NFT
+3. **Simple DeFi**: Create a basic AMM or lending protocol
+4. **DAO governance**: Implement voting and proposal system
+
+## Need help choosing?
+
+Still not sure which path is right for you? Consider these questions:
+
+- **Do you want to learn Rust?** → Path 1 or Path 4
+- **Do you already know Solidity?** → Path 2
+- **Are you experienced with other smart contract platforms?** → Path 3
+- **Do you want the fastest path to deployment?** → Path 2 or Path 3
+- **Do you want to deeply understand the technology?** → Path 1 or Path 4
+
+## Next steps
+
+Ready to start? Begin with these essential guides:
+
+1. [Prerequisites and setup](/stylus/fundamentals/prerequisites)
+2. [Quickstart: Deploy your first contract](/stylus/quickstart)
+3. [Project structure](/stylus/fundamentals/project-structure)
+
+Good luck on your Stylus journey!
diff --git a/docs/stylus/fundamentals/contracts.mdx b/docs/stylus/fundamentals/contracts.mdx
new file mode 100644
index 0000000000..b5ba85e034
--- /dev/null
+++ b/docs/stylus/fundamentals/contracts.mdx
@@ -0,0 +1,1654 @@
+---
+title: 'Stylus contracts'
+description: 'Stylus Rust SDK contracts, methods, and lifecycle'
+author: chrisco
+sme: chrisco
+sidebar_position: 2
+target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts.
+displayed_sidebar: buildStylusSidebar
+---
+
+Stylus smart contracts are fully compatible with Solidity contracts on Arbitrum chains. They compile to WebAssembly and share the same EVM state trie as Solidity contracts, enabling seamless interoperability.
+
+## Contract basics
+
+A Stylus contract consists of three main components:
+
+1. **Storage Definition**: Defines the contract's persistent state
+2. **Entrypoint**: Marks the main contract struct that handles incoming calls
+3. **Public Methods**: Functions exposed to external callers via the `#[public]` macro
+
+### Minimal Contract
+
+Here's the simplest possible Stylus contract:
+
+```rust
+#![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
+extern crate alloc;
+
+use stylus_sdk::prelude::*;
+
+#[storage]
+#[entrypoint]
+pub struct HelloWorld;
+
+#[public]
+impl HelloWorld {
+ fn user_main(_input: Vec) -> ArbResult {
+ Ok(Vec::new())
+ }
+}
+```
+
+This contract:
+
+- Uses `#[storage]` to define the contract struct (empty in this case)
+- Uses `#[entrypoint]` to mark it as the contract's entry point
+- Uses `#[public]` to expose the `user_main` function
+- Returns `ArbResult`, which is `Result, Vec>`
+
+## Storage Definition
+
+Stylus contracts use the `sol_storage!` macro or `#[storage]` attribute to define persistent storage that maps directly to Solidity storage slots.
+
+### Using `sol_storage!` (Solidity-style)
+
+The `sol_storage!` macro lets you define storage using Solidity syntax:
+
+```rust
+use stylus_sdk::prelude::*;
+use alloy_primitives::{Address, U256};
+
+sol_storage! {
+ #[entrypoint]
+ pub struct Counter {
+ uint256 count;
+ address owner;
+ mapping(address => uint256) balances;
+ }
+}
+```
+
+This creates a contract with:
+
+- A `count` field of type `StorageU256`
+- An `owner` field of type `StorageAddress`
+- A `balances` mapping from `Address` to `StorageU256`
+
+### Using `#[storage]` (Rust-style)
+
+Alternatively, use the `#[storage]` attribute with explicit storage types:
+
+```rust
+use stylus_sdk::prelude::*;
+use stylus_sdk::storage::{StorageU256, StorageAddress, StorageMap};
+use alloy_primitives::{Address, U256};
+
+#[storage]
+#[entrypoint]
+pub struct Counter {
+ count: StorageU256,
+ owner: StorageAddress,
+ balances: StorageMap,
+}
+```
+
+Both approaches produce identical storage layouts and are fully interoperable with Solidity contracts using the same storage structure.
+
+## The `#[entrypoint]` Macro
+
+The `#[entrypoint]` macro marks a struct as the contract's main entry point. It automatically implements the `TopLevelStorage` trait, which enables:
+
+- Routing incoming calls to public methods
+- Managing contract storage
+- Handling reentrancy protection (unless the `reentrant` feature is enabled)
+
+**Key requirements:**
+
+- Exactly one struct per contract must have `#[entrypoint]`
+- The struct must also have `#[storage]` or be defined in `sol_storage!`
+- The entrypoint struct represents the contract's root storage
+
+**Example:**
+
+```rust
+sol_storage! {
+ #[entrypoint]
+ pub struct MyContract {
+ uint256 value;
+ }
+}
+```
+
+The `#[entrypoint]` macro generates:
+
+1. An implementation of `TopLevelStorage` for the struct
+2. A `user_entrypoint` function that Stylus calls when the contract receives a transaction
+3. Method routing logic to dispatch calls to `#[public]` methods
+
+## Public Methods with `#[public]`
+
+The `#[public]` macro exposes Rust methods as external contract functions callable from Solidity, other Stylus contracts, or external callers.
+
+### Basic Public Methods
+
+```rust
+use stylus_sdk::prelude::*;
+use alloy_primitives::U256;
+
+sol_storage! {
+ #[entrypoint]
+ pub struct Calculator {
+ uint256 result;
+ }
+}
+
+#[public]
+impl Calculator {
+ // View function (read-only)
+ pub fn get_result(&self) -> U256 {
+ self.result.get()
+ }
+
+ // Write function (mutates state)
+ pub fn set_result(&mut self, value: U256) {
+ self.result.set(value);
+ }
+
+ // Pure function (no state access)
+ pub fn add(a: U256, b: U256) -> U256 {
+ a + b
+ }
+}
+```
+
+### State Mutability
+
+The SDK automatically infers state mutability from the method signature:
+
+| Signature | Mutability | Solidity Equivalent | Description |
+| ----------- | ---------- | ------------------- | --------------------- |
+| `&self` | `view` | `view` | Read contract state |
+| `&mut self` | Write | (default) | Modify contract state |
+| Neither | `pure` | `pure` | No state access |
+
+**Examples:**
+
+```rust
+#[public]
+impl MyContract {
+ // View: can read state, cannot modify
+ pub fn balance_of(&self, account: Address) -> U256 {
+ self.balances.get(account)
+ }
+
+ // Write: can read and modify state
+ pub fn transfer(&mut self, to: Address, amount: U256) {
+ let sender = self.vm().msg_sender();
+ let balance = self.balances.get(sender);
+ self.balances.setter(sender).set(balance - amount);
+ self.balances.setter(to).set(self.balances.get(to) + amount);
+ }
+
+ // Pure: no state access at all
+ pub fn calculate_fee(amount: U256) -> U256 {
+ amount * U256::from(3) / U256::from(100)
+ }
+}
+```
+
+## Constructor
+
+The `#[constructor]` attribute marks a function that runs once during contract deployment.
+
+### Basic Constructor
+
+```rust
+use stylus_sdk::prelude::*;
+use alloy_primitives::{Address, U256};
+
+sol_storage! {
+ #[entrypoint]
+ pub struct Token {
+ address owner;
+ uint256 total_supply;
+ }
+}
+
+#[public]
+impl Token {
+ #[constructor]
+ pub fn constructor(&mut self, initial_supply: U256) {
+ let deployer = self.vm().msg_sender();
+ self.owner.set(deployer);
+ self.total_supply.set(initial_supply);
+ }
+
+ pub fn owner(&self) -> Address {
+ self.owner.get()
+ }
+}
+```
+
+### Constructor Features
+
+**Payable Constructor:**
+
+```rust
+#[public]
+impl Token {
+ #[constructor]
+ #[payable]
+ pub fn constructor(&mut self, initial_supply: U256) {
+ // Contract can receive ETH during deployment
+ let received = self.vm().msg_value();
+ self.owner.set(self.vm().msg_sender());
+ self.total_supply.set(initial_supply);
+ }
+}
+```
+
+**Important Notes:**
+
+- The constructor name can be anything (doesn't have to be `constructor`)
+- Only one constructor per contract
+- Constructor runs exactly once when the contract is deployed
+- Use `tx_origin()` instead of `msg_sender()` when deploying via a factory contract
+
+## Method Attributes
+
+### `#[payable]`
+
+Marks a function as able to receive ETH:
+
+```rust
+#[public]
+impl PaymentProcessor {
+ #[payable]
+ pub fn deposit(&mut self) -> U256 {
+ let sender = self.vm().msg_sender();
+ let amount = self.vm().msg_value();
+
+ let current = self.balances.get(sender);
+ self.balances.setter(sender).set(current + amount);
+
+ amount
+ }
+
+ // Non-payable function will revert if ETH is sent
+ pub fn withdraw(&mut self, amount: U256) {
+ // Will revert if msg.value > 0
+ let sender = self.vm().msg_sender();
+ let balance = self.balances.get(sender);
+ self.balances.setter(sender).set(balance - amount);
+ }
+}
+```
+
+**Important:** Without `#[payable]`, sending ETH to a function causes a revert.
+
+### `#[receive]`
+
+Handles plain ETH transfers without calldata (equivalent to Solidity's `receive()` function):
+
+```rust
+use alloy_sol_types::sol;
+
+sol! {
+ event EtherReceived(address indexed sender, uint256 amount);
+}
+
+#[public]
+impl Wallet {
+ #[receive]
+ #[payable]
+ pub fn receive(&mut self) -> Result<(), Vec> {
+ let sender = self.vm().msg_sender();
+ let amount = self.vm().msg_value();
+
+ let balance = self.balances.get(sender);
+ self.balances.setter(sender).set(balance + amount);
+
+ self.vm().log(EtherReceived { sender, amount });
+ Ok(())
+ }
+}
+```
+
+**Notes:**
+
+- Must be combined with `#[payable]`
+- Called when the contract receives ETH without calldata
+- Only one `#[receive]` function per contract
+- Must have signature: `fn name(&mut self) -> Result<(), Vec>`
+
+### `#[fallback]`
+
+Handles calls to non-existent functions or as a fallback for ETH transfers:
+
+```rust
+use alloy_sol_types::sol;
+
+sol! {
+ event FallbackCalled(address indexed sender, bytes4 selector, uint256 value);
+}
+
+#[public]
+impl Contract {
+ #[fallback]
+ #[payable]
+ pub fn fallback(&mut self, calldata: &[u8]) -> ArbResult {
+ let sender = self.vm().msg_sender();
+ let value = self.vm().msg_value();
+
+ // Extract function selector if present
+ let selector = if calldata.len() >= 4 {
+ [calldata[0], calldata[1], calldata[2], calldata[3]]
+ } else {
+ [0; 4]
+ };
+
+ self.vm().log(FallbackCalled {
+ sender,
+ selector: selector.into(),
+ value,
+ });
+
+ Ok(vec![])
+ }
+}
+```
+
+**Fallback is called when:**
+
+1. A function call doesn't match any existing function signature
+2. Plain ETH transfer when no `#[receive]` function exists
+3. The contract receives calldata but no function matches
+
+**Notes:**
+
+- Must have signature: `fn name(&mut self, calldata: &[u8]) -> ArbResult`
+- Can optionally include `#[payable]` to accept ETH
+- Only one `#[fallback]` function per contract
+
+### `#[selector]`
+
+Customizes the Solidity function selector:
+
+```rust
+#[public]
+impl Token {
+ // Use a custom name in the ABI
+ #[selector(name = "balanceOf")]
+ pub fn get_balance(&self, account: Address) -> U256 {
+ self.balances.get(account)
+ }
+
+ // Explicitly set the 4-byte selector
+ #[selector(bytes = "0x70a08231")]
+ pub fn balance_of_custom(&self, account: Address) -> U256 {
+ self.balances.get(account)
+ }
+}
+```
+
+This is useful for:
+
+- Matching existing Solidity interfaces exactly
+- Avoiding naming conflicts
+- Implementing multiple methods with the same name but different selectors
+
+## Contract trait-based composition
+
+Stylus supports code reuse via trait-based composition.
+
+Define reusable functionality as traits and implement them on your contract:
+
+```rust
+use stylus_sdk::prelude::*;
+use alloy_primitives::{Address, U256};
+
+// Define interface traits
+#[public]
+trait IOwnable {
+ fn owner(&self) -> Address;
+ fn transfer_ownership(&mut self, new_owner: Address) -> bool;
+}
+
+#[public]
+trait IErc20 {
+ fn name(&self) -> String;
+ fn symbol(&self) -> String;
+ fn balance_of(&self, account: Address) -> U256;
+ fn transfer(&mut self, to: Address, value: U256) -> bool;
+}
+
+// Define storage components
+#[storage]
+struct Ownable {
+ owner: StorageAddress,
+}
+
+#[storage]
+struct Erc20 {
+ balances: StorageMap,
+}
+
+// Compose into main contract
+#[storage]
+#[entrypoint]
+struct MyToken {
+ ownable: Ownable,
+ erc20: Erc20,
+}
+
+// Declare which interfaces this contract implements
+#[public]
+#[implements(IOwnable, IErc20)]
+impl MyToken {}
+
+// Implement each trait
+#[public]
+impl IOwnable for MyToken {
+ fn owner(&self) -> Address {
+ self.ownable.owner.get()
+ }
+
+ fn transfer_ownership(&mut self, new_owner: Address) -> bool {
+ let caller = self.vm().msg_sender();
+ if caller != self.ownable.owner.get() {
+ return false;
+ }
+ self.ownable.owner.set(new_owner);
+ true
+ }
+}
+
+#[public]
+impl IErc20 for MyToken {
+ fn name(&self) -> String {
+ "MyToken".into()
+ }
+
+ fn symbol(&self) -> String {
+ "MTK".into()
+ }
+
+ fn balance_of(&self, account: Address) -> U256 {
+ self.erc20.balances.get(account)
+ }
+
+ fn transfer(&mut self, to: Address, value: U256) -> bool {
+ let from = self.vm().msg_sender();
+ let from_balance = self.erc20.balances.get(from);
+ if from_balance < value {
+ return false;
+ }
+ self.erc20.balances.setter(from).set(from_balance - value);
+ let to_balance = self.erc20.balances.get(to);
+ self.erc20.balances.setter(to).set(to_balance + value);
+ true
+ }
+}
+```
+
+**Benefits:**
+
+- Clear separation of concerns
+- Explicit interface declarations
+- Type-safe composition
+- Easy to test components independently
+- Compatible with Solidity interface standards
+
+### Accessing VM Context
+
+All public methods can access blockchain context via `self.vm()`:
+
+```rust
+#[public]
+impl MyContract {
+ pub fn get_caller_info(&self) -> (Address, U256, U256) {
+ let vm = self.vm();
+ (
+ vm.msg_sender(), // Caller's address
+ vm.msg_value(), // ETH sent with call
+ vm.block_number(), // Current block number
+ )
+ }
+}
+```
+
+See the [Global Variables and Functions](./global-variables-and-functions.mdx) documentation for a complete list of available VM methods.
+
+## Events
+
+Events allow contracts to log data to the blockchain, enabling off-chain monitoring and indexing.
+
+### Defining Events
+
+Use the `sol!` macro to define events with Solidity-compatible signatures:
+
+```rust
+use alloy_sol_types::sol;
+use alloy_primitives::{Address, U256};
+
+sol! {
+ // Up to 3 parameters can be indexed
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event Approval(address indexed owner, address indexed spender, uint256 value);
+ event DataUpdated(string indexed key, bytes data);
+}
+```
+
+**Indexed parameters:**
+
+- Allow filtering events by that parameter
+- Limited to 3 indexed parameters per event
+- Indexed parameters are stored in log topics, not data
+
+### Emitting Events
+
+Use `self.vm().log()` to emit events:
+
+```rust
+#[public]
+impl Token {
+ pub fn transfer(&mut self, to: Address, value: U256) -> bool {
+ let from = self.vm().msg_sender();
+
+ // Transfer logic...
+ let from_balance = self.balances.get(from);
+ if from_balance < value {
+ return false;
+ }
+ self.balances.setter(from).set(from_balance - value);
+ self.balances.setter(to).set(self.balances.get(to) + value);
+
+ // Emit event
+ self.vm().log(Transfer { from, to, value });
+
+ true
+ }
+}
+```
+
+### Raw Log Emission
+
+For advanced use cases, emit raw logs directly:
+
+```rust
+use alloy_primitives::FixedBytes;
+
+#[public]
+impl Contract {
+ pub fn emit_raw_log(&self) {
+ let user = Address::from([0x22; 20]);
+ let balance = U256::from(1000);
+
+ // Topics (up to 4, must be FixedBytes<32>)
+ let topics = &[user.into_word()];
+
+ // Data (arbitrary bytes)
+ let mut data: Vec = vec![];
+ data.extend_from_slice(&balance.to_be_bytes::<32>());
+
+ self.vm().raw_log(topics, &data).unwrap();
+ }
+}
+```
+
+## External Contract Calls
+
+Stylus contracts can call other contracts (Solidity or Stylus) using typed interfaces or raw calls.
+
+### Defining Contract Interfaces
+
+Use `sol_interface!` to define interfaces for external contracts:
+
+```rust
+use stylus_sdk::prelude::*;
+
+sol_interface! {
+ interface IToken {
+ function balanceOf(address account) external view returns (uint256);
+ function transfer(address to, uint256 amount) external returns (bool);
+ }
+
+ interface IOracle {
+ function getPrice() external view returns (uint256);
+ }
+}
+```
+
+### Calling External Contracts
+
+#### View Calls (Read-Only)
+
+```rust
+use stylus_sdk::call::Call;
+
+#[public]
+impl MyContract {
+ pub fn get_token_balance(&self, token: IToken, account: Address) -> U256 {
+ // Call::new() for view calls (no state mutation)
+ let config = Call::new();
+ token.balance_of(self.vm(), config, account).unwrap()
+ }
+}
+```
+
+#### Mutating Calls
+
+```rust
+#[public]
+impl MyContract {
+ pub fn transfer_tokens(&mut self, token: IToken, to: Address, amount: U256) -> bool {
+ // Call::new_mutating(self) for state-changing calls
+ let config = Call::new_mutating(self);
+ token.transfer(self.vm(), config, to, amount).unwrap()
+ }
+}
+```
+
+#### Payable Calls
+
+```rust
+#[public]
+impl MyContract {
+ #[payable]
+ pub fn forward_payment(&mut self, recipient: IPaymentProcessor) -> Result<(), Vec> {
+ // Forward received ETH to another contract
+ let value = self.vm().msg_value();
+ let config = Call::new_payable(self, value);
+
+ recipient.process_payment(self.vm(), config)?;
+ Ok(())
+ }
+}
+```
+
+#### Configuring Gas
+
+```rust
+#[public]
+impl MyContract {
+ pub fn call_with_limited_gas(&mut self, token: IToken, to: Address) -> bool {
+ let config = Call::new_mutating(self)
+ .gas(self.vm().evm_gas_left() / 2); // Use half remaining gas
+
+ token.transfer(self.vm(), config, to, U256::from(100)).unwrap()
+ }
+}
+```
+
+### Low-Level Calls
+
+For maximum flexibility, use raw calls:
+
+```rust
+use stylus_sdk::call::{call, static_call, RawCall};
+
+#[public]
+impl MyContract {
+ // Low-level call (state-changing)
+ pub fn execute_call(&mut self, target: Address, calldata: Vec) -> Result, Vec> {
+ let config = Call::new_mutating(self)
+ .gas(self.vm().evm_gas_left());
+
+ call(self.vm(), config, target, &calldata)
+ }
+
+ // Static call (read-only)
+ pub fn execute_static_call(&self, target: Address, calldata: Vec) -> Result, Vec> {
+ static_call(self.vm(), Call::new(), target, &calldata)
+ }
+
+ // Unsafe raw call with advanced options
+ pub fn execute_raw_call(&mut self, target: Address, calldata: Vec) -> Result, Vec> {
+ unsafe {
+ RawCall::new_delegate(self.vm())
+ .gas(2100)
+ .limit_return_data(0, 32)
+ .flush_storage_cache()
+ .call(target, &calldata)
+ }
+ }
+}
+```
+
+**Call Types:**
+
+- `call()`: State-changing call to another contract
+- `static_call()`: Read-only call (equivalent to Solidity `staticcall`)
+- `RawCall`: Low-level unsafe calls with fine-grained control
+
+## Error Handling
+
+Stylus contracts can define and return custom errors using Solidity-compatible error types.
+
+### Defining Errors
+
+```rust
+use alloy_sol_types::sol;
+
+sol! {
+ error Unauthorized();
+ error InsufficientBalance(address from, uint256 have, uint256 want);
+ error InvalidAddress(address addr);
+}
+
+#[derive(SolidityError)]
+pub enum TokenError {
+ Unauthorized(Unauthorized),
+ InsufficientBalance(InsufficientBalance),
+ InvalidAddress(InvalidAddress),
+}
+```
+
+### Using Errors in Methods
+
+```rust
+#[public]
+impl Token {
+ pub fn transfer(&mut self, to: Address, amount: U256) -> Result {
+ let from = self.vm().msg_sender();
+
+ if to == Address::ZERO {
+ return Err(TokenError::InvalidAddress(InvalidAddress { addr: to }));
+ }
+
+ let balance = self.balances.get(from);
+ if balance < amount {
+ return Err(TokenError::InsufficientBalance(InsufficientBalance {
+ from,
+ have: balance,
+ want: amount,
+ }));
+ }
+
+ self.balances.setter(from).set(balance - amount);
+ self.balances.setter(to).set(self.balances.get(to) + amount);
+
+ Ok(true)
+ }
+}
+```
+
+**Error handling notes:**
+
+- Errors automatically encode as Solidity-compatible error data
+- Use `Result` where `E` implements `SolidityError`
+- Error data includes the error signature and parameters
+- Compatible with Solidity `try/catch` blocks
+
+## Complete Example
+
+Here's a complete ERC-20-style token contract demonstrating all major features:
+
+```rust
+#![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
+extern crate alloc;
+
+use alloy_primitives::{Address, U256};
+use alloy_sol_types::sol;
+use stylus_sdk::prelude::*;
+
+// Define events
+sol! {
+ event Transfer(address indexed from, address indexed to, uint256 value);
+ event Approval(address indexed owner, address indexed spender, uint256 value);
+}
+
+// Define errors
+sol! {
+ error InsufficientBalance(address from, uint256 have, uint256 want);
+ error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want);
+ error Unauthorized();
+}
+
+#[derive(SolidityError)]
+pub enum TokenError {
+ InsufficientBalance(InsufficientBalance),
+ InsufficientAllowance(InsufficientAllowance),
+ Unauthorized(Unauthorized),
+}
+
+// Define storage
+sol_storage! {
+ #[entrypoint]
+ pub struct SimpleToken {
+ mapping(address => uint256) balances;
+ mapping(address => mapping(address => uint256)) allowances;
+ uint256 total_supply;
+ address owner;
+ }
+}
+
+#[public]
+impl SimpleToken {
+ // Constructor
+ #[constructor]
+ pub fn constructor(&mut self, initial_supply: U256) {
+ let deployer = self.vm().msg_sender();
+ self.owner.set(deployer);
+ self.balances.setter(deployer).set(initial_supply);
+ self.total_supply.set(initial_supply);
+
+ self.vm().log(Transfer {
+ from: Address::ZERO,
+ to: deployer,
+ value: initial_supply,
+ });
+ }
+
+ // View functions
+ pub fn balance_of(&self, account: Address) -> U256 {
+ self.balances.get(account)
+ }
+
+ pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
+ self.allowances.getter(owner).get(spender)
+ }
+
+ pub fn total_supply(&self) -> U256 {
+ self.total_supply.get()
+ }
+
+ pub fn owner(&self) -> Address {
+ self.owner.get()
+ }
+
+ // Write functions
+ pub fn transfer(&mut self, to: Address, value: U256) -> Result {
+ let from = self.vm().msg_sender();
+ self._transfer(from, to, value)?;
+ Ok(true)
+ }
+
+ pub fn approve(&mut self, spender: Address, value: U256) -> bool {
+ let owner = self.vm().msg_sender();
+ self.allowances.setter(owner).setter(spender).set(value);
+
+ self.vm().log(Approval { owner, spender, value });
+ true
+ }
+
+ pub fn transfer_from(
+ &mut self,
+ from: Address,
+ to: Address,
+ value: U256
+ ) -> Result {
+ let spender = self.vm().msg_sender();
+
+ // Check allowance
+ let current_allowance = self.allowances.getter(from).get(spender);
+ if current_allowance < value {
+ return Err(TokenError::InsufficientAllowance(InsufficientAllowance {
+ owner: from,
+ spender,
+ have: current_allowance,
+ want: value,
+ }));
+ }
+
+ // Update allowance
+ self.allowances.setter(from).setter(spender).set(current_allowance - value);
+
+ // Transfer
+ self._transfer(from, to, value)?;
+ Ok(true)
+ }
+
+ // Owner-only functions
+ pub fn mint(&mut self, to: Address, value: U256) -> Result<(), TokenError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(TokenError::Unauthorized(Unauthorized {}));
+ }
+
+ self.balances.setter(to).set(self.balances.get(to) + value);
+ self.total_supply.set(self.total_supply.get() + value);
+
+ self.vm().log(Transfer {
+ from: Address::ZERO,
+ to,
+ value,
+ });
+
+ Ok(())
+ }
+
+ // Internal helper function
+ fn _transfer(&mut self, from: Address, to: Address, value: U256) -> Result<(), TokenError> {
+ let from_balance = self.balances.get(from);
+ if from_balance < value {
+ return Err(TokenError::InsufficientBalance(InsufficientBalance {
+ from,
+ have: from_balance,
+ want: value,
+ }));
+ }
+
+ self.balances.setter(from).set(from_balance - value);
+ self.balances.setter(to).set(self.balances.get(to) + value);
+
+ self.vm().log(Transfer { from, to, value });
+ Ok(())
+ }
+}
+```
+
+## Best Practices
+
+### 1. Use Appropriate State Mutability
+
+```rust
+// Good: Read-only functions use &self
+pub fn get_balance(&self, account: Address) -> U256 {
+ self.balances.get(account)
+}
+
+// Good: State-changing functions use &mut self
+pub fn set_balance(&mut self, account: Address, balance: U256) {
+ self.balances.setter(account).set(balance);
+}
+```
+
+### 2. Validate Inputs Early
+
+```rust
+pub fn transfer(&mut self, to: Address, amount: U256) -> Result {
+ // Validate inputs first
+ if to == Address::ZERO {
+ return Err(TokenError::InvalidAddress(InvalidAddress { addr: to }));
+ }
+
+ if amount == U256::ZERO {
+ return Ok(true); // Nothing to transfer
+ }
+
+ // Then proceed with logic
+ let from = self.vm().msg_sender();
+ // ...
+}
+```
+
+### 3. Use Custom Errors
+
+```rust
+// Good: Descriptive custom errors
+pub fn withdraw(&mut self, amount: U256) -> Result<(), VaultError> {
+ let balance = self.balances.get(self.vm().msg_sender());
+ if balance < amount {
+ return Err(VaultError::InsufficientBalance(InsufficientBalance {
+ have: balance,
+ want: amount,
+ }));
+ }
+ // ...
+}
+
+// Avoid: Generic Vec errors
+pub fn withdraw(&mut self, amount: U256) -> Result<(), Vec> {
+ // Less informative
+}
+```
+
+### 4. Emit Events for State Changes
+
+```rust
+pub fn update_value(&mut self, new_value: U256) {
+ let old_value = self.value.get();
+ self.value.set(new_value);
+
+ // Always emit events for important state changes
+ self.vm().log(ValueUpdated {
+ old_value,
+ new_value,
+ });
+}
+```
+
+### 5. Access Control Patterns
+
+```rust
+// Good: Clear access control checks
+pub fn admin_function(&mut self) -> Result<(), TokenError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(TokenError::Unauthorized(Unauthorized {}));
+ }
+ // Admin logic...
+ Ok(())
+}
+
+// Consider: Reusable modifier-like helper
+impl Token {
+ fn only_owner(&self) -> Result<(), TokenError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(TokenError::Unauthorized(Unauthorized {}));
+ }
+ Ok(())
+ }
+
+ pub fn admin_function(&mut self) -> Result<(), TokenError> {
+ self.only_owner()?;
+ // Admin logic...
+ Ok(())
+ }
+}
+```
+
+### 6. Gas-Efficient Storage Access
+
+```rust
+// Good: Read once, use multiple times
+pub fn complex_calculation(&self, account: Address) -> U256 {
+ let balance = self.balances.get(account); // Read once
+ let result = balance * U256::from(2) + balance / U256::from(10);
+ result
+}
+
+// Avoid: Multiple reads of same storage slot
+pub fn inefficient_calculation(&self, account: Address) -> U256 {
+ self.balances.get(account) * U256::from(2) + self.balances.get(account) / U256::from(10)
+}
+```
+
+### 7. Check Effects Interactions Pattern
+
+```rust
+// Good: Check-Effects-Interactions pattern
+pub fn withdraw(&mut self, amount: U256) -> Result<(), VaultError> {
+ let caller = self.vm().msg_sender();
+
+ // Checks
+ let balance = self.balances.get(caller);
+ if balance < amount {
+ return Err(VaultError::InsufficientBalance(InsufficientBalance {
+ have: balance,
+ want: amount,
+ }));
+ }
+
+ // Effects (update state BEFORE external calls)
+ self.balances.setter(caller).set(balance - amount);
+
+ // Interactions (external calls last)
+ // self.transfer_eth(caller, amount)?;
+
+ Ok(())
+}
+```
+
+### 8. Use Type-Safe Interfaces for External Calls
+
+```rust
+// Good: Use sol_interface! for type safety
+sol_interface! {
+ interface IToken {
+ function transfer(address to, uint256 amount) external returns (bool);
+ }
+}
+
+pub fn call_token(&mut self, token: IToken, to: Address, amount: U256) -> bool {
+ let config = Call::new_mutating(self);
+ token.transfer(self.vm(), config, to, amount).unwrap()
+}
+
+// Avoid: Raw calls unless necessary
+pub fn raw_call(&mut self, token: Address, to: Address, amount: U256) -> Vec {
+ // Less type-safe, more error-prone
+ let config = Call::new_mutating(self);
+ let calldata = /* manually construct */;
+ call(self.vm(), config, token, &calldata).unwrap()
+}
+```
+
+## Delegate calls
+
+Delegate calls allow a contract to execute code from another contract while maintaining its own context. When Contract A executes a delegate call to Contract B, B's code runs using Contract A's storage, `msg.sender`, and `msg.value`. This means any state changes affect Contract A, and the original sender and value of the transaction are preserved.
+
+This pattern is essential for building upgradeable contracts, proxy patterns, and modular smart contract systems.
+
+### Using the low-level `delegate_call` function
+
+The `delegate_call` function is a low-level operation similar to `call` and `static_call`. It is considered unsafe because it requires trusting the external contract to maintain safety.
+
+```rust
+pub unsafe fn delegate_call(
+ context: impl MutatingCallContext,
+ to: Address,
+ data: &[u8],
+) -> Result, Error>
+```
+
+**Example usage:**
+
+```rust
+use stylus_sdk::call::delegate_call;
+
+pub fn low_level_delegate_call(
+ &mut self,
+ calldata: Vec,
+ target: Address,
+) -> Result, DelegateCallErrors> {
+ unsafe {
+ let result = delegate_call(self, target, &calldata)
+ .map_err(|_| DelegateCallErrors::DelegateCallFailed(DelegateCallFailed {}))?;
+ Ok(result)
+ }
+}
+```
+
+### Using `RawCall` with `new_delegate()`
+
+For scenarios requiring untyped calls with more configuration options, `RawCall` offers a fluent interface. You can set up a delegate call by chaining optional configuration methods.
+
+```rust
+use stylus_sdk::call::RawCall;
+
+pub fn raw_delegate_call(
+ &mut self,
+ calldata: Vec,
+ target: Address,
+) -> Result, Vec> {
+ let data = RawCall::new_delegate() // Configure a delegate call
+ .gas(2100) // Supply 2100 gas
+ .limit_return_data(0, 32) // Only read the first 32 bytes back
+ .call(target, &calldata)?;
+
+ Ok(data)
+}
+```
+
+### Safety considerations
+
+:::caution
+Delegate calls are inherently unsafe and require careful consideration before use.
+:::
+
+- **Trust requirement**: The calling contract must trust the external contract to uphold safety requirements
+- **Storage modification**: The external contract can arbitrarily change the calling contract's storage
+- **Ether spending**: The external contract may spend ether or perform other critical operations on behalf of the caller
+- **Cache clearing**: While the `delegate_call` function clears any cached values, it cannot prevent unsafe actions by the external contract
+
+### Complete delegate call example
+
+```rust
+#![cfg_attr(not(feature = "export-abi"), no_main)]
+extern crate alloc;
+
+use alloy_sol_types::sol;
+use stylus_sdk::{
+ alloy_primitives::Address,
+ call::{delegate_call, RawCall},
+ prelude::*,
+};
+
+#[storage]
+#[entrypoint]
+pub struct DelegateExample;
+
+sol! {
+ error DelegateCallFailed();
+}
+
+#[derive(SolidityError)]
+pub enum DelegateCallErrors {
+ DelegateCallFailed(DelegateCallFailed),
+}
+
+#[public]
+impl DelegateExample {
+ // Low-level delegate call
+ pub fn low_level_delegate_call(
+ &mut self,
+ calldata: Vec,
+ target: Address,
+ ) -> Result, DelegateCallErrors> {
+ unsafe {
+ let result = delegate_call(self, target, &calldata)
+ .map_err(|_| DelegateCallErrors::DelegateCallFailed(DelegateCallFailed {}))?;
+
+ Ok(result)
+ }
+ }
+
+ // RawCall delegate call with configuration
+ pub fn raw_delegate_call(
+ &mut self,
+ calldata: Vec,
+ target: Address,
+ ) -> Result, Vec> {
+ let data = RawCall::new_delegate()
+ .gas(2100)
+ .limit_return_data(0, 32)
+ .call(target, &calldata)?;
+
+ Ok(data)
+ }
+}
+```
+
+## Sending ether
+
+Stylus provides multiple ways to send ether from a contract. Unlike Solidity's `transfer` method which is capped at 2300 gas, Stylus's `transfer_eth` forwards all gas to the recipient. You can cap gas using the low-level `call` method if needed.
+
+### Methods for sending ether
+
+| Method | Description | Gas behavior |
+| ------------------------ | --------------------------------------- | ------------------------------- |
+| `transfer_eth()` | Simple ether transfer | Forwards all gas |
+| `call()` with `.value()` | Low-level call with value | Forwards all gas (configurable) |
+| Payable external calls | Call payable methods on other contracts | Forwards all gas (configurable) |
+
+### Using `transfer_eth()`
+
+The simplest way to send ether:
+
+```rust
+use stylus_sdk::call::transfer_eth;
+
+#[public]
+impl SendEther {
+ #[payable]
+ pub fn send_via_transfer(to: Address) -> Result<(), Vec> {
+ transfer_eth(to, msg::value())?;
+ Ok(())
+ }
+}
+```
+
+### Using low-level `call()` with value
+
+For more control over the transfer:
+
+```rust
+use stylus_sdk::call::{call, Call};
+
+#[public]
+impl SendEther {
+ #[payable]
+ pub fn send_via_call(&mut self, to: Address) -> Result<(), Vec> {
+ call(Call::new_in(self).value(msg::value()), to, &[])?;
+ Ok(())
+ }
+}
+```
+
+These two approaches are equivalent under the hood:
+
+```rust
+// These are equivalent:
+transfer_eth(recipient, value)?;
+call(Call::new_in(self).value(value), recipient, &[])?;
+```
+
+### Sending with a gas limit
+
+To cap the gas forwarded to the recipient (similar to Solidity's `transfer`):
+
+```rust
+#[payable]
+pub fn send_via_call_gas_limit(&mut self, to: Address, gas_amount: u64) -> Result<(), Vec> {
+ call(
+ Call::new_in(self).value(msg::value()).gas(gas_amount),
+ to,
+ &[],
+ )?;
+ Ok(())
+}
+```
+
+### Sending with calldata
+
+To trigger a fallback function on the receiving contract:
+
+```rust
+use stylus_sdk::abi::Bytes;
+
+#[payable]
+pub fn send_via_call_with_calldata(
+ &mut self,
+ to: Address,
+ data: Bytes,
+) -> Result<(), Vec> {
+ call(Call::new_in(self).value(msg::value()), to, data.as_slice())?;
+ Ok(())
+}
+```
+
+### Sending to payable contract methods
+
+Use typed interfaces to call payable methods on other contracts:
+
+```rust
+sol_interface! {
+ interface ITarget {
+ function receiveEther() external payable;
+ }
+}
+
+#[public]
+impl SendEther {
+ #[payable]
+ pub fn send_to_contract(&mut self, to: Address) -> Result<(), Vec> {
+ let target = ITarget::new(to);
+ let config = Call::new_in(self).value(msg::value());
+ target.receive_ether(config)?;
+ Ok(())
+ }
+}
+```
+
+### Where you can send ether
+
+1. **Externally owned account (EOA) addresses**: Directly send ether to any EOA address
+2. **Solidity contracts with `receive()` function**: Send ether without calldata to contracts implementing `receive()`
+3. **Solidity contracts with `fallback()` function**: Send ether with calldata to contracts implementing `fallback()`
+4. **Contracts with payable methods**: Call any payable method on Solidity or Stylus contracts
+
+### Complete sending ether example
+
+```rust
+#![cfg_attr(not(any(feature = "export-abi", test)), no_main)]
+extern crate alloc;
+
+use alloy_primitives::Address;
+use stylus_sdk::{
+ abi::Bytes,
+ call::{call, transfer_eth, Call},
+ msg,
+ prelude::*,
+};
+
+sol_interface! {
+ interface ITarget {
+ function receiveEther() external payable;
+ }
+}
+
+#[storage]
+#[entrypoint]
+pub struct SendEther;
+
+#[public]
+impl SendEther {
+ // Simple transfer
+ #[payable]
+ pub fn send_via_transfer(to: Address) -> Result<(), Vec> {
+ transfer_eth(to, msg::value())?;
+ Ok(())
+ }
+
+ // Low-level call
+ #[payable]
+ pub fn send_via_call(&mut self, to: Address) -> Result<(), Vec> {
+ call(Call::new_in(self).value(msg::value()), to, &[])?;
+ Ok(())
+ }
+
+ // With gas limit
+ #[payable]
+ pub fn send_via_call_gas_limit(&mut self, to: Address, gas_amount: u64) -> Result<(), Vec> {
+ call(
+ Call::new_in(self).value(msg::value()).gas(gas_amount),
+ to,
+ &[],
+ )?;
+ Ok(())
+ }
+
+ // With calldata (triggers fallback)
+ #[payable]
+ pub fn send_via_call_with_calldata(
+ &mut self,
+ to: Address,
+ data: Bytes,
+ ) -> Result<(), Vec> {
+ call(Call::new_in(self).value(msg::value()), to, data.as_slice())?;
+ Ok(())
+ }
+
+ // To payable contract method
+ #[payable]
+ pub fn send_to_contract(&mut self, to: Address) -> Result<(), Vec> {
+ let target = ITarget::new(to);
+ let config = Call::new_in(self).value(msg::value());
+ target.receive_ether(config)?;
+ Ok(())
+ }
+}
+```
+
+## Factory contract deployment (coming soon)
+
+The factory pattern allows a contract to deploy other contracts programmatically. This is useful for creating contract instances on-demand, such as deploying new token contracts or creating user-specific vaults.
+
+:::note
+Advanced deployment patterns documentation is in development. This section will cover:
+
+- Deploying contracts from within a contract
+- Passing constructor arguments
+- Deterministic deployment with CREATE2
+- Handling deployment failures
+ :::
+
+**Constructor considerations for factory-deployed contracts:**
+
+When a contract is deployed via a factory contract (rather than directly by an EOA), the `msg_sender()` in the constructor will be the factory contract's address, not the original deployer. If you need the original deployer's address, use `tx_origin()` instead:
+
+```rust
+#[public]
+impl FactoryDeployedContract {
+ #[constructor]
+ pub fn constructor(&mut self) {
+ // msg_sender() = factory contract address
+ // tx_origin() = original transaction sender (EOA)
+ let original_deployer = self.vm().tx_origin();
+ self.owner.set(original_deployer);
+ }
+}
+```
+
+## Function modifiers and access control patterns
+
+Unlike Solidity, Rust does not have built-in modifier syntax. However, you can achieve similar functionality using helper functions that return `Result<(), Error>` combined with the `?` operator.
+
+### Basic modifier pattern
+
+Create helper functions that perform checks and return early on failure:
+
+```rust
+sol! {
+ error Unauthorized();
+ error Paused();
+}
+
+#[derive(SolidityError)]
+pub enum ContractError {
+ Unauthorized(Unauthorized),
+ Paused(Paused),
+}
+
+#[public]
+impl MyContract {
+ // Modifier-like helper function
+ fn only_owner(&self) -> Result<(), ContractError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(ContractError::Unauthorized(Unauthorized {}));
+ }
+ Ok(())
+ }
+
+ // Using the "modifier" with the ? operator
+ pub fn admin_function(&mut self) -> Result<(), ContractError> {
+ self.only_owner()?; // Returns early if check fails
+
+ // Admin logic here...
+ Ok(())
+ }
+}
+```
+
+### Multiple guard functions
+
+You can combine multiple checks by chaining helper functions:
+
+```rust
+#[public]
+impl MyContract {
+ fn only_owner(&self) -> Result<(), ContractError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(ContractError::Unauthorized(Unauthorized {}));
+ }
+ Ok(())
+ }
+
+ fn when_not_paused(&self) -> Result<(), ContractError> {
+ if self.paused.get() {
+ return Err(ContractError::Paused(Paused {}));
+ }
+ Ok(())
+ }
+
+ fn only_after(&self, timestamp: u64) -> Result<(), ContractError> {
+ if self.vm().block_timestamp() < timestamp {
+ return Err(ContractError::TooEarly(TooEarly {}));
+ }
+ Ok(())
+ }
+
+ // Combining multiple "modifiers"
+ pub fn protected_action(&mut self) -> Result<(), ContractError> {
+ self.only_owner()?;
+ self.when_not_paused()?;
+ self.only_after(self.unlock_time.get())?;
+
+ // Protected logic here...
+ Ok(())
+ }
+}
+```
+
+### Reusable access control module
+
+For larger projects, encapsulate access control in a reusable module:
+
+```rust
+// Access control helpers
+impl MyContract {
+ fn require_role(&self, role: FixedBytes<32>, account: Address) -> Result<(), ContractError> {
+ if !self.has_role(role, account) {
+ return Err(ContractError::MissingRole(MissingRole { role, account }));
+ }
+ Ok(())
+ }
+
+ fn has_role(&self, role: FixedBytes<32>, account: Address) -> bool {
+ self.roles.getter(role).get(account)
+ }
+
+ // Grant role (admin only)
+ pub fn grant_role(
+ &mut self,
+ role: FixedBytes<32>,
+ account: Address,
+ ) -> Result<(), ContractError> {
+ self.require_role(self.admin_role(), self.vm().msg_sender())?;
+ self.roles.setter(role).setter(account).set(true);
+ Ok(())
+ }
+}
+```
+
+### Complete access control example
+
+```rust
+#![cfg_attr(not(any(test, feature = "export-abi")), no_main)]
+extern crate alloc;
+
+use alloy_primitives::{Address, U256};
+use alloy_sol_types::sol;
+use stylus_sdk::prelude::*;
+
+sol! {
+ error Unauthorized();
+ error Paused();
+ error InvalidAmount();
+}
+
+#[derive(SolidityError)]
+pub enum VaultError {
+ Unauthorized(Unauthorized),
+ Paused(Paused),
+ InvalidAmount(InvalidAmount),
+}
+
+sol_storage! {
+ #[entrypoint]
+ pub struct Vault {
+ address owner;
+ bool paused;
+ mapping(address => uint256) balances;
+ }
+}
+
+#[public]
+impl Vault {
+ // Modifier-like helpers
+ fn only_owner(&self) -> Result<(), VaultError> {
+ if self.vm().msg_sender() != self.owner.get() {
+ return Err(VaultError::Unauthorized(Unauthorized {}));
+ }
+ Ok(())
+ }
+
+ fn when_not_paused(&self) -> Result<(), VaultError> {
+ if self.paused.get() {
+ return Err(VaultError::Paused(Paused {}));
+ }
+ Ok(())
+ }
+
+ fn valid_amount(&self, amount: U256) -> Result<(), VaultError> {
+ if amount == U256::ZERO {
+ return Err(VaultError::InvalidAmount(InvalidAmount {}));
+ }
+ Ok(())
+ }
+
+ // Public functions using modifiers
+ #[payable]
+ pub fn deposit(&mut self) -> Result<(), VaultError> {
+ self.when_not_paused()?;
+
+ let sender = self.vm().msg_sender();
+ let amount = self.vm().msg_value();
+ self.valid_amount(amount)?;
+
+ let current = self.balances.get(sender);
+ self.balances.setter(sender).set(current + amount);
+ Ok(())
+ }
+
+ pub fn pause(&mut self) -> Result<(), VaultError> {
+ self.only_owner()?;
+ self.paused.set(true);
+ Ok(())
+ }
+
+ pub fn unpause(&mut self) -> Result<(), VaultError> {
+ self.only_owner()?;
+ self.paused.set(false);
+ Ok(())
+ }
+}
+```
+
+## See Also
+
+- [Primitives](./data-types/primitives.mdx) - Basic data types
+- [Compound Types](./data-types/compound-types.mdx) - Arrays, structs, tuples
+- [Storage Types](./data-types/storage.mdx) - Persistent storage
+- [Global Variables and Functions](./global-variables-and-functions.mdx) - VM context methods
diff --git a/docs/stylus/fundamentals/data-types/compound-types.mdx b/docs/stylus/fundamentals/data-types/compound-types.mdx
new file mode 100644
index 0000000000..6cef7b8c79
--- /dev/null
+++ b/docs/stylus/fundamentals/data-types/compound-types.mdx
@@ -0,0 +1,845 @@
+---
+title: 'Stylus compound types'
+description: 'Stylus Rust SDK compound types'
+author: chrisco
+sme: chrisco
+sidebar_position: 2
+target_audience: Developers using the Stylus Rust SDK to write and deploy smart contracts.
+displayed_sidebar: buildStylusSidebar
+---
+
+Compound types allow you to group multiple values together in Stylus contracts. The SDK provides full support for tuples, structs, arrays, and vectors with automatic ABI encoding/decoding and Solidity type mappings.
+
+## Tuples
+
+Tuples group multiple values of different types together. They map directly to Solidity tuples.
+
+### Basic tuples
+
+```rust
+use alloy_primitives::{Address, U256, Bytes};
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ // Return multiple values as a tuple
+ pub fn get_data(&self) -> (U256, Address, bool) {
+ (U256::from(100), Address::ZERO, true)
+ }
+
+ // Accept tuple as parameter
+ pub fn process_tuple(&mut self, data: (U256, U256, U256)) -> U256 {
+ let (a, b, c) = data;
+ a + b + c
+ }
+
+ // Nested tuples
+ pub fn nested(&self) -> ((U256, U256), bool) {
+ ((U256::from(1), U256::from(2)), true)
+ }
+}
+```
+
+### Tuple destructuring
+
+```rust
+use alloy_primitives::U256;
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ pub fn calculate(&self) -> (U256, U256) {
+ let values = (U256::from(100), U256::from(200));
+
+ // Destructure the tuple
+ let (first, second) = values;
+
+ // Return new tuple
+ (first * U256::from(2), second * U256::from(2))
+ }
+
+ // Pattern matching with tuples
+ pub fn match_tuple(&self, data: (bool, U256)) -> U256 {
+ match data {
+ (true, value) => value * U256::from(2),
+ (false, value) => value,
+ }
+ }
+}
+```
+
+### Tuple type mappings
+
+| Rust Type | Solidity Type | ABI Signature |
+| ---------------------- | ---------------------------- | ---------------------------- |
+| `(U256,)` | `(uint256)` | `"(uint256)"` |
+| `(U256, Address)` | `(uint256, address)` | `"(uint256,address)"` |
+| `(bool, U256, Bytes)` | `(bool, uint256, bytes)` | `"(bool,uint256,bytes)"` |
+| `((U256, U256), bool)` | `((uint256, uint256), bool)` | `"((uint256,uint256),bool)"` |
+
+**Tuple Limitations**:
+
+- Tuples support up to 24 elements
+- Tuples are always returned as `memory` in Solidity
+- Empty tuple `()` represents no return value
+
+## Structs
+
+Structs define custom data types with named fields. Use the `sol!` macro to define Solidity-compatible structs.
+
+### Defining structs with `sol!`
+
+```rust
+use alloy_primitives::{Address, U256};
+use alloy_sol_types::sol;
+use stylus_sdk::prelude::*;
+
+sol! {
+ #[derive(Debug, AbiType)]
+ struct User {
+ address account;
+ uint256 balance;
+ string name;
+ }
+
+ #[derive(Debug, AbiType)]
+ struct Token {
+ string name;
+ string symbol;
+ uint8 decimals;
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn get_user(&self) -> User {
+ User {
+ account: Address::ZERO,
+ balance: U256::from(1000),
+ name: "Alice".to_string(),
+ }
+ }
+
+ pub fn process_user(&mut self, user: User) -> U256 {
+ // Access struct fields
+ user.balance
+ }
+
+ pub fn get_token_info(&self) -> Token {
+ Token {
+ name: "MyToken".to_string(),
+ symbol: "MTK".to_string(),
+ decimals: 18,
+ }
+ }
+}
+```
+
+### Nested structs
+
+Structs can contain other structs, enabling complex data structures:
+
+```rust
+use alloy_primitives::Address;
+use alloy_sol_types::sol;
+use stylus_sdk::prelude::*;
+
+sol! {
+ #[derive(Debug, AbiType)]
+ struct Dog {
+ string name;
+ string breed;
+ }
+
+ #[derive(Debug, AbiType)]
+ struct User {
+ address account;
+ string name;
+ Dog[] dogs;
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn create_user(&self) -> User {
+ let dogs = vec![
+ Dog {
+ name: "Rex".to_string(),
+ breed: "Labrador".to_string(),
+ },
+ Dog {
+ name: "Max".to_string(),
+ breed: "Beagle".to_string(),
+ },
+ ];
+
+ User {
+ account: Address::ZERO,
+ name: "Alice".to_string(),
+ dogs,
+ }
+ }
+
+ pub fn get_dog_count(&self, user: User) -> u256 {
+ user.dogs.len() as u256
+ }
+}
+```
+
+### Struct best practices
+
+1. **Always use `#[derive(AbiType)]`** for structs that will be used in contract interfaces:
+
+ ```rust
+ sol! {
+ #[derive(Debug, AbiType)]
+ struct MyData {
+ uint256 value;
+ address owner;
+ }
+ }
+ ```
+
+2. **Add `Debug` derive** for easier debugging:
+
+ ```rust
+ sol! {
+ #[derive(Debug, AbiType)]
+ struct Config {
+ bool enabled;
+ uint256 timeout;
+ }
+ }
+ ```
+
+3. **Use descriptive field names** that match Solidity conventions:
+ ```rust
+ sol! {
+ #[derive(Debug, AbiType)]
+ struct VestingSchedule {
+ address beneficiary;
+ uint256 startTime;
+ uint256 cliffDuration;
+ uint256 totalAmount;
+ }
+ }
+ ```
+
+## Arrays
+
+Arrays are fixed-size collections of elements. Stylus supports both Rust arrays and Solidity-style arrays.
+
+### Fixed-size arrays
+
+```rust
+use alloy_primitives::U256;
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ // Return a fixed-size array
+ pub fn get_numbers(&self) -> [U256; 5] {
+ [
+ U256::from(1),
+ U256::from(2),
+ U256::from(3),
+ U256::from(4),
+ U256::from(5),
+ ]
+ }
+
+ // Accept fixed-size array as parameter
+ pub fn sum_array(&self, numbers: [U256; 5]) -> U256 {
+ numbers.iter().fold(U256::ZERO, |acc, &x| acc + x)
+ }
+
+ // Nested arrays
+ pub fn matrix(&self) -> [[u32; 2]; 3] {
+ [[1, 2], [3, 4], [5, 6]]
+ }
+}
+```
+
+### Array operations
+
+```rust
+use alloy_primitives::{Address, U256};
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ // Iterate over array
+ pub fn process_addresses(&self, addresses: [Address; 10]) -> U256 {
+ let mut count = U256::ZERO;
+ for addr in addresses.iter() {
+ if *addr != Address::ZERO {
+ count += U256::from(1);
+ }
+ }
+ count
+ }
+
+ // Array of booleans
+ pub fn check_flags(&self, flags: [bool; 8]) -> bool {
+ flags.iter().all(|&f| f)
+ }
+}
+```
+
+### Array type mappings
+
+| Rust Type | Solidity Type | Description |
+| --------------------- | -------------- | ------------------------- |
+| `[U256; 5]` | `uint256[5]` | 5-element uint256 array |
+| `[bool; 10]` | `bool[10]` | 10-element bool array |
+| `[Address; 3]` | `address[3]` | 3-element address array |
+| `[[u32; 2]; 4]` | `uint32[2][4]` | Nested array (4x2 matrix) |
+| `[FixedBytes<32>; 2]` | `bytes32[2]` | 2-element bytes32 array |
+
+## Vectors
+
+Vectors are dynamic arrays that can grow or shrink at runtime. They map to Solidity dynamic arrays.
+
+### Basic vector usage
+
+```rust
+use alloy_primitives::{Address, U256, Bytes};
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ // Return a vector
+ pub fn get_numbers(&self) -> Vec {
+ vec![U256::from(1), U256::from(2), U256::from(3)]
+ }
+
+ // Accept vector as parameter
+ pub fn sum_vec(&self, numbers: Vec) -> U256 {
+ numbers.iter().fold(U256::ZERO, |acc, x| acc + *x)
+ }
+
+ // Vector of addresses
+ pub fn get_addresses(&self) -> Vec {
+ vec![Address::ZERO, Address::ZERO]
+ }
+
+ // Vector of bytes
+ pub fn get_data_list(&self) -> Vec {
+ vec![
+ Bytes::from(vec![1, 2, 3]),
+ Bytes::from(vec![4, 5, 6]),
+ ]
+ }
+}
+```
+
+### Vector operations
+
+```rust
+use alloy_primitives::U256;
+use stylus_sdk::prelude::*;
+
+#[public]
+impl MyContract {
+ // Filter vector
+ pub fn filter_even(&self, numbers: Vec) -> Vec {
+ numbers
+ .into_iter()
+ .filter(|n| n.byte(0) % 2 == 0)
+ .collect()
+ }
+
+ // Map over vector
+ pub fn double_values(&self, numbers: Vec) -> Vec {
+ numbers
+ .into_iter()
+ .map(|n| n * U256::from(2))
+ .collect()
+ }
+
+ // Find in vector
+ pub fn contains_value(&self, numbers: Vec, target: U256) -> bool {
+ numbers.contains(&target)
+ }
+
+ // Get vector length
+ pub fn get_length(&self, items: Vec) -> U256 {
+ U256::from(items.len())
+ }
+}
+```
+
+### Vectors of structs
+
+```rust
+use alloy_primitives::Address;
+use alloy_sol_types::sol;
+use stylus_sdk::prelude::*;
+
+sol! {
+ #[derive(Debug, AbiType)]
+ struct Transaction {
+ address from;
+ address to;
+ uint256 amount;
+ }
+}
+
+#[public]
+impl MyContract {
+ pub fn get_transactions(&self) -> Vec {
+ vec![
+ Transaction {
+ from: Address::ZERO,
+ to: Address::ZERO,
+ amount: U256::from(100),
+ },
+ Transaction {
+ from: Address::ZERO,
+ to: Address::ZERO,
+ amount: U256::from(200),
+ },
+ ]
+ }
+
+ pub fn total_amount(&self, txs: Vec) -> U256 {
+ txs.iter()
+ .fold(U256::ZERO, |acc, tx| acc + tx.amount)
+ }
+}
+```
+
+### Vector type mappings
+
+| Rust Type | Solidity Type | ABI Signature | Storage |
+| --------------- | ------------- | --------------------- | ------- |
+| `Vec` | `uint256[]` | `"uint256[] memory"` | Dynamic |
+| `Vec` | `address[]` | `"address[] memory"` | Dynamic |
+| `Vec` | `bool[]` | `"bool[] memory"` | Dynamic |
+| `Vec` | `bytes[]` | `"bytes[] memory"` | Dynamic |
+| `Vec` | `MyStruct[]` | `"MyStruct[] memory"` | Dynamic |
+
+**Important Notes**:
+
+- Vectors are **always returned as `memory`** in Solidity, never as `calldata`
+- `Vec` maps to `uint8[]`, not `bytes` (use `Bytes` for Solidity `bytes`)
+- Vectors have dynamic size and consume more gas than fixed arrays
+
+## Bytes Types
+
+The SDK provides `Bytes` for dynamic byte arrays and `FixedBytes