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: 45/72
Findings: 2
Award: $144.11
🌟 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
92.0693 USDC - $92.07
pragma abicoder V2
Since CitadelMinter and GlobalAccessControl was using pragma experimental ABIEncoderV2
and used pragma 0.8.12, it because the ABI coder v2 is not considered experimental anymore, it can be selected via pragma abicoder v2 instead since Solidity 0.7.4.
##POC
https://docs.soliditylang.org/en/develop/layout-of-source-files.html#spdx-license-identifier
##Tools
Manual Review
##Recommended Mitigation
you can change it into pragma abicoder v2;
since no return
was used here, it can be deleted or use it instead if necessary.
##Tool used Manual Review
Since, CitadelMinter, KnightingRound, StakedCitadel, StakedCitadelVester, SupplySchedule -> 0.8.12 CitadelToken, GlobalAccessControl -> ^0.8.0
since CitadelToken & GlobalAccessControl can be using the same as the others for consistency pragma
##Tool Used Manual Review
##POC Make sure though that you do not allow multiple initializations. For just a few parameters, simply add a check for each parameter, e.g., require(rate != 0). For many parameters, add an isInitialized boolean state variable:
such as e.g
bool isInitialized = false; function initialize( address _param1, address _param2, address _param3, address _param4, ) public { require(!isInitialized, 'Contract is already initialized!'); isInitialized = true;
this function order can be change to minimizing error. so commonly function add()
must be in order first then remove()
after. it should be better on that way.
##Tool Used Manual Review
🌟 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
52.043 USDC - $52.04
= 0
If a variable was not set/initialized, it is assumed to have default value to 0
this implementation was used for saving more gas by removing = 0
##TOOLS USED Visual Studio Code, Manual Review
##Mitigation Step
Remove = 0
uint256 lockingAmount = 0; uint256 stakingAmount = 0; uint256 fundingAmount = 0;
Another Occurance
main/src/CitadelMinter.sol#L366
uint256 i = 0
into uint i
for saving more gasthis implementation can saving more gas for each loops.
##Tool Used Manual Review
##Recommended Mitigation Change it
main/src/CitadelMinter.sol#L152 main/src/SupplySchedule.sol#L103 main/src/SupplySchedule.sol#L192
Using i++ instead ++i for all the loops, the variable i is incremented using i++. It is known that implementation by using ++i costs less gas per iteration than i++.
Manual Review
main/src/CitadelMinter.sol#L152
Since address
was 20 bytes and uint256
was 32 bytes on struct, this implementation below can be used for gas opt
##Tool Used Manual Review
##Recommended Mitigation
struct FundingParams { address discountManager; uint256 discount; uint256 minDiscount; uint256 maxDiscount; uint256 assetCumulativeFunded; /// persistent sum of asset amount in over lifetime of contract. uint256 assetCap; /// Max asset token that can be taken in by the contract (defines the cap for citadel sold) }
Every reason string takes at least 32 bytes. Use short reason strings that fits in 32 bytes or it will become more expensive.
##Tool Used Manual Review
##Occurance :
main/src/CitadelMinter.sol#L301 main/src/CitadelMinter.sol#L321 main/src/CitadelMinter.sol#L328 main/src/CitadelMinter.sol#L370 main/src/CitadelMinter.sol#L377 main/src/Funding.sol#L148 main/src/Funding.sol#L298 main/src/Funding.sol#L325 main/src/StakedCitadelVester.sol#L137 main/src/StakedCitadelVester.sol#L138
This implementation below can be saving more gas
##Tool Used Manual Review
##Recommended Mitigation
uint256 public constant INITIAL_VESTING_DURATION = 86400 * 21; // 21 days of vesting
changed to
uint256 public constant INITIAL_VESTING_DURATION = 1814400; // 21 days of vesting
This implementation can be used for saving more gas, instead cache vesting[msg.sender].claimedAmounts it can be changed by using +=
instead.
##POC https://www.tutorialspoint.com/solidity/solidity_operators.htm
##Tool Used Manual Review
##Recommended Mitigation
vesting[msg.sender].claimedAmounts += amount;