很多人说 Ethereum 用户体验不好。
这个说法太客气了。
Ethereum 真正荒诞的地方不是“操作步骤有点多”,而是:
它要求一个只是想转账、买币、使用应用的人,理解本应该只有协议工程师才需要理解的东西。
Nonce。
Gas Limit。
Base Fee。
Priority Fee。
Allowance。
Calldata。
EOA。
Contract Account。
Signature。
Typed Data。
MEV。
Slippage。
RPC。
Chain ID。
Bridge。
Finality。
UserOperation。
Bundler。
Paymaster。
你甚至不能简单地说:
我有 ETH。
因为接下来还可能有人问:
Native ETH 还是 WETH?
这已经不是学习曲线陡峭。
这是一套基础设施把自己的内部实现细节全部吐到了用户脸上,然后告诉用户:
请自行学习。
更离谱的是,Crypto 用户用了几年以后,居然开始觉得这一切很正常。
所以这一篇我不想再讨论“Ethereum Gas 很贵”这种已经被说烂的问题。
我们讨论一些更荒唐的东西。
Ethereum 有一种最重要的 Token 标准:
ERC-20。
USDC 是 ERC-20。
USDT 是 ERC-20。
DAI 是 ERC-20。
UNI 是 ERC-20。
几乎整个 Ethereum DeFi 世界都围绕 ERC-20 工作。
那么:
ETH 是不是 ERC-20?
不是。
Ethereum 自己的原生货币,居然不符合 Ethereum 后来最重要的 Token Interface。
原因也非常朴素:
ETH 出生得太早了。
官方文档自己承认,ETH 早于 ERC-20,因此并不符合 ERC-20 标准;为了让 ETH 能够像普通 ERC-20 一样被 DeFi 应用处理,人们后来创造了 WETH。
于是 Ethereum 出现了一幅非常有时代特色的画面:
Ethereum 原生货币:ETH
Ethereum Token 标准:ERC-20
问题:
ETH 不符合 Ethereum Token 标准
解决办法:
写一个合约,把 ETH 存进去,
再铸造一个代表 ETH 的 ERC-20 Token。
名字:
Wrapped ETH
也就是说:
Ethereum 为了让自己的原生货币能够正常进入自己的 Token 生态,需要先把自己的原生货币包装成一个“假的自己”。
这件事情已经存在太久,以至于大家甚至不觉得滑稽。
你有 10 ETH。
进入某些 DeFi 协议:
请先 Wrap。
于是:
ETH
↓
WETH
用完:
WETH
↓
ETH
最精彩的是:
你可以拥有价值几万美元的 WETH,
但是如果 Native ETH 恰好为 0,
你仍然可能没法发送需要 Gas 的交易。
Ethereum 官方甚至特意提醒:
Wrap ETH 的时候记得留一些 Native ETH 支付 Gas。
换句话说:
你有 ETH。
但不是那个能付 Gas 的 ETH。
这种事情如果第一次接触 Crypto 的人觉得莫名其妙,不是因为他不懂区块链。
而是因为它本来就莫名其妙。
接下来是我认为 Ethereum 用户体验史上最有喜剧效果的设计之一。
假设你发了一笔交易。
Gas 给低了。
它卡在 mempool 里。
你后悔了。
你点击:
Cancel Transaction
一个正常人理解的“取消”是什么?
把刚才那笔交易撤回来。
Ethereum 的取消是什么?
再发一笔新的交易。
而且要:
这不是段子。
MetaMask 官方给高级用户的手动取消方法就是:
给自己发送一笔 0 ETH 交易,使用旧交易相同的 nonce,并提高 Gas。
所以 Ethereum 里的:
Cancel
真正的语义其实是:
用另一笔更贵的交易,
和原来的交易争夺同一个序号,
争取先把这个序号消耗掉。
甚至“取消”都不是协议里的真正操作。
它是一场竞速。
你的旧交易:
我要执行。
你的取消交易:
不,你别执行,让我先执行。
然后用户还要额外付钱,请验证者优先执行:
“请不要执行我刚才那笔交易。”
这已经不是 UX 不好。
这是一个人在操作数据库事务日志。
为什么刚才那套骚操作有效?
因为 Ethereum EOA 的 transaction nonce 是严格递增的。
比如你的账户已经执行到:
nonce = 100
接下来:
101
102
103
104
必须按照顺序。
官方文档把 nonce 定义为账户发送交易时顺序递增的计数器,同一 nonce 不能执行两次。
这意味着一个特别荒唐的用户体验:
你发了 nonce 101。
Gas 太低。
101 卡住。
然后你发:
102。
103。
104。
很可能全部一起在那里等。
因为:
101 还没过去。
MetaMask 的官方排障文档甚至要求用户从最老的 pending nonce 开始处理,并明确说明不能先取消 nonce 10 而留下 nonce 9。
想象一下一个银行系统:
你上午发了一笔 10 元转账。
它卡住了。
于是下午你所有其他支付:
咖啡。
打车。
午饭。
网购。
全部不许进行。
银行告诉你:
因为第 71 号支付指令尚未执行。
请先处理第 71 号。
你会觉得这个银行疯了。
在 Ethereum:
这叫 nonce management。
甚至还有开发者专门写:
Nonce Manager
来管理这件事情。
一个只想转钱的人,最后需要理解分布式系统里的 ordered sequence number。
然后大家说:
Web3 需要用户教育。
教育个鬼。
这是数据库内部细节跑出来要求用户手工维护。
“Speed Up”也一样。
你看到钱包里有个:
Speed Up
看起来像是修改了原交易。
实际上通常是在干什么?
构造另一笔拥有相同 nonce、但出价更高的 replacement transaction。
MetaMask 明确说明,加速操作会重新提交交易,并复用原交易的 nonce,提高 Gas 使它更有机会先被打包。
Ethereum 甚至曾经有人专门提交 EIP-2831 来标准化这种 transaction replacement 的通知,因为 replacement 机制会给 dapp 开发者带来追踪问题。
所以 Ethereum 钱包里的两个按钮:
Speed Up
Cancel
背后的真实实现分别是:
同 nonce,发另一笔更贵的相同交易
和:
同 nonce,发另一笔更贵的空交易
如果一个普通用户必须知道这件事情,他会觉得整个系统像 1990 年代写出来的。
我们只是用漂亮 UI 把它遮住了。
假设你去便利店买东西。
付款失败。
东西没拿到。
收银员说:
交易失败了。
你:
好。
收银员:
但是我们还是扣了你 20 块。
你:
为什么?
收银员:
因为我们刚才尝试处理你的付款,产生了计算成本。
这就是 Ethereum。
Smart Contract 执行一半:
REVERT
状态修改回滚。
Token 没交换。
NFT 没买到。
借款没借出来。
但已经消耗的 Gas:
不给你退。
官方 Gas 文档明确说明:如果交易执行过程中耗尽 Gas,状态修改会回滚,但用于执行工作的 Gas 仍然被消耗;REVERT 虽然不会像早期失败机制那样烧掉全部剩余 Gas,但已经执行掉的计算成本仍然存在。
工程上当然合理。
CPU 已经算了。
节点已经执行了。
所以收钱。
问题是:
用户为什么要承担应用执行路径预测失败的成本?
Web2 里服务器写了 Bug:
公司付服务器费。
Ethereum:
智能合约写了 Bug,或者状态刚好变化导致交易 revert:
用户付服务器费。
这是 Crypto 特别伟大的商业模式:
程序执行失败了,
账单寄给调用程序的人。
再看 Gas。
普通用户希望看到:
手续费:$2
Ethereum 底层给他的东西是:
gasLimit
maxFeePerGas
maxPriorityFeePerGas
baseFeePerGas
EIP-1559 以后,一笔 Type-2 交易甚至明确包含:
max_priority_fee_per_gas
max_fee_per_gas
而 Base Fee 根据区块利用率动态变化。
翻译成人话:
你不是在支付一个手续费。
你是在说:
我最多愿意为一个计算单位支付多少钱;
其中最多愿意拿多少钱给出块者当小费;
协议还会按照当前拥堵程度产生一个动态基础价格;
如果价格合适,请帮我执行这段程序。
一个用户只是想:
Swap 100 USDC。
结果钱包底层实际上在帮他参与:
全球分布式计算资源的实时拍卖。
然后大家夸:
钱包现在都会自动估 Gas,用户不用懂。
很好。
这句话本身就像在说:
我们这辆车的发动机启动需要手工调节喷油比例,不过现在仪表盘已经帮用户自动调了,所以体验还不错。
真正正常的 UX 是:
这个旋钮从一开始就不应该存在于用户的认知世界里。
Ethereum DeFi 另一个经典设计:
approve(spender, amount)
我要 Swap 100 USDC。
正常人的语义:
花掉我的 100 USDC。
ERC-20 的经典语义却是:
我授权这个地址,以后可以从我的账户里主动拉走最多 N USDC。
这两个概念根本不是一回事。
而早年为了避免每次使用都重新 Approve,大量应用干脆让用户:
Approve Unlimited
于是:
我只是想买一次东西。
最终授权的是:
这个智能合约未来可以持续动我的 Token。
更加精彩的是,ERC-20 标准本身关于 approve() 的说明里就留下了一段历史伤疤:
客户端在修改非零 allowance 时,应该考虑先把 allowance 设成 0,再设置新值,以降低已知的攻击向量;但标准又不能直接强制这样做,因为必须兼容已经部署的旧合约。
仔细读这一段。
这几乎是上一篇《Ethereum 技术有多脏》的用户版缩影:
权限模型有问题。
↓
不能修改旧标准。
↓
让客户端 UI 帮忙规避。
↓
因为还得兼容以前的合约。
最后谁承担这个历史债?
用户。
所以你会看到有些 Token 修改 allowance 时:
先:
Approve 0
再:
Approve 100
一项正常的“修改授权额度”,可能变成两笔链上状态修改。
这不是金融创新。
这像是在操作一个从来没有完成过数据库 migration 的生产环境。
普通金融系统识别一种资产:
USD
Crypto:
USDC
很好。
然后发现任何人都能部署一个 Token:
name = USD Coin
symbol = USDC
logo = 看起来也一样
怎么办?
Crypto 老用户会非常认真地告诉新人:
别看名字。
一定要确认 Contract Address。
于是识别一种“美元”,最终需要识别:
0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
这种字符串。
我们甚至把:
会检查 Contract Address
当成了一种 Crypto 素养。
不。
这不是用户素养。
这是命名系统基本不存在以后产生的生存技能。
正常系统面对:
Microsoft.com
不会告诉用户:
域名容易被骗,为安全起见请记住服务器的 IPv6 地址。
Web3 差不多就是这么干的。
普通产品:
Confirm
通常意味着:
确认眼前这件事情。
Ethereum 钱包:
Sign
可能意味着:
证明你控制这个地址。
可能是登录。
可能是订单。
可能是 Permit。
可能是授权。
可能是 Typed Data。
可能最终让另一个协议拥有某种资产操作权。
用户看到的共同动作却都是:
签。
真正离谱的是:
Ethereum 的安全模型建立在密码学签名非常可靠的基础上,
但用户安全却经常建立在:
希望用户能读懂自己到底在签什么。
密码学可以证明:
确实是你签的。
密码学证明不了:
你知不知道自己签了什么。
这两件事之间,隔着整个 Crypto Scam 行业。
Ethereum 发现 EOA 太弱。
怎么办?
EIP-7702。
现在 EOA 可以通过 authorization,把账户执行逻辑 delegate 给一段合约代码。
这确实可以实现非常强大的钱包能力。
但 EIP-7702 自己的安全章节说了一件极其值得玩味的话:
应用不应该直接给用户一个界面,让用户随意签 delegation authorization。
为什么?
因为被授权的代码可以拥有对账户极大的控制能力,而普通用户没有能力审核自己到底 delegate 给了什么代码。
EIP 原文甚至明确指出:
没有安全的通用界面能够简单解决这件事;很少有用户具备合理审查 delegation code 的专业能力。
请欣赏这个技术进化过程:
第一代:
私钥控制账户。
大家嫌功能太弱。
于是下一代:
用户可以签名,
让自己的账户执行另一个地址的代码。
然后协议设计者必须提醒钱包:
千万不要让用户自己随便签这个。
这已经非常 Ethereum 了。
增加一个能力。
然后增加一整套安全基础设施,
防止用户真的直接使用这个能力。
这是我认为 Ethereum 用户体验最“赛博朋克”的一个地方。
你想在 DEX 买一个 Token。
于是把交易广播出去。
交易还没上链。
但是搜索者可以看到:
这个人马上要买。
然后可以围绕你的交易排序。
例如最臭名昭著的:
Sandwich Attack。
攻击者:
先在你之前买。
你的交易把价格推高。
再在你后面卖。
利润来自哪里?
来自你获得了更差的成交价格。
Ethereum 官方 MEV 文档非常直接地承认,sandwich trading 会让用户遭受更大的 slippage 和更差的交易执行。
也就是说:
Ethereum 上一个用户准备交易时,默认模型长期以来近似是:
先把自己的交易意图广播给全世界
↓
告诉大家:
我要买什么
买多少
愿意接受多少滑点
↓
等待别人决定
有没有办法在你的交易前后插东西赚钱
这像什么?
你走进证券交易所,大喊:
我要市价买 500 万美元的这只股票!
然后所有高频交易员先听见。
交易完之后你再问:
为什么成交价格这么差?
别人回答:
这是 MEV。
一个普通人竟然需要知道什么是:
Private RPC
MEV Protection
Slippage
Sandwich
Builder
Searcher
才能比较安全地完成一次兑换。
我们居然把这种系统叫:
Permissionless Finance。
确实 permissionless。
连抢你滑点的人也 permissionless。
DEX 还有一个神奇设置:
Slippage Tolerance
0.5%
1%
5%
普通用户想表达的是:
帮我用 1000 USDC 买 ETH。
Ethereum DeFi 要他回答:
在交易真正执行的时候,如果市场状态发生变化,你允许最终结果比当前 Quote 差多少?
这实际上是在让用户自己配置:
交易执行保护参数。
设太低:
交易 revert。
Gas 可能白花。
设太高:
可能遭遇差价格,甚至扩大 sandwich 的盈利空间。
这是什么用户体验?
用户需要在:
交易失败风险
和:
被别人抽取价值风险
之间手动调一个滑块。
然后 UI 告诉你:
建议 0.5%。
非常现代。
Nonce 难用?
钱包加:
Speed Up
Cancel
Gas 难懂?
钱包自动估算。
MEV 有问题?
加:
MEV Protection
Approve 危险?
钱包做:
Allowance Warning
签名看不懂?
钱包开始模拟交易。
L2 太多?
钱包隐藏网络。
账户太弱?
4337。
Gas Token 麻烦?
Paymaster。
EOA 太弱?
7702。
你会发现 Ethereum 的一个惊人规律:
底层每留下一个不适合普通人的抽象,上层就需要再生长一整套产品,把这个抽象藏起来。
然后我们把隐藏复杂度所产生的新基础设施称为:
Innovation。
看看 4337 到底在解决什么:
Gas Sponsorship。
Batch Calls。
Smart Account。
自定义认证。
账户恢复。
ERC-20 支付 Gas。
听起来很好。
但是这些需求其实证明了一件事情:
原始 Ethereum Transaction 根本不够用了。
于是 4337 做了一件极其 Ethereum 的事情。
不改底层。
在底层交易系统上面:
重新实现一套类似交易的系统。
它甚至故意不把自己的对象叫 Transaction。
而叫:
UserOperation
然后加入:
UserOperation
Bundler
EntryPoint
Paymaster
Factory
Aggregator
UserOp Mempool
ERC-4337 官方规范明确写着,它通过一个额外 mempool、UserOperation、Bundler 以及 EntryPoint,避免修改 Ethereum consensus layer。
这就像一个操作系统的进程模型实在不好用了。
但不能改 ABI。
怎么办?
在用户态重新造一个:
准操作系统。
然后终于实现:
普通用户不需要理解 Gas。
多么感人。
为了让用户终于不用理解 Ethereum,
Ethereum 先在 Ethereum 上面又实现了一层 Ethereum。
用户看到:
Gas Sponsored
很简单。
后台是什么?
Paymaster。
Paymaster 要先往 EntryPoint 存 ETH。
Bundler 要判断这个 Paymaster 愿不愿意付款。
还需要模拟 UserOperation。
需要检查验证逻辑。
Paymaster 可能造成 DoS。
Bundler 甚至需要对某些实体做 reputation / staking 约束。
也就是说为了实现一个 Web2 世界平平无奇的功能:
商家帮用户支付操作成本。
Ethereum 搭出来的是:
User
↓
Smart Account
↓
UserOperation
↓
Bundler
↓
Paymaster Validation
↓
EntryPoint
↓
Ethereum Transaction
↓
Block
用户最后看到:
Fee: $0
然后大家说:
看,Web3 UX 已经跟 Web2 一样了。
是的。
只需要在按钮下面埋七层基础设施。
L2 流动性割裂已经被说烂了。
真正更值得吐槽的是:
Ethereum 生态把“我有多少钱”从一个标量变成了一个向量。
以前:
balance = 10,000 USDC
现在实际上更接近:
balance = {
Ethereum: 2000,
Base: 3500,
Arbitrum: 4000,
Optimism: 500
}
甚至这还没包括:
native USDC。
bridged USDC。
其他桥的 representation。
所以钱包里显示:
Total Balance: $10,000
这其实是一种 UI 幻觉。
因为这 10,000 美元:
并不是任意时刻都具有同样的可使用性。
资产总额相同,
但状态空间不同。
你能否马上用它,
取决于:
哪个 Rollup。
哪个 Token Contract。
有没有 Gas。
应用部署在哪。
有没有 bridge liquidity。
这才是 L2 最大的 UX 失败。
Ethereum 把“余额”这种人类几千年都理解得非常清楚的概念,重新变成了一道分布式系统题。
EOA 有个很大的好处:
同一把私钥可以在大量 EVM 网络得到同一个地址。
然后我们开始拥抱 Smart Account。
问题出现了:
智能账户是合约。
合约地址取决于部署机制、factory、salt、init code 等条件。
为了让同一个智能账户能够在不同链拥有一致地址,生态甚至需要专门依赖 CREATE2、确定性部署 factory、counterfactual deployment 等机制。
EIP-7955 的动机甚至直接写明:
让合约在多个链拥有相同地址和代码,本身就是一个困难问题。
所以我们走过了一条很奇妙的路线:
EOA:
功能太简单,
但地址很好理解。
↓
Smart Account:
功能终于丰富了。
↓
等等,
现在得解决怎么让我的账户
在不同链还是“同一个账户”。
Ethereum 的很多进步都有这种味道:
解决旧问题,顺便创造一个更高级的新问题。
今天钱包越来越强调:
Transaction Simulation。
为什么?
因为用户根本看不懂:
to
value
data
也看不懂合约调用到底最终会产生什么状态变化。
所以钱包开始在真正发送之前:
先模拟执行一次。
然后告诉用户:
预计你会失去 100 USDC。
预计你会得到 0.03 ETH。
这当然是巨大的 UX 进步。
但它同时也是一个极其讽刺的事实:
Ethereum 的原始交易格式提供给用户的信息,
已经不足以让一个普通人知道自己签名以后究竟会发生什么。
于是钱包需要运行 EVM。
需要追踪内部调用。
需要解析 Token Transfer。
需要识别恶意合约。
再把这一切翻译成人话。
我们最终造出了一个惊人架构:
人
↓
钱包
↓
交易模拟器
↓
RPC
↓
EVM
↓
智能合约
↓
再回到钱包
↓
告诉人类:
“你刚才想做的事情大概是这个。”
一个确认按钮背后,
运行着半套区块链分析系统。
如果 Ethereum 是一个创业公司内部系统:
重构。
删掉 EOA。
统一账户模型。
改 Token 标准。
重新设计 Gas。
废弃 Legacy Transaction。
重新设计权限。
甚至重新设计执行模型。
但是 Ethereum 做不到。
原因恰恰是它成功了。
数千亿美元资产。
十几年历史。
无数合约。
无数钱包。
无数假设。
全部必须继续工作。
所以:
ETH 不是 ERC-20?
不能重来。
包一个 WETH。
EOA 太弱?
不能删。
加 4337。
4337 不够原生?
再加 7702。
账户没有 Gas?
加 Paymaster。
公开 mempool 被 MEV?
搞 Private Orderflow。
Nonce 体验差?
钱包做 transaction replacement。
Approve 危险?
钱包做提醒、simulation、revoke。
多 L2 太碎?
搞 Chain Abstraction。
你会发现:
Ethereum 已经很少真正消灭复杂度。
它更擅长的是:
把旧复杂度包起来,再增加一个抽象层。
“Ethereum 难用”其实还是在替它说话。
因为“难用”听起来像:
UI 还需要优化。
按钮还需要调整。
新手教程还需要完善。
不是。
更准确的说法应该是:
Ethereum 把一个分布式计算机应该自己承担的复杂性,大规模外包给了钱包、应用开发者,最后不可避免地外包给用户。
于是一个普通人只是想:
拿 USDC 买 ETH。
背后可能隐含着:
你的 ETH 是不是 WETH?
你在哪条链?
你有没有 Native ETH?
Allowance 够不够?
要不要 Approve?
Approve 多少?
Gas 多少?
Base Fee 多少?
Priority Fee 多少?
Nonce 是多少?
前面有没有 Pending Transaction?
Slippage 设多少?
会不会被 Sandwich?
交易是否会 Revert?
钱包模拟结果可信吗?
走 Public RPC 还是 Protected RPC?
这是 Transaction 还是 UserOperation?
谁是 Bundler?
有没有 Paymaster?
然后行业告诉你:
Mass Adoption is coming.
这里的“蠢”不是贬义。
恰恰是技术成功的最高境界。
你刷信用卡的时候,
不需要理解 ISO 8583。
你用 HTTPS 的时候,
不需要检查 TLS handshake。
你发邮件的时候,
不需要知道 SMTP relay。
你打开手机的时候,
不需要知道虚拟内存页表。
技术越成熟,
用户需要知道的越少。
而 Ethereum 很长时间以来恰恰反过来。
使用得越深入,
需要理解的东西越多。
最后一个所谓“Crypto Native User”,其实是什么?
不是一个普通消费者。
而是一个被训练出来的:
兼职钱包安全工程师 + Gas 交易员 + RPC 运维 + 智能合约审计员 + 跨链清算员。
他知道怎么换 RPC。
知道怎么改 nonce。
知道怎么 replacement transaction。
知道怎么 revoke allowance。
知道 ETH 和 WETH 的区别。
知道怎么看 Contract Address。
知道怎么避开 sandwich。
知道什么情况下交易 revert 还会烧 Gas。
知道什么时候该用 bridge。
甚至知道什么叫 Bundler 和 Paymaster。
然后这个人转过头来对新人说:
Ethereum 其实不难,用几次就习惯了。
这句话本身,
可能就是 Ethereum 用户体验失败最好的墓志铭。
不是 Ethereum 终于变简单了。
是你终于被它训练成了协议工程师。