JPEG'd contest - securerodd's results

Bridging the gap between DeFi and NFTs.

General Information

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

JPEG'd

Findings Distribution

Researcher Performance

Rank: 53/62

Findings: 1

Award: $87.39

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

87.3915 USDC - $87.39

Labels

bug
G (Gas Optimization)
sponsor acknowledged

External Links

JPEGd Contest

April 12, 2022

@securerodd

Gas Optimizations

1. Refactor Beggining of borrow() in NFTVault.sol

The check performed in the beginning to see if the positionOwner is not yet set is semi-redundant as it is reperformed later in the code to determine if a position should be opened. This can be refactored to increase gas efficiency.

Current code:

accrue(); require( msg.sender == positionOwner[_nftIndex] || address(0) == positionOwner[_nftIndex], "unauthorized" ); require(_amount > 0, "invalid_amount"); require( totalDebtAmount + _amount <= settings.borrowAmountCap, "debt_cap" ); if (positionOwner[_nftIndex] == address(0)) { _openPosition(msg.sender, _nftIndex); }

Gas:

___________________________________________________________________________________________ | Contract · Method · Min · Max · Avg · | ___________________________________________________________________________________________ | NFTVault · borrow · 142891 · 428260 · 402204 · │ ___________________________________________________________________________________________

Recommended code:

accrue(); require(_amount != 0, "invalid_amount"); require( totalDebtAmount + _amount <= settings.borrowAmountCap, "debt_cap" ); if (positionOwner[_nftIndex] == address(0)) { _openPosition(msg.sender, _nftIndex); } else { require( msg.sender == positionOwner[_nftIndex], "unauthorized" ); }

Gas:

___________________________________________________________________________________________ | Contract · Method · Min · Max · Avg · | ___________________________________________________________________________________________ | NFTVault · borrow · 142868 · 427851 · 401821 · │ ___________________________________________________________________________________________

2. For Loop Refactoring

The for loops used throughout the codebase can benefit from one or more of the following gas saving techniques:

  1. Storing the array length in a local variable in memory before comparisons
  2. Using a prefix increment instead of postfix

Current Code in harvest() function of StrategyPUSDConvex.sol:

for (uint256 i = 0; i < rewardTokens.length; i++) { uint256 balance = rewardTokens[i].balanceOf(address(this)); ... }

Gas:

___________________________________________________________________________________________ | Contract · Method · Min · Max · Avg · | ___________________________________________________________________________________________ | StrategyPUSDConvex· harvest · 635854 · 747869 · 691862 · │ ___________________________________________________________________________________________

Recommended logic:

uint rewardTokenLen = rewardTokens.length; for (uint256 i = 0; i < rewardTokenLen; ++i) { uint256 balance = rewardTokens[i].balanceOf(address(this)); ... }

Gas:

___________________________________________________________________________________________ | Contract · Method · Min · Max · Avg · | ___________________________________________________________________________________________ | StrategyPUSDConvex· harvest · 635762 · 747777 · 691770 · │ ___________________________________________________________________________________________

Example nested for loops in the initializer of NFTVault.sol:

for (uint256 i = 0; i < _typeInitializers.length; i++) { NFTCategoryInitializer memory initializer = _typeInitializers[i]; nftTypeValueETH[initializer.hash] = initializer.valueETH; for (uint256 j = 0; j < initializer.nfts.length; j++) { nftTypes[initializer.nfts[j]] = initializer.hash; } }

Recommended logic:

uint256 typeInits = _typeInitializers.length; for (uint256 i = 0; i < typeInits; ++i) { NFTCategoryInitializer memory initializer = _typeInitializers[i]; nftTypeValueETH[initializer.hash] = initializer.valueETH; uint256 nftsLength = initializer.nfts.length; for (uint256 j = 0; j < nftsLength; ++j) { nftTypes[initializer.nfts[j]] = initializer.hash; } }

Other locations that could benefit from some or all of these suggestions:

  • balanceOfJPEG() in StrategyPUSDConvex.sol
  • The constructor in StrategyPUSDConvex.sol
  • _massUpdatePools() in LPFarming.sol
  • _claimAll() in LPFarming.sol

3. != 0 is More Efficient than > 0 in Require Statements

within require statements, != comparisons are slightly more gas efficient than > and when a uint is being compared to 0, != is effectively the same as > 0.

Locations:

  • contracts/farming/LPFarming.sol::114 => require(_rewardPerBlock > 0, "Invalid reward per block");
  • contracts/farming/LPFarming.sol::218 => require(_amount > 0, "invalid_amount");
  • contracts/farming/LPFarming.sol::239 => require(_amount > 0, "invalid_amount");
  • contracts/farming/LPFarming.sol::337 => require(rewards > 0, "no_reward");
  • contracts/farming/LPFarming.sol::354 => require(rewards > 0, "no_reward");
  • contracts/farming/yVaultLPFarming.sol::101 => require(_amount > 0, "invalid_amount");
  • contracts/farming/yVaultLPFarming.sol::118 => require(_amount > 0, "invalid_amount");
  • contracts/farming/yVaultLPFarming.sol::139 => require(rewards > 0, "no_reward");
  • contracts/lock/JPEGLock.sol::40 => require(_newTime > 0, "Invalid lock time");
  • contracts/staking/JPEGStaking.sol::32 => require(_amount > 0, "invalid_amount");
  • contracts/vaults/FungibleAssetVaultForDAO.sol::108 => require(answer > 0, "invalid_oracle_answer");
  • contracts/vaults/FungibleAssetVaultForDAO.sol::142 => require(amount > 0, "invalid_amount");
  • contracts/vaults/FungibleAssetVaultForDAO.sol::164 => require(amount > 0, "invalid_amount");
  • contracts/vaults/FungibleAssetVaultForDAO.sol::180 => require(amount > 0, "invalid_amount");
  • contracts/vaults/FungibleAssetVaultForDAO.sol::194 => require(amount > 0 && amount <= collateralAmount, "invalid_amount");
  • contracts/vaults/NFTVault.sol::280 => require(_newFloor > 0, "Invalid floor");
  • contracts/vaults/NFTVault.sol::367 => require(pendingValue > 0, "no_pending_value");
  • contracts/vaults/NFTVault.sol::464 => require(answer > 0, "invalid_oracle_answer");
  • contracts/vaults/NFTVault.sol::689 => require(_amount > 0, "invalid_amount");
  • contracts/vaults/NFTVault.sol::766 => require(_amount > 0, "invalid_amount");
  • contracts/vaults/NFTVault.sol::772 => require(debtAmount > 0, "position_not_borrowed");
  • contracts/vaults/NFTVault.sol::884 => require(position.liquidatedAt > 0, "not_liquidated");
  • contracts/vaults/NFTVault.sol::928 => require(position.liquidatedAt > 0, "not_liquidated");
  • contracts/vaults/yVault/strategies/StrategyPUSDConvex.sol::335 => require(wethBalance > 0, "NOOP");
  • contracts/vaults/yVault/yVault.sol::143 => require(_amount > 0, "INVALID_AMOUNT");
  • contracts/vaults/yVault/yVault.sol::167 => require(_shares > 0, "INVALID_AMOUNT");
  • contracts/vaults/yVault/yVault.sol::170 => require(supply > 0, "NO_TOKENS_DEPOSITED");
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