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.
Instances include:
./ActivePool.sol:133: for (uint i = 0; i < poolColl.tokens.length; i++) {
./ActivePool.sol:167: for (uint i = 0; i < _tokens.length; i++) {
./ActivePool.sol:184: for (uint i = 0; i < _tokens.length; i++) {
./BorrowerOperations.sol:699: for (uint256 i = 0; i < _tokensIn.length; i++) {
./BorrowerOperations.sol:873: for (uint256 i = 0; i < _colls.length; i++) {
./BorrowerOperations.sol:890: for (uint256 i = 0; i < arr.length; i++) {
./BorrowerOperations.sol:920: for (uint256 i = 0; i < _colls1.length; i++) {
./BorrowerOperations.sol:921: for (uint256 j = 0; j < _colls2.length; j++) {
./BorrowerOperations.sol:929: for (uint256 i = 0; i < _colls.length; i++) {
./BorrowerOperations.sol:930: for (uint256 j = i + 1; j < _colls.length; j++) {
./BorrowerOperations.sol:1061: for (uint i = 0; i < _routers.length; i++) {
./BorrowerOperations.sol:1068: for (uint i = 0; i < _indices.length - 1; i++) {
./DefaultPool.sol:97: for (uint256 i = 0; i < poolColl.tokens.length; i++) {
./DefaultPool.sol:133: for (uint256 i = 0; i < _tokens.length; i++) {
./Dependencies/LiquityBase.sol:63: for (uint i = 0; i < _coll.tokens.length; i++) {
./Dependencies/LiquityBase.sol:97: for (uint i = 0; i < _tokens.length; i++) {
./Dependencies/LiquityBase.sol:106: for (uint i = 0; i < _colls.tokens.length; i++) {
./Dependencies/LiquityBase.sol:115: for (uint i = 0; i < _colls.tokens.length; i++) {
./Dependencies/LiquityBase.sol:149: for (uint i = 0; i < tokens.length; i++) {
./Dependencies/LiquityBase.sol:158: for (uint i = 0; i < coll.tokens.length; i++) {
./Dependencies/LiquityBase.sol:168: for (uint i = 0; i < _coll.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:36: for (uint256 i = 0; i < _coll1.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:44: for (uint256 i = 0; i < _coll2.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:59: for (uint256 i = 0; i < coll3.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:104: for (uint256 i = 0; i < _tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:122: for (uint256 i = 0; i < _subTokens.length; i++) {
./Dependencies/YetiCustomBase.sol:144: for (uint256 i = 0; i < _coll1.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:152: for (uint256 i = 0; i < _tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:165: for (uint256 i = 0; i < coll3.tokens.length; i++) {
./Dependencies/YetiCustomBase.sol:178: for (uint i = 0; i < _arr.length; i++) {
./HintHelpers.sol:139: for (uint256 i = 0; i < colls.tokens.length; i++) {
./MultiTroveGetter.sol:110: for (uint i = 0; i < data.allColls.length; i++) {
./StabilityPool.sol:562: for (uint256 i = 0; i < _amountsAdded.length; i++) {
./StabilityPool.sol:588: for (uint256 i = 0; i < _amountsAdded.length; i++) {
./StabilityPool.sol:592: for (uint256 i = 0; i < _amountsAdded.length; i++) {
./StabilityPool.sol:628: for (uint256 i = 0; i < _assets.length; i++) {
./StabilityPool.sol:720: for (uint256 i = 0; i < assets.length; i++) {
./StabilityPool.sol:942: for (uint256 i = 0; i < assets.length; i++) {
./StabilityPool.sol:994: for (uint256 i = 0; i < colls.length; ++i) {
./StabilityPool.sol:1011: for (uint256 i = 0; i < allColls.length; i++) {
./TroveManager.sol:234: for (uint i = 0; i < _lowerHints.length; i++) {
./TroveManager.sol:348: for (uint i = 0; i < allColls.length; i++) {
./TroveManager.sol:374: for (uint i = 0; i < allColls.length; i++ ) {
./TroveManager.sol:397: for (uint i = 0; i < allColls.length; i++ ) {
./TroveManager.sol:420: for (uint i = 0; i < assets.length; i++) {
./TroveManager.sol:460: for (uint i = 0; i < borrowerColls.length; i++) {
./TroveManager.sol:476: for (uint i = 0; i < Troves[_borrower].colls.tokens.length; i++) {
./TroveManager.sol:525: for (uint i = 0; i < _tokens.length; i++) {
./TroveManager.sol:582: for (uint i = 0; i < allColls.length; i++) {
./TroveManager.sol:603: for (uint i = 0; i < _tokens.length; i++) {
./TroveManagerLiquidations.sol:255: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
./TroveManagerLiquidations.sol:334: for (vars.i = 0; vars.i < _troveArray.length; vars.i++) {
./TroveManagerLiquidations.sol:394: for (uint256 i = 0; i < vars.collToLiquidate.tokens.length; i++) {
./TroveManagerLiquidations.sol:475: for (uint256 i = 0; i < vars.collToLiquidate.tokens.length; i++) {
./TroveManagerLiquidations.sol:701: for (uint256 i = 0; i < _collsToLiquidate.tokens.length; i++) {
./TroveManagerLiquidations.sol:721: for (uint256 i = 0; i < _collsToLiquidate.tokens.length; i++) {
./TroveManagerLiquidations.sol:808: for (uint i = 0; i < _troveTokens.length; i++) {
./TroveManagerLiquidations.sol:840: for (uint i = 0; i < _colls.tokens.length; i++) {
./TroveManagerRedemptions.sol:304: for (uint256 i = 0; i < colls.tokens.length; i++) {
./TroveManagerRedemptions.sol:367: for (uint256 i = 0; i < colls.tokens.length; i++) {
./TroveManagerRedemptions.sol:517: for (uint256 i = 0; i < coll.amounts.length; i++) {
./YETI/BoringCrypto/BoringBatchable.sol:37: for (uint256 i = 0; i < calls.length; i++) {
Tools Used
VS Code
Recommended Mitigation Steps
Store the array's length in a variable before the for-loop, and use it instead.
Handle
Dravee
Vulnerability details
Impact
Increased gas cost
Proof of Concept
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.
Instances include:
Tools Used
VS Code
Recommended Mitigation Steps
Store the array's length in a variable before the for-loop, and use it instead.