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

Audit and Optimize ERC20 and ERC721 #36

Open
wants to merge 1 commit into
base: tokens
Choose a base branch
from
Open
Show file tree
Hide file tree
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
220 changes: 196 additions & 24 deletions contracts/ERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,242 @@ pragma solidity ^0.8.24;

import "./IERC20.sol";

/**
* @title ERC20
* @dev Implementation of the ERC20 token standard
*/
contract ERC20 is IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(
address indexed owner, address indexed spender, uint256 value
);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);

/**
* @dev Emitted when ownership of the contract is transferred from `old` to `new`.
*/
event Owned(address indexed old, address indexed new);

/**
* @notice Total supply of tokens.
*/
uint256 public totalSupply;

/**
* @notice Mapping of account balances.
*/
mapping(address => uint256) public balanceOf;

/**
* @notice Mapping of allowances for each spender.
*/
mapping(address => mapping(address => uint256)) public allowance;

/**
* @notice The name of the token.
*/
string public name;

/**
* @notice The symbol of the token.
*/
string public symbol;

/**
* @notice The number of decimals the token uses.
*/
uint8 public decimals;

/**
* @dev The address of the contract owner.
*/
address private owner;

/**
* @dev Error for insufficient balance in operations.
*/
error InsufficientBalance();

/**
* @dev Error for invalid recipient addresses (e.g., zero address).
*/
error InvalidRecipient();

/**
* @dev Error for arithmetic overflow during operations.
*/
error Overflow();

/**
* @dev Error for arithmetic underflow during operations.
*/
error Underflow();

/**
* @notice Initializes the contract with a `name`, `symbol`, and `decimals`.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
* @param _decimals The number of decimals the token uses.
*/
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
owner = msg.sender;
}

function transfer(address recipient, uint256 amount)
external
returns (bool)
{
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
/**
* @dev Modifier to allow only the owner to execute certain functions.
*/
modifier onlyOwner() {
require(msg.sender == owner, "Unauthorized");
_;
}

/**
* @notice Transfers `amount` tokens to the `recipient`.
* @param recipient The address of the recipient.
* @param amount The amount of tokens to transfer.
* @return True if the operation was successful.
* @dev Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool) {
if (recipient == address(0)) {
revert InvalidRecipient();
}

unchecked {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
}

emit Transfer(msg.sender, recipient, amount);
return true;
}

/**
* @notice Approves `spender` to spend `amount` on behalf of the caller.
* @param spender The address of the spender.
* @param amount The amount of tokens to approve.
* @return True if the operation was successful.
* @dev Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool) {
if (spender == address(0)) {
revert InvalidRecipient();
}
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}

function transferFrom(address sender, address recipient, uint256 amount)
external
returns (bool)
{
/**
* @notice Transfers `amount` tokens from `sender` to `recipient` using the allowance mechanism.
* @param sender The address of the sender.
* @param recipient The address of the recipient.
* @param amount The amount of tokens to transfer.
* @return True if the operation was successful.
* @dev Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
require(msg.sender != recipient, "cannot transfer to self");
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
if (recipient == address(0)) {
revert InvalidRecipient();
}

uint b = allowance[sender][msg.sender];
uint c = b - amount;
if (c > b) {
revert InsufficientBalance();
}

unchecked {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
}

emit Transfer(sender, recipient, amount);
return true;
}

/**
* @dev Internal function to mint `amount` tokens to `to`.
* @param to The address to receive the minted tokens.
* @param amount The amount of tokens to mint.
* @dev Emits a {Transfer} event from the zero address.
*/
function _mint(address to, uint256 amount) internal {
balanceOf[to] += amount;
totalSupply += amount;
if (to == address(0)) {
revert InvalidRecipient();
}

if (totalSupply + amount < totalSupply) {
revert Overflow();
}

unchecked {
balanceOf[to] += amount;
totalSupply += amount;
}
emit Transfer(address(0), to, amount);
}

/**
* @dev Internal function to burn `amount` tokens from `from`.
* @param from The address from which tokens will be burned.
* @param amount The amount of tokens to burn.
* @dev Emits a {Transfer} event to the zero address.
*/
function _burn(address from, uint256 amount) internal {
balanceOf[from] -= amount;
totalSupply -= amount;
if (totalSupply - amount > totalSupply) {
revert Underflow();
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loop hole in function: It does not check whether the from address has enough tokens to be burnt from

unchecked {
balanceOf[from] -= amount;
totalSupply -= amount;
}

emit Transfer(from, address(0), amount);
}

function mint(address to, uint256 amount) external {
/**
* @notice Mints `amount` tokens to `to`. Can only be called by the owner.
* @param to The address to receive the minted tokens.
* @param amount The amount of tokens to mint.
*/
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}

function burn(address from, uint256 amount) external {
/**
* @notice Burns `amount` tokens from `from`. Can only be called by the owner.
* @param from The address from which tokens will be burned.
* @param amount The amount of tokens to burn.
*/
function burn(address from, uint256 amount) external onlyOwner {
_burn(from, amount);
}
}

/**
* @notice Transfers ownership of the contract to `newOwner`.
* @param newOwner The address of the new owner.
* @return True if the operation was successful.
* @dev Emits an {Owned} event.
*/
function changeOwner(address newOwner) public onlyOwner returns (bool) {
address oldOwner = owner;
owner = newOwner;
emit Owned(oldOwner, newOwner);
return true;
}
}
Loading