Files
openzeppelin-contracts/contracts/token/ERC20/ERC20.sol

294 lines
10 KiB
Solidity
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev {IERC20} 接口的实现。
*
* 此实现与代币创建方式无关。这意味着必须在派生合约中使用 {_mint} 添加供应机制。
*
* 提示:有关详细说明,请参阅我们的指南
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[如何实现供应机制]。
*
* {decimals} 的默认值是 18。要更改此值您应该重写此函数以返回不同的值。
*
* 我们遵循了 OpenZeppelin 合约的一般准则:函数在失败时回退而不是返回 `false`。
* 这种行为仍然是常规的,并且不会与 ERC-20 应用程序的期望冲突。
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev 设置 {name} 和 {symbol} 的值。
*
* 这两个值都是不可变的:它们只能在构造期间设置一次。
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev 返回代币的名称。
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev 返回代币的符号,通常是名称的较短版本。
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev 返回用于获取其用户表示的小数位数。
* 例如,如果 `decimals` 等于 `2`,则应该向用户显示 `505` 代币的余额为 `5.05` (`505 / 10 ** 2`)。
*
* 代币通常选择值为 18模仿以太币和 Wei 之间的关系。除非被重写,否则这是此函数返回的默认值。
*
* 注意:此信息仅用于 _显示_ 目的:它不会以任何方式影响合约的任何算术,
* 包括 {IERC20-balanceOf} 和 {IERC20-transfer}。
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev 参见 {IERC20-transfer}。
*
* 要求:
*
* - `to` 不能是零地址。
* - 调用者必须有至少 `value` 的余额。
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev 参见 {IERC20-approve}。
*
* 注意:如果 `value` 是最大 `uint256`,则在 `transferFrom` 时不会更新授权额度。
* 这在语义上等同于无限授权。
*
* 要求:
*
* - `spender` 不能是零地址。
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev 参见 {IERC20-transferFrom}。
*
* 跳过发出 {Approval} 事件以指示授权额度更新。ERC 不要求这样做。
* 参见 {xref-ERC20-_approve-address-address-uint256-bool-}[_approve]。
*
* 注意:如果当前授权额度是最大 `uint256`,则不会更新授权额度。
*
* 要求:
*
* - `from` 和 `to` 不能是零地址。
* - `from` 必须有至少 `value` 的余额。
* - 调用者必须对 `from` 的代币有至少 `value` 的授权额度。
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}