|
@@ -216,87 +216,81 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
|
|
|
*
|
|
|
* Emits a {Transfer} event.
|
|
|
*
|
|
|
- * Requirements:
|
|
|
- *
|
|
|
- * - `from` cannot be the zero address.
|
|
|
- * - `to` cannot be the zero address.
|
|
|
- * - `from` must have a balance of at least `amount`.
|
|
|
+ * NOTE: This function is not virtual, {_update} should be overridden instead.
|
|
|
*/
|
|
|
function _transfer(
|
|
|
address from,
|
|
|
address to,
|
|
|
uint256 amount
|
|
|
- ) internal virtual {
|
|
|
+ ) internal {
|
|
|
require(from != address(0), "ERC20: transfer from the zero address");
|
|
|
require(to != address(0), "ERC20: transfer to the zero address");
|
|
|
+ _update(from, to, amount);
|
|
|
+ }
|
|
|
|
|
|
- _beforeTokenTransfer(from, to, amount);
|
|
|
-
|
|
|
- uint256 fromBalance = _balances[from];
|
|
|
- require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
|
|
|
- unchecked {
|
|
|
- _balances[from] = fromBalance - amount;
|
|
|
- // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
|
|
|
- // decrementing then incrementing.
|
|
|
- _balances[to] += amount;
|
|
|
+ /**
|
|
|
+ * @dev Transfers `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 amount
|
|
|
+ ) internal virtual {
|
|
|
+ if (from == address(0)) {
|
|
|
+ _totalSupply += amount;
|
|
|
+ unchecked {
|
|
|
+ // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
|
|
|
+ _balances[to] += amount;
|
|
|
+ }
|
|
|
+ } else if (to == address(0)) {
|
|
|
+ uint256 fromBalance = _balances[from];
|
|
|
+ require(fromBalance >= amount, "ERC20: burn amount exceeds balance");
|
|
|
+ _totalSupply -= amount;
|
|
|
+ unchecked {
|
|
|
+ // Overflow not possible: amount <= fromBalance <= totalSupply.
|
|
|
+ _balances[from] = fromBalance - amount;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ uint256 fromBalance = _balances[from];
|
|
|
+ require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
|
|
|
+ unchecked {
|
|
|
+ _balances[from] = fromBalance - amount;
|
|
|
+ // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
|
|
|
+ // decrementing then incrementing.
|
|
|
+ _balances[to] += amount;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
emit Transfer(from, to, amount);
|
|
|
-
|
|
|
- _afterTokenTransfer(from, to, amount);
|
|
|
}
|
|
|
|
|
|
- /** @dev Creates `amount` tokens and assigns them to `account`, increasing
|
|
|
- * the total supply.
|
|
|
+ /**
|
|
|
+ * @dev Creates `amount` 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.
|
|
|
*
|
|
|
- * Requirements:
|
|
|
- *
|
|
|
- * - `account` cannot be the zero address.
|
|
|
+ * NOTE: This function is not virtual, {_update} should be overridden instead.
|
|
|
*/
|
|
|
- function _mint(address account, uint256 amount) internal virtual {
|
|
|
+ function _mint(address account, uint256 amount) internal {
|
|
|
require(account != address(0), "ERC20: mint to the zero address");
|
|
|
-
|
|
|
- _beforeTokenTransfer(address(0), account, amount);
|
|
|
-
|
|
|
- _totalSupply += amount;
|
|
|
- unchecked {
|
|
|
- // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
|
|
|
- _balances[account] += amount;
|
|
|
- }
|
|
|
- emit Transfer(address(0), account, amount);
|
|
|
-
|
|
|
- _afterTokenTransfer(address(0), account, amount);
|
|
|
+ _update(address(0), account, amount);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * @dev Destroys `amount` tokens from `account`, reducing the
|
|
|
- * total supply.
|
|
|
+ * @dev Destroys `amount` tokens from `account`, by transferring it to address(0).
|
|
|
+ * Relies on the `_update` mechanism.
|
|
|
*
|
|
|
* Emits a {Transfer} event with `to` set to the zero address.
|
|
|
*
|
|
|
- * Requirements:
|
|
|
- *
|
|
|
- * - `account` cannot be the zero address.
|
|
|
- * - `account` must have at least `amount` tokens.
|
|
|
+ * NOTE: This function is not virtual, {_update} should be overridden instead
|
|
|
*/
|
|
|
- function _burn(address account, uint256 amount) internal virtual {
|
|
|
+ function _burn(address account, uint256 amount) internal {
|
|
|
require(account != address(0), "ERC20: burn from the zero address");
|
|
|
-
|
|
|
- _beforeTokenTransfer(account, address(0), amount);
|
|
|
-
|
|
|
- uint256 accountBalance = _balances[account];
|
|
|
- require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
|
|
|
- unchecked {
|
|
|
- _balances[account] = accountBalance - amount;
|
|
|
- // Overflow not possible: amount <= accountBalance <= totalSupply.
|
|
|
- _totalSupply -= amount;
|
|
|
- }
|
|
|
-
|
|
|
- emit Transfer(account, address(0), amount);
|
|
|
-
|
|
|
- _afterTokenTransfer(account, address(0), amount);
|
|
|
+ _update(account, address(0), amount);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -345,44 +339,4 @@ contract ERC20 is Context, IERC20, IERC20Metadata {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * @dev Hook that is called before any transfer of tokens. This includes
|
|
|
- * minting and burning.
|
|
|
- *
|
|
|
- * Calling conditions:
|
|
|
- *
|
|
|
- * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
|
|
- * will be transferred to `to`.
|
|
|
- * - when `from` is zero, `amount` tokens will be minted for `to`.
|
|
|
- * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
|
|
|
- * - `from` and `to` are never both zero.
|
|
|
- *
|
|
|
- * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
|
|
- */
|
|
|
- function _beforeTokenTransfer(
|
|
|
- address from,
|
|
|
- address to,
|
|
|
- uint256 amount
|
|
|
- ) internal virtual {}
|
|
|
-
|
|
|
- /**
|
|
|
- * @dev Hook that is called after any transfer of tokens. This includes
|
|
|
- * minting and burning.
|
|
|
- *
|
|
|
- * Calling conditions:
|
|
|
- *
|
|
|
- * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
|
|
|
- * has been transferred to `to`.
|
|
|
- * - when `from` is zero, `amount` tokens have been minted for `to`.
|
|
|
- * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
|
|
|
- * - `from` and `to` are never both zero.
|
|
|
- *
|
|
|
- * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
|
|
|
- */
|
|
|
- function _afterTokenTransfer(
|
|
|
- address from,
|
|
|
- address to,
|
|
|
- uint256 amount
|
|
|
- ) internal virtual {}
|
|
|
}
|