294 lines
10 KiB
Solidity
294 lines
10 KiB
Solidity
// 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);
|
||
}
|
||
}
|
||
}
|
||
}
|