Platform: Code4rena
Start Date: 14/04/2022
Pot Size: $75,000 USDC
Total HM: 8
Participants: 72
Period: 7 days
Judge: Jack the Pug
Total Solo HM: 2
Id: 110
League: ETH
Rank: 38/72
Findings: 2
Award: $168.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: IllIllI
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xkatana, AmitN, CertoraInc, Dravee, Funen, Hawkeye, Jujic, MaratCerby, Picodes, Ruhum, SolidityScan, TerrierLover, TomFrenchBlockchain, TrungOre, VAD37, Yiko, berndartmueller, cmichel, csanuragjain, danb, defsec, delfin454000, dipp, ellahi, fatherOfBlocks, georgypetrov, gs8nrv, gzeon, horsefacts, hubble, hyh, ilan, jah, joestakey, kebabsec, kenta, kyliek, m9800, minhquanym, oyc_109, p_crypt0, peritoflores, rayn, reassor, remora, rfa, robee, scaraven, securerodd, shenwilly, sorrynotsorry, tchkvsky, teryanarmen, z3s
91.5484 USDC - $91.55
The _setupRole function is deprecated according to the Open Zeppelin comment
Deprecated. This function has issues similar to the ones found in {IERC20-approve}, and its usage is discouraged.
safeApprove instances in code https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L142 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L133-L136
This Open Zeppelin comment indicates it is deprecated https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/utils/SafeERC20.sol#L39-L43
Manual analysis
Replace safeApprove with {safeIncreaseAllowance} or {safeDecreaseAllowance} instead
_setupRole
function usedThe _setupRole function is deprecated according to the Open Zeppelin comment
NOTE: This function is deprecated in favor of {_grantRole}
Use the recommended _grantRole function instead.
The _setupRole function, which is deprecated, is found in two places https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/GlobalAccessControl.sol#L69-L71
This Open Zeppelin comment indicates it is deprecated https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol#L195
Manual analysis
Replace the _setupRole function with _grantRole from the same Open Zeppelin library
The sweep function has this comment
/** * @notice Transfers out any tokens accidentally sent to the contract. Can only be called by owner * @dev The contract transfers all `asset` directly to `saleRecipient` during a sale so it's safe * to sweep `asset`. For `citadel`, the function only sweeps the extra amount * (current contract balance - amount left to be claimed) * @param _token The token to sweep */
But there is no code to sweep asset
in the amount of "contract balance - amount" as described. This comment does not match the code and needs modifying.
The incorrect comment https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L308-L314
Manual analysis
The comment should say "For citadel
, use claimAssetToTreasury
" and remove the claim about specific amounts.
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xAsm0d3us, 0xBug, 0xDjango, 0xNazgul, 0xkatana, CertoraInc, Cityscape, Funen, Hawkeye, IllIllI, MaratCerby, SolidityScan, TerrierLover, TomFrenchBlockchain, Tomio, TrungOre, bae11, berndartmueller, csanuragjain, defsec, delfin454000, ellahi, fatherOfBlocks, gs8nrv, gzeon, horsefacts, ilan, jah, joestakey, joshie, kebabsec, kenta, nahnah, oyc_109, rayn, rfa, robee, saian, securerodd, simon135, slywaters, sorrynotsorry, tchkvsky, teryanarmen, z3s
76.5844 USDC - $76.58
Using a prefix increment (++i) instead of a postfix increment (i++) saves gas for each loop cycle and so can have a big gas impact when the loop executes on a large number of elements.
There are three examples of this https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L152 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/SupplySchedule.sol#L208 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/lib/GlobalAccessControlManaged.sol#L48
Manual analysis
Use prefix not postfix to increment in a loop
Use calldata instead of memory for function parameters. Having function arguments use calldata instead of memory can save gas.
There are many cases of function arguments using memory instead of calldata
Manual analysis
Change function arguments from memory to calldata
Strings in solidity are handled in 32 byte chunks. A require string longer than 32 bytes uses more gas. Shortening these strings will save gas.
Several cases of this gas optimization were found. These are a few examples, but more may exist
Manual analysis
Shorten require strings
Solidity does not recognize null as a value, so uint variables are initialized to zero. Setting a uint variable to zero is redundant and can waste gas.
There are several places where an int is initialized to zero, which looks like
uint256 amount = 0;
Instances in code: https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/SupplySchedule.sol#L103 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/SupplySchedule.sol#L192 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L152 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L180-L182
Manual analysis
Remove the redundant zero initialization
uint256 amount;
Caching the array length outside a loop saves reading it on each iteration, as long as the array's length is not changed during the loop. This saves gas.
This optimization is already used in some places, but is not used in the GlobalAccessControlManaged lib https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/lib/GlobalAccessControlManaged.sol#L48
Manual analysis
Cache the array length before the for loop
_setupRole
call with _grantRole
The _setupRole function calls _grantRole, so it would save gas to call _grantRole directly
The _setupRole function, which is deprecated, is found in two places https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/GlobalAccessControl.sol#L69-L71
This Open Zeppelin _setupRole code shows it is deprecated and only calls _grantRole https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol#L197-L199
Manual analysis
Replace the _setupRole function with _grantRole from the same Open Zeppelin library
Using > 0
uses slightly more gas than using != 0
. Use != 0
when comparing uint variables to zero, which cannot hold values below zero
Locations where this was found include https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L270 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/CitadelMinter.sol#L343 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L170 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L209 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L322 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L340 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L424 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L452 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/StakedCitadel.sol#L835 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/StakedCitadel.sol#L908 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L125 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L129 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L172 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L184 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L215 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L313 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L332 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L411 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/StakedCitadelVester.sol#L138
grep
Replace > 0
with != 0
to save gas
Use unchecked when there is no overflow risk to save gas
This is lines 209-213 https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L209-L213
if (funding.discount > 0) { citadelAmount_ = (citadelAmountWithoutDiscount * MAX_BPS) / (MAX_BPS - funding.discount); }
The entire citadelAmount_ calculation line can be unchecked because
funding.discount <= funding.maxDiscount
from the setDiscount function and funding.maxDiscount < MAX_BPS
from the setDiscountLimits function, so funding.discount < MAX_BPS
meaning the subtraction will not overflowManual analysis
Add unchecked around math that can't overflow for gas savings
Use unchecked when there is no overflow risk to save gas
Several lines in SupplySchedule are not at risk of overflow, underflow, or divide by zero cases and can use unchecked to save gas https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/SupplySchedule.sol#L108
This can be unchecked because block.timestamp >= cachedGlobalStartTimestamp
and epochLength is a non-zero constant so the value cannot create division problems.
Manual analysis
Add unchecked around math that can't overflow for gas savings
Use unchecked when there is no overflow risk to save gas
The getTokenInLimitLeft function is not at risk of underflow and can use unchecked to save gas https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L249-L251
This can be unchecked because the check totalTokenIn < tokenInLimit
will prevent underflow in tokenInLimit - totalTokenIn
Manual analysis
Add unchecked around math that can't overflow for gas savings
The sweep function follows the checks-effects-interactions pattern for preventing reentrancy. The reentrant modifier is unnecessary and wastes gas.
Funding sweep function https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/Funding.sol#L315
KnightingRound sweep function https://github.com/code-423n4/2022-04-badger-citadel/blob/main/src/KnightingRound.sol#L402
Manual analysis
Add nonreentrant modifier only when reentrancy is a risk. Remove the reentrancy import in the KnightingRound contract