Skip to content

Commit

Permalink
refactor: capitalize ERC in ERC-20
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulRBerg committed Apr 4, 2022
1 parent 3b59759 commit cf62d8c
Show file tree
Hide file tree
Showing 53 changed files with 1,308 additions and 239 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ Once installed, you can use the contracts like this:
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;
import "@prb/contracts/token/erc20/Erc20.sol";
import "@prb/contracts/token/erc20/Erc20Permit.sol";
import "@prb/contracts/token/erc20/ERC20.sol";
import "@prb/contracts/token/erc20/ERC20Permit.sol";
contract MyToken is Erc20, Erc20Permit {
contract MyToken is ERC20, ERC20Permit {
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) Erc20Permit(name_, symbol_, decimals_) {}
) ERC20Permit(name_, symbol_, decimals_) {}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
// solhint-disable func-name-mixedcase
pragma solidity >=0.8.4;

import "../token/erc20/Erc20Recover.sol";
import "../token/erc20/ERC20Recover.sol";

/// @title GodModeErc20Recover
/// @title GodModeERC20Recover
/// @author Paul Razvan Berg
/// @dev Strictly for test purposes. Do not use in production.
contract GodModeErc20Recover is Erc20Recover {
contract GodModeERC20Recover is ERC20Recover {
function __godMode_getIsRecoverInitialized() external view returns (bool) {
return isRecoverInitialized;
}
Expand Down
207 changes: 207 additions & 0 deletions contracts/token/erc20/ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./IERC20.sol";

/// @title ERC20
/// @author Paul Razvan Berg
contract ERC20 is IERC20 {
/// PUBLIC STORAGE ///

/// @inheritdoc IERC20
string public override name;

/// @inheritdoc IERC20
string public override symbol;

/// @inheritdoc IERC20
uint8 public immutable override decimals;

/// @inheritdoc IERC20
uint256 public override totalSupply;

/// INTERNAL STORAGE ///

/// @dev Internal mapping of balances.
mapping(address => uint256) internal balances;

/// @dev Internal mapping of allowances.
mapping(address => mapping(address => uint256)) internal allowances;

/// CONSTRUCTOR ///

/// @notice All three of these arguments are immutable: they can only be set once during construction.
/// @param name_ ERC-20 name of this token.
/// @param symbol_ ERC-20 symbol of this token.
/// @param decimals_ ERC-20 decimal precision of this token.
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) {
name = name_;
symbol = symbol_;
decimals = decimals_;
}

/// PUBLIC CONSTANT FUNCTIONS ///

/// @inheritdoc IERC20
function allowance(address owner, address spender) public view override returns (uint256) {
return allowances[owner][spender];
}

function balanceOf(address account) public view virtual override returns (uint256) {
return balances[account];
}

/// PUBLIC NON-CONSTANT FUNCTIONS ///

/// @inheritdoc IERC20
function approve(address spender, uint256 amount) public virtual override returns (bool) {
approveInternal(msg.sender, spender, amount);
return true;
}

/// @inheritdoc IERC20
function decreaseAllowance(address spender, uint256 subtractedAmount) public virtual override returns (bool) {
uint256 newAllowance = allowances[msg.sender][spender] - subtractedAmount;
approveInternal(msg.sender, spender, newAllowance);
return true;
}

/// @inheritdoc IERC20
function increaseAllowance(address spender, uint256 addedAmount) public virtual override returns (bool) {
uint256 newAllowance = allowances[msg.sender][spender] + addedAmount;
approveInternal(msg.sender, spender, newAllowance);
return true;
}

/// @inheritdoc IERC20
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
transferInternal(msg.sender, recipient, amount);
return true;
}

/// @inheritdoc IERC20
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
transferInternal(sender, recipient, amount);

uint256 currentAllowance = allowances[sender][msg.sender];
if (currentAllowance < amount) {
revert ERC20__InsufficientAllowance(currentAllowance, amount);
}
unchecked {
approveInternal(sender, msg.sender, currentAllowance - amount);
}

return true;
}

/// INTERNAL NON-CONSTANT FUNCTIONS ///

