Platform: Code4rena
Start Date: 22/09/2022
Pot Size: $30,000 USDC
Total HM: 12
Participants: 133
Period: 3 days
Judge: 0xean
Total Solo HM: 2
Id: 165
League: ETH
Rank: 34/133
Findings: 2
Award: $78.53
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rotcivegaf
Also found by: 0x040, 0x1f8b, 0x4non, 0xNazgul, 0xSmartContract, 0xf15ers, 8olidity, Aymen0909, B2, Bahurum, Bnke0x0, Ch_301, CodingNameKiki, Deivitto, Diana, Funen, IllIllI, JC, JLevick, KIntern_NA, Lambda, OptimismSec, PaludoX0, RockingMiles, Rolezn, Sm4rty, Soosh, Tagir2003, Tointer, TomJ, Triangle, Trust, V_B, Waze, Yiko, __141345__, a12jmx, ajtra, asutorufos, ayeslick, aysha, bbuddha, bharg4v, bobirichman, brgltd, bytera, c3phas, cryptostellar5, cryptphi, csanuragjain, datapunk, delfin454000, durianSausage, exd0tpy, gogo, got_targ, jag, joestakey, karanctf, ladboy233, leosathya, lukris02, mics, millersplanet, natzuu, neko_nyaa, obront, oyc_109, parashar, peritoflores, rbserver, ret2basic, rokinot, ronnyx2017, rvierdiiev, sach1r0, seyni, sikorico, slowmoses, tnevler, yasir, yongskiws
56.5427 USDC - $56.54
Total Low Severity: 3 Total Non-Critical Severity: 9
Severity: Low
It is deprecated in favor of safeIncreaseAllowance() and safeDecreaseAllowance(). If only setting the initial allowance to the value that means infinite, safeIncreaseAllowance() can be used instead.
https://docs.openzeppelin.com/contracts/3.x/api/token/erc20#IERC20-approve-address-uint256-
frxETHToken.approve(address(sfrxETHToken), msg.value);
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L75
Consider using safeIncreaseAllowance() / safeDecreaseAllowance() instead.
Severity: Low
function submitAndDeposit(address recipient) external payable returns (uint256 shares) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L70
function _submit(address recipient) internal nonReentrant {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L85
function submitAndGive(address recipient) external payable {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L109
Consider adding zero-address checks in the mentioned codebase.
Severity: Low
The non-upgradeable standard version of OpenZeppelin’s library are inherited / used by the contracts. It would be safer to use the upgradeable versions of the library contracts to avoid unexpected behaviour.
import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L27
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L28
import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L27
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L4
import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L5
import "openzeppelin-contracts/contracts/token/ERC20/extensions/draft-ERC20Permit.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L6
import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L7
Where applicable, use the contracts from @openzeppelin/contracts-upgradeable instead of @openzeppelin/contracts.
Severity: Non-Critical
Found usage of floating pragmas ^0.8.0 of Solidity
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETH.sol#L2
Found usage of floating pragmas ^0.8.0 of Solidity
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L2
Found usage of floating pragmas ^0.8.0 of Solidity
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L2
Found usage of floating pragmas ^0.8.0 of Solidity
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L2
Found usage of floating pragmas ^0.8.0 of Solidity
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L2
Severity: Non-Critical
return convertToAssets(1e18);
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L55
Severity: Non-Critical
Some parameters of constructors are not checked for invalid values.
constructor( address depositContractAddress, address frxETHAddress, address sfrxETHAddress, address _owner, address _timelock_address, bytes memory _withdrawalCredential ) OperatorRegistry(_owner, _timelock_address, _withdrawalCredential) { depositContract = IDepositContract(depositContractAddress); frxETHToken = frxETH(frxETHAddress); sfrxETHToken = IsfrxETH(sfrxETHAddress); withholdRatio = 0; // No ETH is withheld initially currentWithheldETH = 0; }
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L52-L65
constructor(address _owner, address _timelock_address, bytes memory _withdrawal_pubkey) Owned(_owner) { timelock_address = _timelock_address; curr_withdrawal_pubkey = _withdrawal_pubkey; }
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L40-L43
constructor( address _creator_address, address _timelock_address, string memory _name, string memory _symbol ) ERC20(_name, _symbol) ERC20Permit(_name) Owned(_creator_address) { timelock_address = _timelock_address; }
Validate the parameters.
OpenZeppelin recommends that the initializer modifier be applied to constructors. Per OZs Post implementation contract should be initialized to avoid potential griefs or exploits. https://forum.openzeppelin.com/t/uupsupgradeable-vulnerability-post-mortem/15680/5
Severity: Non-Critical
contract frxETH is ERC20PermitPermissionedMint
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETH.sol#L33
contract frxETHMinter is OperatorRegistry, ReentrancyGuard
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L37
contract OperatorRegistry is Owned
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L28
contract sfrxETH is xERC4626, ReentrancyGuard
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L40
contract ERC20PermitPermissionedMint is ERC20Permit, ERC20Burnable, Owned
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L14
Severity: Non-Critical
The current form of relative path import is not recommended for use because it can unpredictably pollute the namespace. Instead, the Solidity docs recommend specifying imported symbols explicitly. https://docs.soliditylang.org/en/v0.8.15/layout-of-source-files.html#importing-other-source-files
import "./OperatorRegistry.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L30
import "./Utils/Owned.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L24
import "../Utils/Owned.sol";
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L8
Use specific imports syntax per solidity docs recommendation.
Severity: Non-Critical
Use a solidity version of at least 0.8.4 to get bytes.concat() instead of abi.encodePacked(<bytes>,<bytes>) Use a solidity version of at least 0.8.12 to get string.concat() instead of abi.encodePacked(<str>,<str>) Use a solidity version of at least 0.8.13 to get the ability to use using for with a list of free functions
Found old version 0.8.0 of Solidity in [.\Projects\2022-09-frax\src\frxETH.sol]
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETH.sol#L2
Found old version 0.8.0 of Solidity in [.\Projects\2022-09-frax\src\frxETHMinter.sol]
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L2
Found old version 0.8.0 of Solidity in [.\Projects\2022-09-frax\src\OperatorRegistry.sol]
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L2
Found old version 0.8.0 of Solidity in [.\Projects\2022-09-frax\src\sfrxETH.sol]
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L2
Found old version 0.8.0 of Solidity in [.\Projects\2022-09-frax\src\ERC20\ERC20PermitPermissionedMint.sol]
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L2
Consider updating to a more recent solidity version.
Severity: Non-Critical
Contracts are allowed to override their parents’ functions and change the visibility from external to public.
function addValidator(
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L53
function popValidators(
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L82
function removeValidator(
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L93
function minter_burn_from(
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L53
function addMinter(
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L65
function removeMinter(
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L76
Severity: Non-Critical
Events that are declared but not used may be indicative of unused declarations where it makes sense to remove them for better readability/maintainability/auditability, or worse indicative of a missing emit which is bad for monitoring or missing logic that would have emitted that event.
event KeysCleared
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L215
Add emit or remove event declaration.
Severity: Non-Critical
Block timestamps have historically been used for a variety of applications, such as entropy for random numbers (see the Entropy Illusion for further details), locking funds for periods of time, and various state-changing conditional statements that are time-dependent. Miners have the ability to adjust timestamps slightly, which can prove to be dangerous if block timestamps are used incorrectly in smart contracts.
if (block.timestamp >= rewardsCycleEnd) { syncRewards(); }
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L50
Block timestamps should not be used for entropy or generating random numbers—i.e., they should not be the deciding factor (either directly or through some derivation) for winning a game or changing an important state.
Time-sensitive logic is sometimes required; e.g., for unlocking contracts (time-locking), completing an ICO after a few weeks, or enforcing expiry dates. It is sometimes recommended to use block.number and an average block time to estimate times; with a 10 second block time, 1 week equates to approximately, 60480 blocks. Thus, specifying a block number at which to change a contract state can be more secure, as miners are unable to easily manipulate the block number.
🌟 Selected for report: pfapostol
Also found by: 0x040, 0x1f8b, 0x4non, 0x5rings, 0xA5DF, 0xNazgul, 0xSmartContract, 0xmatt, 0xsam, Amithuddar, Aymen0909, B2, Ben, Bnke0x0, Chom, CodingNameKiki, Deivitto, Diana, Fitraldys, Funen, IllIllI, JAGADESH, JC, Metatron, Ocean_Sky, PaludoX0, Pheonix, RaymondFam, ReyAdmirado, RockingMiles, Rohan16, Rolezn, Satyam_Sharma, Sm4rty, SnowMan, SooYa, Tagir2003, TomJ, Tomio, Triangle, V_B, Waze, __141345__, ajtra, albincsergo, asutorufos, aysha, beardofginger, bobirichman, brgltd, bulej93, bytera, c3phas, ch0bu, cryptostellar5, cryptphi, d3e4, delfin454000, dharma09, drdr, durianSausage, emrekocak, erictee, fatherOfBlocks, gogo, got_targ, imare, jag, karanctf, ladboy233, leosathya, lukris02, medikko, mics, millersplanet, natzuu, neko_nyaa, oyc_109, peanuts, prasantgupta52, rbserver, ret2basic, rokinot, ronnyx2017, rotcivegaf, sach1r0, samruna, seyni, slowmoses, tnevler, wagmi, zishansami
21.9922 USDC - $21.99
Total Gas Optimizations: 13
Severity: Gas Optimizations
The overheads outlined below are PER LOOP, excluding the first loop
storage arrays incur a Gwarmaccess (100 gas) memory arrays use MLOAD (3 gas) calldata arrays use CALLDATALOAD (3 gas)
Caching the length changes each of these to a DUP<N> (3 gas), and gets rid of the extra DUP<N> needed to store the stack offset
for (uint256 i = 0; i < original_validators.length; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L114
for (uint i = 0; i < minters_array.length; i++){
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L84
Severity: Gas Optimizations
For cases of: if (<x> == true), use if (<x>) instead For cases of: if (<x> == false), use if (!<x>) instead
require(minters[minter_address] == false, "Address already exists");
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L68
require(minters[minter_address] == true, "Address nonexistant");
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L78
Severity: Gas Optimizations
Saves 6 gas per loop
for (uint i = 0; i < minters_array.length; i++){
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L84
For example, use ++i instead of i++
Severity: Gas Optimizations
for (uint256 i = 0; i < numDeposits; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L129
for (uint256 i = 0; i < arrayLength; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L63
for (uint256 i = 0; i < times; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L84
for (uint256 i = 0; i < original_validators.length; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L114
Severity: Gas Optimizations
This change saves 6 gas per instance
require(sfrxeth_recieved > 0, 'No sfrxETH was returned');
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L79
require(numDeposits > 0, "Not enough ETH in contract");
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L126
Severity: Gas Optimizations
function submitAndDeposit(address recipient) external payable returns (uint256 shares) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L70
function depositWithSignature( uint256 assets, address receiver, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external nonReentrant returns (uint256 shares) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L59
function mintWithSignature( uint256 shares, address receiver, uint256 deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external nonReentrant returns (uint256 assets) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L75
Severity: Gas Optimizations
currentWithheldETH += withheld_amt;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L97
currentWithheldETH -= amount;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L168
Severity: Gas Optimizations
If needed, the value can be read from the verified contract source code. Savings are due to the compiler not having to create non-payable getter functions for deployment calldata, and not adding another entry to the method ID table
uint256 public constant DEPOSIT_SIZE = 32 ether; uint256 public constant RATIO_PRECISION = 1e6;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L38-L39
Set variable to private.
Severity: Gas Optimizations
The following functions could be set external to save gas and improve code quality. External call cost is less expensive than of public functions.
function popValidators(uint256 times) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L82
function removeValidator(uint256 remove_idx, bool dont_care_about_ordering) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L93
function pricePerShare() public view returns (uint256) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/sfrxETH.sol#L54
function minter_burn_from(address b_address, uint256 b_amount) public onlyMinters {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L53
function addMinter(address minter_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L65
function removeMinter(address minter_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L76
function setTimelock(address _timelock_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L94
Severity: Gas Optimizations
require(!activeValidators[pubKey], "Validator already has 32 ETH");
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L140
require(amount <= currentWithheldETH, "Not enough withheld ETH in contract");
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L167
Severity: Gas Optimizations
The unchecked keyword is new in solidity version 0.8.0, so this only applies to that version or higher, which these instances are. This saves 30-40 gas PER LOOP
for (uint256 i = 0; i < numDeposits; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L129
for (uint256 i = 0; i < arrayLength; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L63
for (uint256 i = 0; i < times; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L84
for (uint256 i = 0; i < original_validators.length; ++i) {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L114
for (uint i = 0; i < minters_array.length; i++){
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L84
Severity: Gas Optimizations
Save 6 gas per instance if using assembly to check for address(0)
e.g. assembly { if iszero(_addr) { mstore(0x00, "AddressZero") revert(0x00, 0x20) } }
function setTimelock(address _timelock_address) external onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/OperatorRegistry.sol#L202
function addMinter(address minter_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L65
function removeMinter(address minter_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L76
function setTimelock(address _timelock_address) public onlyByOwnGov {
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L94
Severity: Gas Optimizations
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(bytes => bool) public activeValidators;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L43
bool public submitPaused;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L49
bool public depositEtherPaused;
https://github.com/code-423n4/2022-09-frax/tree/main/src/frxETHMinter.sol#L50
mapping(address => bool) public minters;
https://github.com/code-423n4/2022-09-frax/tree/main/src/ERC20/ERC20PermitPermissionedMint.sol#L20