关于 frontend-playbook
面向 Scaffold-ETH 2 项目的开发技能,涵盖本地分叉、构建验证及 IPFS、Vercel 和 ENS 部署流程。适合开发和发布以太坊应用的团队,重点在于静态路由、构建产物和链上状态的检查。
主要功能
- 01分叉 Base 等真实网络状态
- 02配置静态导出与 IPFS 路由
- 03检查构建产物和部署路由
- 04配置 ENS 子域名与内容哈希
- 05涵盖合约及浏览器端 QA
适用场景
- 01在真实协议状态上测试合约
- 02将 Scaffold-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 会创建真实 Base 的副本,其中包含 Uniswap、Aave、USDC、真实巨鲸余额等所有内容(已验证地址:addresses/SKILL.md)。始终使用 fork。
“我部署到 IPFS 了,而且能用。” CID 变了吗?如果没变,你部署的是过期输出。路由能用吗?没有 trailingSlash: true,除了 / 之外的所有路由都会返回 404。你检查过 OG 图片吗?没有 NEXT_PUBLIC_PRODUCTION_URL,它就会指向 localhost:3000。
“我会手动设置项目。” 别这么做。npx create-eth@latest 会处理好一切——Foundry、Next.js、RainbowKit、scaffold hooks。不要运行 forge init,也不要从头创建 Next.js 项目。
Fork 模式设置
为什么用 Fork,而不是 Chain
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 易错点
使用 fork 模式时,前端目标网络必须是 chains.foundry(chain ID 31337),而不是你正在 fork 的链。
Fork 在 Anvil 上本地运行,chain ID 为 31337。即使你 fork 的是 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,在 fork 脚本中添加 --block-time 1。
部署到 IPFS(推荐)
IPFS 是 SE2 推荐的部署方式。它完全避开 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 Polyfill(必需)
Node.js 25+ 自带的 localStorage 对象缺少标准 WebStorage API 方法(getItem、setItem)。这会导致 next-themes、RainbowKit,以及任何在静态页面生成期间调用 localStorage.getItem() 的库出错。
你会看到的错误:
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; },
};
}
**为什么用 --require,而不是 instrumentation.ts?**Next.js 会启动单独的构建工作进程来预渲染。--require 会注入到每个 Node 进程中(包括工作进程)。next.config.ts 中的 polyfill 只会在主进程中运行。instrumentation.ts 不会在构建工作进程中运行。只有 --require 有效。
IPFS 路由——路由失效的原因
IPFS 网关提供静态文件,没有服务器处理路由。以下三点必须成立:
1. 在 next.config.ts 中设置 output: "export"——生成静态 HTML 文件。
2. 设置 trailingSlash: true(关键)——这是路由失效的首要原因:
trailingSlash: false(默认值)→ 生成debug.htmltrailingSlash: 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 是一个 monorepo——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 monorepo 超过 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 部署。
交易 1:创建子域名(仅限新应用)
- 打开
https://app.ens.domains/yourname.eth - 前往“Subnames”选项卡 → “New subname”
- 输入标签(例如
myapp)→ Next → Skip profile → Open Wallet → Confirm - 如果 Gas 卡住:将 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 中确认
对于现有应用的更新:跳过交易 1,只执行交易 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.link,不要使用 .eth.limo——.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 内容哈希。共两笔主网交易。
步骤 7:验证 🤖
- 内容哈希与链上记录一致
.eth.link网关返回 200- OG 图片正确加载
- 路由可用(
/debug/等)
步骤 8:汇报 👤
“已上线:https://myapp.yourname.eth.link——ENS 内容哈希已确认上链,展开预览元数据已设置。”
构建验证流程
代码编译通过并不代表构建完成。只有像真实用户那样测试过,才算完成。
阶段 1:代码 QA(自动化)
- 扫描
.tsx文件中的原始地址字符串(应使用<Address/>) - 扫描多个按钮之间是否共享
isLoading状态 - 扫描交易按钮是否缺少
disabled属性 - 验证 RPC 配置和轮询间隔
- 验证 OG 元数据是否使用绝对 URL
- 验证所有文件中均未使用公共 RPC
阶段 2:智能合约测试
forge test # All tests pass
forge test --fuzz-runs 10000 # Fuzz testing
测试边界情况:零金额、最大金额、未经授权的调用方、重入尝试。
阶段 3:浏览器测试(真正的测试)
打开应用并进行完整的全流程检查:
- 加载应用 — 是否正确渲染?
- 检查页面标题 — 是否正确,而不是“Scaffold-ETH 2”?
- 连接钱包 — 连接流程是否正常?
- 错误的网络 — 在错误的链上连接,确认是否显示“Switch to Base”
- 切换网络 — 点击切换按钮,确认是否生效
- 授权流程 — 确认是否显示授权按钮,点击后等待交易,确认操作按钮是否出现
- 主要操作 — 点击主要操作按钮,确认加载指示器是否显示,等待交易,确认状态是否更新
- 错误处理 — 在钱包中拒绝交易,确认界面是否恢复正常
- 地址显示 — 所有地址是否都显示 ENS/Blockies,而不是原始十六进制?
- 分享 URL — 检查 OG 展开效果(图片、标题、描述)
阶段 4:QA 子 Agent(复杂构建)
对于较大的项目,使用全新上下文启动一个子 Agent。向它提供仓库路径和已部署的 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 技术树: https://www.ethtechtree.com
- BuidlGuidl IPFS: https://upload.bgipfs.com