Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reuse ERC20Lib logic #203

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 35 additions & 99 deletions contracts/token/ERC20/ERC20Upgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IER
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
import {ERC20Lib} from "@openzeppelin/contracts/token/ERC20/libs/ERC20Lib.sol";

/**
* @dev Implementation of the {IERC20} interface.
Expand All @@ -33,22 +34,12 @@ import {Initializable} from "../../proxy/utils/Initializable.sol";
* these events, as it isn't required by the specification.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;

mapping(address account => mapping(address spender => uint256)) _allowances;

uint256 _totalSupply;

string _name;
string _symbol;
}
using ERC20Lib for ERC20Lib.Storage;

// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;

function _getERC20Storage() private pure returns (ERC20Storage storage $) {
function _getERC20Storage() private pure returns (ERC20Lib.Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
Expand All @@ -65,26 +56,25 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
}

function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
ERC20Lib.Storage storage $ = _getERC20Storage();
$.init(name_, symbol_);
}

/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._name;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.name();
}

/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._symbol;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.symbol();
}

/**
Expand All @@ -108,16 +98,16 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._totalSupply;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.totalSupply();
}

/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._balances[account];
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.balanceOf(account);
}

/**
Expand All @@ -129,17 +119,16 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.transfer(_msgSender(), to, value);
}

/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._allowances[owner][spender];
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.allowance(owner, spender);
}

/**
Expand All @@ -153,9 +142,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.approve(_msgSender(), spender, value);
}

/**
Expand All @@ -175,10 +163,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* `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;
ERC20Lib.Storage storage $ = _getERC20Storage();
return $.transferFrom(_msgSender(), from, to, value);
}

/**
Expand All @@ -192,13 +178,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* 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);
ERC20Lib.Storage storage $ = _getERC20Storage();
$.transfer(from, to, value);
}

/**
Expand All @@ -209,34 +190,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
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);
ERC20Lib.Storage storage $ = _getERC20Storage();
$.update(from, to, value);
}

/**
Expand All @@ -248,10 +203,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* 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);
ERC20Lib.Storage storage $ = _getERC20Storage();
$.mint(account, value);
}

/**
Expand All @@ -263,10 +216,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* 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);
ERC20Lib.Storage storage $ = _getERC20Storage();
$.burn(account, value);
}

/**
Expand All @@ -285,7 +236,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* 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);
ERC20Lib.Storage storage $ = _getERC20Storage();
$._approve(owner, spender, value, true);
}

/**
Expand All @@ -306,17 +258,8 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
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);
}
ERC20Lib.Storage storage $ = _getERC20Storage();
return $._approve(owner, spender, value, emitEvent);
}

/**
Expand All @@ -328,14 +271,7 @@ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20,
* 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);
}
}
ERC20Lib.Storage storage $ = _getERC20Storage();
return $._spendAllowance(owner, spender, value);
}
}