Step-by-step walkthrough of building a Web3 dApp with Claude Code Sidekick
This example shows how to build a crypto landing page with wallet authentication using the Sidekick workflow.
- How to define Web3 requirements in PROJECT_STARTER.md
- How to use /project-planner for dApp projects
- How to integrate Privy wallet authentication
- How to fix TypeScript errors during development
This example builds a complete crypto dashboard with:
- ✅ Privy wallet connection
- ✅ Signature verification (proves wallet ownership)
- ✅ Elaborate dashboard UI
- ✅ Dark neon theme (GigaBrain-inspired)
- ✅ Glassmorphism effects
- ✅ Demo mode (works without Privy key)
Open PROJECT_STARTER.md to see the requirements.
### Functional Requirements
1. WHEN user clicks "Connect Wallet" THE SYSTEM SHALL open Privy modal
2. WHEN wallet connects THE SYSTEM SHALL request signature verification
3. WHEN signature verified THE SYSTEM SHALL display dashboard
4. WHILE user is authenticated THE SYSTEM SHALL show wallet address### Non-Functional Requirements
- Theme: Dark background (#0a0a0f) with neon accents
- Effects: Glassmorphism cards with backdrop-filter blur
- Typography: Inter for body, JetBrains Mono for addresses/project-planner
See .claude/project-plan/phase_1.md
The plan identifies:
- Authentication flow (connect → sign → dashboard)
- Component hierarchy
- CSS architecture decisions
/task-planner
See .claude/tasks/phase-1-tasks.md
Tasks are broken down by component:
| ID | Agent | Task | Status |
|---|---|---|---|
| TASK-001 | init | Project setup | ✅ |
| TASK-002 | dev | CSS theme | ✅ |
| TASK-003 | dev | Privy integration | ✅ |
| TASK-004 | dev | Navbar | ✅ |
| TASK-005 | dev | Hero section | ✅ |
| TASK-006 | dev | Signature modal | ✅ |
| TASK-007 | dev | Dashboard | ✅ |
/task-runner
Claude executes each task:
┌──────────────────────────────────────────────────────────────────┐
│ TASK-006: Signature Modal [dev] │
├──────────────────────────────────────────────────────────────────┤
│ ▶ Creating SignatureModal.tsx... │
│ ▶ Adding message display... │
│ ▶ Implementing sign handler... │
│ ✅ COMPLETE │
└──────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 1. User on Landing Page │
│ ↓ │
│ 2. Clicks "Connect Wallet" │
│ ↓ │
│ 3. Privy modal opens (MetaMask, WalletConnect, etc.) │
│ ↓ │
│ 4. Wallet connects successfully │
│ ↓ │
│ 5. Signature modal appears │
│ ↓ │
│ 6. User signs verification message │
│ ↓ │
│ 7. Dashboard is displayed │
│ │
└─────────────────────────────────────────────────────────────────┘
cd examples/crypto-dashboard
npm installWithout a Privy app ID, the example runs in demo mode:
npm run dev
# Opens http://localhost:5173Demo mode simulates:
- Wallet connection
- Signature flow
- Full dashboard
- Get an app ID from privy.io
- Create
.env:VITE_PRIVY_APP_ID=your-app-id - Run
npm run dev
crypto-dashboard/
├── src/
│ ├── components/
│ │ ├── Navbar.tsx # Top nav with wallet badge
│ │ ├── Hero.tsx # Landing hero section
│ │ ├── SignatureModal.tsx # Verification overlay
│ │ ├── Dashboard.tsx # Post-auth dashboard
│ │ ├── StatsCard.tsx # Portfolio stat cards
│ │ ├── TokenList.tsx # Token holdings
│ │ └── ActivityFeed.tsx # Transaction history
│ ├── styles/
│ │ └── globals.css # Neon dark theme (13KB)
│ ├── App.tsx # Privy provider + routing
│ ├── main.tsx # Entry point
│ └── vite-env.d.ts # TypeScript env types
├── PROJECT_STARTER.md
├── CLAUDE.md
└── package.json
<PrivyProvider
appId={import.meta.env.VITE_PRIVY_APP_ID}
config={{
appearance: {
theme: 'dark',
accentColor: '#00f5ff',
},
loginMethods: ['wallet'],
}}
>
<AppContent />
</PrivyProvider>const handleSign = async () => {
const message = `Welcome to Nexus Protocol!
Timestamp: ${Date.now()}
Nonce: ${crypto.randomUUID()}`;
// In real app: await signMessage(message)
// Then verify signature
onSuccess();
};function AppContent() {
const { authenticated } = usePrivy();
const [signatureVerified, setSignatureVerified] = useState(false);
// Not connected → show Hero
// Connected but not signed → show SignatureModal
// Signed → show Dashboard
}:root {
--color-bg: #0a0a0f;
--color-cyan: #00f5ff;
--color-purple: #a855f7;
--color-pink: #ec4899;
}.glass {
background: rgba(18, 18, 26, 0.8);
backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 1rem;
}
.glass:hover {
border-color: rgba(0, 245, 255, 0.2);
box-shadow: 0 0 30px rgba(0, 245, 255, 0.4);
}.btn--glow {
background: linear-gradient(135deg, #00f5ff, #a855f7, #ec4899);
position: relative;
}
.btn--glow::before {
content: '';
position: absolute;
inset: -3px;
background: inherit;
filter: blur(16px);
opacity: 0;
transition: opacity 0.3s;
}
.btn--glow:hover::before {
opacity: 0.6;
}Add src/vite-env.d.ts:
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_PRIVY_APP_ID: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}Remove unused imports/variables or prefix with _:
// Before
const [showModal, setShowModal] = useState(false);
// After (if unused)
// Remove the line entirelynpm installEdit src/styles/globals.css:
:root {
--color-cyan: #your-primary;
--color-purple: #your-secondary;
}Edit src/components/TokenList.tsx:
const tokens = [
{ symbol: 'ETH', name: 'Ethereum', balance: '12.45', value: '$31,284' },
// Add more tokens...
];Replace mock data with:
- Alchemy/Infura for balances
- CoinGecko for prices
- The Graph for history
- GigaBrain - AI token aesthetics
- Dune Analytics - Dashboard layouts
- DeBank - Portfolio presentation
- Zapper - Activity feeds
- Read the Getting Started Tutorial
- Try the SSG Starter Example
- Learn about Available Agents