Skip to main content

Command Palette

Search for a command to run...

AI-Assisted Blockchain Development: Building an Arbitrum dApp Scaffold with Claude Code Skills

Published
6 min read
B

I am a second career developer who previously spent a decade in the fields of adult education, community organizing, and non-profit management. I work as a senior developer advocate at Couchbase by day and experiment with open source projects at night. I write regularly on the intersection of community development and tech.

Originally from Southern California and a long-time resident of New York City, I now reside near Tel Aviv.

The tooling gap between AI coding assistants and blockchain development is real. LLMs can generate a React component or a REST endpoint with reasonable accuracy, but ask one to scaffold a Stylus Rust contract with the correct sol_storage! layout, wire up a local nitro-devnode, and configure viem transports with explicit RPC URLs -- and you will spend more time fixing hallucinated APIs than you saved. I built arbitrum-dapp-skill to close that gap for the Arbitrum stack.

This is not a wrapper or a boilerplate generator. It is a Claude Code skill: a structured knowledge layer that loads into Claude's context on demand, giving it accurate, version-pinned reference material for every layer of an Arbitrum dApp. The result is that Claude produces code you can actually compile and deploy without hand-editing import paths and deprecated function signatures.

Why Arbitrum and Stylus

If you are reading this on Hashnode, you probably already know Arbitrum. The piece worth highlighting for this discussion is Stylus. Stylus lets you write smart contracts in Rust (or C/C++) that compile to WASM and execute on Arbitrum alongside Solidity contracts. The contracts share the same address space, the same storage model, and the same ABI encoding. A Solidity contract can call a Stylus contract through a standard interface, and vice versa, with no bridging or special adapters.

The practical implications: compute-heavy logic (cryptographic operations, data transformations, anything that burns gas in the EVM) can be written in Rust and executed on the WASM VM at significantly lower gas costs. You keep your existing Solidity contracts and incrementally move performance-critical paths to Stylus. This dual-language reality is exactly why a skill that understands both paths matters.

Architecture of the Skill

Decision Tree Routing

The skill's entry point is SKILL.md, which contains a decision flow that Claude evaluates before writing any contract code:

  1. Need maximum performance or lower gas? Route to Stylus Rust via references/stylus-rust-contracts.md.
  2. Need broad tooling compatibility or rapid prototyping? Route to Solidity via references/solidity-contracts.md.
  3. Hybrid? Use both. The skill covers cross-contract interop patterns.

This is a deliberate design choice. Without the decision tree, Claude tends to default to Solidity regardless of requirements. The explicit routing forces the model to evaluate the user's constraints before generating code.

On-Demand Reference Loading

The skill ships with six reference documents, but SKILL.md only contains the scaffolding overview and decision flow. Deeper material -- Stylus SDK patterns, Foundry workflows, viem/wagmi integration, devnode setup, deployment procedures, and testing strategies -- lives in separate files that Claude loads when the conversation reaches that layer.

This is a context efficiency decision. Claude Code skills load into the model's context window. Dumping all six references into the main file would consume thousands of tokens before the user even asks a question. The on-demand pattern keeps the initial footprint small and loads specifics only when needed.

Opinionated Defaults

The skill is deliberately opinionated. It specifies stylus-sdk v0.10+, Solidity 0.8.x with Foundry, pnpm workspaces, viem (not ethers.js), wagmi for React hooks, and a specific monorepo layout. These are not arbitrary choices -- they are the combinations I have tested together and know produce correct output from the model. When you give an LLM freedom to choose between ethers v5, ethers v6, and viem, it will happily mix APIs from all three in a single file. Constraining the stack eliminates that failure mode.

The Full Stack: Monorepo Layout

Every project the skill scaffolds follows this 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

The apps/ directory is a pnpm workspace. The devnode runs as a git submodule pointing to OffchainLabs/nitro-devnode. Stylus contracts live in a standalone Cargo project. Solidity contracts live in a Foundry project. The frontend is a Next.js app with viem and wagmi pre-configured.

This layout means each component has its own toolchain and build process, but they share a single workspace root for coordinated development.

Stylus Contracts: What the Skill Teaches Claude

The Stylus reference covers the sol_storage! macro for defining Solidity-compatible storage, the #[public] attribute for ABI-exposed methods, event emission via alloy-sol-types, and cross-contract calls using sol_interface!. Here is what a typical contract looks like when Claude generates it with the skill loaded:

use stylus_sdk::prelude::*;
use alloy_primitives::U256;

sol_storage! {
    #[entrypoint]
    pub struct Counter {
        uint256 number;
    }
}

#[public]
impl Counter {
    pub fn number(&self) -> U256 {
        self.number.get()
    }

    pub fn set_number(&mut self, new_number: U256) {
        self.number.set(new_number);
    }

    pub fn increment(&mut self) {
        let number = self.number.get();
        self.number.set(number + U256::from(1));
    }
}

The sol_storage! macro generates a storage layout that is binary-compatible with Solidity's storage. The #[entrypoint] attribute designates the struct as the contract's entry point. The #[public] attribute on the impl block generates the ABI dispatch logic. Without the skill, Claude frequently gets the macro syntax wrong, uses deprecated SDK versions, or invents non-existent attributes.

Frontend Integration: viem Chain Config

The frontend reference is where the skill prevents the most subtle bugs. One common failure mode: calling http() with no argument in a wagmi transport config. This works for built-in chains but silently fails for custom chains defined with defineChain. The skill explicitly documents this and provides the correct pattern.

For the local devnode, the chain config looks like this:

import { defineChain } from "viem";

export const arbitrumLocal = defineChain({
  id: 412346,
  name: "Arbitrum Local",
  nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
  rpcUrls: {
    default: { http: ["http://localhost:8547"] },
  },
});

The skill also covers the CORS problem: nitro-devnode does not return CORS headers, so browser-based frontends cannot hit localhost:8547 directly. The reference includes a Next.js API route proxy pattern and the corresponding wagmi transport configuration pointing at /api/rpc.

Another pattern the skill enforces is hydration safety. Wagmi hooks like useAccount() return different values on the server versus the client. Without a mounted guard, Next.js throws hydration mismatches. The skill includes the exact pattern for wrapping wallet-dependent components so Claude applies it consistently.

Getting Started

Install the skill with one command:

bash <(curl -s https://raw.githubusercontent.com/hummusonrails/arbitrum-dapp-skill/main/install.sh)

Or install via ClawHub:

npx clawhub@latest install arbitrum-dapp-skill

Then open Claude Code and ask it to build something:

> Create a Stylus contract for an ERC-20 token with a React frontend

The skill loads automatically and Claude will scaffold the monorepo, write the contract with correct sol_storage! and #[public] patterns, configure the devnode, wire up viem transports with explicit URLs, and set up wagmi hooks with hydration guards.

Watch it in action: demo video.

Contributing

The skill is MIT licensed and contributions are welcome. The reference docs are the highest-leverage place to contribute -- if you have patterns for specific contract types (NFTs, governance, DeFi primitives) or have hit edge cases with the Stylus SDK that Claude gets wrong, open an issue or PR on GitHub.

The broader idea here is that skills are composable domain expertise for AI coding tools. The blockchain space moves fast enough that general-purpose training data is always behind. Skills are the mechanism for keeping the model's knowledge current and correct for a specific stack. If you are building on Arbitrum, this one should save you some time.