176 lines
6.5 KiB
Solidity
176 lines
6.5 KiB
Solidity
// SPDX-License-Identifier: UNLICENSED
|
|
pragma solidity ^0.8.30;
|
|
|
|
import {IERC20Errors} from "../lib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sol";
|
|
import {IERC20} from "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
|
|
import {Context} from "../lib/openzeppelin-contracts/contracts/utils/Context.sol";
|
|
|
|
// Copied from OpenZeppelin's ERC20 implementation, but split into internal and external parts
|
|
|
|
abstract contract ERC20Internal is Context, IERC20Errors {
|
|
mapping(address account => uint256) internal _balances;
|
|
mapping(address account => mapping(address spender => uint256)) internal _allowances;
|
|
uint256 internal _totalSupply;
|
|
string internal _name;
|
|
string internal _symbol;
|
|
|
|
|
|
/**
|
|
* @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 IERC20.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 IERC20.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 = _allowances[owner][spender];
|
|
if (currentAllowance < type(uint256).max) {
|
|
if (currentAllowance < value) {
|
|
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
|
|
}
|
|
unchecked {
|
|
_approve(owner, spender, currentAllowance - value, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|