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: 10/72
Findings: 3
Award: $2,832.74
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: cccz
Also found by: 0xBug, 0xDjango, CertoraInc, TrungOre, VAD37, berndartmueller, georgypetrov, horsefacts, m9800, pedroais, rayn, reassor, scaraven, wuwe1
431.1404 USDC - $431.14
https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadelVester.sol#L132-L136 https://github.com/code-423n4/2022-04-badger-citadel/blob/dab143a990a9c355578fbb15cd3c884614e33f42/src/interfaces/citadel/IVesting.sol#L5-L9 https://github.com/code-423n4/2022-04-badger-citadel/blob/dab143a990a9c355578fbb15cd3c884614e33f42/src/StakedCitadel.sol#L830
Contract StakedCitadel
uses _withdraw
internal function for withdrawing tokens which then are supposed to be sent to StakedCitadelVester
contract. Interface IVesting
is used to interact with StakedCitadelVester
contract. The issue is that there is function name mismatch between interface and its implementation which ends up with revert of every transaction that tries to withdraw staked tokens. The staked tokens are effectively lost.
StakedCitadelVester
implements vest
function:function vest( address recipient, uint256 _amount, uint256 _unlockBegin ) external {
IVesting
is defined setupVesting
function:interface IVesting { function setupVesting( address recipient, uint256 _amount, uint256 _unlockBegin ) external;
StakedCitadel
in _withdraw
function tries to send it to StakedCitadelVester
contract:IVesting(vesting).setupVesting(msg.sender, _amount, block.timestamp);
withdraw
, withdrawAll
ends up with revert.Manual Review / VSCode
The solution is to properly implement interface in StakedCitadelVester
with setupVesting
function:
function setupVesting( address recipient, uint256 _amount, uint256 _unlockBegin ) external {
Alternative solution is changing IVesting
interface the function setupVesting
to vest
:
interface IVesting { function vest( address recipient, uint256 _amount, uint256 _unlockBegin ) external;
And invoking vest
in StakedCitadel
contract:
IVesting(vesting).vest(msg.sender, _amount, block.timestamp)
#0 - GalloDaSballo
2022-04-23T02:08:57Z
The contract simply doesn't work
#1 - jack-the-pug
2022-05-29T07:41:43Z
Dup #9
2289.8123 USDC - $2,289.81
Function.buy
buys the tokens for whatever price is set as tokenOutPrice
. This might lead to accidental collisions or front-running attacks when user is trying to buy the tokens and his transaction is being included after the transaction of changing the price of the token via setTokenOutPrice
.
Scenario:
buy
tokens and can see price tokenOutPrice
buy
tokensCONTRACT_GOVERNANCE_ROLE
account is increasing tokenOutPrice
through setTokenOutPrice
setTokenOutPrice
transaction is included before user's buy
transactionAnother variation of this attack can be performed using front-running.
Manual Review / VSCode
It is recommended to add additional parameter uint256 believedPrice
to KnightingRound.buy
function and check if believedPrice
is equal to tokenOutPrice
.
#0 - GalloDaSballo
2022-04-23T01:41:38Z
I think we should add a minOut check honestly, @dapp-whisperer ?
🌟 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
111.7859 USDC - $111.79
Low
Project's contracts are missing zero address checks for setting state variables and using addresses for internal transaction logic. Setting some of the state variables to the zero address, whether intentional or not, can break the protocol functionality. Adding these checks consistently would prevent this scenario.
CitadelToken.sol
address _gac
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelToken.sol#L25address dest
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelToken.sol#L40GlobalAccessControl.sol
address _initialContractGovernance
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/GlobalAccessControl.sol#L61Funding.sol
address _gac
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L105address _citadel
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L106address _asset
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L107address _xCitadel
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L108address _discountManager
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L373address _token
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L315KnightingRound.sol
address _globalAccessControl
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L110address _tokenOut
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L111address _tokenIn
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L112StakedCitadelVester.sol
address _gac
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadelVester.sol#L60address recipient
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadelVester.sol#L85SupplySchedule.sol
address _gac
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/SupplySchedule.sol#L43GlobalAccessControlManaged.sol
address _globalAccessControl
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/GlobalAccessControlManaged.sol#L27SettAccessControl.sol
address _strategist
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/SettAccessControl.sol#L37address _keeper
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/SettAccessControl.sol#L44address _governance
- https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/SettAccessControl.sol#L51Manual Review / VSCode
It is recommended to add zero address checks to listed functions.
Low
Function Funding.setDiscountLimits
does not properly validate arguments. There is a missing check in case _minDiscount
is bigger than _maxDiscount
. Setting funding.maxDiscount
to smaller value than funding.minDiscount
might lead to undefined behavior.
Manual Review / VSCode
It is recommended to add check if passed _minDiscount
is smaller or equal to _maxDiscount
.
Low
Most of Funding
contract functions contain nonReentrant
modifier that protects against reentrancy vulnerabilities. Function claimAssetToTreasury
is missing that check.
Manual Review / VSCode
It is recommended to add nonReentrant
modifier to function Funding.claimAssetToTreasury
.
Non-Critical
Function _withdraw
of StakedCitadel
contract performs calculations to retrieve tokens based on the shares. One of the calculations lead to division by zero if the amount of shares totalSupply
is equal to 0
.
Manual Review / VSCode
It is recommended to check if totalSupply
is not equal to 0
.
Non-Critical
Contract Funding
and KnightingRound
use ERC20 tokens as an asset token and implement sweep
mechanism to withdraw any tokens that have been accidentally sent to the contract(s). Function sweep
checks if the the address of the token is the one used by the contract but this might be bypassed if the ERC20 token has multiple addresses.
Reference:
Manual Review / VSCode
It is recommended to validate asset tokens to make sure that used ERC20 token has not multiple addresses.
Non-Critical
Modifier onlyWhenPriceNotFlagged
of Funding
contract uses false
boolean expression for comparing.
Manual Review / VSCode
It is recommended to use !citadelPriceFlag
expression in require statement.
Non-Critical
Multiple functions contains typos in their error messages.
Manual Review / VSCode
It is recommended to fix typos.