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: 27/62
Findings: 2
Award: $290.53
🌟 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
208.077 USDC - $208.08
position.borrowType == BorrowType.USE_INSURANCE
in if
statementif(position.borrowType == BorrowType.USE_INSURANCE || _useInsurance)
can be changed to
if(_useInsurance)
Because, if the BorrowType
is USE_INSURANCE then
require( position.borrowType == BorrowType.NOT_CONFIRMED || (position.borrowType == BorrowType.USE_INSURANCE && _useInsurance) || (position.borrowType == BorrowType.NON_INSURANCE && !_useInsurance), "invalid_insurance_mode" );
will pass only if _useInsurance = True
. Therefore,
position.borrowType == BorrowType.USE_INSURANCE
-> _useInsurance = True
.
Hence, it is enough to check if(_useInsurance)
setNFTTypeValueETH
, setPendingNFTValueETH
Functions setNFTTypeValueETH
, setPendingNFTValueETH
in NFTVault
Should check that _amountETH > 0
to avoid uneccesary write to nftTypeValueETH[_type]
storage variable.
@return
commentThese functions have a return but do not document it:
LPFarming.sol._withdrawReward, yVaultLPFarming.sol._withdrawReward, NFTEscrow._encodeFlashEscrowPayload, EtherRocksHelper._encodeFlashEscrowPayload, CryptoPunksHelper._encodeFlashEscrowPayload, StrategyPUSDConvex.withdraw
Some structs have untrivial names / variables and should be documented.
JPEGLock.LockPosition, NFTVault.Position, NFTVault.VaultSettings, NFTVault.NFTCategoryInitializer, NFTVault.NFTInfo, NFTVault.PositionPreview, Rate (multiple contracts)
JPEG.sol
missing documentationComments should be added to the file JPEG.sol
.
These functions should be external as they are public but not called locally in the contract.
Controller.withdraw, yVault.setFarmingPool
In NFTVault.sol
, the following code is used:
uint256 interestPerSec = interestPerYear / 365 days; return elapsedTime * interestPerSec;
notice that if interestPerYear < 31536000
, then interestPerSec would be 0. Therefore,
elapsedTime * interestPerSec;
would be 0 and the additional interest returned would be 0, even
though elapsedTime could be very high.
change the above code to
uint256 additionalInterestYearly = elapsedTime * interestPerYear; return additionalinterestPerYearly / 365 days;
When making an external call during a loop, if the external call failes it will result in all of the calls for all the loop iterations to fail. This could happen in the contract LPFarming.
In LPFarming._massUpdatePools()
, _updatePool()
is called on every pool using a loop.
Inside _updatePool()
, we can find the external call
uint256 lpSupply = pool.lpToken.balanceOf(address(this));
Now, if, for any reason, balanceOf
for a specific token will revert, it will revert the whole
_massUpdatePools()
operation. This could happen if an attacker is able to insert a token with a dangerous balanceOf
on purpose, or if it reverts by accident.
Therefore, after this attack the functions newEpoch, add, set
which call _massUpdatePools()
will revert
🌟 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
82.4497 USDC - $82.45
i++ is generally more expensive because it must increment a value and return the old value, so it may require holding two numbers in memory. ++i only uses one number in memory.
Example:
for (uint256 i = 0; i < poolInfo.length; i++) -> for (uint256 i = 0; i < poolInfo.length; i++)
(LPFarming 348)
Affected contracts: LPFarming.sol, NFTVault.sol, StrategyPUSDConvex.sol
If a variable is not initialized it is automatically set to the default value (0 for uint, false for bool, address(0) for address...). Explicitly initializing it with its default value is an anti-pattern and wastes gas.
Example:
for (uint256 i = 0; i < poolInfo.length; i++) -> for (uint256 i; i < poolInfo.length; i++)
Affected contracts: LPFarming.sol, NFTVault.sol, StrategyPUSDConvex.sol
Calling storage/memory variable which does not change, every loop iteration, is wrong and wastes gas.
for (uint256 i = 0; i < poolInfo.length; i++)
->
uint256 length = poolInfo.length;
for (uint256 i = 0; i < length; i++)
Affected contracts: LPFarming.sol, NFTVault.sol, StrategyPUSDConvex.sol
Use calldata instead of memory for external functions where the function argument is read-only.
function setBorrowAmountCap(uint256 _borrowAmountCap) external
->
function setBorrowAmountCap(uint256 calldata _borrowAmountCap) external
Affected contracts: Neary every contract
Some variables could be set immutable to save gas
collateralAsset
in FungibleAssetVaultForDAO
_collateralUnit
in FungibleAssetVaultForDAO
unused import wastes gas.
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
in NFTEscrow