核心心智模型的转变
作为 Web2 开发者,你需要调整几个根深蒂固的思维习惯:
从"可变"到"追加"
在 Web2 中,数据是可变的。用户修改昵称?UPDATE users SET name = 'NewName' WHERE id = 123。
在区块链上,历史是不可变的。用户修改昵称?实际上是创建一条新记录:"用户 123 在区块 #10000 将昵称更新为 NewName"。旧昵称仍然存在于历史中,只是"最新状态"变了。
这类似于 Git 的工作方式:每次提交都是追加的,你可以查看任何历史版本。
从"信任后端"到"验证一切"
在 Web2 中,前端信任后端返回的数据。用户余额是 100 元?后端说是,那就是。
在 Web3 中,"Don't trust, verify"(不要信任,去验证)。任何关键数据都应该从链上验证,因为你的前端可能连接到恶意 RPC 节点。
javascript// Web2 思维:直接使用 API 返回值 const balance = await api.getBalance(userId); // Web3 思维:从链上获取并验证 const accountInfo = await connection.getAccountInfo(publicKey); const balance = accountInfo.lamports; // 还可以验证账户的 owner、data 结构等
从"服务端签名"到"用户签名"
在 Web2 中,用户的操作由服务器代为执行。用户点击"购买",服务器扣款、发货、记录,用户只需要输入密码授权。
在 Web3 中,每个重要操作都需要用户用私钥签名。服务器(或智能合约)无法代替用户做决定。这意味着用户体验的设计需要根本性的重新思考。
从"回滚"到"补偿"
在 Web2 中,事务出错可以回滚。数据库支持 ROLLBACK,最坏情况下可以恢复备份。
在区块链上,交易一旦上链就不可逆转。如果出了问题,你只能发起"补偿交易"——再发一笔交易来"修正"之前的错误。这要求你在设计时考虑所有边界情况。
开发流程的差异
传统 Web2 工作流
需求 → 设计 → 编码 → 单元测试 → 部署到测试环境 → 集成测试 → 部署到生产 → 监控 → 热修复
Solana 开发工作流
需求 → 设计(特别注意账户结构和安全性) → 编码程序(Rust/Anchor) → 本地测试(solana-test-validator) → 部署到 Devnet → 前端集成测试 → 安全审计(强烈建议) → 部署到 Mainnet → 监控 → 如需升级,通过升级授权发布新版本
关键区别:
- 更长的设计阶段:账户结构和权限模型一旦确定,修改成本很高
- 更严格的测试:没有"热修复"的奢侈,每次部署都是高风险操作
- 安全审计:对于处理真实资产的程序,专业审计几乎是必须的
- 渐进式发布:先 Devnet 验证,再小规模 Mainnet 测试,最后全量上线
常见模式映射
用户认证
Web2: JWT Token + Session
Web3: 钱包签名验证
javascript// 前端请求用户签名 const message = "Sign in to MyApp at " + Date.now(); const signature = await wallet.signMessage(Buffer.from(message)); // 后端验证签名 const isValid = nacl.sign.detached.verify( Buffer.from(message), signature, publicKey.toBytes() );
数据存储
Web2: MySQL/PostgreSQL + Redis
Web3: 链上账户(关键数据)+ 链下存储(大文件)
// 链上存储(每字节都有成本)
- 用户余额、NFT 所有权、投票记录
// 链下存储(IPFS/Arweave/传统数据库)
- 图片、视频、详细描述
- 在链上存储 hash 或 URI 作为指针
API 调用
Web2: REST API / GraphQL
Web3: RPC 调用 + 事件监听
javascript// 读取数据 const accountInfo = await connection.getAccountInfo(publicKey); // 写入数据(需要交易) const tx = await program.methods.createPost(content).accounts({...}).rpc(); // 监听事件 connection.onAccountChange(publicKey, (accountInfo) => { console.log("Account changed:", accountInfo); });
后台任务
Web2: Cron Job / Message Queue
Web3: Clockwork (链上定时器) / 链下 Keeper 服务
javascript// 链下 Keeper:监控链上状态,必要时发起交易 while (true) { const state = await program.account.auction.fetch(auctionPda); if (state.endTime < Date.now() && !state.settled) { await program.methods.settleAuction().accounts({...}).rpc(); } await sleep(1000); }
常见陷阱与最佳实践
陷阱 1:忽视交易原子性
错误做法:
javascript// 危险!两个独立交易不是原子的 await program.methods.withdraw(amount).rpc(); await program.methods.deposit(amount, otherPool).rpc(); // 如果第二笔失败,资金可能丢失
正确做法:
javascript// 把相关操作放在同一个交易中 const tx = new Transaction(); tx.add(withdrawInstruction); tx.add(depositInstruction); await sendAndConfirmTransaction(connection, tx, [user]);
陷阱 2:前端暴露敏感逻辑
在 Web2 中,我们习惯把业务逻辑放在后端。但在纯 Web3 应用中,很多逻辑必须放在前端或链上。
关键原则:
- 所有安全检查必须在链上程序中完成
- 前端只是帮助用户构造交易,不应该依赖前端来保证安全性
- 假设任何人都可以直接调用你的程序,绕过你的前端
陷阱 3:低估 Gas 成本
即使 Solana 的交易费很低,频繁的链上操作也会累积。
优化策略:
- 批量操作:把多个小操作合并成一个大交易
- 链下计算:只把最终结果上链
- 选择性上链:不是所有数据都需要上链
陷阱 4:忽视程序升级
部署后发现 bug?在 Web2 中,修改代码重新部署就好。在 Solana 上,你需要:
- 预先设置升级授权(upgrade authority)
- 部署新程序版本
- 通过升级指令更新程序账户
如果没有设置升级授权(或者主动放弃),程序将永远无法修改。
最佳实践清单
- 账户设计
- 预先规划账户结构,考虑未来扩展
- 使用 PDA 实现确定性寻址
- 合理划分数据,避免单个账户过大
- 安全性
- 所有输入都要验证
- 检查所有账户的 owner 和权限
- 使用成熟的库(如 Anchor)减少底层错误
- 重要项目进行专业审计
- 用户体验
- 提供清晰的交易预览
- 处理好网络错误和重试逻辑
- 显示交易状态(pending/confirmed/finalized)
- 可维护性
- 保留升级能力(除非有充分理由放弃)
- 编写完善的测试
- 记录账户结构和安全性