Badger Citadel contest - saian's results

Bringing BTC to DeFi

General Information

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

BadgerDAO

Findings Distribution

Researcher Performance

Rank: 64/72

Findings: 1

Award: $63.65

🌟 Selected for report: 0

🚀 Solo Findings: 0

Gas Optimizations

revert strings > 32 bytes

Impact

Revert strings greater than 32 bytes will increase deployment gas as well as when require condition is met

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/GlobalAccessControl.sol#L118

require( keccak256(bytes(roleString)) == role, "Role string and role do not match" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L301

require( _fundingBps + _stakingBps + _lockingBps == MAX_BPS, "CitadelMinter: Sum of propvalues must be 10000 bps" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L321

require( lastMintTimestamp == 0, "CitadelMinter: last mint timestamp already initialized" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L328

require( globalStartTimestamp != 0, "CitadelMinter: supply schedule start not initialized" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L370

require( fundingPools.remove(_pool), "CitadelMinter: funding pool does not exist for removal" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L377

require( fundingPools.add(_pool), "CitadelMinter: funding pool already exists" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L148

require( citadelPriceFlag == false, "Funding: citadel price from oracle flagged and pending review" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L298

require( _assetCap > funding.assetCumulativeFunded, "cannot decrease cap below global sum of assets in" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L325

require( _token != address(asset), "cannot sweep funding asset, use claimAssetToTreasury()" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L390

require( _saleRecipient != address(0), "Funding: sale recipient should not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L120-L135

require( _saleStart >= block.timestamp, "KnightingRound: start date may not be in the past" ); require( _saleDuration > 0, "KnightingRound: the sale duration must not be zero" ); require( _tokenOutPrice > 0, "KnightingRound: the price must not be zero" ); require( _saleRecipient != address(0), "KnightingRound: sale recipient should not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L295-L296

require( _saleStart >= block.timestamp, "KnightingRound: start date may not be in the past" ); require(!finalized, "KnightingRound: already finalized");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L314

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L351

require( _saleRecipient != address(0), "KnightingRound: sale recipient should not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L384

require(!finalized, "KnightingRound: already finalized");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L537

require( _fees <= PERFORMANCE_FEE_HARD_CAP, "performanceFeeStrategist too high" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadelVester.sol#L137

require(msg.sender == vault, "StakedCitadelVester: only xCTDL vault"); require(_amount > 0, "StakedCitadelVester: cannot vest 0");

Mitigation

Revert string can be reduced to 32 bytes or can be replaced with custom errors

Initialization with default value

When variables are declared it contains the default values, so explicit initilization of default value is not necessary

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L152

for (uint256 i = 0; i < numPools; i++) {

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L180-L182

uint256 lockingAmount = 0; uint256 stakingAmount = 0; uint256 fundingAmount = 0;

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L137

// No circuit breaker on price by default minCitadelPriceInAsset = 0;

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/GlobalAccessControlManaged.sol#L48

for (uint256 i = 0; i < roles.length; i++) {

Mitigation

Initization with default value can be removed

Avoid storage re-read and save gas

Storage variables can be stored in a local variables and re-used instead of reading from storage to save gas

Proof of concept

citadelToken in CitadelMinter:mintAndDistribute()

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L178

funding.discount in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L212

if (funding.discount > 0) { citadelAmount_ = (citadelAmountWithoutDiscount * MAX_BPS) / (MAX_BPS - funding.discount); }

asset in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L339

uint256 amount = asset.balanceOf(address(this)); require(amount > 0, "nothing to claim"); asset.safeTransfer(saleRecipient, amount); emit ClaimToTreasury(address(asset), amount);

minCitadelPriceInAsset, maxCitadelPriceInAsset in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L427-L435

if ( _citadelPriceInAsset < minCitadelPriceInAsset || _citadelPriceInAsset > maxCitadelPriceInAsset ) { citadelPriceFlag = true; emit CitadelPriceFlag( _citadelPriceInAsset, minCitadelPriceInAsset, maxCitadelPriceInAsset );

saleStart, guestList in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L167-L179

totalTokenin, tokenInLimit in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L249-L250

strategy in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L507

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L723

vesting in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L831

strategist in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L925-L928

Mitigation

storage variable can be cached in a temporary variable and re-used

Postfix increment can be replaced with prefix increment

As return value of postfix i++ is not used, it can be replaced with prefix increment ++i, and unchecked can be added as there is no risk of overflow

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/SupplySchedule.sol#L208

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L152

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L344

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/lib/GlobalAccessControlManaged.sol#L48

Mitigation

postfix increment can be converted to prefix increment

> can be replaced with !=

!= 0 is more efficient than > 0 with uint in require statements

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L343

require(length > 0, "CitadelMinter: no funding pools");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L170

require(_assetAmountIn > 0, "_assetAmountIn must not be 0");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L322

require(amount > 0, "nothing to sweep");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L340

require(amount > 0, "nothing to claim");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L424

require(_citadelPriceInAsset > 0, "citadel price must not be zero");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L124

require( _saleDuration > 0, "KnightingRound: the sale duration must not be zero" ); require( _tokenOutPrice > 0, "KnightingRound: the price must not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L172

require(_tokenInAmount > 0, "_tokenInAmount should be > 0");

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L312

require( _saleDuration > 0, "KnightingRound: the sale duration must not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L331

require( _tokenOutPrice > 0, "KnightingRound: the price must not be zero" );

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L411

require(amount > 0, "nothing to sweep");

Value can be used directly

Storage values used only once in function can be used directly instead of storing in a temporary variable

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/CitadelMinter.sol#L363

uint256 currentPoolWeight = fundingPoolWeights[_pool]; totalFundingPoolWeight = totalFundingPoolWeight - currentPoolWeight;

using unchecked can save gas

When expression cant overflow/underflow using unchecked can avoid default overflow/underflow checks introduced in 0.8 and save gas

Proof of concept

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/Funding.sol#L236

if (assetCumulativeFunded < assetCap) { limitLeft_ = assetCap - assetCumulativeFunded; }

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/KnightingRound.sol#L249

if (totalTokenIn < tokenInLimit) { limitLeft_ = tokenInLimit - totalTokenIn; }

Unused variables

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/GlobalAccessControl.sol#L51

bool public transferFromDisabled; // Set to true in initialize

Mitigation

Unused variable can be removed

Function return value can be cached and reused

Proof of concept

totalSupply() in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L285-L288

if (totalSupply() == 0) { return ONE_ETH; } return (balance() * ONE_ETH) / totalSupply();

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L890

if (totalSupply() == 0) { shares = _amount; } else { shares = (_amount * totalSupply()) / _pool; }

balance() in

https://github.com/code-423n4/2022-04-badger-citadel/blob/18f8c392b6fc303fe95602eba6303725023e53da/src/StakedCitadel.sol#L909-L918

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter