以太坊有多难用
很多人说 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 很贵”这种已经被说烂的问题。
我们讨论一些更荒唐的东西。
一、以太坊最荒诞的事实之一:ETH 甚至不是一个正常的 Ethereum Token
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 原生货币:ETHEthereum 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 有一种很神奇的“取消交易”方式:再发一笔交易
接下来是我认为 Ethereum 用户体验史上最有喜剧效果的设计之一。
假设你发了一笔交易。
Gas 给低了。
它卡在 mempool 里。
你后悔了。
你点击:
Cancel Transaction
一个正常人理解的“取消”是什么?
把刚才那笔交易撤回来。
Ethereum 的取消是什么?
再发一笔新的交易。
而且要:
- 使用和原交易完全相同的 nonce;
- 给更高的手续费;
- 通常给自己发送 0 ETH;
- 让新交易抢在旧交易之前被打包;
- 依靠“同一个 nonce 只能执行一次”的规则,把旧交易挤掉。
这不是段子。
MetaMask 官方给高级用户的手动取消方法就是:
给自己发送一笔 0 ETH 交易,使用旧交易相同的 nonce,并提高 Gas。
所以 Ethereum 里的:
Cancel真正的语义其实是:
用另一笔更贵的交易,和原来的交易争夺同一个序号,争取先把这个序号消耗掉。甚至“取消”都不是协议里的真正操作。
它是一场竞速。
你的旧交易:
我要执行。
你的取消交易:
不,你别执行,让我先执行。
然后用户还要额外付钱,请验证者优先执行:
“请不要执行我刚才那笔交易。”
这已经不是 UX 不好。
这是一个人在操作数据库事务日志。
三、最精彩的是:一笔交易卡住,可以把你后面的交易全部堵死
为什么刚才那套骚操作有效?
因为 Ethereum EOA 的 transaction nonce 是严格递增的。
比如你的账户已经执行到:
nonce = 100接下来:
101102103104必须按照顺序。
官方文档把 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 需要用户教育。
教育个鬼。
这是数据库内部细节跑出来要求用户手工维护。
四、Ethereum 甚至把“加速交易”实现成了“重新发一次交易”
“Speed Up”也一样。
你看到钱包里有个:
Speed Up
看起来像是修改了原交易。
实际上通常是在干什么?
构造另一笔拥有相同 nonce、但出价更高的 replacement transaction。
MetaMask 明确说明,加速操作会重新提交交易,并复用原交易的 nonce,提高 Gas 使它更有机会先被打包。
Ethereum 甚至曾经有人专门提交 EIP-2831 来标准化这种 transaction replacement 的通知,因为 replacement 机制会给 dapp 开发者带来追踪问题。
所以 Ethereum 钱包里的两个按钮:
Speed UpCancel背后的真实实现分别是:
同 nonce,发另一笔更贵的相同交易和:
同 nonce,发另一笔更贵的空交易如果一个普通用户必须知道这件事情,他会觉得整个系统像 1990 年代写出来的。
我们只是用漂亮 UI 把它遮住了。
五、Ethereum 还发明了一种消费体验:东西没买到,钱照扣
假设你去便利店买东西。
付款失败。
东西没拿到。
收银员说:
交易失败了。
你:
好。
收银员:
但是我们还是扣了你 20 块。
你:
为什么?
收银员:
因为我们刚才尝试处理你的付款,产生了计算成本。
这就是 Ethereum。
Smart Contract 执行一半:
REVERT状态修改回滚。
Token 没交换。
NFT 没买到。
借款没借出来。
但已经消耗的 Gas:
不给你退。
官方 Gas 文档明确说明:如果交易执行过程中耗尽 Gas,状态修改会回滚,但用于执行工作的 Gas 仍然被消耗;REVERT 虽然不会像早期失败机制那样烧掉全部剩余 Gas,但已经执行掉的计算成本仍然存在。
工程上当然合理。
CPU 已经算了。
节点已经执行了。
所以收钱。
问题是:
用户为什么要承担应用执行路径预测失败的成本?
Web2 里服务器写了 Bug:
公司付服务器费。
Ethereum:
智能合约写了 Bug,或者状态刚好变化导致交易 revert:
用户付服务器费。
这是 Crypto 特别伟大的商业模式:
程序执行失败了,
账单寄给调用程序的人。
六、Ethereum 的手续费不是“手续费”,而是一场实时计算资源竞价
再看 Gas。
普通用户希望看到:
手续费:$2Ethereum 底层给他的东西是:
gasLimitmaxFeePerGasmaxPriorityFeePerGasbaseFeePerGasEIP-1559 以后,一笔 Type-2 交易甚至明确包含:
max_priority_fee_per_gasmax_fee_per_gas而 Base Fee 根据区块利用率动态变化。
翻译成人话:
你不是在支付一个手续费。
你是在说:
我最多愿意为一个计算单位支付多少钱;
其中最多愿意拿多少钱给出块者当小费;
协议还会按照当前拥堵程度产生一个动态基础价格;
如果价格合适,请帮我执行这段程序。
一个用户只是想:
Swap 100 USDC。
结果钱包底层实际上在帮他参与:
全球分布式计算资源的实时拍卖。
然后大家夸:
钱包现在都会自动估 Gas,用户不用懂。
很好。
这句话本身就像在说:
我们这辆车的发动机启动需要手工调节喷油比例,不过现在仪表盘已经帮用户自动调了,所以体验还不错。
真正正常的 UX 是:
这个旋钮从一开始就不应该存在于用户的认知世界里。
七、Approve 是一个堪称反人类的权限模型
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 的生产环境。
八、最荒唐的安全建议之一:不要相信 Token 的名字,要核对 40 位十六进制地址
普通金融系统识别一种资产:
USDCrypto:
USDC很好。
然后发现任何人都能部署一个 Token:
name = USD Coinsymbol = USDClogo = 看起来也一样怎么办?
Crypto 老用户会非常认真地告诉新人:
别看名字。
一定要确认 Contract Address。
于是识别一种“美元”,最终需要识别:
0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48这种字符串。
我们甚至把:
会检查 Contract Address
当成了一种 Crypto 素养。
不。
这不是用户素养。
这是命名系统基本不存在以后产生的生存技能。
正常系统面对:
Microsoft.com不会告诉用户:
域名容易被骗,为安全起见请记住服务器的 IPv6 地址。
Web3 差不多就是这么干的。
九、Ethereum 的“签名”可能是整个消费者互联网里最危险的确认按钮
普通产品:
Confirm通常意味着:
确认眼前这件事情。
Ethereum 钱包:
Sign可能意味着:
证明你控制这个地址。
可能是登录。
可能是订单。
可能是 Permit。
可能是授权。
可能是 Typed Data。
可能最终让另一个协议拥有某种资产操作权。
用户看到的共同动作却都是:
签。
真正离谱的是:
Ethereum 的安全模型建立在密码学签名非常可靠的基础上,
但用户安全却经常建立在:
希望用户能读懂自己到底在签什么。
密码学可以证明:
确实是你签的。
密码学证明不了:
你知不知道自己签了什么。
这两件事之间,隔着整个 Crypto Scam 行业。
十、然后 EIP-7702 把这件事情推向了新的境界:一次授权,可以让 EOA 开始执行别人的代码
Ethereum 发现 EOA 太弱。
怎么办?
EIP-7702。
现在 EOA 可以通过 authorization,把账户执行逻辑 delegate 给一段合约代码。
这确实可以实现非常强大的钱包能力。
但 EIP-7702 自己的安全章节说了一件极其值得玩味的话:
应用不应该直接给用户一个界面,让用户随意签 delegation authorization。
为什么?
因为被授权的代码可以拥有对账户极大的控制能力,而普通用户没有能力审核自己到底 delegate 给了什么代码。
EIP 原文甚至明确指出:
没有安全的通用界面能够简单解决这件事;很少有用户具备合理审查 delegation code 的专业能力。
请欣赏这个技术进化过程:
第一代:
私钥控制账户。大家嫌功能太弱。
于是下一代:
用户可以签名,让自己的账户执行另一个地址的代码。然后协议设计者必须提醒钱包:
千万不要让用户自己随便签这个。
这已经非常 Ethereum 了。
增加一个能力。
然后增加一整套安全基础设施,
防止用户真的直接使用这个能力。
十一、公开 mempool:你甚至要先公开告诉全世界你准备怎么交易,然后祈祷没人从你身上赚钱
这是我认为 Ethereum 用户体验最“赛博朋克”的一个地方。
你想在 DEX 买一个 Token。
于是把交易广播出去。
交易还没上链。
但是搜索者可以看到:
这个人马上要买。
然后可以围绕你的交易排序。
例如最臭名昭著的:
Sandwich Attack。
攻击者:
先在你之前买。
你的交易把价格推高。
再在你后面卖。
利润来自哪里?
来自你获得了更差的成交价格。
Ethereum 官方 MEV 文档非常直接地承认,sandwich trading 会让用户遭受更大的 slippage 和更差的交易执行。
也就是说:
Ethereum 上一个用户准备交易时,默认模型长期以来近似是:
先把自己的交易意图广播给全世界↓告诉大家:我要买什么买多少愿意接受多少滑点↓等待别人决定有没有办法在你的交易前后插东西赚钱这像什么?
你走进证券交易所,大喊:
我要市价买 500 万美元的这只股票!
然后所有高频交易员先听见。
交易完之后你再问:
为什么成交价格这么差?
别人回答:
这是 MEV。
一个普通人竟然需要知道什么是:
Private RPCMEV ProtectionSlippageSandwichBuilderSearcher才能比较安全地完成一次兑换。
我们居然把这种系统叫:
Permissionless Finance。
确实 permissionless。
连抢你滑点的人也 permissionless。
十二、Slippage 本身就是一个非常荒唐的用户输入项
DEX 还有一个神奇设置:
Slippage Tolerance0.5%1%5%普通用户想表达的是:
帮我用 1000 USDC 买 ETH。
Ethereum DeFi 要他回答:
在交易真正执行的时候,如果市场状态发生变化,你允许最终结果比当前 Quote 差多少?
这实际上是在让用户自己配置:
交易执行保护参数。
设太低:
交易 revert。
Gas 可能白花。
设太高:
可能遭遇差价格,甚至扩大 sandwich 的盈利空间。
这是什么用户体验?
用户需要在:
交易失败风险和:
被别人抽取价值风险之间手动调一个滑块。
然后 UI 告诉你:
建议 0.5%。
非常现代。
十三、最 Ethereum 的产品逻辑:系统先制造问题,再让钱包发明“聪明模式”遮住它
Nonce 难用?
钱包加:
Speed UpCancelGas 难懂?
钱包自动估算。
MEV 有问题?
加:
MEV ProtectionApprove 危险?
钱包做:
Allowance Warning签名看不懂?
钱包开始模拟交易。
L2 太多?
钱包隐藏网络。
账户太弱?
4337。
Gas Token 麻烦?
Paymaster。
EOA 太弱?
7702。
你会发现 Ethereum 的一个惊人规律:
底层每留下一个不适合普通人的抽象,上层就需要再生长一整套产品,把这个抽象藏起来。
然后我们把隐藏复杂度所产生的新基础设施称为:
Innovation。
十四、ERC-4337 更像一个证据:Ethereum 原始交易模型已经不适合现代用户了
看看 4337 到底在解决什么:
Gas Sponsorship。
Batch Calls。
Smart Account。
自定义认证。
账户恢复。
ERC-20 支付 Gas。
听起来很好。
但是这些需求其实证明了一件事情:
原始 Ethereum Transaction 根本不够用了。
于是 4337 做了一件极其 Ethereum 的事情。
不改底层。
在底层交易系统上面:
重新实现一套类似交易的系统。
它甚至故意不把自己的对象叫 Transaction。
而叫:
UserOperation然后加入:
UserOperationBundlerEntryPointPaymasterFactoryAggregatorUserOp MempoolERC-4337 官方规范明确写着,它通过一个额外 mempool、UserOperation、Bundler 以及 EntryPoint,避免修改 Ethereum consensus layer。
这就像一个操作系统的进程模型实在不好用了。
但不能改 ABI。
怎么办?
在用户态重新造一个:
准操作系统。
然后终于实现:
普通用户不需要理解 Gas。
多么感人。
为了让用户终于不用理解 Ethereum,
Ethereum 先在 Ethereum 上面又实现了一层 Ethereum。
十五、甚至 4337 的“用户不用管 Gas”,背后都需要一整个影子金融系统
用户看到:
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 一样了。
是的。
只需要在按钮下面埋七层基础设施。
十六、Layer 2 最荒诞的地方不是“流动性割裂”,而是 Ethereum 连“余额”这个概念都搞得不再完整
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 的很多进步都有这种味道:
解决旧问题,顺便创造一个更高级的新问题。
十八、Ethereum 的终极荒诞:最先进的密码学系统,最后却依靠钱包猜用户想干什么
今天钱包越来越强调:
Transaction Simulation。
为什么?
因为用户根本看不懂:
tovaluedata也看不懂合约调用到底最终会产生什么状态变化。
所以钱包开始在真正发送之前:
先模拟执行一次。
然后告诉用户:
预计你会失去 100 USDC。
预计你会得到 0.03 ETH。
这当然是巨大的 UX 进步。
但它同时也是一个极其讽刺的事实:
Ethereum 的原始交易格式提供给用户的信息,
已经不足以让一个普通人知道自己签名以后究竟会发生什么。
于是钱包需要运行 EVM。
需要追踪内部调用。
需要解析 Token Transfer。
需要识别恶意合约。
再把这一切翻译成人话。
我们最终造出了一个惊人架构:
人 ↓钱包 ↓交易模拟器 ↓RPC ↓EVM ↓智能合约 ↓再回到钱包 ↓告诉人类:“你刚才想做的事情大概是这个。”一个确认按钮背后,
运行着半套区块链分析系统。
十九、Ethereum 最大的问题已经不是“不够简单”,而是它几乎没有能力重新变简单
如果 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 真正可怕的不是难用
“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 终于变简单了。
是你终于被它训练成了协议工程师。