Platform: Code4rena
Start Date: 07/04/2022
Pot Size: $100,000 USDC
Total HM: 20
Participants: 62
Period: 7 days
Judge: LSDan
Total Solo HM: 11
Id: 107
League: ETH
Rank: 25/62
Findings: 2
Award: $346.26
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Dravee
Also found by: 0x1f8b, 0xDjango, 0xkatana, AuditsAreUS, Cityscape, Foundation, Funen, Hawkeye, IllIllI, JC, JMukesh, Jujic, Kthere, PPrieditis, Picodes, Ruhum, TerrierLover, TrungOre, WatchPug, berndartmueller, catchup, cccz, cmichel, delfin454000, dy, ellahi, hickuphh3, horsefacts, hubble, hyh, ilan, jayjonah8, kebabsec, kenta, minhquanym, pauliax, rayn, reassor, rfa, robee, samruna
155.9803 USDC - $155.98
There are two for loops used in LPFarming.sol. ++pid is used at one loop while i++ is used at the other loop.
2022-04-jpegd/contracts/farming/LPFarming.sol 281: for (uint256 pid = 0; pid < length; ++pid) { 348: for (uint256 i = 0; i < poolInfo.length; i++) {
At the whole codebase, postfix increment is used. So it may be logical to use the postfix increment.
Error message in the require check seems not to be consistent. The below code is an example: one uses invalid_amount while the other uses INVALID_AMOUNT.
require(_amount > 0, "invalid_amount");
require(_amount > 0, "INVALID_AMOUNT");
For the consistency, it should standardize the error message among the codebase for the better code quality.
_mint(msg.sender, totalSupply); _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
To be consistent, _msgSender() should be used.
_mint(_msgSender(), totalSupply); _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_collateralUnit
variable is inconsistent at FungibleAssetVaultForDAO.soluint256 private _collateralUnit;
Other variables does not have _
at its prefix, but only _collateralUnit
contains _
.
Simply remove _
from its prefix.
uint256 private collateralUnit;
function stake(uint256 _amount) external {
unstake function has nonReentrant modifier but stake function does not. For the consistency, it may not harm adding nonReentrant modifier at stake function as well.
function stake(uint256 _amount) external nonReentrant {
🌟 Selected for report: Dravee
Also found by: 0v3rf10w, 0x1f8b, 0xDjango, 0xNazgul, 0xkatana, Cityscape, Cr4ckM3, FSchmoede, Foundation, Funen, Hawkeye, IllIllI, JMukesh, Meta0xNull, PPrieditis, Picodes, TerrierLover, Tomio, WatchPug, berndartmueller, catchup, delfin454000, dirk_y, ellahi, hickuphh3, ilan, kebabsec, kenta, nahnah, rayn, rfa, robee, rokinot, saian, securerodd, slywaters, sorrynotsorry
190.2785 USDC - $190.28
2022-04-jpegd/contracts/farming/LPFarming.sol 114: require(_rewardPerBlock > 0, "Invalid reward per block"); 218: require(_amount > 0, "invalid_amount"); 239: require(_amount > 0, "invalid_amount"); 320: if (pending > 0) { 337: require(rewards > 0, "no_reward"); 354: require(rewards > 0, "no_reward");
2022-04-jpegd/contracts/farming/yVaultLPFarming.sol 84: if (block.number > lastRewardBlock && staked > 0) { 101: require(_amount > 0, "invalid_amount"); 118: require(_amount > 0, "invalid_amount"); 139: require(rewards > 0, "no_reward"); 181: if (pending > 0) userPendingRewards[account] += pending;
2022-04-jpegd/contracts/lock/JPEGLock.sol 40: require(_newTime > 0, "Invalid lock time");
2022-04-jpegd/contracts/staking/JPEGStaking.sol 32: require(_amount > 0, "invalid_amount"); 46: _amount > 0 && _amount <= balanceOf(msg.sender),
2022-04-jpegd/contracts/vaults/FungibleAssetVaultForDAO.sol 94: _creditLimitRate.denominator > 0 && 142: require(amount > 0, "invalid_amount"); 164: require(amount > 0, "invalid_amount"); 180: require(amount > 0, "invalid_amount"); 194: require(amount > 0 && amount <= collateralAmount, "invalid_amount");
2022-04-jpegd/contracts/vaults/NFTVault.sol 278: require(_newFloor > 0, "Invalid floor"); 327: _type == bytes32(0) || nftTypeValueETH[_type] > 0, 365: require(pendingValue > 0, "no_pending_value"); 402: rate.denominator > 0 && rate.denominator >= rate.numerator, 637: uint256 debtAmount = positions[_nftIndex].liquidatedAt > 0 687: require(_amount > 0, "invalid_amount"); 764: require(_amount > 0, "invalid_amount"); 770: require(debtAmount > 0, "position_not_borrowed"); 882: require(position.liquidatedAt > 0, "not_liquidated"); 926: require(position.liquidatedAt > 0, "not_liquidated");
2022-04-jpegd/contracts/vaults/yVault/yVault.sol 100: _rate.numerator > 0 && _rate.denominator >= _rate.numerator, 143: require(_amount > 0, "INVALID_AMOUNT"); 167: require(_shares > 0, "INVALID_AMOUNT"); 170: require(supply > 0, "NO_TOKENS_DEPOSITED");
2022-04-jpegd/contracts/vaults/yVault/strategies/StrategyPUSDConvex.sol 182: _performanceFee.denominator > 0 && 322: if (balance > 0) 334: require(wethBalance > 0, "NOOP");
Use use != 0 instead of > 0 at the above codebase. It checks with uint256 variable, so != and > 0 has a same meaning gramatically. Using != 0 can reduce the gas deployment cost of these contracts.
bytes memory bytecode = _encodeFlashEscrow(_idx); //hash from which the contract address can be derived bytes32 hash = keccak256( abi.encodePacked( bytes1(0xff), address(this), salt, keccak256(bytecode)
Simply do not define bytecode variable to reduce the gas cost.
//hash from which the contract address can be derived bytes32 hash = keccak256( abi.encodePacked( bytes1(0xff), address(this), salt, keccak256(_encodeFlashEscrow(_idx))
2022-04-jpegd/contracts/farming/LPFarming.sol 281: for (uint256 pid = 0; pid < length; ++pid) { 348: for (uint256 i = 0; i < poolInfo.length; i++) {
2022-04-jpegd/contracts/vaults/NFTVault.sol 181: for (uint256 i = 0; i < _typeInitializers.length; i++) { 184: for (uint256 j = 0; j < initializer.nfts.length; j++) {
2022-04-jpegd/contracts/vaults/yVault/strategies/StrategyPUSDConvex.sol 145: for (uint256 i = 0; i < _strategyConfig.rewardTokens.length; i++) { 231: for (uint256 i = 0; i < length; i++) { 319: for (uint256 i = 0; i < rewardTokens.length; i++) {
Just do not initialize with 0 for the variable used in for loop. The example code is as follows:
for (uint256 pid; pid < length; ++pid) {
uint256 staked = totalStaked; //if blockNumber is greater than the pool's `lastRewardBlock` the pool's `accRewardPerShare` is outdated, //we need to calculate the up to date amount to return an accurate reward value if (block.number > lastRewardBlock && staked > 0) { (rewardShare, ) = _computeUpdate(); }
staked variable is not used anywhere, so there is no need to define staked variable.
//if blockNumber is greater than the pool's `lastRewardBlock` the pool's `accRewardPerShare` is outdated, //we need to calculate the up to date amount to return an accurate reward value if (block.number > lastRewardBlock && totalStaked > 0) { (rewardShare, ) = _computeUpdate(); }
This change can reduce the gas cost since it does not define variable.
newRewards is only used once so no need to be defined.
uint256 newRewards = currentBalance - previousBalance; newAccRewardsPerShare = accRewardPerShare + newRewards * 1e36 / totalStaked;
newAccRewardsPerShare = accRewardPerShare + (currentBalance - previousBalance) * 1e36 / totalStaked;
address account = punks.punkIndexToAddress(_idx); //if the owner is this address we don't need to go through {NFTEscrow} if (account != address(this)) { _executeTransfer(_from, _idx); }
//if the owner is this address we don't need to go through {NFTEscrow} if (punks.punkIndexToAddress(_idx) != address(this)) { _executeTransfer(_from, _idx); }
function repay(uint256 amount) external onlyRole(WHITELISTED_ROLE) nonReentrant { require(amount > 0, "invalid_amount"); amount = amount > debtAmount ? debtAmount : amount; debtAmount -= amount;
At line 184, debtAmount - amount
becomes more than or equals to 0. So debtAmount -= amount;
can be wrapped by unchecked.
function repay(uint256 amount) external onlyRole(WHITELISTED_ROLE) nonReentrant { require(amount > 0, "invalid_amount"); amount = amount > debtAmount ? debtAmount : amount; unchecked { debtAmount -= amount; }
function withdraw(uint256 amount) external onlyRole(WHITELISTED_ROLE) nonReentrant { require(amount > 0 && amount <= collateralAmount, "invalid_amount"); uint256 creditLimit = getCreditLimit(collateralAmount - amount); require(creditLimit >= debtAmount, "insufficient_credit"); collateralAmount -= amount;
At line 184, collateralAmount - amount
becomes more than or equals to 0. So collateralAmount -= amount;
can be wrapped by unchecked.
function withdraw(uint256 amount) external onlyRole(WHITELISTED_ROLE) nonReentrant { require(amount > 0 && amount <= collateralAmount, "invalid_amount"); uint256 creditLimit = getCreditLimit(collateralAmount - amount); require(creditLimit >= debtAmount, "insufficient_credit"); unchecked { collateralAmount -= amount; }
Simply avoiding defining collateralValue.
function getCreditLimit(uint256 amount) public view returns (uint256) { return (_getCollateralValue(amount) * creditLimitRate.numerator) / creditLimitRate.denominator; }
This would reduce the deployment gas cost.
uint256 creditLimit = getCreditLimit(collateralAmount); uint256 newDebtAmount = debtAmount + amount; require(newDebtAmount <= creditLimit, "insufficient_credit");
creditLimit variable is only used once, so it can avoid be defined.
Simply avoid defining creditLimit. This would decrease the deployment gas cost.
uint256 newDebtAmount = debtAmount + amount; require(newDebtAmount <= getCreditLimit(collateralAmount), "insufficient_credit");
uint256 creditLimit = getCreditLimit(collateralAmount - amount); require(creditLimit >= debtAmount, "insufficient_credit");
creditLimit variable is only used once, so it can avoid be defined.
Simply avoid defining creditLimit. This would decrease the deployment gas cost.
require(getCreditLimit(collateralAmount - amount) >= debtAmount, "insufficient_credit");