Platform: Code4rena
Start Date: 23/06/2023
Pot Size: $60,500 USDC
Total HM: 31
Participants: 132
Period: 10 days
Judge: 0xean
Total Solo HM: 10
Id: 254
League: ETH
Rank: 69/132
Findings: 1
Award: $80.43
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: JCN
Also found by: 0xAnah, DavidGiladi, MohammedRizwan, Rageur, Raihan, ReyAdmirado, Rolezn, SAAJ, SAQ, SM3_SS, Sathish9098, ayo_dev, dharma09, fatherOfBlocks, hunter_w3b, mgf15, mrudenko, naman1778, shamsulhaq123, souilos, turvy_fuzz
80.434 USDC - $80.43
If variables occupying the same slot are both written the same function or by the constructor, avoids a separate Gsset (20000 gas). Reads of the variables are also cheaper.
stableToken
and premiumTradingEnabled
can be put together into one slot reducing slots used by 1 and also they are accessed in one function.
isEUSDBuyoutAllowed
and esLBR
can be put together into one slot reducing slots used by 1 and also they are accessed in one function.
The instances below point to the second+ access of a state variable within a function. Caching of a state variable replace each Gwarmaccess (100 gas) with a much cheaper stack read. Other less obvious fixes/optimizations include having local memory caches of state variable structs, or having local caches of state variable contracts/addresses.
in the modifier here rewardPerToken()
is being called to get rewardPerTokenStored
but first we can cache what the function returns and use that cached version to assign rewardPerTokenStored
#L57 and userRewardPerTokenPaid[_account]
#L62. saves 100 gas per call of modifier --> 400 gas save as it is called 4 times
in the modifier here rewardPerToken()
is being called to get rewardPerTokenStored
but first we can cache what the function returns and use that cached version to assign rewardPerTokenStored
#L73 and userRewardPerTokenPaid[_account]
#L78. saves 100 gas per call of modifier --> 400 gas save as it is called 4 times
duration
is being read twice(once in one of if or else then in #L239) cache it before the if statement to save 100 gas
depositedAsset[onBehalfOf]
is being read twice, once in #L156 and #L159 so it should be cached before to reduce one complex SLOAD
depositedAsset[onBehalfOf]
is being read twice, once in #L190 and #L192 so it should be cached before to reduce one complex SLOAD
borrowed[provider]
will be read twice if the require in #L234 doesnt revert so we can cache before the require to reduce one complex SLOAD
depositedAsset[onBehalfOf]
is being read twice, once in #L127 and #L130 so it should be cached before to reduce one complex SLOAD
borrowed[provider]
will be read twice if the require in #L159 doesnt revert so we can cache before the require to reduce one complex SLOAD
if block.timestamp >= finishAt
isnt true finishAt
will be read twice and we can save 97 gas if we cache it before the if statement with just risking 3 gas loss if the condition turns out true
if time2fullRedemption[msg.sender] > block.timestamp
condition be true time2fullRedemption[msg.sender]
is gonna be read twice so we can cache if before the if statement and use that instead to reduce one complex SLOAD. saves lots of gas by risking losing 3 if the condition is false.
if time2fullRedemption[user] > lastWithdrawTime[user]
condition be true lastWithdrawTime[user]
will be read twice(one in condition and one in the inside of if statement) so we can cache it before the if statement to reduce one complex SLOAD by just risking losing 3 gas if the condition is false.
if (time2fullRedemption[user] > block.timestamp
condition be true (time2fullRedemption[user]
will be read twice from storage so we can cache it before the if statement to reduce one complex SLOAD with just risking losing 3 gas if the condition be false
if _totalShares == 0
is false _totalShares
will be read twice and we can save 97 gas if we cache it before the if statement with just risking 3 gas loss if the condition turns out true
if vaultSafeCollateralRatio[pool] == 0
is false vaultSafeCollateralRatio[pool]
will be read twice and we can reduce one complex SLOAD if we cache it before the if statement with just risking 3 gas loss if the condition turns out true
if vaultBadCollateralRatio[pool] == 0
is false vaultBadCollateralRatio[pool]
will be read twice and we can reduce one complex SLOAD if we cache it before the if statement with just risking 3 gas loss if the condition turns out true
make the variable outside the loop and only give the value to variable inside
pool
borrowed
There is a chance that the first part will be true so the second part doesn’t need to be checked, so it is better to use the part that we have first instead of the part that needs to be called.
swap the 2 sides for the chance of price <= 1005000
being true and not using a SLOAD on premiumTradingEnabled
calldata
instead of memory
for read-only arguments in external functions saves gasWhen a function with a memory array is called externally, the abi.decode() step has to use a for-loop to copy each index of the calldata to the memory index. Each iteration of this for-loop costs at least 60 gas (i.e. 60 * <mem_array>.length). Using calldata directly, obliviates the need for such a loop in the contract code and runtime execution.
Using ternary operator instead of the if else statement saves gas.
saves 6 gas per instance
maxSupply
is not being updated in this contract and it always has a constant value so it should be declared as a constant saving Gsset (20000 gas) and 100 gas per call
these parts of the code can be pre calculated and given to the contract as constants this will stop the use of extra operations even if its for code readability consider putting comments instead.
instead of 100 * 1e18
put the answer to it in the code to reduce one operation use
(86400 * 365) / 10000
can be pre calculated to reduce two operation uses
instead of doing some operations again answer can be cached at the first time and be used every where.
balance - preBalance
is calculated in #L25 so if we cache it then we can use the cached version in #L31
same here
reward - eUSDShare
The current form of relative path import is not recommended for use because it can unpredictably pollute the namespace. Instead, the Solidity docs recommend specifying imported symbols explicitly.
A good example:
import {OwnableUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {SafeCastLib} from "solmate/utils/SafeCastLib.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {IProducer} from "src/interfaces/IProducer.sol"; import {GlobalState, UserState} from "src/Common.sol";
#0 - c4-pre-sort
2023-07-27T21:29:17Z
JeffCX marked the issue as high quality report
#1 - c4-judge
2023-07-27T23:42:11Z
0xean marked the issue as grade-a
#2 - c4-sponsor
2023-07-29T09:01:00Z
LybraFinance marked the issue as sponsor acknowledged