Performance comparison of Rust-based Stylus contracts vs their Solidity equivalents on Arbitrum. Measures gas consumption, execution time, and code size across industry-standard contract primitives.
- Milestone 1: Ownable + AccessControl
- Milestone 2: ERC-20 + ERC-721
- Milestone 3: ERC-1155 + ERC-6909
- Milestone 4: ERC-2981 + ERC-1967 Proxy
- Milestone 5: Beacon Proxy + EIP-712
- Milestone 6 contracts: ECDSA + Token Vesting (final comprehensive report pending)
contracts/
solidity/
src/access/ Ownable.sol, AccessControl.sol
src/token/ ERC20.sol, ERC721.sol, ERC1155.sol, ERC6909.sol
src/common/ ERC2981.sol
src/proxy/ ERC1967Proxy.sol (+ CounterLogic), BeaconProxy.sol (+ UpgradeableBeacon)
src/utils/ EIP712.sol, ECDSA.sol
src/finance/ VestingWallet.sol
test/ correctness tests (*.t.sol) + Benchmark.t.sol (isolated gas/time)
foundry.toml pinned solc 0.8.24, optimizer 200 runs
stylus/
Cargo.toml workspace root
access/ ownable/, access-control/
token/ erc20/, erc721/, erc1155/, erc6909/
common/ erc2981/
proxy/ erc1967/, counter-logic/, upgradeable-beacon/, beacon-proxy/
utils/ eip712/, ecdsa/
finance/ vesting-wallet/
scripts/
benchmark.sh combined Solidity + Stylus benchmark runner
reports/ per-milestone data and findings
Each contract is implemented in both Solidity (OpenZeppelin patterns) and Rust (Stylus SDK). Implementations are functionally equivalent — same interface, same events, same error selectors.
Three metrics per operation:
- Gas: Foundry gas reports (Solidity), transaction receipts via
cast(Stylus on-chain) - Execution time: per-test wall-clock duration from
forge test --json, median of 3 runs - Code size:
forge build --sizes(Solidity runtime bytecode) vswc -con WASM binaries (Stylus, pre-compression)
# Solidity only (no Docker needed)
./scripts/benchmark.sh
# Solidity + Stylus on-chain (requires Nitro devnode)
./scripts/benchmark.sh --styluscd contracts/solidity
forge build
forge test
forge test --gas-reportcd contracts/stylus
cargo build --release --target wasm32-unknown-unknown --workspaceRequires Docker and a local Nitro devnode. ERC-1155 compresses above the default 24 KB EIP-170 limit; start the devnode with a raised contract size:
git clone https://github.com/OffchainLabs/nitro-devnode.git
cd nitro-devnode && ./run-dev-node.sh --contract-size 128000
# then in another terminal:
./scripts/benchmark.sh --stylusOn macOS, Colima with the Apple Virtualization backend works in place of Docker Desktop:
colima start --vm-type=vzUnit tests are in each Rust contract's lib.rs behind #[cfg(test)]. They exercise error variants and validation logic and run on the host with cargo test --workspace --lib. Full storage/interaction tests require motsu (the Stylus unit-test framework), which needs a compatible Rust compiler version and is not currently wired in. End-to-end interaction tests run against the Nitro devnode via ./scripts/benchmark.sh --stylus.
- Foundry (forge, solc 0.8.24)
- Rust +
wasm32-unknown-unknowntarget (rustup target add wasm32-unknown-unknown) - Docker (for Stylus on-chain benchmarks only)