You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The code is annotated at multiple places with //@audit comments to pinpoint the issues. Please, pay attention to them for more details.
Findings
Version
Upgrade pragma to at least 0.8.4
Using newer compiler versions and the optimizer give gas optimizations. Also, additional safety checks are available for free.
The advantages here are:
Low level inliner (>= 0.8.2): Cheaper runtime gas (especially relevant when the contract has small functions).
Optimizer improvements in packed structs (>= 0.8.3)
Custom errors (>= 0.8.4): cheaper deployment cost and runtime cost. Note: the runtime cost is only relevant when the revert condition is met. In short, replace revert strings by custom errors.
Starting from Solidity v0.8.4, there is a convenient and gas-efficient way to explain to users why an operation failed through the use of custom errors. Until now, you could already use strings to give more information about failures (e.g., revert("Insufficient funds.");), but they are rather expensive, especially when it comes to deploy cost, and it is difficult to use dynamic information in them.
Custom errors are defined using the error statement, which can be used inside and outside of contracts (including interfaces and libraries).
Help the optimizer by saving a storage variable's reference instead of repeatedly fetching it
To help the optimizer, declare a storage type variable and use it instead of repeatedly fetching the reference in a map or an array.
The effect can be quite significant.
As an example, instead of repeatedly calling someMap[someIndex], save its reference like this: SomeStruct storage someStruct = someMap[someIndex]; and use it.
In LenderPool.sol, there is 1 practice that should change:
Instead of using creditLines[_id] (and creditLines[id]) directly, I suggest using a storage variable _creditLine after declaring it as such: LendingInfo storage _creditLine = creditLines[_id]; (and LendingInfo storage _creditLine = creditLines[id];, without the underscore). Instances include:
In PooledCreditLine.sol, there are 3 practices that should change:
Instead of using creditLineVariables[_id] directly, I suggest using a storage variable _creditLineVariable after declaring it as such: CreditLineVariables storage _creditLineVariable = creditLineVariables[_id];. Instances include:
Instead of using creditLineConstants[_id] directly, I suggest using a storage variable _creditLineConstant after declaring it as such: CreditLineConstants storage _creditLineConstant = creditLineConstants[_id];. Instances include:
Instead of using collateralShareInStrategy[_id] directly, I suggest using a storage variable _collateralShareInStrategy after declaring it as such: mapping(uint => uint256) storage _collateralShareInStrategy = collateralShareInStrategy[_id];. Instances include:
Do not use SafeMath functions on arithmetics operations that can't underflow/overflow
When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by not using the SafeMath library. This would avoid making an unnecessary check.
Instances include (see @audit tags):
contracts/PooledCreditLine/LenderPool.sol:
122if (_totalLent.add(_amount) > _maxLent) {
123: _amountToLend = _maxLent.sub(_totalLent); //@audit gas: safemath not needed here due to L122
contracts/PooledCreditLine/PooledCreditLine.sol:
581if (_lastPrincipalUpdateTime <= _endTime &&block.timestamp> _endTime) {
582: penalityInterest = (block.timestamp.sub(_endTime)).mul(_penaltyRate).div(SCALING_FACTOR); //@audit gas: safemath not needed here due to L581822uint256 _protocolFee = _borrowedAmount.mul(protocolFeeFraction).div(SCALING_FACTOR);
823: _borrowedAmount = _borrowedAmount.sub(_protocolFee); //@audit gas: safemath not needed here due to L822 (_protocolFee <= _borrowedAmount due to bounded protocolFeeFraction)874if (_amount > _interestToPay) {
875: _principalPaid = _amount.sub(_interestToPay); //@audit gas: safemath not needed here due to L8741009if (_collateralNeeded >= _totalCollateralTokens) {
1010return0;
1011 }
1012: return _totalCollateralTokens.sub(_collateralNeeded); //@audit gas: safemath not needed here due to L1009-L1010
For-Loops
An array's length should be cached to save gas in for-loops
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
Here, I suggest storing the array's length in a variable before the for-loop, and use it instead:
PooledCreditLine/LenderPool.sol:359: for (uint256 i; i < ids.length; ++i) {
The text was updated successfully, but these errors were encountered:
Gas Report
Table of Contents:
Foreword
@audit
tagsFindings
Version
Upgrade pragma to at least 0.8.4
Using newer compiler versions and the optimizer give gas optimizations. Also, additional safety checks are available for free.
The advantages here are:
Consider upgrading pragma to at least 0.8.4:
Use Custom Errors instead of Revert Strings to save Gas after upgrading to 0.8.4+
Custom errors from Solidity 0.8.4 are cheaper than revert strings (cheaper deployment cost and runtime cost when the revert condition is met)
Source: https://blog.soliditylang.org/2021/04/21/custom-errors/:
Custom errors are defined using the
error
statement, which can be used inside and outside of contracts (including interfaces and libraries).Instances include:
Storage
Tighly pack storage variables
Here,
struct LendingInfo
can be tightly packed from:to
Which would save 1 storage slot.
Help the optimizer by saving a storage variable's reference instead of repeatedly fetching it
To help the optimizer, declare a
storage
type variable and use it instead of repeatedly fetching the reference in a map or an array.The effect can be quite significant.
As an example, instead of repeatedly calling
someMap[someIndex]
, save its reference like this:SomeStruct storage someStruct = someMap[someIndex];
and use it.In
LenderPool.sol
, there is 1 practice that should change:creditLines[_id]
(andcreditLines[id]
) directly, I suggest using a storage variable_creditLine
after declaring it as such:LendingInfo storage _creditLine = creditLines[_id];
(andLendingInfo storage _creditLine = creditLines[id];
, without the underscore). Instances include:In
PooledCreditLine.sol
, there are 3 practices that should change:creditLineVariables[_id]
directly, I suggest using a storage variable_creditLineVariable
after declaring it as such:CreditLineVariables storage _creditLineVariable = creditLineVariables[_id];
. Instances include:creditLineConstants[_id]
directly, I suggest using a storage variable_creditLineConstant
after declaring it as such:CreditLineConstants storage _creditLineConstant = creditLineConstants[_id];
. Instances include:collateralShareInStrategy[_id]
directly, I suggest using a storage variable_collateralShareInStrategy
after declaring it as such:mapping(uint => uint256) storage _collateralShareInStrategy = collateralShareInStrategy[_id];
. Instances include:Arithmetics
Do not use SafeMath functions on arithmetics operations that can't underflow/overflow
When an overflow or an underflow isn't possible (as an example, when a comparison is made before the arithmetic operation), some gas can be saved by not using the SafeMath library. This would avoid making an unnecessary check.
Instances include (see
@audit
tags):For-Loops
An array's length should be cached to save gas in for-loops
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
Here, I suggest storing the array's length in a variable before the for-loop, and use it instead:
The text was updated successfully, but these errors were encountered: