﻿---
title: "为什么有些代币授权必须先归零？"
description: "有些 ERC-20 代币拒绝将一个非零授权额度直接改为另一个非零值。本文说明何时必须先归零、两笔交易为何要依次确认，以及仍然存在的风险。"
image: "https://wiki.fcontext.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://wiki.fcontext.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 为什么有些代币授权必须先归零？

> 仅供教育参考，不构成投资建议；投资可能产生损失。

<a id="answer"></a>

## 直接答案

有些 ERC-20 实现会在现有授权额度和 `newAmount` 都非零时拒绝 `approve(spender, newAmount)`。对于这类代币，应先提交 `approve(spender, 0)` 并等待确认，然后才能提交新的非零授权。

并非所有 ERC-20 代币都必须这样做。ERC-20 将 `approve` 定义为覆盖当前额度，并建议客户端界面先归零，以缓解修改授权时的竞态；但为了兼容性，标准同时说明代币合约本身不应强制这一流程。部分已部署代币仍然实施了该限制。因此，先归零既是兼容性步骤，也是一个有用的检查点，但不能保证旧额度在归零交易确认前不会被使用。

<a id="mechanism"></a>

## 工作原理

1. 核对链、代币合约、所有者、支出方和预期金额。直接从代币合约读取 `allowance(owner, spender)`，不要只相信钱包显示的标签。
2. 如果额度已经是 `0`，只需提交一次目标授权。如果额度非零，直接替换为另一个非零值可能在标准实现上成功，也可能在要求先归零的代币上回滚。
3. 采用先归零流程时，提交 `approve(spender, 0)` 并等待成功回执。随后再次读取同一个所有者与支出方的额度，确认它是 `0`。
4. 重新检查代币余额、支出方和授权用途。只有确认无误后，才提交 `approve(spender, newAmount)`，并等待确认后再把新额度视为生效。
5. 核验最终额度，并检查期间发生的 `Transfer` 和 `Approval` 事件。成功的交易回执证明调用已执行，而当前合约状态才显示尚存的权限。

OpenZeppelin 的 `SafeERC20.forceApprove` 为合约实现了兼容性回退：它先尝试目标值；如果调用失败，则依次尝试 `0` 和目标值。这个辅助函数修改的是调用合约自身的授权额度，不会自动修复用户钱包的授权，也不能取代对交易顺序和最终状态的核验。

<a id="example"></a>

## 示例

某所有者给支出方的额度为 `1000` 枚代币，希望降到 `100`。对于要求先归零的代币，直接调用 `approve(spender, 100)` 会回滚，因此链上额度仍是 `1000`；回滚的调用不会只完成部分状态更新。

所有者改为提交 `approve(spender, 0)`。在这笔交易确认前，支出方使用了 `400`，额度剩下 `600`；随后确认的归零交易把剩余额度替换为 `0`。所有者检查减少后的代币余额，再决定是否授予新的 `100`。如果授予，支出方已经使用 `400`，之后还可最多使用 `100`。先归零让所有者在新权限授出前发现了中途支出，但不会撤销这笔支出。

<a id="risks"></a>

## 风险

- 旧额度在归零交易执行前仍可使用。支出方可能抢在待处理的撤销或降额交易前支出。
- 如果不等待第一笔确认就同时广播归零与替换交易，预期的检查点就会消失，也可能掩盖中途发生的支出。
- 链、代币地址或支出方地址错误，会创建或撤销另一项权限。代币符号不是唯一标识。
- 必须分两步时会产生两笔交易成本，而且任一笔都可能失败、被替换或长期待处理。不能仅凭已提交签名推断链上状态。
- 无限授权以及可升级或已被攻破的支出方可能危及未来存入的代币。应使用实际可行的最小额度，并在用完后核对剩余额度。
- 合约集成必须明确处理非标准返回值和授权行为。兼容性封装无法让不可信的支出方变得安全。

<a id="misconceptions"></a>

## 常见误区

- **每种 ERC-20 都要求先归零。** 标准建议客户端采用这一顺序，但说明代币合约不应强制执行；只有部分实现拒绝从非零值直接改为非零值。
- **先归零能彻底解决授权竞态。** 支出方仍可在归零交易确认前使用旧额度。
- **替换授权回滚后，旧额度就被清除了。** 回滚会撤销本次尝试的状态变更，因此原额度通常仍然存在。
- **同时发送两笔交易等同于等待确认。** 安全检查点来自确认并检查零额度状态，然后再决定是否提交替换授权。
- **断开网站连接就会撤销授权。** 钱包连接状态与代币合约中的链上授权额度彼此独立。

<a id="related"></a>

## 相关主题

- [ERC-20 授权竞态](/zh-cn/crypto/erc20-approval-race-condition/)
- [钱包授权](/zh-cn/crypto/wallet-approval/)
- [ERC-2612 Permit 的 nonce 与 deadline](/zh-cn/crypto/erc2612-permit-nonce-deadline/)
- [Permit2 签名风险](/zh-cn/crypto/permit2-signature-risk/)

<a id="sources"></a>

## 来源

- [ERC-20：代币标准](https://eips.ethereum.org/EIPS/eip-20) - Ethereum Improvement Proposals（查阅日期：2026-08-21）
- [ERC20 | OpenZeppelin 文档](https://docs.openzeppelin.com/contracts/5.x/api/token/erc20) - OpenZeppelin（查阅日期：2026-08-21）
- [SafeERC20.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol) - OpenZeppelin（查阅日期：2026-08-21）

Source: https://wiki.fcontext.com/zh-cn/crypto/token-approval-zero-first/index.mdx
