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: 16/72
Findings: 3
Award: $1,178.76
🌟 Selected for report: 0
🚀 Solo Findings: 0
896.081 USDC - $896.08
Calling balance
only returns the balance of underlying token in StakedCitadel, while it should also includes tokens deposited in the strategy. As balance
is heavily used within the contract, this wrong value will cause a lot of problems.
In particular, withdrawal uses balance
to calculate the amount of token to withdraw per share. If a user tries to withdraw when tokens are deposited in the strategy, they will receive less amount than they should actually get.
Include the token balance of strategy in balance()
.
#0 - jack-the-pug
2022-05-30T06:52:57Z
Dup #74
184.248 USDC - $184.25
https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L202-L216 https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L177
When funding.discount
is set to 0, calling getAmountOut
will always return 0 instead of the actual amount without discount. As a result, users calling deposit
will receive 0 xCitadel, essentially losing their funds. This is particularly dangerous as the contract is initialized with discount value of 0.
citadelAmount_
is not assigned value when funding.discount is 0.
Modify L207-2013 to:
citadelAmount_ = _assetAmountIn * citadelPriceInAsset; if (funding.discount > 0) { citadelAmount_ = (citadelAmount_ * MAX_BPS) / (MAX_BPS - funding.discount); }
or alternatively, add a new conditional statement for funding.discount == 0
.
#0 - jack-the-pug
2022-05-29T07:11:24Z
Dup #149
🌟 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
98.4337 USDC - $98.43
While StakedCitadel.initialize
has a zero-address check on strategist, there is no such check in SettAccessControl.setStrategist
it inherits. Strategist being set to zero address would cause StakedCitadel.reportAdditionalToken
to revert.
Add zero address check in setStrategist.
Passing a wrong address to SettAccessControl.setGovernance
would mean losing access to governance functions, and would require a contract redeployment to fix.
Use two-step process (set pending governance and accepting the governance) for setting new governance address.
transferFromDisabled
GlobalAccessControl.transferFromDisabled
is not used anywhere. The comment stated that it should be set to true in initialize
, but there is no such statement in the initialize function.
Remove if unused or implement it according to the specification.
deposit
Funding.deposit
comment mistakenly defines _minCitadelOut
param as "ID of DAO to vote for".
Change the comment to "the minimal amount of citadel token to receive".
min <= max
Some functions are missing require statement that checks whether min
param is lower/equal than max
param:
_minDiscount
higher than _maxDiscount
would make setDiscount
to always revert._minPrice
higher than _maxPrice
would cause citadelPriceFlag
to always be set to true whenever updateCitadelPriceInAsset
is called.Add the comparison checks.