01 / Layers
区分读取、连接和写入
读取公共链上数据通常不需要用户授权;连接钱包是获取账户地址的交互;发送交易或签名消息则需要用户明确确认。界面不应把三者混成一个按钮。
Public client读取区块、余额、合约返回值和事件。
Wallet client请求账户、签名消息和提交用户确认的交易。
应用状态同步连接状态、网络、交易哈希和最终结果。
02 / Read
先让页面在不连接钱包时也能工作
只读数据可以展示资产、协议参数和历史状态。读取前要明确链 ID、合约地址、ABI 和区块标签,不要把用户输入直接拼成未知合约调用。
const balance = await publicClient.getBalance({
address: userAddress,
});
const value = await publicClient.readContract({
address: contractAddress,
abi,
functionName: "value",
});显示来源:页面应告诉用户当前网络、数据更新时间和合约地址,让“读到的数据”可以被复核。
03 / Write
交易前先展示将要发生什么
发送交易前,前端应展示目标网络、合约、函数、参数、预计费用和可能的授权范围。用户拒绝签名、钱包切换账户或网络不匹配,都应被当作正常状态处理。
const hash = await walletClient.writeContract({
address: contractAddress,
abi,
functionName: "set",
args: [nextValue],
account,
});不要在前端硬编码“自动确认”或隐藏参数;不要把私钥放进浏览器、环境变量公开构建物或仓库。
04 / States
交易哈希不是交易完成
| 状态 | 界面应表达什么 |
|---|---|
| 准备中 | 检查网络、账户、参数和余额,允许用户返回修改。 |
| 等待钱包 | 说明正在等待用户确认,不能把取消显示为成功。 |
| 已提交 | 展示交易哈希和目标网络,提示仍在等待确认。 |
| 已确认 | 读取链上结果并展示区块、事件或状态变化。 |
| 失败 / 被拒绝 | 区分用户拒绝、余额不足、网络错误和合约回滚。 |
05 / Safety
前端也是安全边界
域名、依赖、RPC、合约地址和 ABI 都可能成为供应链或配置风险。生产环境固定依赖版本,核对官方地址,限制授权额度,避免把未知签名请求包装成普通登录。
安全底线:任何前端都不能替用户判断陌生签名是否安全;页面应提供可理解的信息,而不是催促用户点击确认。