你在链上换币,设了 1% 的滑点容忍。交易成功了,成交价正好差 1%。
不是运气不好。是有人看到了你这笔还没执行的交易,抢在你前面买、等你成交推高价格、再立刻卖掉——他赚走的那部分,正好是你允许的最大滑点。
第十八篇讲的执行层是交易所内部的:加仓、止损、滑移、异常处理。这一篇讲链上的执行层。
打个比方
在交易所下单,像是把纸条投进一个不透明的箱子,撮合在里面完成。
在链上下单,像是把你的出价举过头顶,在拍卖厅里走一圈,然后才轮到你举牌。所有人都提前看到了你要做什么、愿意出多少。
⭐ 区别就是这一条:在交易所,你的订单进入的是一个私有的撮合引擎;在链上,你的交易在被执行之前,是公开的。这一篇的大部分内容都是从这一条派生出来的——包括开头那 1%。
对有开发背景的人,这一篇也是最容易直接转化成工具的一篇。
本文仅用于记录和普及基础知识,不构成投资建议。
一、Gas 机制
EIP-1559 之后的费用结构
2021 年 8 月的伦敦升级改变了以太坊的收费模型:
总费用 = Gas 用量 × (基础费 Base Fee + 优先费 Priority Fee)
基础费(Base Fee):
- 由协议根据上一个区块的拥堵程度自动调整
- ⭐ 被销毁,不给任何人
- 区块用量超过目标值则上调,低于则下调(每块最多变动 12.5%)
优先费(Priority Fee,也叫 tip):
- 你自愿给出块者的小费
- ⭐ 决定你的交易被打包的优先级
还有一个 Max Fee(你愿意支付的上限)。实际扣费是 Base Fee + Priority Fee,多余的退还。
⚠️ 交易失败也要付 Gas
这是新手最容易被打到的一条:
Gas 支付的是「计算被执行」,不是「结果符合预期」。
⚠️ 这一条第一次被打到的人几乎都会觉得不合理——没成交凭什么收钱?
换个角度就说得通了:你付的不是"成交费",是占用全网算力的费用。矿工/验证者确实为你跑完了这段计算,只是跑到一半发现条件不满足。这就像打车打到一半你说不去了——车确实开了那段路。
理解这一条的实际价值在于它的推论:在拥堵时反复重试是有成本的,而失败率最高的时刻,恰恰也是 Gas 最贵的时刻。这两件事同时变坏,不是巧合——它们是同一个原因(拥堵)造成的。
交易 revert(比如滑点超限、余额不足、被抢跑)时,已经消耗的 Gas 不退。在网络拥堵时,一笔失败的交易可能花掉几十美元。
推论:在拥堵时反复重试是有成本的,而且失败率越高的时刻通常正是费用最高的时刻。
Gas Limit vs Gas Used
Gas Limit:你授权的上限
Gas Used :实际消耗
设太低 → 执行到一半 out of gas → 交易失败,Gas 照扣
设太高 → 不会多扣(只按实际用量),但会占用你的余额做预扣
所以 Gas Limit 宁可设高一点。 钱包的自动估算在与新合约交互时可能不准。
二、Nonce 与卡单
Nonce 是什么
每个地址的交易有一个严格递增的序号(nonce)。必须按顺序执行:nonce 5 没确认,nonce 6 就算费用给得再高也不会被打包。
这导致一个常见困境
你发了一笔交易,费用给低了 → 卡在内存池里
你又发了三笔 → 全部排在它后面 → 全卡住
两种处理方式
| 操作 | 做法 | 结果 |
|---|---|---|
| 加速 | 用相同 nonce 重发,提高优先费 | 原交易被替换,新的被打包 |
| 取消 | 用相同 nonce 发一笔转 0 给自己的交易 | 占掉这个 nonce,原交易作废 |
⚠️ 钱包上的「加速」按钮做的就是第一件事——它不是让原交易更快,是用一笔新交易替换掉它(替换通常要求费用有最低涨幅,常见是 10%)。
⚠️ 在替换成功之前,原交易仍可能被打包。 加速和取消都不是保证,只是竞争。
开发角度
自己管理 nonce 时,pending 和 latest 两种查询会给出不同的值。批量发交易需要自己维护 nonce 序列,否则并发请求会拿到相同的 nonce 而互相替换。
三、抢跑与防护
⭐ 这一节就是"举牌前先喊出出价"的直接后果:你的意图先于你的成交被公开。下面的防护手段,本质上全是在处理同一个问题——要么别喊(私有通道),要么喊了也没用(限制可提取的空间)。
问题的根源
第四篇讲过 MEV 和三明治攻击。核心机制是:
交易在被执行前,先进入公开的内存池(mempool)。任何人都能看到你要做什么,并抢在你前面。
这是链上和交易所最本质的差别之一。在交易所,你的限价单挂出去之后是公开的,但你还没下的单是私密的;在链上,你已经签名、还没执行的交易是公开的。
三明治攻击的经济学
你要买入,滑点容忍设为 5%
↓
攻击者看到你的交易,抢先买入把价格推高
↓
你的交易以更差的价格成交(在你容忍的 5% 之内)
↓
攻击者立刻卖出,赚走差价
⭐ 关键认知:你设置的滑点容忍度,就是你愿意付给攻击者的上限。
这就是第四篇说滑点设置是双刃剑的原因:
设太低 → 交易失败,Gas 白付
设太高 → 明确告诉攻击者可以拿走多少
防护手段
| 手段 | 原理 | 代价 |
|---|---|---|
| 私有内存池 | 交易不进公开 mempool,直接给出块者 | 依赖该服务的可用性与诚实性 |
| 收紧滑点 | 降低可被榨取的空间 | 失败率上升,Gas 损耗 |
| 拆单 | 减少单笔的可榨取价值 | Gas 成本翻倍 |
| 避开拥堵时段 | 竞争少,价格影响小 | 不总是可选 |
| 用聚合器 | 路由分散到多个池 | 多一层合约风险 |
⚠️ 私有内存池不是万灵药:你把交易交给了一个特定的中继方。它解决的是「公开可见」的问题,代价是引入了一个新的信任对象。
四、L2 特有风险
L2 便宜、快,但它引入了一整套主网上不存在的风险。
排序器(Sequencer)
大多数 L2 目前由单一排序器决定交易顺序:
🔴 单点故障 —— 排序器宕机,整条链停止出块
🔴 审查能力 —— 理论上可以拒绝打包特定交易
🔴 你的「已确认」是排序器给的软确认,不是 L1 的最终性
「交易已上链」在 L2 上有两个不同的含义:被排序器接受(快,但可回滚),和被 L1 最终确认(慢,才是真的)。
⚠️ 大额操作应该按第二个含义来判断,尤其是跨链和出金。
强制包含(Forced Inclusion)
设计良好的 L2 提供一个逃生通道:直接向 L1 合约提交交易,绕过排序器。这是排序器审查你时的最后手段。
□ 这条 L2 有没有强制包含机制?
□ 延迟多久生效?
□ 我实际知道怎么操作吗?
多数人从没试过,也不知道流程。在需要它的时候才去学,通常已经晚了。
⭐ Optimistic Rollup 的七天挑战期
这是最需要提前知道的一条:
Optimistic Rollup 假设提交的状态是对的,
留出一个挑战窗口(典型 7 天)让人提交欺诈证明。
→ 通过官方桥从 L2 提款到 L1,需要等这 7 天。
你不能在需要用钱的时候临时决定提款。
而市面上的「快速提款」服务,做法是有人提前垫付给你,自己去等那 7 天。这意味着:
快速提款 = 你把 7 天的等待,换成了对第三方的信任敞口。
这就回到了第十七篇的跨链桥风险。它不是一个免费的便利。
ZK Rollup 的提款依赖有效性证明的生成和验证,通常快得多,但也不是即时的。
其他 L2 要点
□ 数据可用性依赖 L1 —— L1 出问题,L2 的可验证性受影响
□ 同一个项目在不同 L2 上的合约,可能是不同的部署,不同的权限
□ Gas 代币可能不同
□ ⚠️ L2 上的「同名代币」不一定是官方桥接的那个
最后一条很重要:一条 L2 上可能同时存在同一个代币的多个版本(官方桥的、第三方桥的、有人自己发的同名的)。只认合约地址。
五、跨链与地址
同一个地址在不同链上
EVM 兼容链共用地址格式,所以你的地址在以太坊、BNB Chain、Arbitrum 上看起来一样。这带来两个后果:
好的:同一个私钥管所有链。
坏的:⚠️ 转错链 = 资产可能永久丢失。
交易所提币时选错网络
→ 资产到了另一条链上的同一个地址
→ 如果你控制私钥,通常还能救(导入钱包,切换网络)
→ ⚠️ 如果目标地址是交易所的充值地址(合约或它不支持的链),大概率救不回来
第十四篇的规则在这里重复一次:大额转账先小额测试,而且要测完整链路。
合约地址在不同链上不是同一个实体
同名协议在不同链的部署是独立的合约,权限配置、版本、审计状态都可能不同。在 A 链上尽调过,不代表 B 链上的那个是安全的。
六、开发者角度的实操要点
如果你会自己写脚本调链上接口,这几条能直接省下真金白银(第 44 篇讲的是同一类问题在数据侧的版本):
RPC 节点
⚠️ 公共 RPC 有限流,高频调用会被丢请求
⚠️ 不同节点的 pending 状态可能不一致
⚠️ 免费节点可能返回缓存数据 —— 对交易决策是致命的
→ 涉及资金的操作用自建节点或付费服务
交易模拟
在真正发送前,用 eth_call 或模拟服务跑一遍,能提前发现 revert、看到实际的输出。
模拟一次的成本是零,一笔失败交易的成本是真金白银。
⚠️ 但模拟基于当前状态。你的交易执行时,状态可能已经变了(这正是抢跑的原理)。模拟能排除确定性错误,不能排除竞争性失败。
事件日志 vs 交易回执
交易回执(receipt):成功/失败、Gas 用量
事件日志(logs) :合约主动 emit 的结构化数据
→ 判断「实际成交了多少」,要读事件日志,不能只看回执状态
→ ⚠️ 交易「成功」不代表你想要的事情发生了
最后这句值得展开:一笔交易可以成功执行,但因为路由、部分成交、或合约内部的容错逻辑,结果和你预期的不同。回执的 status = 1 只说明没有 revert。
七、这一篇的结论
- 链上和交易所最本质的差别:你已签名、未执行的交易是公开的。 这派生出了抢跑的全部问题。
- EIP-1559:基础费自动调整并被销毁,优先费给出块者并决定打包优先级。
- ⚠️ 交易失败照样扣 Gas。 Gas 付的是「计算被执行」,不是「结果符合预期」。而失败率最高的时刻通常正是费用最高的时刻。
- Gas Limit 宁可设高——只按实际用量扣,但设低了会 out of gas 并且照样扣钱。
- Nonce 严格递增,一笔卡住后面全卡。 加速 = 用相同 nonce 替换,不是让原交易变快;⚠️ 替换成功前原交易仍可能被打包。
- ⭐ 你设置的滑点容忍度,就是你愿意付给抢跑者的上限。 设太低失败白付 Gas,设太高等于明码标价。
- 私有内存池解决公开可见问题,代价是引入一个新的信任对象。 不是免费的。
- L2 的「已确认」有两个含义:排序器软确认(快、可回滚)和 L1 最终确认(慢、才是真的)。大额操作按第二个判断。
- 排序器目前多为单点:可宕机、理论上可审查。强制包含是逃生通道——但要在需要之前就知道怎么用。
- ⭐ Optimistic Rollup 官方桥提款要等约 7 天。 「快速提款」是把等待换成了对第三方的信任敞口,回到第十七篇的桥风险。
- 只认合约地址。 同一条 L2 上可能有同名代币的多个版本。同名协议在不同链是独立部署,权限和审计状态可能不同。
- 开发实操:用付费/自建 RPC(免费节点的缓存数据对交易决策是致命的)、发送前先模拟(成本为零)、读事件日志而不是只看回执——
status = 1只说明没有 revert,不说明你想要的事发生了。