/// @notice Sets `amount` as the allowance of `spender` over the `owner`s tokens.
///
/// @dev Emits an {Approval} event.
///
/// Requirements:
///
/// - `owner` cannot be the zero address.
/// - `spender` cannot be the zero address.
function approveInternal(
address owner,
address spender,
uint256 amount
) internal virtual {
if (owner == address(0)) {
revert ERC20__ApproveOwnerZeroAddress();
}
if (spender == address(0)) {
revert ERC20__ApproveSpenderZeroAddress();
}

allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}

/// @notice Destroys `burnAmount` tokens from `holder`, reducing the token supply.
///
/// @dev Emits a {Transfer} event.
///
/// Requirements:
///
/// - `holder` must have at least `amount` tokens.
function burnInternal(address holder, uint256 burnAmount) internal {
if (holder == address(0)) {
revert ERC20__BurnZeroAddress();
}

// Burn the tokens.
balances[holder] -= burnAmount;

// Reduce the total supply.
totalSupply -= burnAmount;

emit Transfer(holder, address(0), burnAmount);
}

/// @notice Prints new tokens into existence and assigns them to `beneficiary`, increasing the
/// total supply.
///
/// @dev Emits a {Transfer} event.
///
/// Requirements:
///
/// - The beneficiary's balance and the total supply cannot overflow.
function mintInternal(address beneficiary, uint256 mintAmount) internal {
if (beneficiary == address(0)) {
revert ERC20__MintZeroAddress();
}

/// Mint the new tokens.
balances[beneficiary] += mintAmount;

/// Increase the total supply.
totalSupply += mintAmount;

emit Transfer(address(0), beneficiary, mintAmount);
}

/// @notice Moves `amount` tokens from `sender` to `recipient`.
///
/// @dev Emits a {Transfer} event.
///
/// Requirements:
///
/// - `sender` cannot be the zero address.
/// - `recipient` cannot be the zero address.
/// - `sender` must have a balance of at least `amount`.
function transferInternal(
address sender,
address recipient,
uint256 amount
) internal virtual {
if (sender == address(0)) {
revert ERC20__TransferSenderZeroAddress();
}
if (recipient == address(0)) {
revert ERC20__TransferRecipientZeroAddress();
}

uint256 senderBalance = balances[sender];
if (senderBalance < amount) {
revert ERC20__InsufficientBalance(senderBalance, amount);
}
unchecked {
balances[sender] = senderBalance - amount;
}

balances[recipient] += amount;

emit Transfer(sender, recipient, amount);
}
}
91 changes: 91 additions & 0 deletions contracts/token/erc20/ERC20Permit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: Unlicense
// solhint-disable var-name-mixedcase
pragma solidity >=0.8.4;

import "./ERC20.sol";
import "./IERC20Permit.sol";

/// @title ERC20Permit
/// @author Paul Razvan Berg
contract ERC20Permit is
IERC20Permit, // one dependency
ERC20 // one dependency
{
/// PUBLIC STORAGE ///

/// @inheritdoc IERC20Permit
bytes32 public immutable override DOMAIN_SEPARATOR;

/// @inheritdoc IERC20Permit
bytes32 public constant override PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

/// @inheritdoc IERC20Permit
mapping(address => uint256) public override nonces;

/// @inheritdoc IERC20Permit
string public constant override version = "1";

/// CONSTRUCTOR ///

constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) ERC20(_name, _symbol, _decimals) {
uint256 chainId;
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainId,
address(this)
)
);
}

/// PUBLIC NON-CONSTANT FUNCTIONS ///

/// @inheritdoc IERC20Permit
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public override {
if (owner == address(0)) {
revert ERC20Permit__OwnerZeroAddress();
}
if (spender == address(0)) {
revert ERC20Permit__SpenderZeroAddress();
}
if (deadline < block.timestamp) {
revert ERC20Permit__PermitExpired(deadline);
}

// It's safe to use unchecked here because the nonce cannot realistically overflow, ever.
bytes32 hashStruct;
unchecked {
hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline));
}
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct));
address recoveredOwner = ecrecover(digest, v, r, s);

if (recoveredOwner == address(0)) {
revert ERC20Permit__RecoveredOwnerZeroAddress();
}
if (recoveredOwner != owner) {
revert ERC20Permit__InvalidSignature(v, r, s);
}

approveInternal(owner, spender, value);
}
}
Loading

0 comments on commit cf62d8c

Please sign in to comment.