Platform: Code4rena
Start Date: 04/02/2022
Pot Size: $30,000 USDC
Total HM: 3
Participants: 37
Period: 3 days
Judge: leastwood
Id: 84
League: ETH
Rank: 6/37
Findings: 4
Award: $1,796.93
π Selected for report: 0
π Solo Findings: 0
The setTokenOutPrice
function allow owner to update the tokenOutPrice
. The owner of the contract can potential "rug" users by front-runing buy transactions to set a very high price. Since the README file specified "Rug Vectors" in the scope, while this attack require privileged access it should still be taken care of.
function setTokenOutPrice(uint256 _tokenOutPrice) external onlyOwner { require(_tokenOutPrice > 0, "TokenSale: the price must not be zero"); tokenOutPrice = _tokenOutPrice; emit TokenOutPriceUpdated(_tokenOutPrice); }
setTokenOutPrice
function ; ormaxBuyPrice
parameter to buy
function#0 - GalloDaSballo
2022-02-14T13:23:15Z
I disagree with the finding because this implies a buyer can be retroactively rugged, that is not the case as shown in the claim
function: https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L195
Other findings show that buyers can be fronturn, I agree with those, but disagree with this one
#1 - 0xleastwood
2022-03-14T11:58:46Z
While I agree with the sponsor that the wording is somewhat misleading. I do believe the warden is highlighting the same issue.
#2 - 0xleastwood
2022-03-14T11:58:52Z
Duplicate of #50
#3 - 0xleastwood
2022-03-16T12:57:36Z
Duplicate of #105
64.508 USDC - $64.51
Anyone can run the initialize
function
https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L96
tokenInLimit
can be set to lower than totalTokenIn
function setTokenInLimit(uint256 _tokenInLimit) external onlyOwner { require(!finalized, "TokenSale: already finalized"); tokenInLimit = _tokenInLimit; emit TokenInLimitUpdated(_tokenInLimit); }
initialize
lack of input validation_tokenOut
and _tokenIn
are not made sure !=address(0)
_tokenInLimit
is not made sure !=0
initialize
might consider emit these event in initialize
to allow tracking of these parameters
event SaleStartUpdated(uint64 saleStart); event SaleDurationUpdated(uint64 saleDuration); event TokenOutPriceUpdated(uint256 tokenOutPrice); event SaleRecipientUpdated(address indexed recipient); event GuestlistUpdated(address indexed guestlist); event TokenInLimitUpdated(uint256 tokenInLimit);
_daoId
can be anything within the range of uint8, might consider adding a mapping of votable dao
#0 - GalloDaSballo
2022-02-14T13:10:40Z
I feel like all these should be non-critical instead of low risk
Initialization is a non issue as we deploy and initialize atomically
Everything else is informational in nature, hence non-critical
37.4407 USDC - $37.44
> 0
is less efficient than != 0
for uint in require conditionRef: https://twitter.com/GalloDaSballo/status/1485430908165443590 https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L111
require( _saleDuration > 0, "TokenSale: the sale duration must not be zero" );
grep -Rn "> 0" ./contracts/ | grep require ./contracts/TokenSaleUpgradeable.sol:114: require(_tokenOutPrice > 0, "TokenSale: the price must not be zero"); ./contracts/TokenSaleUpgradeable.sol:154: require(_tokenInAmount > 0, "_tokenInAmount should be > 0"); ./contracts/TokenSaleUpgradeable.sol:197: require(tokenOutAmount_ > 0, "nothing to claim"); ./contracts/TokenSaleUpgradeable.sol:304: require(_tokenOutPrice > 0, "TokenSale: the price must not be zero"); ./contracts/TokenSaleUpgradeable.sol:364: require(amount > 0, "nothing to sweep");
Solidity ^0.8.4 allow the use of custom errors to optimize gas usage. https://blog.soliditylang.org/2021/04/21/custom-errors/
Use uint(1) instead of bool(true) to save gas by avoiding masking ops https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L40
mapping(address => bool) public hasClaimed;
Use uint256 instead of uint8 to save gas by avoiding masking ops https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L57
mapping(address => uint8) public daoVotedFor;
// out = x * y unchecked{/} z function fmul(uint256 x, uint256 y, uint256 z) internal pure returns(uint256 out){ assembly{ if iszero(eq(div(mul(x,y),x),y)) {revert(0,0)} out := div(mul(x,y),z) } }
can be applied to floating multiplication such as in https://github.com/code-423n4/2022-02-badger-citadel/blob/84596551d62f243d13fcb2d486346dde08002f7b/contracts/TokenSaleUpgradeable.sol#L221
tokenOutAmount_ = (_tokenInAmount * 10**tokenOut.decimals()) / tokenOutPrice;
boughtAmounts[msg.sender] = boughtAmountTillNow + tokenOutAmount_; daoCommitments[_daoId] += tokenOutAmount_; totalTokenIn += _tokenInAmount; totalTokenOutBought += tokenOutAmount_;
can be changed into
totalTokenOutBought += tokenOutAmount_; unchecked{ boughtAmounts[msg.sender] = boughtAmountTillNow + tokenOutAmount_; daoCommitments[_daoId] += tokenOutAmount_; totalTokenIn += _tokenInAmount; }
since
totalTokenOutBought
will not overflow in L156totalTokenOutBought
does not overflow, boughtAmounts[msg.sender]
and daoCommitments[_daoId]
will not overflow#0 - GalloDaSballo
2022-02-14T13:09:37Z
I think the report is very well thought out, my only criticism is with:
Use uint256 instead of bool
and Use uint256 instead of uint8
, while for every operation there will be 3 gas extra cost in masking, using a smaller size will make storage reads cheaper.