diff --git a/readme.md b/readme.md index 103d5fe..012dd99 100644 --- a/readme.md +++ b/readme.md @@ -290,3 +290,9 @@ Some tokens ([DAI, RAI, GLM, STAKE, CHAI, HAKKA, USDFL, HNY](https://github.com/ Some tokens (e.g., `cUSDCv3`) contain a special case for `amount == type(uint256).max` in their transfer functions that results in only the user's balance being transferred. This may cause issues with systems that transfer a user-supplied `amount` to their contract and then credit the user with the same value in storage (e.g., Vault-type systems) without checking the amount that has actually been transferred. + +## Tokens without a `name` / `symbol` / `decimal` functions + +ERC20 compliant contracts do no need to implement the `name`, `symbol` nor `decimal` functions as specified by the [EIP20](https://github.com/ethereum/ercs/blob/master/ERCS/erc-20.md) since they are optional. + +*example*: [ERC20WithoutOptional.sol](./src/ERC20WithoutOptional.sol) \ No newline at end of file diff --git a/src/ERC20WithoutOptional.sol b/src/ERC20WithoutOptional.sol new file mode 100644 index 0000000..f4ddfac --- /dev/null +++ b/src/ERC20WithoutOptional.sol @@ -0,0 +1,66 @@ +// Copyright (C) 2017, 2018, 2019, 2020, 2024 dbrock, rain, mrchico, d-xo, sqrlfirst +// SPDX-License-Identifier: AGPL-3.0-only + +pragma solidity >=0.6.12; + +contract Math { + // --- Math --- + function add(uint x, uint y) internal pure returns (uint z) { + require((z = x + y) >= x); + } + + function sub(uint x, uint y) internal pure returns (uint z) { + require((z = x - y) <= x); + } +} + +contract ERC20WithoutOptional is Math { + // --- ERC20 Data --- + string private constant name = "Token"; + string private constant symbol = "TKN"; + uint8 private decimals = 18; + uint256 public totalSupply; + + mapping(address => uint) public balanceOf; + mapping(address => mapping(address => uint)) public allowance; + + event Approval(address indexed src, address indexed guy, uint wad); + event Transfer(address indexed src, address indexed dst, uint wad); + + // --- Init --- + constructor(uint _totalSupply) public { + totalSupply = _totalSupply; + balanceOf[msg.sender] = _totalSupply; + emit Transfer(address(0), msg.sender, _totalSupply); + } + + // --- Token --- + function transfer(address dst, uint wad) public virtual returns (bool) { + return transferFrom(msg.sender, dst, wad); + } + + function transferFrom( + address src, + address dst, + uint wad + ) public virtual returns (bool) { + require(balanceOf[src] >= wad, "insufficient-balance"); + if (src != msg.sender && allowance[src][msg.sender] != type(uint).max) { + require( + allowance[src][msg.sender] >= wad, + "insufficient-allowance" + ); + allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad); + } + balanceOf[src] = sub(balanceOf[src], wad); + balanceOf[dst] = add(balanceOf[dst], wad); + emit Transfer(src, dst, wad); + return true; + } + + function approve(address usr, uint wad) public virtual returns (bool) { + allowance[msg.sender][usr] = wad; + emit Approval(msg.sender, usr, wad); + return true; + } +}