Created
March 21, 2024 21:35
-
-
Save ernestognw/334ce36076d1d936e44b7fa1ad2a6320 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/MyERC20Capped.sol | |
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import {ERC20Capped, ERC20} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol"; | |
contract MyERC20Capped is ERC20Capped { | |
uint256 addressZeroBalance; | |
function balanceOf(address account) public view override returns (uint256) { | |
if (account == address(0)) return addressZeroBalance; | |
return super.balanceOf(account); | |
} | |
function _update( | |
address from, | |
address to, | |
uint256 value | |
) internal virtual override { | |
super._update(from, to, value); | |
if (to == address(0)) { | |
addressZeroBalance++; | |
} | |
} | |
constructor( | |
string memory _name, | |
string memory _symbol, | |
uint256 _cap | |
) ERC20(_name, _symbol) ERC20Capped(_cap) { | |
_mint(msg.sender, _cap); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// test/MyERC20Capped.t.sol | |
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import {MyERC20Capped} from "../src/MyERC20Capped.sol"; | |
contract MyERC20CappedTest is Test { | |
MyERC20Capped public token; | |
function setUp() public { | |
token = new MyERC20Capped("MyERC20Capped", "MTK", 1000); | |
} | |
function testTransfer() public { | |
token.transfer(address(this), 100); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// src/MyERC20CappedModified.sol | |
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import {Context, IERC20, IERC20Metadata, IERC20Errors} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | |
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { | |
// 1. Make all variables public | |
mapping(address account => uint256) public _balances; | |
mapping(address account => mapping(address spender => uint256)) | |
public _allowances; | |
uint256 public _totalSupply; | |
string public _name; | |
string public _symbol; | |
constructor(string memory name_, string memory symbol_) { | |
_name = name_; | |
_symbol = symbol_; | |
} | |
function name() public view virtual returns (string memory) { | |
return _name; | |
} | |
function symbol() public view virtual returns (string memory) { | |
return _symbol; | |
} | |
function decimals() public view virtual returns (uint8) { | |
return 18; | |
} | |
function totalSupply() public view virtual returns (uint256) { | |
return _totalSupply; | |
} | |
function balanceOf(address account) public view virtual returns (uint256) { | |
return _balances[account]; | |
} | |
function transfer(address to, uint256 value) public virtual returns (bool) { | |
address owner = _msgSender(); | |
_transfer(owner, to, value); | |
return true; | |
} | |
function allowance( | |
address owner, | |
address spender | |
) public view virtual returns (uint256) { | |
return _allowances[owner][spender]; | |
} | |
function approve( | |
address spender, | |
uint256 value | |
) public virtual returns (bool) { | |
address owner = _msgSender(); | |
_approve(owner, spender, value); | |
return true; | |
} | |
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; | |
} | |
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); | |
} | |
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 Transfer(from, to, value); | |
} | |
function _mint(address account, uint256 value) internal { | |
if (account == address(0)) { | |
revert ERC20InvalidReceiver(address(0)); | |
} | |
_update(address(0), account, value); | |
} | |
function _burn(address account, uint256 value) internal { | |
if (account == address(0)) { | |
revert ERC20InvalidSender(address(0)); | |
} | |
_update(account, address(0), value); | |
} | |
function _approve(address owner, address spender, uint256 value) internal { | |
_approve(owner, spender, value, true); | |
} | |
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 Approval(owner, spender, value); | |
} | |
} | |
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); | |
} | |
} | |
} | |
} | |
abstract contract ERC20CappedModified is ERC20 { | |
uint256 private immutable _cap; | |
error ERC20ExceededCap(uint256 increasedSupply, uint256 cap); | |
error ERC20InvalidCap(uint256 cap); | |
constructor(uint256 cap_) { | |
if (cap_ == 0) { | |
revert ERC20InvalidCap(0); | |
} | |
_cap = cap_; | |
} | |
function cap() public view virtual returns (uint256) { | |
return _cap; | |
} | |
function _update( | |
address from, | |
address to, | |
uint256 value | |
) internal virtual override { | |
// 3. Move the cap check before the _update | |
if (from == address(0)) { | |
uint256 maxSupply = cap(); | |
uint256 supply = totalSupply(); | |
if (supply > maxSupply) { | |
revert ERC20ExceededCap(supply, maxSupply); | |
} | |
} | |
super._update(from, to, value); | |
} | |
} | |
contract MyERC20CappedModified is ERC20CappedModified { | |
constructor( | |
string memory _name, | |
string memory _symbol, | |
uint256 _cap | |
) ERC20(_name, _symbol) ERC20CappedModified(_cap) { | |
_mint(msg.sender, _cap); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// test/MyERC20CappedModified.t.sol | |
// SPDX-License-Identifier: UNLICENSED | |
pragma solidity ^0.8.13; | |
import "forge-std/Test.sol"; | |
import {MyERC20Capped} from "../src/MyERC20Capped.sol"; | |
contract MyERC20CappedModifiedTest is Test { | |
MyERC20Capped public token; | |
function setUp() public { | |
token = new MyERC20Capped("MyERC20Capped", "MTK", 1000); | |
} | |
function testTransfer() public { | |
token.transfer(address(this), 100); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment