There is a gap in Web3 developer tooling that has always frustrated me. On one side, you have hello-world tutorials that deploy a counter contract and stop there. On the other, you have production codebases with thousands of lines of configuration that took months to evolve. The space in between -- where you need a working dApp with contracts, a frontend, a local chain, and a deployment pipeline -- is where most developers get stuck.
I work in DevRel at Arbitrum, and I kept seeing the same pattern: developers would ask Claude Code to help them build on Arbitrum, and it would produce plausible-looking code that used outdated SDK versions, wrong RPC endpoints, or patterns that simply do not work on Stylus. The model has general Solidity knowledge, but it lacks the specific, opinionated context needed to scaffold a real Arbitrum project from scratch.
So I built a Claude Code skill to fix that.
What Are Claude Code Skills?
Claude Code skills are structured markdown files that you drop into ~/.claude/skills/. When Claude Code starts a session, it reads these files and treats them as domain-specific knowledge. Think of a skill as a system prompt, but modular and version-controlled.
A skill has two parts. First, a SKILL.md file at the root that defines the core knowledge: what tools to use, what decisions to make, and what patterns to follow. Second, a references/ directory containing deeper documentation that Claude loads on demand. This two-tier approach keeps the context window lean -- Claude reads the decision tree up front and only pulls in detailed reference material when the conversation heads in that direction.
The key insight is that skills are not code generators. They do not template out files. Instead, they give Claude the structured knowledge it needs to generate correct code itself, tailored to whatever the developer actually asks for.
Architecture of the Skill
The arbitrum-dapp-skill is organized around a decision tree and six reference documents.
The decision tree lives at the top of SKILL.md and handles the first question any Arbitrum project faces: Stylus Rust, Solidity, or both?
- Need maximum performance and lower gas costs? Route to Stylus Rust, load
references/stylus-rust-contracts.md. - Need broad tooling compatibility and rapid prototyping? Route to Solidity, load
references/solidity-contracts.md. - Hybrid project? Use both. Stylus and Solidity contracts are fully interoperable on Arbitrum -- they share the same address space, storage model, and ABI encoding.
Below the decision tree, the skill defines an opinionated project structure:
my-arbitrum-dapp/
├── apps/
│ ├── frontend/ # React / Next.js app
│ ├── contracts-stylus/ # Rust Stylus contracts
│ ├── contracts-solidity/ # Foundry Solidity contracts
│ └── nitro-devnode/ # Local dev chain (git submodule)
├── pnpm-workspace.yaml
└── package.json
This is a pnpm monorepo. The apps/ directory separates concerns cleanly. The nitro-devnode directory is a git submodule pointing to the official Offchain Labs devnode, which gives you a local Arbitrum chain running in Docker on localhost:8547 with pre-funded accounts ready for deployment.
The six reference documents cover the full development lifecycle:
-
stylus-rust-contracts.md-- Stylus SDK patterns,sol_storage!macros,#[public]methods, events, error handling -
solidity-contracts.md-- Solidity 0.8.x on Arbitrum with Foundry workflows -
frontend-integration.md-- viem + wagmi setup, hydration safety, contract reads and writes -
local-devnode.md-- Docker setup, pre-funded accounts, CORS proxy for browser access -
deployment.md-- Testnet (Arbitrum Sepolia) and mainnet (Arbitrum One) deployment -
testing.md-- Unit testing strategies for both Stylus and Solidity contracts
Why These Tech Choices
Every choice in the stack is deliberate.
Stylus SDK v0.10+ because earlier versions had breaking API differences in storage macros and entrypoint attributes. Pinning to 0.10+ means Claude generates code that actually compiles.
Foundry over Hardhat for Solidity because Foundry's forge is faster, its testing framework uses Solidity itself (no JavaScript test wrappers), and cast provides a clean CLI for chain interaction.
viem over ethers.js because viem provides strict TypeScript types for contract interaction. When Claude generates a readContract call with the wrong function name, TypeScript catches it at compile time rather than failing silently at runtime.
wagmi because it wraps viem in React hooks with built-in state management for wallet connections, transaction confirmations, and contract reads. The skill includes hydration safety patterns for Next.js, which is a common stumbling block.
pnpm because workspaces let the frontend import ABIs directly from the contract directories, and pnpm's strict dependency resolution avoids the phantom dependency issues that plague npm in monorepos.
A Concrete Walkthrough
Here is what it looks like in practice. You install the skill, start Claude Code, and type:
Build me an NFT contract using Stylus Rust with a React frontend that lets users mint tokens.
Claude reads the skill, routes to the Stylus path, and starts scaffolding. It creates the monorepo structure, generates a Stylus contract using sol_storage! for the token state, implements mint and balance_of as #[public] methods, and emits a Transfer event using alloy_sol_types:
sol_storage! {
#[entrypoint]
pub struct NftContract {
mapping(uint256 => address) owners;
mapping(address => uint256) balances;
uint256 total_supply;
}
}
#[public]
impl NftContract {
pub fn mint(&mut self) -> U256 {
let token_id = self.total_supply.get() + U256::from(1);
self.total_supply.set(token_id);
self.owners.setter(token_id).set(msg::sender());
self.balances.setter(msg::sender()).set(
self.balances.get(msg::sender()) + U256::from(1)
);
evm::log(Transfer {
from: Address::ZERO,
to: msg::sender(),
value: token_id,
});
token_id
}
}
On the frontend side, Claude sets up the wagmi provider, creates a mint button using useWriteContract, and wires up a display component with useReadContract -- all with proper hydration guards for Next.js. It exports the ABI from cargo stylus export-abi and places it where the frontend can import it.
The entire project is configured to run against nitro-devnode out of the box. You start Docker, run the devnode script, deploy with cargo stylus deploy, and the frontend connects through a CORS proxy route.
Install It
One command:
bash <(curl -s https://raw.githubusercontent.com/hummusonrails/arbitrum-dapp-skill/main/install.sh)
This clones the skill into ~/.claude/skills/arbitrum-dapp-skill. The next time you start Claude Code, it picks it up automatically. You can also install via ClawHub:
npx clawhub@latest install arbitrum-dapp-skill
Or if you prefer manual installation:
git clone https://github.com/hummusonrails/arbitrum-dapp-skill.git ~/.claude/skills/arbitrum-dapp-skill
To see it in action before installing, there is a demo video on YouTube that walks through the full workflow.
I also wrote a deeper technical thread on X covering the design decisions in more detail.
What's Next
The skill covers the core development lifecycle, but there is more to build. Token bridging patterns between L1 and Arbitrum, integration with Orbit chains, and more advanced Stylus patterns like precompile access are all on the roadmap.
If you are building on Arbitrum and run into a pattern the skill does not cover well, open an issue or submit a PR on the GitHub repo. The whole point of packaging this as a skill rather than a tutorial is that it evolves with the ecosystem. As the Stylus SDK ships new versions, as viem adds new features, the skill updates and every developer who installed it gets the improvements on their next git pull.
Skills are a new primitive for developer tooling. Instead of writing documentation that developers read and then translate into code, you write structured knowledge that an AI agent consumes directly. The developer describes what they want, and the agent applies the knowledge to generate a working implementation. I think this pattern is going to reshape how we think about developer education in Web3 and beyond.
You can find the full source, documentation, and installation instructions at hummusonrails.github.io/arbitrum-dapp-skill.


Top comments (0)