Platform: Code4rena
Start Date: 24/02/2022
Pot Size: $30,000 USDC
Total HM: 0
Participants: 28
Period: 3 days
Judge: Jack the Pug
Id: 95
League: ETH
Rank: 9/28
Findings: 2
Award: $1,001.22
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: hickuphh3
Also found by: 0x1f8b, 0xwags, Dravee, IllIllI, Omik, Rhynorater, Ruhum, TerrierLover, cccz, cmichel, csanuragjain, defsec, gzeon, jayjonah8, kenta, kirk-baird, kyliek, leastwood, minhquanym, pedroais, peritoflores, robee, securerodd
873.3805 USDC - $873.38
Your version of Pausable.sol
does a good job of saying the exact source of the fork as well as most of the changes
/** * @notice Base contract which allows children to implement an emergency stop * mechanism * @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/feb665136c0dae9912e08397c1a21c4af3651ef3/contracts/lifecycle/Pausable.sol * Modifications: * 1. Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018) * 2. Removed whenNotPause/whenPaused from pause/unpause (6/14/2018) * 3. Removed whenPaused (6/14/2018) * 4. Switches ownable library to use ZeppelinOS (7/12/18) * 5. Remove constructor (7/13/18) * 6. Reformat, conform to Solidity 0.6 syntax and add error messages (5/13/20) * 7. Make public functions external (5/27/20) */
There are a lot of other files that do not have such changelogs
$ find ./contracts -name "*.sol" | xargs grep -li openzeppelin | xargs grep -iL "forked from" ./contracts/proxy/ERC1967Proxy.sol ./contracts/proxy/Proxy.sol ./contracts/util/SafeERC20.sol ./contracts/util/Context.sol ./contracts/util/ECRecover.sol ./contracts/util/Address.sol ./contracts/util/IERC20.sol ./contracts/test/ERC20.sol ./contracts/test/IERC20Metadata.sol ./contracts/upgradeability/ERC1967Upgrade.sol ./contracts/upgradeability/UUPSUpgradeable.sol ./contracts/upgradeability/draft-IERC1822.sol ./contracts/v1/Ownable.sol
For example, Ownable
has one of its functions removed, and all of the files have __gap
storage variables added. Having a changelog will make upgrading again easier, and will make security reviews more thorough.
__gap
variableAbstractFiatTokenV1.sol
is missing a __gap
storage variable. Consider adding one so that storage can be used in this contract in future versions
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/AbstractFiatTokenV1.sol
I see that there has been an attempt to get rid of sensitive terms such as 'blacklist' by using 'blocklist'. The contracts still use some of the others, and I would suggest changing 'whitelist' to 'allowlist', and 'masterMinter' to 'primaryMinter' or 'minterAdmin' https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol
#0 - thurendous
2022-03-01T07:23:54Z
All of 3 are valid.
No. 1 Forked files should have changelog
Thank you for pointing out.
No. 2 Not all contracts have added a __gap variable
We would add gap
in abstract contract.
No. 3 Update other sensitive terms
Thank you for pointing out. We actually already have plans to change these things.
#1 - thurendous
2022-03-28T08:16:47Z
AbstractFiatTokenV1.sol is missing a __gap storage variable.
Fixed and thanks.
Final change can be viewed here.
#2 - thurendous
2022-03-28T08:20:46Z
Update other sensitive terms
Forked files should have changelog
Fixed and thanks.
🌟 Selected for report: Dravee
Also found by: 0x1f8b, IllIllI, Omik, Rhynorater, TerrierLover, Tomio, defsec, gzeon, kenta, pedroais, peritoflores, rfa, robee, securerodd, sorrynotsorry, ye0lde
127.8418 USDC - $127.84
bool
s for storage incurs overhead// Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled.
mapping(address => mapping(bytes32 => bool)) private _authorizationStates;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP3009.sol#L53
bool internal initialized;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L54
mapping(address => bool) internal minters;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L59
bool internal initialized;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L54
mapping(address => bool) internal minters;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L59
bool public paused = false;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Pausable.sol#L49
mapping(address => bool) internal blocklisted;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Blocklistable.sol#L35
bool internal initialized;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L54
mapping(address => bool) internal minters;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L59
mapping(address => bool) internal whitelisted;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L62
abi.encode()
is less efficient than abi.encodePacked()
bytes memory data = abi.encode(
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP3009.sol#L102
bytes memory data = abi.encode(
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP3009.sol#L148
bytes memory data = abi.encode(
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP3009.sol#L183
bytes memory data = abi.encode(
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP2612.sol#L72
require()
strings longer than 32 bytes cost extra gasrequire(oldAllowance >= value, "SafeERC20decreased allowance below zero");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/SafeERC20.sol#L76
require(abi.decode(returndata, (bool)), "SafeERC20ERC20 operation did not succeed");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/SafeERC20.sol#L96
require(Address.isContract(newImplementation), "ERC1967new implementation is not a contract");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/StorageSlot.sol#L23*
require(success, "Addressunable to send value, recipient may have reverted");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/Address.sol#L59
require(address(this).balance >= value, "Addressinsufficient balance for call");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/Address.sol#L129
require(isContract(target), "Addressstatic call to non-contract");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/Address.sol#L157
require(isContract(target), "Addressdelegate call to non-contract");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/util/Address.sol#L184
require(Address.isContract(newImplementation), "ERC1967new implementation is not a contract");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/upgradeability/ERC1967Upgrade.sol#L45
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgradeunsupported proxiableUUID");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/upgradeability/ERC1967Upgrade.sol#L92
require(to == msg.sender, "EIP3009caller must be the payee");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/EIP3009.sol#L145
require(!initialized, "FiatTokencontract is already initialized");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L78
require(minters[msg.sender], "FiatTokencaller is not a minter");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L116
require(_to != address(0), "FiatTokenmint to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L135
require(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L136
require(owner != address(0), "ERC20approve from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L245
require(spender != address(0), "ERC20approve to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L246
require(from != address(0), "ERC20transfer from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L309
require(to != address(0), "ERC20transfer to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L310
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L368
require(balance >= _amount, "FiatTokenburn amount exceeds balance");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L369
require(!initialized, "FiatTokencontract is already initialized");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L78
require(minters[msg.sender], "FiatTokencaller is not a minter");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L116
require(_to != address(0), "FiatTokenmint to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L135
require(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L136
require(owner != address(0), "ERC20approve from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L245
require(spender != address(0), "ERC20approve to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L246
require(from != address(0), "ERC20transfer from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L309
require(to != address(0), "ERC20transfer to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L310
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L368
require(balance >= _amount, "FiatTokenburn amount exceeds balance");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L369
require(msg.sender == pauser, "Pausablecaller is not the pauser");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Pausable.sol#L63
require(newOwner != address(0), "Ownablenew owner is the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Ownable.sol#L52
require(msg.sender == _rescuer, "Rescuablecaller is not the rescuer");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Rescuable.sol#L50
require(!initialized, "FiatTokencontract is already initialized");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L84
require(minters[msg.sender], "FiatTokencaller is not a minter");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L122
require(_to != address(0), "FiatTokenmint to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L142
require(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L143
require(owner != address(0), "ERC20approve from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L253
require(spender != address(0), "ERC20approve to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L254
require(from != address(0), "ERC20transfer from the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L319
require(to != address(0), "ERC20transfer to the zero address");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L320
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L378
require(balance >= _amount, "FiatTokenburn amount exceeds balance");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L379
1e18
rather than 10**18
saves gasif (_value > 100000 * 10 ** 18) {
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L624
external
rather than public
function transferOwnership(address newOwner) public virtual onlyOwner {
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/Ownable.sol#L51
function setName2(string memory _name) public {
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2test.sol#L11
> 0
costs more gas than != 0
when used on uints in a require()
statementrequire(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L136
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L368
require(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L136
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L368
require(_amount > 0, "FiatTokenmint amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L143
require(_amount > 0, "FiatTokenburn amount not greater than 0");
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L378
uint256 internal totalSupply_ = 0;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L58
minterAllowed[minter] = 0;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.sol#L350
uint256 internal totalSupply_ = 0;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v1/FiatTokenV1.altered.sol#L58
uint256 internal totalSupply_ = 0;
https://github.com/code-423n4/2022-02-jpyc/blob/main/contracts/v2/FiatTokenV2.sol#L58
#0 - retocrooman
2022-03-02T02:16:25Z
Using bools for storage incurs overhead abi.encode() is less efficient than abi.encodePacked() require() strings longer than 32 bytes cost extra gas
We need to check if these are ture. If they are true we will fix them.
Using 1e18 rather than 10**18 saves gas
We think this is valid. And we will fix this.
Using > 0 costs more gas than != 0 when used on uints in a require() statement It costs more gas to initialize variables to zero than to let the default of zero be applied
This part is the duplicate of #2
#1 - thurendous
2022-03-29T03:21:42Z
Using bools for storage incurs overhead
Using 1e18 rather than 10**18 saves gas
Fixed and thanks!
Final change can be viewed here.