frontend-playbook について
Scaffold-ETH 2 プロジェクト向けの開発スキルで、ローカルフォーク、ビルド検証、IPFS・Vercel・ENS へのデプロイを扱います。Ethereum アプリの開発・公開を行うチーム向けに、静的ルート、ビルド成果物、オンチェーン状態の確認手順をまとめています。
主な機能
- 01Base など実ネットワーク状態のフォーク
- 02静的エクスポートと IPFS ルーティングの設定
- 03ビルド成果物とデプロイルートの確認
- 04ENS サブドメインとコンテンツハッシュの設定
- 05コントラクトとブラウザーの QA
活用シーン
- 01実際のプロトコル状態でのコントラクトテスト
- 02Scaffold-ETH 2 アプリの IPFS デプロイ
- 03静的ルートや古いビルドの問題調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ austintgriffith/ ethskills --skill frontend-playbookインストール後、Claude Code で/frontend-playbookを入力して読み込まれたか確認します。
詳細
フロントエンドのプレイブック
よくある間違い
「yarn chain を使います。」 それは間違いです。yarn chain では、プロトコルもトークンも状態もない空のローカルチェーンが作成されます。yarn fork --network base を使えば、Uniswap、Aave、USDC、実際の大口保有者の残高など、すべてを含む実際の Base のコピーが作成されます(アドレスの確認先:addresses/SKILL.md)。必ずフォークしてください。
「IPFS にデプロイして動作しました。」 CID は変わりましたか? 変わっていなければ、古いビルド成果物をデプロイしています。ルートは動作しましたか? trailingSlash: true がないと、/ 以外のすべてのルートで 404 が返されます。OG 画像は確認しましたか? NEXT_PUBLIC_PRODUCTION_URL がないと、参照先は localhost:3000 になります。
「プロジェクトを手動でセットアップします。」 その必要はありません。npx create-eth@latest がすべてを処理します。Foundry、Next.js、RainbowKit、scaffold のフックがセットアップされます。forge init を実行したり、Next.js プロジェクトをゼロから作成したりしないでください。
フォークモードのセットアップ
チェーンではなくフォークを使う理由
yarn chain (WRONG) yarn fork --network base (CORRECT)
└─ Empty local chain └─ Fork of real Base mainnet
└─ No protocols └─ Uniswap, Aave, etc. available
└─ No tokens └─ Real USDC, WETH exist
└─ Testing in isolation └─ Test against REAL state
セットアップ
npx create-eth@latest # Select: foundry, target chain, name
cd <project-name>
yarn install
yarn fork --network base # Terminal 1: fork of real Base
yarn deploy # Terminal 2: deploy contracts to fork
yarn start # Terminal 3: Next.js frontend
重要:Chain ID に関する注意点
フォークモードを使う場合、フロントエンドのターゲットネットワークは、フォーク元のチェーンではなく、必ず chains.foundry(Chain ID 31337)にしてください。
フォークは Chain ID 31337 の Anvil 上でローカルに実行されます。Base をフォークする場合でも、次のようにします。
// scaffold.config.ts during development
targetNetworks: [chains.foundry], // ✅ NOT chains.base!
コントラクトを実際のネットワークにデプロイする場合に限り、chains.base に切り替えてください。
ブロックマイニングを有効にする
# In a new terminal — REQUIRED for time-dependent logic
cast rpc anvil_setIntervalMining 1
これを設定しないと、block.timestamp が固定されたままになります。タイムスタンプを使うコントラクトのロジック(期限、失効、ベスティングなど)は、気付かないうちに動作しなくなります。
設定を永続化するには、packages/foundry/package.json を編集し、フォーク用スクリプトに --block-time 1 を追加してください。
IPFS へのデプロイ(推奨)
SE2 では IPFS へのデプロイを推奨します。Vercel のメモリ制限を完全に回避でき、完全に分散化された静的サイトを作成できます。
ビルドコマンドの全体
cd packages/nextjs
rm -rf .next out # ALWAYS clean first
NEXT_PUBLIC_PRODUCTION_URL="https://yourapp.yourname.eth.link" \
NODE_OPTIONS="--require ./polyfill-localstorage.cjs" \
NEXT_PUBLIC_IPFS_BUILD=true \
NEXT_PUBLIC_IGNORE_BUILD_ERROR=true \
yarn build
# Upload to BuidlGuidl IPFS
yarn bgipfs upload out
# Save the CID!
Node 25 以降の localStorage ポリフィル(必須)
Node.js 25 以降には localStorage オブジェクトが組み込まれていますが、標準の WebStorage API メソッド(getItem、setItem)が欠けています。そのため、静的ページの生成中に localStorage.getItem() を呼び出す next-themes、RainbowKit、およびその他のライブラリで問題が発生します。
表示されるエラー:
TypeError: localStorage.getItem is not a function
Error occurred prerendering page "/_not-found"
修正方法: packages/nextjs/ に polyfill-localstorage.cjs を作成します。
if (typeof globalThis.localStorage !== "undefined" &&
typeof globalThis.localStorage.getItem !== "function") {
const store = new Map();
globalThis.localStorage = {
getItem: (key) => store.get(key) ?? null,
setItem: (key, value) => store.set(key, String(value)),
removeItem: (key) => store.delete(key),
clear: () => store.clear(),
key: (index) => [...store.keys()][index] ?? null,
get length() { return store.size; },
};
}
なぜ instrumentation.ts ではなく --require なのか? Next.js はプリレンダリングのために別のビルドワーカープロセスを起動します。--require はすべての Node プロセス(ワーカーを含む)に読み込まれます。next.config.ts のポリフィルはメインプロセスでしか実行されません。instrumentation.ts はビルドワーカーでは実行されません。機能するのは --require だけです。
IPFS のルーティング — ルートが機能しない理由
IPFS ゲートウェイは静的ファイルを配信します。ルーティングを処理するサーバーはありません。次の3つの条件を必ず満たしてください。
1. next.config.ts の output: "export" — 静的 HTML ファイルを生成します。
2. trailingSlash: true(重要) — ルートが機能しない最大の原因です。
trailingSlash: false(デフォルト)→debug.htmlを生成しますtrailingSlash: true→debug/index.htmlを生成します- IPFS ゲートウェイはディレクトリを自動的に
index.htmlに解決しますが、拡張子のないファイル名は解決しません - 末尾のスラッシュがない場合:
/debug→ 404 ❌ - 末尾のスラッシュがある場合:
/debug→debug/→debug/index.html✅
3. ページが静的プリレンダリングを正常に完了すること — yarn build 中にクラッシュするページ(インポート時のブラウザー API、localStorage など)は、エラーなしでスキップされ、IPFS で 404 になります。
IPFS に対応した next.config.ts の完全なパターン:
const isIpfs = process.env.NEXT_PUBLIC_IPFS_BUILD === "true";
if (isIpfs) {
nextConfig.output = "export";
nextConfig.trailingSlash = true;
nextConfig.images = { unoptimized: true };
}
SE2 のブロックエクスプローラーページはインポート時に localStorage を使うため、静的エクスポート中にクラッシュします。不要な場合は、app/blockexplorer を app/_blockexplorer-disabled に名前変更してください。
古いビルドの検出
IPFS で最も起こりやすい失敗: コードを編集してから、古いビルドをデプロイしてしまうことです。
# MANDATORY after ANY code change:
rm -rf .next out # 1. Delete old artifacts
# ... run full build command ... # 2. Rebuild from scratch
grep -l "YOUR_STRING" out/_next/static/chunks/app/*.js # 3. Verify changes present
# Timestamp check:
stat -f '%Sm' app/page.tsx # Source modified time
stat -f '%Sm' out/ # Build output time
# Source NEWER than out/ = STALE BUILD. Rebuild first!
CID が証拠になります: デプロイ後に IPFS CID が変わっていない場合、同じコンテンツをデプロイしています。コードに実際の変更があれば、必ず新しい CID が生成されます。
デプロイ後にルートを検証する
ls out/*/index.html # Each route has a directory + index.html
curl -s -o /dev/null -w "%{http_code}" -L "https://GATEWAY/ipfs/CID/debug/"
# Should return 200, not 404
Vercel へのデプロイ(代替方法)
SE2 はモノレポのため、Vercel には特別な設定が必要です。
設定
- ルートディレクトリ:
packages/nextjs - インストールコマンド:
cd ../.. && yarn install - ビルドコマンド: デフォルトのまま(
next build) - 出力ディレクトリ: デフォルトのまま(
.next)
# Via API:
curl -X PATCH "https://api.vercel.com/v9/projects/PROJECT_ID" \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rootDirectory": "packages/nextjs", "installCommand": "cd ../.. && yarn install"}'
よくある失敗
| エラー | 原因 | 対処方法 |
|---|---|---|
| "No Next.js version detected" | ルートディレクトリが設定されていない | packages/nextjs に設定します |
| "cd packages/nextjs: No such file" | ビルドコマンドに cd が含まれている | 削除します。ルートディレクトリが処理します |
| OOM / exit code 129 | SE2 モノレポが 8GB を超えている | 代わりに IPFS を使用するか、vercel --prebuilt を実行します |
判断ツリー
Want to deploy SE2?
├─ IPFS (recommended) → yarn ipfs / manual build + upload
│ └─ Fully decentralized, no memory limits, works with ENS
├─ Vercel → Set rootDirectory + installCommand
│ └─ Fast CDN, but centralized. May OOM on large projects
└─ vercel --prebuilt → Build locally, push artifacts to Vercel
└─ Best of both: local build power + Vercel CDN
ENS サブドメインの設定
ENS サブドメインを IPFS デプロイに紐づけるには、メインネットで 2 件のトランザクションを実行します。
トランザクション 1:サブドメインの作成(新規アプリのみ)
https://app.ens.domains/yourname.ethを開きます- 「Subnames」タブ →「New subname」に移動します
- ラベル(例:
myapp)を入力 →「Next」→ プロフィール設定をスキップ →「Open Wallet」→ 確認します - ガスが詰まった場合:MetaMask を Ethereum に切り替え →「Activity」タブ →「Speed up」
トランザクション 2:IPFS コンテンツハッシュの設定
https://app.ens.domains/myapp.yourname.ethに移動します- 「Records」タブ →「Edit Records」→「Other」タブ
- Content Hash フィールドに貼り付けます:
ipfs://<CID> - 保存 →「Open Wallet」→ MetaMask で確認します
既存アプリを更新する場合:Tx 1 はスキップし、Tx 2 のみ実行します。
確認
# 1. Onchain content hash matches
RESOLVER=$(cast call 0x00000000000C2e074eC69A0dFb2997BA6C7d2e1e \
"resolver(bytes32)(address)" $(cast namehash myapp.yourname.eth) \
--rpc-url https://eth.llamarpc.com)
cast call $RESOLVER "contenthash(bytes32)(bytes)" \
$(cast namehash myapp.yourname.eth) --rpc-url https://eth.llamarpc.com
# 2. Gateway responds (may take 5-15 min for cache)
curl -s -o /dev/null -w "%{http_code}" -L "https://myapp.yourname.eth.link"
# 3. OG metadata correct (not localhost)
curl -s -L "https://myapp.yourname.eth.link" | grep 'og:image'
.eth.limo ではなく .eth.link を使用してください — .eth.link の方がモバイルで適切に動作します。
本番環境へ — 完全なチェックリスト
ユーザーが「リリースして」と言ったら、必ずこの順序で進めてください。
ステップ 1:最終コードレビュー 🤖
- すべてのフィードバックを反映済み
- h1 の重複なし、生のアドレスなし、共有の isLoading なし
scaffold.config.tsにrpcOverridesとpollingInterval: 3000がある
ステップ 2:ドメインの選択 👤
質問します:「どのサブドメインにしますか? 例:myapp.yourname.eth → myapp.yourname.eth.link」
ステップ 3:OG 画像の生成とメタデータの修正 🤖
- 1200×630 PNG(
public/thumbnail.png)を作成します — SE2 の標準サムネイルは使用しません NEXT_PUBLIC_PRODUCTION_URLを本番ドメインに設定しますog:imageが本番環境の絶対 URL に解決されることを確認します
ステップ 4:クリーンビルドと IPFS デプロイ 🤖
cd packages/nextjs && rm -rf .next out
NEXT_PUBLIC_PRODUCTION_URL="https://myapp.yourname.eth.link" \
NODE_OPTIONS="--require ./polyfill-localstorage.cjs" \
NEXT_PUBLIC_IPFS_BUILD=true NEXT_PUBLIC_IGNORE_BUILD_ERROR=true \
yarn build
# Verify before uploading:
ls out/*/index.html # Routes exist
grep 'og:image' out/index.html # Not localhost
stat -f '%Sm' app/page.tsx # Source older than out/
stat -f '%Sm' out/
yarn bgipfs upload out # Save the CID
ステップ 5:承認のために共有 👤
送信します:「ビルドのレビュー準備ができました:https://community.bgipfs.com/ipfs/<CID>」
ENS に触れる前に、必ず承認を待ってください。
ステップ 6:ENS の設定 🤖
サブドメインを作成(新規の場合)し、IPFS コンテンツハッシュを設定します。メインネットで 2 件のトランザクションを実行します。
ステップ 7:確認 🤖
- コンテンツハッシュがオンチェーンの値と一致している
.eth.linkゲートウェイが 200 を返す- OG 画像が正しく読み込まれる
- ルート(
/debug/など)が動作する
ステップ 8:報告 👤
「https://myapp.yourname.eth.link で公開中です — ENS コンテンツハッシュがオンチェーンで確認され、アンファール用メタデータも設定済みです。」
ビルド検証プロセス
コードがコンパイルできても、ビルドが完了したことにはなりません。実際のユーザーと同じようにテストして、初めて完了です。
フェーズ 1:コード QA(自動)
.tsxファイルをスキャンして、生のアドレス文字列がないことを確認します(<Address/>を使用する必要があります)- 複数のボタン間で
isLoading状態が共有されていないことを確認します - トランザクションボタンに
disabledプロパティがない箇所をスキャンします - RPC 設定とポーリング間隔を確認します
- 絶対 URL を使った OG メタデータを確認します
- どのファイルにもパブリック RPC がないことを確認します
フェーズ 2:スマートコントラクトのテスト
forge test # All tests pass
forge test --fuzz-runs 10000 # Fuzz testing
ゼロの金額、最大の金額、権限のない呼び出し元、リエントランシーの試行など、エッジケースをテストします。
フェーズ 3:ブラウザテスト(本番のテスト)
アプリを開き、最初から最後まで徹底的に操作します。
- アプリを読み込む — 正しく表示されますか?
- ページタイトルを確認する — 正しいタイトルになっていますか?「Scaffold-ETH 2」ではありませんか?
- ウォレットを接続する — 接続フローは動作しますか?
- 間違ったネットワーク — 間違ったチェーンで接続し、「Switch to Base」が表示されることを確認します
- ネットワークを切り替える — 切り替えボタンをクリックし、正常に動作することを確認します
- 承認フロー — 承認ボタンが表示されることを確認してクリックし、トランザクションが完了するまで待ってから、アクションボタンが表示されることを確認します
- メインアクション — 主要なアクションをクリックし、ローダーが表示されることを確認して、トランザクションが完了するまで待ち、状態が更新されることを確認します
- エラー処理 — ウォレットでトランザクションを拒否し、UI が復旧することを確認します
- アドレスの表示 — すべてのアドレスが生の 16 進数ではなく、ENS または blockies で表示されていますか?
- 共有 URL — OG の展開表示(画像、タイトル、説明)を確認します
フェーズ 4:QA サブエージェント(複雑なビルド)
大規模なプロジェクトでは、新しいコンテキストでサブエージェントを起動します。リポジトリのパスとデプロイ済み URL を渡します。サブエージェントは UX ルールに照らしてすべてのコードを確認し、ブラウザを開いて独自に操作し、問題を報告します。
やってはいけないこと
- ❌
yarn chain—yarn fork --network <chain>を使います - ❌
forge init—npx create-eth@latestを使います - ❌ Next.js を手動でセットアップする — SE2 が処理します
- ❌ ウォレットを手動で接続する — SE2 には RainbowKit が事前設定されています
- ❌
deployedContracts.tsを編集する —yarn deployによって自動生成されます - ❌
scaffold.config.tsに API キーをハードコードする —.env.localを使います - ❌ 本番環境で
mainnet.base.orgを使う — Alchemy などを使います
リソース
- SE2 ドキュメント: https://docs.scaffoldeth.io/
- UI コンポーネント: https://ui.scaffoldeth.io/
- SpeedRun Ethereum: https://speedrunethereum.com/
- ETH Tech Tree: https://www.ethtechtree.com
- BuidlGuidl IPFS: https://upload.bgipfs.com