本文档介绍了 OpenZeppelin Contracts 库中与 ERC20 代币标准相关的接口、合约和实用程序。
你当前阅读的不是此文档的最新版本。5.x 是当前版本。
在 https://docs.openzeppelin.com/contracts/api/token/erc20 查看本文档效果更佳 |
这组接口、合约和实用程序都与 ERC20 Token Standard 相关。
有关 ERC20 token 的概述以及如何创建 token 合约的演练,请阅读我们的 ERC20 指南。 |
有一些核心合约实现了 EIP 中指定的行为:
此外,还有多个自定义扩展,包括:
ERC20Permit
: token 的 gasless approval。
ERC20Snapshot
: 有效存储过去的 token 余额,以便以后在任何时间点查询。
ERC20Burnable
: 销毁自己的 token 。
ERC20Capped
: 在铸造 token 时强制对总供应量设置上限。
ERC20Pausable
: 暂停 token 转移的能力。
最后,有一些实用程序可以以各种方式与 ERC20 合约交互。
SafeERC20
: 接口的包装器,无需处理布尔返回值。
TokenTimelock
: 为受益人持有 token ,直到指定时间。
以下相关 EIP 处于草案状态,可以在草案目录中找到。
这组核心合约旨在保持公正,允许开发人员访问 ERC20 中的内部函数(例如 _mint ),并以他们喜欢的方式将它们公开为外部函数。另一方面,ERC20 Presets(例如 ERC20PresetMinterPauser )使用固定的模式进行设计,以便为开发人员提供现成的、可部署的合约。 |
IERC20
EIP 中定义的 ERC20 标准接口。
函数
事件
totalSupply() → uint256
external返回现有 token 的数量。
balanceOf(address account) → uint256
external返回 account
拥有的 token 数量。
transfer(address recipient, uint256 amount) → bool
external将 amount
个 token 从调用者的帐户转移到 recipient
。
返回一个布尔值,指示操作是否成功。
发出一个 Transfer
事件。
allowance(address owner, address spender) → uint256
external返回 spender
将被允许代表 owner
通过 transferFrom
支配的剩余 token 数量。 默认为零。
当调用 approve
或 transferFrom
时,此值会发生变化。
approve(address spender, uint256 amount) → bool
external设置 amount
作为 spender
对调用者 token 的津贴。
返回一个布尔值,指示操作是否成功。
请注意,使用此方法更改津贴会带来风险,即有人可能会通过不幸的交易排序来同时使用旧津贴和新津贴。一种可能的解决方案是首先将消费者的津贴减少到 0,然后设置所需的值:https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 |
发出一个 Approval
事件。
transferFrom(address sender, address recipient, uint256 amount) → bool
external使用授权机制将 amount
个 token 从 sender
转移到 recipient
。 然后从调用者的授信额度中扣除 amount
。
返回一个布尔值,指示操作是否成功。
发出一个 Transfer
事件。
Transfer(address from, address to, uint256 value)
event当 value
个 token 从一个帐户(from
)转移到另一个帐户(to
)时发出。
请注意,value
可能为零。
Approval(address owner, address spender, uint256 value)
event当通过调用 approve
设置 spender
对 owner
的授权时发出。 value
是新的授权。
ERC20
IERC20
接口的实现。
此实现与 token 的创建方式无关。 这意味着必须在使用 _mint
的派生合约中添加供应机制。
有关通用机制,请参见 ERC20PresetMinterPauser
。
有关详细的撰写,请参见我们的指南如何实现供应机制。 |
我们遵循了通用的 OpenZeppelin 指南:函数在失败时恢复,而不是返回 false
。 尽管如此,此行为是常规的,并且不与 ERC20 应用程序的期望相冲突。
此外,在调用 transferFrom
时会发出 Approval
事件。
这允许应用程序仅通过侦听所述事件来重建所有帐户的授权。 EIP 的其他实现可能不会发出这些事件,因为规范中没有要求。
最后,添加了非标准的 decreaseAllowance
和 increaseAllowance
函数,以缓解围绕设置授权的众所周知的问题。 请参见 IERC20.approve
。
函数
事件
IERC20
constructor(string name_, string symbol_)
public为 name
和 symbol
设置值,使用默认值 18 初始化 decimals
。
要为 decimals
选择其他值,请使用 _setupDecimals
。
所有这三个值都是不可变的:它们只能在构造期间设置一次。
name() → string
public返回 token 的名称。
symbol() → string
public返回 token 的符号,通常是名称的简短版本。
decimals() → uint8
public返回用于获取其用户表示形式的小数位数。 例如,如果 decimals
等于 2
,则应向用户将 505
个 token 的余额显示为 5,05
(505 / 10 ** 2
)。
token 通常选择值 18,以模仿以太币和 Wei 之间的关系。 这是 ERC20
使用的值,除非调用 _setupDecimals
。
此信息仅用于 显示 目的:它绝不影响合约的任何算术,包括 IERC20.balanceOf 和 IERC20.Transfer 。 |
totalSupply() → uint256
public请参见 IERC20.totalSupply
。
balanceOf(address account) → uint256
public请参见 IERC20.balanceOf
。
transfer(address recipient, uint256 amount) → bool
public请参见 IERC20.Transfer
。
要求:
recipient
不能是零地址。
调用者必须至少具有 amount
的余额。
allowance(address owner, address spender) → uint256
public请参见 IERC20.allowance
。
approve(address spender, uint256 amount) → bool
public请参见 IERC20.approve
。
要求:
spender
不能是零地址。transferFrom(address sender, address recipient, uint256 amount) → bool
public请参见 IERC20.transferFrom
。
发出 Approval
事件,指示已更新的津贴。 EIP 中没有对此要求。 请参见 ERC20
开头的注释。
要求:
sender
和 recipient
不能为零地址。
sender
必须至少具有 amount
的余额。
调用者必须至少具有 sender
的 amount
token 的津贴。
increaseAllowance(address spender, uint256 addedValue) → bool
public以原子方式将授予 spender
的津贴增加调用者。
这是 approve
的替代方法,可以用作 IERC20.approve
中描述的问题的缓解措施。
发出 Approval
事件,指示已更新的津贴。
要求:
spender
不能是零地址。decreaseAllowance(address spender, uint256 subtractedValue) → bool
public以原子方式将授予 spender
的津贴减少调用者。
这是 approve
的替代方法,可以用作 IERC20.approve
中描述的问题的缓解措施。
发出 Approval
事件,指示已更新的津贴。
要求:
spender
不能是零地址。
spender
必须至少具有 subtractedValue
的调用者的津贴。
_transfer(address sender, address recipient, uint256 amount)
internal将 amount
个 token 从 sender
转移到 recipient
。
此内部函数等效于 transfer
,可用于例如实施自动 token 费用、削减机制等。
发出一个 transfer
事件。
要求:
sender
不能是零地址。
recipient
不能是零地址。
sender
必须至少具有 amount
的余额。
_mint(address account, uint256 amount)
internal创建 amount
个 token 并将它们分配给 account
,从而增加总供应量。
发出一个 transfer
事件,其中将 from
设置为零地址。
要求:
to
不能是零地址。_burn(address account, uint256 amount)
internal从 account
销毁 amount
个 token,从而减少总供应量。
发出一个 transfer
事件,其中将 to
设置为零地址。
要求:
account
不能是零地址。
account
必须至少具有 amount
个 token。
_approve(address owner, address spender, uint256 amount)
internal设置 amount
作为 spender
对 owner
的 token 的津贴。
此内部函数等效于 approve
,可用于例如为某些子系统设置自动津贴等。
发出一个 Approval
事件。
要求:
owner
不能是零地址。
spender
不能是零地址。
_setupDecimals(uint8 decimals_)
internal将 decimals
设置为默认值 18 以外的值。
此函数应仅从构造函数中调用。 与 token 合约交互的大多数应用程序都不会期望 decimals 发生更改,如果更改可能会导致错误。 |
_beforeTokenTransfer(address from, address to, uint256 amount)
internal在任何 token 转移之前调用的Hook。 这包括铸造和销毁。
调用条件:
当 from
和 to
均为非零值时,from
的 amount
个 token 将被转移到 to
。
当 from
为零时,将为 to
铸造 amount
个 token。
当 to
为零时,将销毁 from
的 amount
个 token。
from
和 to
永远不会都为零。
要了解有关Hook的更多信息,请转到 Using Hooks。
ERC20Snapshot
此合约使用快照机制扩展了 ERC20 token。 创建快照后,将记录当时的点余额和总供应量,以供以后访问。
这可用于安全地创建基于 token 余额的机制,例如免信任股息或加权投票。 在简单的实现中,可以通过重用来自不同帐户的相同余额来执行“双重支出”攻击。 通过使用快照来计算股息或投票权,这些攻击不再适用。 它也可以用于创建有效的 ERC20 分叉机制。
快照由内部 _snapshot
函数创建,该函数将发出 Snapshot
事件并返回快照 id。 要获取创建快照时的总供应量,请使用快照 id 调用函数 totalSupplyAt
。 要获取创建快照时的帐户余额,请使用快照 id 和帐户地址调用 balanceOfAt
函数。
快照是高效的。 快照创建是 O(1)。 从快照中检索余额或总供应量在已创建的快照数量中为 O(log_n),尽管特定帐户的 n 通常会小得多,因为后续快照中的相同余额存储为单个条目。
由于额外的快照记帐,普通 ERC20 转移存在恒定的开销。 对于特定帐户紧随快照之后的第一次传输,此开销仅是显着的。 后续传输将具有正常成本,直到下一个快照,依此类推。
函数
ERC20
事件
IERC20
_snapshot() → uint256
internal创建一个新快照并返回其快照 ID。
发出一个 Snapshot
事件,其中包含相同的 ID。
_snapshot
是 internal
,你必须决定如何将其外部公开。 它的使用可能仅限于一组帐户,例如使用 AccessControl
,或者可能对公众开放。
虽然某些信任最小化机制(例如分叉)需要一种开放的调用 _snapshot 的方法,但你必须考虑到攻击者可能会以两种方式使用它。首先,它可以用于增加从快照中检索值的成本,尽管它会以对数方式增长,从而使此攻击长期无效。 其次,它可以用于定位特定帐户并增加 ERC20 转移的成本,如上面的“Gas Costs”部分中所述。我们尚未衡量实际数字; 如果你对此感兴趣,请与我们联系。 |
balanceOfAt(address account, uint256 snapshotId) → uint256
public检索创建 snapshotId
时的 account
余额。
totalSupplyAt(uint256 snapshotId) → uint256
public检索创建 snapshotId
时的总供应量。
_beforeTokenTransfer(address from, address to, uint256 amount)
internalSnapshot(uint256 id)
event当创建由 id
标识的快照时,由 _snapshot
发出。
ERC20Pausable
具有可暂停的 token 转移、铸造和销毁的 ERC20 token。
适用于诸如在评估期结束之前阻止交易,或具有紧急开关以在出现重大错误时冻结所有 token 转移的情况。
函数
Pausable
事件
Pausable
IERC20
_beforeTokenTransfer(address from, address to, uint256 amount)
internal查看 ERC20._beforeTokenTransfer
.
要求:
ERC20Burnable
ERC20
的扩展,允许 token 持有者销毁他们自己的
token 以及他们拥有授权的 token,这种方式可以通过链下方式识别(通过事件分析)。
函数
ERC20
事件
IERC20
burn(uint256 amount)
public从调用者处销毁 amount
数量的 token。
查看 ERC20._burn
。
burnFrom(address account, uint256 amount)
public从 account
处销毁 amount
数量的 token,从调用者的授权中扣除。
查看 ERC20._burn
和 ERC20.allowance
。
要求:
amount
数量的 accounts
的 token 授权。ERC20Capped
ERC20
的扩展,增加了 token 供应量的上限。
函数
ERC20
事件
IERC20
constructor(uint256 cap_)
internal设置 cap
的值。这个值是不可变的,只能在构造期间设置一次。
cap() → uint256
public返回 token 总供应量的上限。
_beforeTokenTransfer(address from, address to, uint256 amount)
internal查看 ERC20._beforeTokenTransfer
。
要求:
SafeERC20
ERC20 操作的包装器,在失败时抛出异常(当 token
合约返回 false 时)。也支持不返回值(而是在失败时恢复或抛出异常)的 Token,假定非恢复调用是成功的。
要使用此库,你可以将 using SafeERC20 for IERC20;
语句添加到你的合约中,
这允许你将安全操作称为 token.safeTransfer(…)
等。
函数
safeTransfer(contract IERC20 token, address to, uint256 value)
internalsafeTransferFrom(contract IERC20 token, address from, address to, uint256 value)
internalsafeApprove(contract IERC20 token, address spender, uint256 value)
internal已弃用。此函数存在与 IERC20.approve
中发现的问题类似的问题,不鼓励使用。
如果可能,请改用 safeIncreaseAllowance
和
safeDecreaseAllowance
。
safeIncreaseAllowance(contract IERC20 token, address spender, uint256 value)
internalsafeDecreaseAllowance(contract IERC20 token, address spender, uint256 value)
internalTokenTimelock
一个 token 持有者合约,它将允许受益人在给定的释放时间后提取 token。
适用于简单的归属计划,例如“顾问在 1 年后获得他们所有的 token”。
函数
constructor(contract IERC20 token_, address beneficiary_, uint256 releaseTime_)
publictoken() → contract IERC20
publicbeneficiary() → address
publicreleaseTime() → uint256
publicrelease()
public
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!