Omniscia Steadefi Audit
LendingPool Code Style Findings
LendingPool Code Style Findings
LPL-01C: Increase of Fee Function Usability
Type | Severity | Location |
---|---|---|
Code Style | LendingPool.sol:L399, L402 |
Description:
The LendingPool::withdrawReserve
function is meant to allow the owner of the contract to claim any fees that have been stored during the contract's lifetime. Within it, an LendingPool::_updatePoolWithInterestsAndTimestamp
function invocation is performed that will increase the poolReserves
, causing poolReserves
within the contract to be permanently unclaimable unless two withdrawReserve
executions are performed in sequence in the same transaction.
Example:
394/**395 * Withdraw protocol fees from reserves396 * @param _amount Amount to withdraw in token decimals397*/398function withdrawReserve(uint256 _amount) external nonReentrant onlyOwner {399 require(_amount <= poolReserves, "Cannot withdraw more than pool reserves");400
401 // Update pool with accrued interest and latest timestamp402 _updatePoolWithInterestsAndTimestamp(0);403
404 poolReserves = poolReserves - _amount;405
406 IERC20(asset).safeTransfer(treasury, _amount);407}
Recommendation:
We advise the code to instead omit the require
check between _amount
and poolReserves
entirely. As an alternative, an if
clause can be introduced after the LendingPool::_updatePoolWithInterestsAndTimestamp
call that checks whether _amount
exceeds the poolReserves
and in such a case assigns poolReserves
to _amount
, allowing the owner to specify type(uint256).max
as an input to the LendingPool::withdrawReserve
function and withdraw all fees available, including the ones that have yet to be accounted for.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The LendingPool::withdrawReserve
function was refactored per our recommendation, ensuring that the full amount of fees accrued by the system is claimable by its owner in an intuitive way.
LPL-02C: Ineffectual Usage of Safe Arithmetics
Type | Severity | Location |
---|---|---|
Language Specific | LendingPool.sol:L375, L404 |
Description:
The linked mathematical operations are guaranteed to be performed safely by surrounding conditionals evaluated in either require
checks or if-else
constructs.
Example:
370function _to18ConversionFactor(address _token) internal view returns (uint256) {371 require(ERC20(_token).decimals() <= 18, 'decimals are more than 18');372
373 if (ERC20(_token).decimals() == 18) return 1;374
375 return 10**(18 - ERC20(_token).decimals());376}
Recommendation:
Given that safe arithmetics are toggled on by default in pragma
versions of 0.8.X
, we advise the linked statements to be wrapped in unchecked
code blocks thereby optimizing their execution cost.
Alleviation (4325253d6d):
The latter of the two referenced statements has been wrapped in an unchecked
code block, rendering this exhibit partially alleviated.
Alleviation (7c9b2b09db):
The final remaining code block has been safely wrapped in an unchecked
arithmetic block, alleviating this exhibit fully.
LPL-03C: Inefficient Calculation Offsets
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L282, L285, L447, L450 |
Description:
The referenced operations perform both a multiplication and a division with SAFE_MULTIPLIER
in the same arithmetic context.
Example:
282uint256 debt = _repayAmount * SAFE_MULTIPLIER283 / borrowerTotalRepayAmount284 * borrowers[msg.sender].debt285 / SAFE_MULTIPLIER;
Recommendation:
We advise both the multiplication and division to be omitted, instead performing the actual variable-based multiplication prior to the variable-based division in the referenced calculations as the accuracies of those variables cancel each other out.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The calculation was simplified per our recommendation, performing a multiplication prior to the division of same-accuracy numbers that eliminate each other's offset.
LPL-04C: Inefficient Calculation of First Interest
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L352-L363 |
Description:
The first invocation of LendingPool::_pendingInterest
will improperly calculate multiple values until it arrives to a zero value due to the inexistent presence of any totalBorrows
.
Example:
335/**336 * Interest accrual function that calculates accumulated interest from lastUpdatedTimestamp and add to totalBorrows337 * @param _value Additonal amount of assets being deposited in token decimals338*/339function _updatePoolWithInterestsAndTimestamp(uint256 _value) internal {340 uint256 interest = _pendingInterest(_value);341 uint256 toReserve = interest * protocolFee / SAFE_MULTIPLIER;342 poolReserves = poolReserves + toReserve;343 totalBorrows = totalBorrows + interest;344 lastUpdatedAt = block.timestamp;345}346
347/**348 * Returns the pending interest that will be accrued to the reserves in the next call349 * @param _value Newly deposited assets to be subtracted off total available liquidity in token decimals350 * @return interest Amount of interest owned in token decimals351*/352function _pendingInterest(uint256 _value) internal view returns (uint256) {353 uint256 timePassed = block.timestamp - lastUpdatedAt;354 uint256 floating = totalAvailableSupply() == 0 ? 0 : totalAvailableSupply() - _value;355 uint256 ratePerSec = lendingPoolConfig.interestRatePerSecond(totalBorrows, floating);356
357 // First division is due to ratePerSec being in 1e18358 // Second division is due to ratePerSec being in 1e18359 return ratePerSec * totalBorrows360 * timePassed361 / SAFE_MULTIPLIER362 / SAFE_MULTIPLIER;363}
Recommendation:
We advise the function to yield 0
immediately if totalBorrows
is evaluated as 0
, ensuring that the first time LendingPool::_updatePoolWithInterestsAndTimestamp
is invoked the call will incur minimal gas to initialize the lastUpdatedAt
variable.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The statement advised in the recommendation chapter of the exhibit has been introduced to the LendingPool::_pendingInterest
function, optimizing its execution cost significantly during its first query.
LPL-05C: Inefficient Enforcement of EIP-20 Asset Trait
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L371 |
Description:
The LendingPool::_to18ConversionFactor
function mandates that the decimals
of the _token
address are at most 18
whilst the _token
evaluated (asset
) can never change in the lifetime of the LendingPool
contract.
Example:
365/**366 * Conversion factor for tokens with less than 1e18 to return in 1e18367 * @param _token Asset address368 * @return conversionFactor Amount of decimals for conversion to 1e18369*/370function _to18ConversionFactor(address _token) internal view returns (uint256) {371 require(ERC20(_token).decimals() <= 18, 'decimals are more than 18');372
373 if (ERC20(_token).decimals() == 18) return 1;374
375 return 10**(18 - ERC20(_token).decimals());376}
Recommendation:
We advise the decimals
value to be mandated as at most equal to 18
in the constructor
of the contract instead, optimizing all LendingPool::_to18ConversionFactor
function executions and preventing the creation of inoperable LendingPool
implementations.
Alleviation (4325253d6d):
The exhibit appears to have been remediated in PR#156, however, the latest state of the codebase does not reflect this change. As the decimal check is removed entirely, this has been converted to a vulnerability that needs to be remediated.
Alleviation (7c9b2b09db):
The original recommendation of the exhibit has been applied to the codebase, relocating the decimals
validation check to the contract's constructor
thus addressing this exhibit.
LPL-06C: Inefficient Event Emission
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L388-L389, L391 |
Description:
The referenced event emission is suboptimal as it loads the protocolFee
into memory prior to emitting it in the ProtocolFeeUpdated
event.
Example:
380/**381 * Update protocol fee382 * @param _protocolFee Fee percentage in 1e18383*/384function updateProtocolFee(uint256 _protocolFee) external onlyOwner {385 // Update pool with accrued interest and latest timestamp386 _updatePoolWithInterestsAndTimestamp(0);387
388 uint256 previousProtocolFee = protocolFee;389 protocolFee = _protocolFee;390
391 emit ProtocolFeeUpdated(msg.sender, previousProtocolFee, protocolFee);392}
Recommendation:
We advise the LendingPool::updateProtocolFee
function to be updated to instead emit
the ProtocolFeeUpdated
event immediately after _updatePoolWithInterestsAndTimestamp
with the arguments being msg.sender
, protocolFee
, and _protocolFee
; rendering the usage of a local variable unnecessary.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The event's emission has been optimized per our recommendation, no longer requiring an interim local variable and thus optimizing its gas cost.
LPL-07C: Inefficient Recalculation of Formulas
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L116, L124, L127, L144, L147, L270, L272-L273, L279, L354, L371, L373, L375, L435, L437-L438, L444 |
Description:
The referenced function invocations are performed repetitively in the same function context, incurring a significant gas overhead redundantly.
Example:
111/**112 * Returns the the borrow utilization rate of the pool113 * @return utilizationRate Ratio of borrows to total liquidity in 1e18114*/115function utilizationRate() public view returns (uint256){116 return (totalValue() == 0) ? 0 : totalBorrows * SAFE_MULTIPLIER / totalValue();117}
Recommendation:
We advise them to be invoked only once, storing their result to a local variable that is consequently utilized wherever needed.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
All referenced repetitive invocations have been optimized to the greatest extent possible, utilizing either local or immutable
variables that represent the repetitive data point required calculated only once.
LPL-08C: Inefficient mapping
Lookups
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L258-L259, L292-L293 |
Description:
The linked statements perform key-based lookup operations on mapping
declarations from storage multiple times for the same key redundantly.
Example:
257// Update borrower state258borrowers[msg.sender].debt = borrowers[msg.sender].debt + debt;259borrowers[msg.sender].lastUpdatedAt = block.timestamp;
Recommendation:
As the lookups internally perform an expensive keccak256
operation, we advise the lookups to be cached wherever possible to a single local declaration that either holds the value of the mapping
in case of primitive types or holds a storage
pointer to the struct
contained.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The referenced mapping
lookups have been optimized as advised, greatly reducing the gas cost of the referenced statements.
LPL-09C: Repetitive Value Literal
Type | Severity | Location |
---|---|---|
Code Style | LendingPool.sol:L371, L373, L375 |
Description:
The linked value literal is repeated across the codebase multiple times.
Example:
371require(ERC20(_token).decimals() <= 18, 'decimals are more than 18');
Recommendation:
We advise it to be set to a constant
variable instead optimizing the legibility of the codebase.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
The referenced literal is no longer utilized multiple times within the codebase rendering this exhibit inapplicable.
LPL-10C: Variable Mutability Specifiers (Immutable)
Type | Severity | Location |
---|---|---|
Gas Optimization | LendingPool.sol:L85-L86, L88-L89 |
Description:
The linked variables are assigned to only once during the contract's constructor
.
Example:
68/**69 * @param _lendingPoolConfig Contract for Lending Pool Configuration70 * @param _name Name for ibToken for this lending pool, e.g. Interest Bearing AVAX71 * @param _symbol Symbol for ibToken for this lending pool, e.g. ibAVAX72 * @param _asset Contract address for underlying ERC20 asset73 * @param _isNativeAsset Boolean for whether this lending pool accepts the native asset (e.g. AVAX)74 * @param _protocolFee Protocol fee in 1e1875*/76constructor(77 string memory _name,78 string memory _symbol,79 address _asset,80 bool _isNativeAsset,81 uint256 _protocolFee,82 ILendingPoolConfig _lendingPoolConfig,83 address _treasury84 ) ERC20(_name, _symbol) {85 asset = _asset;86 isNativeAsset = _isNativeAsset;87 protocolFee = _protocolFee;88 lendingPoolConfig = _lendingPoolConfig;89 treasury = _treasury;90}
Recommendation:
We advise them to be set as immutable
greatly optimizing their read-access gas cost.
Alleviation (4325253d6de0ea91c1e9fb9e01d2e7e98f3d83a9):
All variables have been set as immutable
per our recommendation, greatly reducing their read-access gas cost.