Skip to content

Instantly share code, notes, and snippets.

@ChaseTheLight01
Created February 28, 2024 23:16
Show Gist options
  • Save ChaseTheLight01/5a35b66752fc4e5d4dec1cdc2895ddd8 to your computer and use it in GitHub Desktop.
Save ChaseTheLight01/5a35b66752fc4e5d4dec1cdc2895ddd8 to your computer and use it in GitHub Desktop.
LightChaserV3_Cantina_Curvance

LightChaser-V3

Twitter: @ChaseTheLight99

Generated for: Cantina : Curvance

Generated on: 2024-02-28

Total findings: 298

Total HIGH findings: 0

Total Medium findings: 4

Total Low findings: 42

Total Gas findings: 86

Total Refactoring findings: 0

Total NonCritical findings: 166

Total Disputed findings: 0

Summary for Medium findings

Number Details Instances
[Medium-1] Privileged functions can create points of failure 5
[Medium-2] Insufficient oracle validation 1
[Medium-3] Using block.timestamp as the deadline/expiry invites MEV 1
[Medium-4] Price should round up 24

Summary for Low findings

Number Details Instances
[Low-1] Potential division by zero should have zero checks in place 7
[Low-2] Division operations should always be performed after multiplication operations 3
[Low-3] Function with two array parameter missing a length check 4
[Low-4] Missing checks for address(0x0) when updating address state variables 1
[Low-5] Missing empty bytes check when assigning bytes/string state variable 1
[Low-6] Unchecked blocks with subtractions may underflow 2
[Low-7] Incorrect comparison against a max value 2
[Low-8] Getting a bool return value does not confirm the existence of a function in an external call 2
[Low-9] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug 13
[Low-10] Mint function with no access control 3
[Low-11] Important function with no access control 2
[Low-12] Chainlink price feeds are not validated 1
[Low-13] Return values not checked for approve() 2
[Low-14] Empty receive functions can cause gas issues 4
[Low-15] The call abi.encodeWithSignature is not safe from typographical errors 1
[Low-16] The call abi.encodeWithSelector is not type safe 2
[Low-17] Token supply should not be centralised at deployment 2
[Low-18] Use SafeCast to safely downcast variables 32
[Low-19] The nonReentrant modifier should be first in a function declaration 4
[Low-20] Function calls within for loops 19
[Low-21] For loops in public or external functions should be avoided due to high gas costs and possible DOS 45
[Low-22] The function decimals() is not part of the ERC20 standard 23
[Low-23] Minting to the zero address should be avoided 6
[Low-24] No limits when setting fees 1
[Low-25] Loss of precision 40
[Low-26] Missing zero address check in constructor 22
[Low-27] Using zero as a parameter 16
[Low-28] Constant decimal values 32
[Low-29] Calculation will revert when totalSupply() returns zero 2
[Low-30] Bridge function must check for native token 1
[Low-31] A year is not always 365/364 days 1
[Low-32] Chainlink answer is not compared against min/max values 1
[Low-33] Unsafe uint to int conversion 2
[Low-34] Calling internal _grantRole bypasses role access checks 1
[Low-35] Direct supportsInterface() calls may cause caller to revert 4
[Low-36] SafeTransferLib does not ensure that the token contract exists 6
[Low-37] External calls in modifiers should be avoided 1
[Low-38] Using a immutable/constant state variable value as a gas limit can be dangerous 1
[Low-39] Prefer skip over revert model in iteration 1
[Low-40] Constructors missing validation 35
[Low-41] Division in comparison 9
[Low-42] Contract contains payable functions but no withdraw/sweep function 14

Summary for NonCritical findings

Number Details Instances
[NonCritical-1] Some if-statement can be converted to a ternary 71
[NonCritical-2] Addresses shouldn't be hard-coded 8
[NonCritical-3] Code does not follow the best practice of check-effects-interaction 49
[NonCritical-4] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction 18
[NonCritical-5] It is standard for all external and public functions to be override from an interface 397
[NonCritical-6] Using abi.encodePacked can result in hash collision when used in hashing functions 1
[NonCritical-7] Overly complicated arithmetic 14
[NonCritical-8] It's not standard to end and begin a code object on the same line 2
[NonCritical-9] Using constants directly, rather than caching the value, saves gas 1
[NonCritical-10] State variables which are not modified within functions should be set as constants or immutable for values set at deployment 18
[NonCritical-11] External call recipient may consume all transaction gas 1
[NonCritical-12] ERC4626 maxWithdraw/maxRedeem function not to spec 2
[NonCritical-13] Function call in event emit 133
[NonCritical-14] int/uint passed into abi.encodePacked without casting to a string. 2
[NonCritical-15] NatSpec: @notice tags missing from contract/abstract/library/interface/function/modifier/constructor/receive/fallback 273
[NonCritical-16] Natspec @author is missing from contract/interface/library 99
[NonCritical-17] Natspec @title is missing from contract/interface/library 90
[NonCritical-18] NatSpec: @dev tags missing from contract/abstract/library/interface/function/modifier/constructor/receive/fallback 527
[NonCritical-19] NatSpec: @param tags missing from function/modifier/constructor 139
[NonCritical-20] NatSpec: @return tags missing from function/modifier/constructor 121
[NonCritical-21] Inconsistent comment spacing 9
[NonCritical-22] Incorrect NatSpec Syntax 13
[NonCritical-23] Use @inheritdoc rather than using a non-standard annotation 1
[NonCritical-24] Solidity version 0.8.20 won't work on all chains due to PUSH0 2
[NonCritical-25] Floating pragma should be avoided 2
[NonCritical-26] Empty function blocks 13
[NonCritical-27] Events regarding state variable changes should emit the previous state variable value 18
[NonCritical-28] In functions which accept an address as a parameter, there should be a zero address check to prevent bugs 386
[NonCritical-29] Enum values should be used in place of constant array indexes 48
[NonCritical-30] Revert statements within external and public functions can be used to perform DOS attacks 130
[NonCritical-31] Functions which are either public or external should not have a preceding _ in their name 1
[NonCritical-32] Functions which are either private or internal should have a preceding _ in their name 21
[NonCritical-33] Private and internal state variables should have a preceding _ in their name unless they are constants 10
[NonCritical-34] Public state variables shouldn't have a preceding _ in their name 1
[NonCritical-35] Contract lines should not be longer than 120 characters for readability 1
[NonCritical-36] Specific imports should be used where possible so only used code is imported 1
[NonCritical-37] Use newer solidity versions 3
[NonCritical-38] Not all event definitions are utilizing indexed variables. 82
[NonCritical-39] Dependence on external protocols 3
[NonCritical-40] Call calls must have their return checked 1
[NonCritical-41] Function names should differ to make the code more readable 478
[NonCritical-42] Functions should not be longer than 50 lines 28
[NonCritical-43] Functions within contracts are not ordered according to the solidity style guide 13
[NonCritical-44] Double type casts create complexity within the code 9
[NonCritical-45] Emits without msg.sender parameter 11
[NonCritical-46] Functions with array parameters should have length checks in place 13
[NonCritical-47] Interface imports should be declared first 72
[NonCritical-48] All interfaces used within a project should be imported 5
[NonCritical-49] A function which defines named returns in it's declaration doesn't need to use return 9
[NonCritical-50] SPDX identifier should be the in the first line of a solidity file 121
[NonCritical-51] Multiple mappings can be replaced with a single struct mapping 13
[NonCritical-52] Having chainId as a parameter can introduce cross chain replay attacks 7
[NonCritical-53] Unused state variables present 1
[NonCritical-54] Constants should be on the left side of the 254
[NonCritical-55] Interface names should have an I as the first character 1
[NonCritical-56] Defined named returns not used within function 18
[NonCritical-57] Both immutable and constant state variables should be CONSTANT_CASE 33
[NonCritical-58] Consider using named mappings 50
[NonCritical-59] Use a single contract or library for system wide constants 37
[NonCritical-60] Consider using modifiers for address control 4
[NonCritical-61] Off-by-one timestamp error 7
[NonCritical-62] Address from parameter can cause issues 1
[NonCritical-63] Variables should be used in place of magic numbers to improve readability 158
[NonCritical-64] Long powers of ten should use scientific notation 1eX 7
[NonCritical-65] Redundant else statement 9
[NonCritical-66] increase/decrease allowance should be used instead of approve/safeApprove 16
[NonCritical-67] Unused errors present 37
[NonCritical-68] Consider adding emergency-stop functionality 66
[NonCritical-69] Employ Explicit Casting to Bytes or Bytes32 for Enhanced Code Clarity and Meaning 8
[NonCritical-70] Custom error has no error variables 259
[NonCritical-71] Large or complicated code bases should implement invariant tests 28
[NonCritical-72] Overridden function has no body 1
[NonCritical-73] Unused structs present 1
[NonCritical-74] Empty bytes check is missing 39
[NonCritical-75] Use max instead of 0xfff... 1
[NonCritical-76] Chainlink price feed decimals not checked 2
[NonCritical-77] Use scopes sparingly 16
[NonCritical-78] No equate comparison checks between to and from address parameters 3
[NonCritical-79] Return bool not explicit 14
[NonCritical-80] Remove unnecessary solhint-disable 1
[NonCritical-81] Do not use underscore at the end of variable name 17
[NonCritical-82] Consider using SMTChecker 121
[NonCritical-83] The function symbol() is not part of the ERC20 standard 3
[NonCritical-84] Contracts should have full test coverage 67
[NonCritical-85] Chainlink oracle roundId and answeredIn variables no longer contain useful information
1
[NonCritical-86] Use OpenZeppelin's ReentrancyGuard/ReentrancyGuardUpgradeable rather than writing your own 1
[NonCritical-87] Consider using SafeTransferLib.safeTransferETH() or Address.sendValue() for clearer semantic meaning 1
[NonCritical-88] Assembly block creates dirty bits 8
[NonCritical-89] Whitespace in expressions 16
[NonCritical-90] Consider using named function calls 339
[NonCritical-91] Using XOR (^) and AND (&) bitwise equivalents 2
[NonCritical-92] Public state variables should include natspec comments 5
[NonCritical-93] Lack Of Brace Spacing 2
[NonCritical-94] .call bypasses function existence check, type checking and argument packing 2
[NonCritical-95] Using while for unbounded loops isn’t recommended 3
[NonCritical-96] Revert should be used on some functions instead of return 3
[NonCritical-97] Common functions should be refactored to a common base contract 94
[NonCritical-98] Use of override is unnecessary 10
[NonCritical-99] No access control on receive/payable fallback 4
[NonCritical-100] If statement control structures do not comply with best practices 9
[NonCritical-101] Cyclomatic complexity in functions 32
[NonCritical-102] Incorrect withdraw declaration 1
[NonCritical-103] Consider adding formal verification proofs 67
[NonCritical-104] Unused events present 1
[NonCritical-105] Unused import 1
[NonCritical-106] function names should be lowerCamelCase 2
[NonCritical-107] safeApprove()/approve() may revert if the current approval is not zero 14
[NonCritical-108] Missing events in sensitive functions 50
[NonCritical-109] Consider disallowing transfers to "address(this)" 2
[NonCritical-110] Add inline comments for unnamed variables in function declarations 16
[NonCritical-111] The function name() is not part of the ERC20 standard 2
[NonCritical-112] Public state arrays should have a getter to return all elements 5
[NonCritical-113] Ensure block.timestamp is only used in long time intervals 5
[NonCritical-114] Don't assume specific ETH balance 3
[NonCritical-115] Avoid mutating function parameters 24
[NonCritical-116] Don't only depend on Solidity's arithmetic ordering. 2
[NonCritical-117] View function is not defined as such in interface 2
[NonCritical-118] Pure function is not defined as such in interface 5
[NonCritical-119] It is best practice to use linear inheritance 21
[NonCritical-120] A event should be emitted if a non immutable state variable is set in a constructor 23
[NonCritical-121] Funds can be trapped due to unreverting local payable call 21
[NonCritical-122] Payable functions which do not interact with the native token should not be payable 2
[NonCritical-123] Uint casted addresses used in conditional checks can be bypassed 4
[NonCritical-124] gasLimit should be uint64 5
[NonCritical-125] Inconstant use of 0x0 and 0x00 for 0 bytes 2
[NonCritical-126] Mint functions should be accompanied by a burn function and vice versa 1
[NonCritical-127] Tautology when checking address value 2
[NonCritical-128] Avoid hard coding gasLimit values 2
[NonCritical-129] Immutable and constant integer state variables should not be casted 2
[NonCritical-130] Numbers downcast to addresses may result in collisions 5
[NonCritical-131] Public variable declarations should have NatSpec descriptions 3
[NonCritical-132] Use -= for mappings 7
[NonCritical-133] Use the Modern Upgradeable Contract Paradigm 67
[NonCritical-134] Upgrade openzeppelin to the Latest Version - 5.0.0 1
[NonCritical-135] Use a struct to encapsulate multiple function parameters 65
[NonCritical-136] Returning a struct instead of returning many variables is better 1
[NonCritical-137] Using delete instead of setting mapping to 0 saves gas 1
[NonCritical-138] Empty function body without natspec comments 1
[NonCritical-139] Long numbers should include underscores to improve readability and prevent typos 21
[NonCritical-140] Consider using ERC20Capped 7
[NonCritical-141] Consider using a format prettier or forge fmt 10
[NonCritical-142] Avoid defining a function in a single line including it's contents 192
[NonCritical-143] Use 'using' keyword when using specific imports rather than calling the specific import directly 290
[NonCritical-144] Try catch statement without human readable error 4
[NonCritical-145] Avoid revertible function calls in a constructor 11
[NonCritical-146] Use the same Solidity version in non library/interface files throughout the project 3
[NonCritical-147] Inconsistent checks of address params against address(0) 4
[NonCritical-148] Avoid declaring variables with the names of defined functions within the project 1743
[NonCritical-149] Reserved keyword 'error' used as a variable/object name 16
[NonCritical-150] Avoid caching global vars used once within the function 1
[NonCritical-151] Upgradeable contract uses non-upgradeable version of the OpenZeppelin libraries/contracts 1
[NonCritical-152] All verbatim blocks are considered identical by deduplicator and can incorrectly be unified 2
[NonCritical-153] Constructors should emit an event 74
[NonCritical-154] Avoid single line non empty object declarations 151
[NonCritical-155] Contract and Abstract files should have a fixed compiler version 83
[NonCritical-156] Variables should be mixedCase 1
[NonCritical-157] Consider using 'using-for' syntax when using libraries 156
[NonCritical-158] Consider validating all user inputs 257
[NonCritical-159] Consider providing a ranged getter for array state variables 8
[NonCritical-160] Consider using named returns 293
[NonCritical-161] Avoid external calls in modifiers 3
[NonCritical-162] Errors should have parameters 276
[NonCritical-163] Avoid using 'owner' or '_owner' as a parameter name 22
[NonCritical-164] Catch return of decimals as uint8 20
[NonCritical-165] While true/false loops can result in infinite loops 2
[NonCritical-166] ERC777 tokens can introduce reentrancy risks 14

Summary for Gas findings

Number Details Instances Gas
[Gas-1] Multiple accesses of the same mapping/array key/index should be cached 24 24192
[Gas-2] The result of a function call should be cached rather than re-calling the function 7 10500
[Gas-3] State variables used within a function more than once should be cached to save gas 5 11000
[Gas-4] Low level call can be optimized with assembly 5 6200
[Gas-5] Consider Using Solady's Gas Optimized Lib for Math 100 0.0
[Gas-6] It is a waste of GAS to emit variable literals 38 11552
[Gas-7] x + y is more efficient than using += for state variables (likewise for -=) 4 80
[Gas-8] Public functions not used internally can be marked as external to save gas 10 0.0
[Gas-9] Calldata should be used in place of memory function parameters when not mutated 4 208
[Gas-10] Usage of smaller uint/int types causes overhead 29 46255
[Gas-11] Mappings are cheaper than arrays for state variable iteration 8 152000
[Gas-12] Use != 0 instead of > 0 59 10443
[Gas-13] Integer increments by one can be unchecked to save on gas fees 43 221880
[Gas-14] Use byte32 in place of string 7 0.0
[Gas-15] Default bool values are manually reset 3 0.0
[Gas-16] Default int values are manually reset 13 0.0
[Gas-17] Mappings used within a function more than once should be cached to save gas 11 12100
[Gas-18] Use assembly to check for the zero address 31 0.0
[Gas-19] Divisions which do not divide by -X cannot overflow or overflow so such operations can be unchecked to save gas 55 0.0
[Gas-20] Can transfer 0 1 0.0
[Gas-21] Redundant state variable getters 4 0.0
[Gas-22] Divisions of powers of 2 can be replaced by a right shift operation to save gas 1 0.0
[Gas-23] multiplications of powers of 2 can be replaced by a left shift operation to save gas 4 0.0
[Gas-24] Struct variables can be packed into fewer storage slots 22 1210000
[Gas-25] Consider activating via-ir for deploying 121 3660250
[Gas-26] Superfluous event fields 2 136
[Gas-27] Use bitmap to save gas 47 154630
[Gas-28] Use assembly hashing 2 0.0
[Gas-29] Consider using OZ EnumerateSet in place of nested mappings 10 100000
[Gas-30] Use selfBalance() in place of address(this).balance 5 20000
[Gas-31] Use assembly to emit events 127 612902
[Gas-32] Shorten the array rather than copying to a new one 24 0.0
[Gas-33] Use unchecked for operations on immutable variables 1 0.0
[Gas-34] Use assembly in place of abi.decode to extract calldata values more efficiently 18 0.0
[Gas-35] Counting down in for statements is more gas efficient 25 0.0
[Gas-36] State variables can be packed into fewer storage slots by truncating timestamp bytes 1 2500
[Gas-37] Using private rather than public for constants and immutables, saves gas 39 0.0
[Gas-38] Mark Functions That Revert For Normal Users As payable 5 625
[Gas-39] Function names can be optimized 67 574592
[Gas-40] Lack of unchecked in loops 18 65880
[Gas-41] Consider migrating require statements to custom errors 3 126
[Gas-42] Consider not using libraries when implementing simple functionality. 7 49000
[Gas-43] Avoid indexing dynamic types 5 0.0
[Gas-44] Using bools for storage incurs overhead 29 58870
[Gas-45] Where a value is casted more than once, consider caching the result to save gas 4 0.0
[Gas-46] The following mappings can be replaced with a bit mask 1 0.0
[Gas-47] State variable can be updated more than once in a function 1 800
[Gas-48] Assembly let var only used on once 2 0.0
[Gas-49] Unnecessary casting as variable is already of the same type 4 352
[Gas-50] Use += for mappings 8 0.0
[Gas-51] Simple checks for zero uint can be done using assembly to save gas 18 1944
[Gas-52] Use Unchecked for Divisions on Constant or Immutable Values 9 0.0
[Gas-53] Using nested if to save gas 43 11094
[Gas-54] Optimize Deployment Size by Fine-tuning IPFS Hash 67 47583400
[Gas-55] Avoid Unnecessary Public Variables 162 577368000
[Gas-56] Optimize Storage with Byte Truncation for Time Related State Variables 11 242000
[Gas-57] Stack variable cost less than state variables while used in emiting event 2 36
[Gas-58] Caching global variables is more expensive than using the actual variable 1 12
[Gas-59] Avoid emitting event on every iteration 1 2250
[Gas-60] Using constants directly, rather than caching the value, saves gas 1 0.0
[Gas-61] Use s.x = s.x + y instead of s.x += y for memory structs (same for -= etc) 17 28900
[Gas-62] Time state variables can be truncated to uint32 3 180000
[Gas-63] Constants are cheaper than Enums 2 80000
[Gas-64] ++X costs slightly less gas than X++ (same with --) 28 3920
[Gas-65] Solidity versions 0.8.19 and above are more gas efficient 3 9000
[Gas-66] Variables that can be set to immutable 5 0.0
[Gas-67] Variable declared within iteration 6 0.0
[Gas-68] Internal functions only used once can be inlined so save gas 34 34680
[Gas-69] Constructors can be marked as payable to save deployment gas 76 0.0
[Gas-70] Use assembly scratch space to build calldata for external calls 376 31102720
[Gas-71] Use assembly scratch space to build calldata for event emits 89 1742620
[Gas-72] Consider using solady's "FixedPointMathLib" 152 0.0
[Gas-73] Same cast is done multiple times 15 0.0
[Gas-74] Assigning to structs can be more efficient 7 6370
[Gas-75] Cache address(this) when used more than once 51 0.0
[Gas-76] Internal functions never used once can be removed 2 0.0
[Gas-77] Only emit event in setter function if the state variable was changed 26 0.0
[Gas-78] Short circuit before external calls 33 0.0
[Gas-79] Use OZ Array.unsafeAccess() to avoid repeated array length checks 54 6123600
[Gas-80] State variable written in a loop 3 173820
[Gas-81] State variable read in a loop 15 2954940
[Gas-82] Use uint256(1)/uint256(2) instead of true/false to save gas for changes 155 205413750
[Gas-83] Avoid emitting events in loops 1 2250
[Gas-84] Enable IR-based code generation 67 0.0
[Gas-85] Call msg.sender directly rather than caching the value to save gas 5 0.0
[Gas-86] Write direct outcome, instead of performing mathematical operations for constant state variables 5 0.0

[Medium-1] Privileged functions can create points of failure

Resolution

Ensure such accounts are protected and consider implementing multi sig to prevent a single point of failure

Num of instances: 5

Findings

Click to show findings

['378']

357:     function _withdraw(
358:         uint256 assets,
359:         address receiver,
360:         address owner,
361:         bool forceRedeemCollateral
362:     ) internal override returns (uint256 shares) {
363:         
364:         uint256 pending = _calculatePendingRewards();
365:         uint256 ta = _totalAssets + pending;
366: 
367:         
368:         if (assets > _convertToAssets(balanceOf(owner), ta)) {
369:             
370:             _revert(0x05203273);
371:         }
372: 
373:         
374:         shares = _previewWithdraw(assets, ta);
375: 
376:         
377:         
378:         if (msg.sender != owner) { // <= FOUND
379:             uint256 allowed = allowance(owner, msg.sender);
380: 
381:             if (allowed != type(uint256).max) {
382:                 _spendAllowance(owner, msg.sender, allowed - shares);
383:             }
384:         }
385:         
386:         
387:         marketManager.canRedeemWithCollateralRemoval( // <= FOUND
388:             address(this),
389:             owner,
390:             balanceOf(owner),
391:             shares,
392:             forceRedeemCollateral
393:         );
394: 
395:         
396:         _gaugePool().withdraw(address(this), owner, shares);
397:         
398:         _processWithdraw(
399:             msg.sender,
400:             receiver,
401:             owner,
402:             assets,
403:             shares,
404:             ta,
405:             pending
406:         );
407:     }

['438']

424:     function _redeem(
425:         uint256 shares,
426:         address receiver,
427:         address owner,
428:         bool delegatedAction,
429:         bool forceRedeemCollateral
430:     ) internal override returns (uint256 assets) {
431:         
432:         
433:         if (delegatedAction) {
434:             if (!_checkIsDelegate(owner, msg.sender)) {
435:                 _revert(_UNAUTHORIZED_SELECTOR);
436:             }
437:         } else {
438:             if (msg.sender != owner) { // <= FOUND
439:                 uint256 allowed = allowance(owner, msg.sender);
440: 
441:                 if (allowed != type(uint256).max) {
442:                     _spendAllowance(owner, msg.sender, allowed - shares);
443:                 }
444:             }
445:         }
446:         
447:         
448:         if (shares > maxRedeem(owner)) {
449:             
450:             _revert(0xcc3c42c0);
451:         }
452: 
453:         
454:         marketManager.canRedeemWithCollateralRemoval( // <= FOUND
455:             address(this),
456:             owner,
457:             balanceOf(owner),
458:             shares,
459:             forceRedeemCollateral
460:         );
461: 
462:         
463:         uint256 pending = _calculatePendingRewards();
464:         uint256 ta = _totalAssets + pending;
465: 
466:         
467:         if ((assets = _previewRedeem(shares, ta)) == 0) {
468:             revert CTokenCompounding__ZeroAssets();
469:         }
470: 
471:         
472:         _gaugePool().withdraw(address(this), owner, shares);
473:         
474:         _processWithdraw(
475:             msg.sender,
476:             receiver,
477:             owner,
478:             assets,
479:             shares,
480:             ta,
481:             pending
482:         );
483:     }

['197']

176:     function _withdraw(
177:         uint256 assets,
178:         address receiver,
179:         address owner,
180:         bool forceRedeemCollateral
181:     ) internal override returns (uint256 shares) {
182:         
183:         uint256 ta = _totalAssets;
184: 
185:         
186:         
187:         if (assets > _convertToAssets(balanceOf(owner), ta)) {
188:             
189:             _revert(0xc6e63cc0);
190:         }
191: 
192:         
193:         shares = _previewWithdraw(assets, ta);
194: 
195:         
196:         
197:         if (msg.sender != owner) { // <= FOUND
198:             uint256 allowed = allowance(owner, msg.sender);
199: 
200:             if (allowed != type(uint256).max) {
201:                 _spendAllowance(owner, msg.sender, allowed - shares);
202:             }
203:         }
204:         
205:         
206:         marketManager.canRedeemWithCollateralRemoval( // <= FOUND
207:             address(this),
208:             owner,
209:             balanceOf(owner),
210:             shares,
211:             forceRedeemCollateral
212:         );
213: 
214:         
215:         _gaugePool().withdraw(address(this), owner, shares);
216:         
217:         _processWithdraw(msg.sender, receiver, owner, assets, shares, ta);
218:     }

['246']

232:     function _redeem(
233:         uint256 shares,
234:         address receiver,
235:         address owner,
236:         bool delegatedAction,
237:         bool forceRedeemCollateral
238:     ) internal override returns (uint256 assets) {
239:         
240:         
241:         if (delegatedAction) {
242:             if (!_checkIsDelegate(owner, msg.sender)) {
243:                 _revert(_UNAUTHORIZED_SELECTOR);
244:             }
245:         } else {
246:             if (msg.sender != owner) { // <= FOUND
247:                 uint256 allowed = allowance(owner, msg.sender);
248: 
249:                 if (allowed != type(uint256).max) {
250:                     _spendAllowance(owner, msg.sender, allowed - shares);
251:                 }
252:             }
253:         }
254: 
255:         
256:         if (shares > maxRedeem(owner)) {
257:             
258:             _revert(0xb1652d68);
259:         }
260: 
261:         
262:         marketManager.canRedeemWithCollateralRemoval( // <= FOUND
263:             address(this),
264:             owner,
265:             balanceOf(owner),
266:             shares,
267:             forceRedeemCollateral
268:         );
269: 
270:         
271:         uint256 ta = _totalAssets;
272: 
273:         
274:         if ((assets = _previewRedeem(shares, ta)) == 0) {
275:             revert CTokenPrimitive__ZeroAssets();
276:         }
277: 
278:         
279:         _gaugePool().withdraw(address(this), owner, shares);
280:         
281:         _processWithdraw(msg.sender, receiver, owner, assets, shares, ta);
282:     }

['476']

472:     function _withdraw(address by, address to, address owner, uint256 assets, uint256 shares)
473:         internal
474:         virtual
475:     {
476:         if (by != owner) _spendAllowance(owner, by, shares); // <= FOUND
477:         _beforeWithdraw(assets, shares);
478:         _burn(owner, shares);
479:         SafeTransferLib.safeTransfer(asset(), to, assets);
480:         
481:         assembly {
482:             
483:             mstore(0x00, assets)
484:             mstore(0x20, shares)
485:             let m := shr(96, not(0))
486:             log4(0x00, 0x40, _WITHDRAW_EVENT_SIGNATURE, and(m, by), and(m, to), and(m, owner))
487:         }
488:     }

[Medium-2] Insufficient oracle validation

Resolution

The contract currently lacks a mechanism to ensure that prices fetched from the Chainlink oracle are up-to-date. In scenarios where the Off-Chain Reporting (OCR) protocol fails to update prices in a timely manner, stale price data could be used inadvertently. This could potentially lead to incorrect contract operations, including mispriced transactions. To mitigate this, consider incorporating a staleness threshold (defined in seconds) into the contract configuration. This would enforce that any price data used is within this specified freshness timeframe, thereby ensuring the contract only operates with relevant and recent price information.

Num of instances: 1

Findings

Click to show findings

['249']

249:     function _parseData(
250:         AdaptorData memory data,
251:         bool inUSD
252:     ) internal view returns (PriceReturnData memory pData) {
253:         pData.inUSD = inUSD;
254:         if (!IOracleRouter(centralRegistry.oracleRouter()).isSequencerValid()) {
255:             pData.hadError = true;
256:             return pData;
257:         }
258: 
259:         (, int256 price, , uint256 updatedAt, ) = IChainlink(data.aggregator)
260:             .latestRoundData(); // <= FOUND
261: 
262:         
263:         if (price <= 0) {
264:             pData.hadError = true;
265:             return pData;
266:         }
267: 
268:         uint256 newPrice = (uint256(price) * WAD) / (10 ** data.decimals);
269: 
270:         pData.price = uint240(newPrice);
271:         pData.hadError = _verifyData(
272:                         uint256(price),
273:                         updatedAt,
274:                         data.max,
275:                         data.min,
276:                         data.heartbeat
277:                     );
278:     }

[Medium-3] Using block.timestamp as the deadline/expiry invites MEV

Resolution

Using block.timestamp for operation deadlines can be exploited by malicious entities in the Miner Extractable Value (MEV) landscape. Instead of ensuring immediacy, this approach lets miners choose when to execute the transaction within the block, potentially manipulating outcomes for their gain. For instance, they might delay the transaction until market conditions shift, triggering maximum allowable slippage or even causing other significant events like liquidations. To mitigate this risk, deadlines should be determined off-chain and explicitly set by the transaction initiator, ensuring a more predictable and secure execution environment and reducing potential MEV opportunities.

Num of instances: 1

Findings

Click to show findings

['292']

273:     function _swapExactTokensForTokens(
274:         address router,
275:         address lpToken,
276:         address tokenIn,
277:         address tokenOut,
278:         uint256 amount,
279:         bool stable
280:     ) internal returns (uint256) {
281:         
282:         SwapperLib._approveTokenIfNeeded(tokenIn, router, amount);
283: 
284:         IVeloRouter.Route[] memory routes = new IVeloRouter.Route[](1);
285:         routes[0].from = tokenIn;
286:         routes[0].to = tokenOut;
287:         routes[0].stable = stable;
288:         routes[0].factory = IVeloPool(lpToken).factory();
289: 
290:         
291:         uint256[] memory amountsOut = IVeloRouter(router)
292:             .swapExactTokensForTokens( // <= FOUND
293:                 amount,
294:                 0,
295:                 routes,
296:                 address(this),
297:                 block.timestamp // <= FOUND
298:             );
299: 
300:         
301:         SwapperLib._removeApprovalIfNeeded(tokenIn, router);
302: 
303:         return amountsOut[amountsOut.length - 1];
304:     }

[Medium-4] Price should round up

Number of instances found

24

Resolution

When calculating price involves division, ensure there is a check of loss of precision in place, if this check is triggered the price should be rounded up by adding 1

Findings

Findings are labeled with ' <= FOUND'

Click to show findings

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/adaptors/redstone/BaseRedstoneCoreAdaptor.sol#L212-L236

212:     function _parseData(
213:         AdaptorData memory data,
214:         bool inUSD
215:     ) internal view returns (PriceReturnData memory pData) {
216:         uint256 price = _extractPrice(data.symbolHash); // <= FOUND
217: 
218:         
219:         uint256 quoteDecimals = data.decimals;
220:         if (quoteDecimals != 18) {
221:             
222:             
223:             if (quoteDecimals < 18) {
224:                 price = price * (10 ** (18 - quoteDecimals)); // <= FOUND
225:             } else {
226:                 
227:                 
228:                 price = price / (10 ** (quoteDecimals - 18)); // <= FOUND
229:             }
230:         }
231: 
232:         pData.hadError = _verifyData(price, data.max);
233: 
234:         if (!pData.hadError) {
235:             pData.inUSD = inUSD;
236:             pData.price = uint240(price); // <= FOUND
237:         }
238:     }

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/adaptors/curve/Curve2PoolAssetAdaptor.sol#L111-L168

111:     function getPrice(
112:         address asset,
113:         bool inUSD,
114:         bool getLower
115:     ) external view override returns (PriceReturnData memory pData) {
116:         AdaptorData memory data = adaptorData[asset];
117:         
118:         
119:         
120:         if (isLocked(data.pool, 2)) {
121:             revert Curve2PoolAssetAdaptor__Reentrant();
122:         }
123: 
124:         
125:         ICurvePool pool = ICurvePool(data.pool);
126: 
127:         
128:         uint256 virtualPrice = pool.get_virtual_price();
129:         _enforceBounds(virtualPrice, data.lowerBound, data.upperBound);
130: 
131:         
132:         IOracleRouter oracleRouter = IOracleRouter(
133:             centralRegistry.oracleRouter()
134:         );
135:         (uint256 basePrice, uint256 errorCode) = oracleRouter.getPrice(
136:             data.baseToken,
137:             inUSD,
138:             getLower
139:         );
140:         if (errorCode > 0) {
141:             pData.hadError = true;
142:             return pData;
143:         }
144: 
145:         
146:         uint256 sample = pool.balances(
147:             uint256(uint128(data.quoteTokenIndex))
148:         ) / 100;
149: 
150:         uint256 out = pool.get_dy(
151:             data.quoteTokenIndex,
152:             data.baseTokenIndex,
153:             sample
154:         );
155: 
156:         uint256 price = (out * WAD * (10 ** data.quoteTokenDecimals)) / // <= FOUND
157:             sample /
158:             (10 ** data.baseTokenDecimals); 
159: 
160:         price = (price * basePrice) / WAD; // <= FOUND

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/adaptors/curve/Curve2PoolLPAdaptor.sol#L106-L188

106:     function getPrice(
107:         address asset,
108:         bool inUSD,
109:         bool getLower
110:     ) external view override returns (PriceReturnData memory pData) {
111:         AdaptorData memory data = adaptorData[asset];
112:         
113:         
114:         
115:         if (isLocked(data.pool, 2)) {
116:             revert Curve2PoolLPAdaptor__Reentrant();
117:         }
118: 
119:         
120:         ICurvePool pool = ICurvePool(data.pool);
121: 
122:         
123:         uint256 virtualPrice = pool.get_virtual_price();
124:         _enforceBounds(virtualPrice, data.lowerBound, data.upperBound);
125: 
126:         
127:         IOracleRouter oracleRouter = IOracleRouter(
128:             centralRegistry.oracleRouter()
129:         );
130:         uint256 price0;
131:         uint256 price1;
132:         uint256 errorCode;
133:         (price0, errorCode) = oracleRouter.getPrice(
134:             data.underlying0,
135:             inUSD,
136:             getLower
137:         );
138:         if (errorCode > 0) {
139:             pData.hadError = true;
140:             return pData;
141:         }
142:         (price1, errorCode) = oracleRouter.getPrice(
143:             data.underlying1,
144:             inUSD,
145:             getLower
146:         );
147:         if (errorCode > 0) {
148:             pData.hadError = true;
149:             return pData;
150:         }
151: 
152:         
153:         uint256 price;
154:         if (data.isCorrelated) {
155:             
156:             if (data.divideRate0 || data.divideRate1) {
157:                 uint256[2] memory rates = pool.stored_rates();
158:                 if (data.divideRate0) {
159:                     price0 = (price0 * WAD) / rates[0];
160:                 }
161:                 if (data.divideRate1) {
162:                     price1 = (price1 * WAD) / rates[1];
163:                 }
164:             }
165: 
166:             if (getLower) {
167:                 
168:                 uint256 minPrice = price0 < price1 ? price0 : price1;
169:                 price = (minPrice * virtualPrice) / WAD; // <= FOUND
170:             } else {
171:                 
172:                 uint256 maxPrice = price0 < price1 ? price1 : price0;
173:                 price = (maxPrice * virtualPrice) / WAD; // <= FOUND
174:             }
175:         } else {
176:             price = // <= FOUND
177:                 (2 * virtualPrice * FixedPointMathLib.sqrt(price0)) /
178:                 FixedPointMathLib.sqrt(price1);
179:             price = (price * price0) / WAD; // <= FOUND
180:         }
181: 
182:         if (_checkOracleOverflow(price)) {
183:             pData.hadError = true;
184:             return pData;
185:         }
186: 
187:         pData.inUSD = inUSD;
188:         pData.price = uint240(price); // <= FOUND
189:     }

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/OracleRouter.sol#L350-L392

350:     function getPricesForAsset(
351:         address asset,
352:         bool inUSD
353:     ) external view returns (FeedData[] memory) {
354:         bool isMToken = mTokenAssets[asset].isMToken;
355:         if (isMToken) {
356:             asset = mTokenAssets[asset].underlying;
357:         }
358: 
359:         uint256 numFeeds = assetPriceFeeds[asset].length;
360:         if (numFeeds == 0) {
361:             _revert(_NOT_SUPPORTED_SELECTOR);
362:         }
363: 
364:         FeedData[] memory data = new FeedData[](numFeeds * 2);
365: 
366:         
367:         
368:         if (numFeeds < 2) {
369:             data[0] = _getPriceFromFeed(asset, 0, inUSD, true);
370:             data[1] = _getPriceFromFeed(asset, 0, inUSD, false);
371:             if (isMToken) {
372:                 uint256 exchangeRate = IMToken(asset).exchangeRateCached();
373:                 data[0].price = uint240((data[0].price * exchangeRate) / WAD); // <= FOUND
374:                 data[1].price = uint240((data[1].price * exchangeRate) / WAD); // <= FOUND
375:             }
376: 
377:             return data;
378:         }
379: 
380:         
381:         
382:         data[0] = _getPriceFromFeed(asset, 0, inUSD, true);
383:         data[1] = _getPriceFromFeed(asset, 0, inUSD, false);
384:         data[2] = _getPriceFromFeed(asset, 1, inUSD, true);
385:         data[3] = _getPriceFromFeed(asset, 1, inUSD, false);
386: 
387:         if (isMToken) {
388:             uint256 exchangeRate = IMToken(asset).exchangeRateCached();
389:             data[0].price = uint240((data[0].price * exchangeRate) / WAD); // <= FOUND
390:             data[1].price = uint240((data[1].price * exchangeRate) / WAD); // <= FOUND
391:             data[2].price = uint240((data[2].price * exchangeRate) / WAD); // <= FOUND
392:             data[3].price = uint240((data[3].price * exchangeRate) / WAD); // <= FOUND
393:         }
394: 
395:         return data;
396:     }

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/OracleRouter.sol#L435-L469

435:     function getPrice(
436:         address asset,
437:         bool inUSD,
438:         bool getLower
439:     ) public view returns (uint256 price, uint256 errorCode) {
440:         address mAsset;
441:         
442:         if (mTokenAssets[asset].isMToken) {
443:             mAsset = asset;
444:             asset = mTokenAssets[asset].underlying;
445:         }
446: 
447:         uint256 numFeeds = assetPriceFeeds[asset].length;
448:         
449:         if (numFeeds == 0) {
450:             _revert(_NOT_SUPPORTED_SELECTOR);
451:         }
452: 
453:         
454:         if (numFeeds < 2) {
455:             (price, errorCode) = _getPriceSingleFeed(asset, inUSD, getLower);
456:         } else {
457:             (price, errorCode) = _getPriceDualFeed(asset, inUSD, getLower);
458:         }
459: 
460:         
461:         
462:         if (price == 0 && errorCode < BAD_SOURCE) { // <= FOUND
463:             errorCode = BAD_SOURCE;
464:         }
465: 
466:         
467:         if (mAsset != address(0)) {
468:             uint256 exchangeRate = IMToken(mAsset).exchangeRateCached();
469:             price = (price * exchangeRate) / WAD; // <= FOUND
470:         }
471:     }

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/adaptors/pendle/PendleLPTokenAdaptor.sol#L86-L121

86:     function getPrice(
87:         address asset,
88:         bool inUSD,
89:         bool getLower
90:     ) external view override returns (PriceReturnData memory pData) {
91:         
92:         if (!isSupportedAsset[asset]) {
93:             revert PendleLPTokenAdaptor__AssetIsNotSupported();
94:         }
95: 
96:         AdaptorData memory data = adaptorData[asset];
97:         
98:         uint256 lpRate = IPMarket(asset).getLpToAssetRate(data.twapDuration);
99: 
100:         (uint256 price, uint256 errorCode) = IOracleRouter(
101:             centralRegistry.oracleRouter()
102:         ).getPrice(data.quoteAsset, inUSD, getLower);
103: 
104:         
105:         if (errorCode > 0) {
106:             pData.hadError = true;
107:             return pData;
108:         }
109: 
110:         
111:         
112:         price = (price * lpRate) / WAD; // <= FOUND
113: 
114:         
115:         if (_checkOracleOverflow(price)) {
116:             pData.hadError = true;
117:             return pData;
118:         }
119: 
120:         pData.inUSD = inUSD;
121:         pData.price = uint240(price); // <= FOUND
122:     }

https://github.com/curvance/Curvance-CantinaCompetition/tree/c01e6bda82a60ac4842bf0d3363d63e89c870758/contracts/oracles/adaptors/pendle/PendlePrincipalTokenAdaptor.sol#L87-L122

87:     function getPrice(
88:         address asset,
89:         bool inUSD,
90:         bool getLower
91:     ) external view override returns (PriceReturnData memory pData) {
92:         
93:         if (!isSupportedAsset[asset]) {
94:             revert PendlePrincipalTokenAdaptor__AssetIsNotSupported();
95:         }
96: 
97:         AdaptorData memory data = adaptorData[asset];
98:         
99:         uint256 ptRate = data.market.getPtToAssetRate(data.twapDuration);
100: 
101:         (uint256 price, uint256 errorCode) = IOracleRouter(
102:             centralRegistry.oracleRouter()
103:         ).getPrice(data.quoteAsset, inUSD, getLower);
104: 
105:         
106:         if (errorCode > 0) {
107:             pData.hadError = true;
108:             return pData;
109:         }
110: 
111:         
112:         
113:         price = (price * ptRate) / WAD; // <= FOUND
114: 
115:         
116:         if (_checkOracleOverflow(price)) {
117:             pData.hadError = true;
118:             return pData;
119:         }
120: 
121:         pData.inUSD = inUSD;
122:         pData.price = uint240(price); // <= FOUND
123:     }

[Low-1] Potential division by zero should have zero checks in place

Resolution

Implement a zero address check for found instances

Num of instances: 7

Findings

Click to show findings

['217']

217:     function _getFairPrice(
218:         uint256 reserve0,
219:         uint256 reserve1,
220:         uint256 price0,
221:         uint256 price1,
222:         uint256 totalSupply
223:     ) internal pure returns (uint256) {
224:         
225:         uint256 sqrtReserve = FixedPointMathLib.sqrt(
226:             FixedPointMathLib.sqrt(reserve0 * reserve1) *
227:                 FixedPointMathLib.sqrt(
228:                     reserve0 * reserve0 + reserve1 * reserve1
229:                 )
230:         );
231:         uint256 ratio = ((1e18) * price0) / price1; // <= FOUND
232:         uint256 sqrtPrice = FixedPointMathLib.sqrt(
233:             FixedPointMathLib.sqrt((1e18) * ratio) *
234:                 FixedPointMathLib.sqrt(1e36 + ratio * ratio)
235:         );
236:         return
237:             ((((1e18) * sqrtReserve) / sqrtPrice) * price0 * 2) / totalSupply;
238:     }

['178']

178:     function claim() external returns (uint256 amount) { // <= FOUND
179:         SaleStatus saleStatus = currentStatus();
180:         if (saleStatus == SaleStatus.NotStarted) {
181:             revert CurvanceDAOLBP__NotStarted();
182:         }
183:         if (saleStatus == SaleStatus.InSale) {
184:             revert CurvanceDAOLBP__InSale();
185:         }
186: 
187:         uint256 payAmount = userCommitted[msg.sender];
188:         userCommitted[msg.sender] = 0;
189: 
190:         uint256 price = currentPrice();
191:         uint256 adjustedPayAmount = _adjustDecimals(
192:             payAmount,
193:             paymentTokenDecimals,
194:             18
195:         );
196:         amount = (adjustedPayAmount * WAD) / price; // <= FOUND
197: 
198:         SafeTransferLib.safeTransfer(cve, msg.sender, amount);
199: 
200:         emit Claimed(msg.sender, amount);
201:     }

['821']

821:     function _getPositiveCFactorResult(
822:         uint256 multiplier,
823:         uint256 adjustmentVelocity,
824:         uint256 decay,
825:         uint256 current, 
826:         uint256 start, 
827:         uint256 end 
828:     ) internal pure returns (uint256) {
829:         
830:         
831:         
832:         
833:         uint256 cFactor = ((current - start) * WAD) / (end - start);
834: 
835:         
836:         
837:         cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
838: 
839:         
840:         
841:         return ((multiplier * cFactor) / WAD_SQUARED) - decay;
842:     }

['867']

867:     function _getNegativeCFactorResult(
868:         uint256 multiplier,
869:         uint256 adjustmentVelocity,
870:         uint256 decay,
871:         uint256 current, 
872:         uint256 start, 
873:         uint256 end 
874:     ) internal pure returns (uint256) {
875:         
876:         
877:         
878:         uint256 cFactor = ((start - current) * WAD) / (start - end);
879: 
880:         
881:         
882:         cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
883: 
884:         
885:         
886:         return ((multiplier * WAD_SQUARED) / cFactor) - decay;
887:     }

['748']

748:     function _executeEpochFeeRouter(
749:         ChainData memory chainData,
750:         uint256 numChains,
751:         uint256 epoch
752:     ) internal returns (uint256) {
753:         IProtocolMessagingHub messagingHub = IProtocolMessagingHub(
754:             centralRegistry.protocolMessagingHub()
755:         );
756: 
757:         IVeCVE veCVE = IVeCVE(centralRegistry.veCVE());
758:         uint256 lockedTokens = (veCVE.chainPoints() -
759:             veCVE.chainUnlocksByEpoch(epoch));
760: 
761:         uint256 totalLockedTokens = lockedTokens;
762: 
763:         
764:         
765:         for (uint256 i; i < numChains; ) {
766:             totalLockedTokens += crossChainLockData[i].lockAmount;
767: 
768:             unchecked {
769:                 ++i;
770:             }
771:         }
772: 
773:         uint256 feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
774: 
775:         
776:         
777:         SafeTransferLib.safeTransfer(
778:             feeToken,
779:             oneBalanceFeeManager,
780:             (feeTokenBalance * vaultCompoundFee()) /
781:                 centralRegistry.protocolHarvestFee()
782:         );
783: 
784:         feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
785: 
786:         uint256 chainId;
787:         uint256 feeTokenBalanceForChain;
788: 
789:         
790:         
791:         for (uint256 i; i < numChains; ) {
792:             chainId = crossChainLockData[i].chainId;
793:             chainData = centralRegistry.supportedChainData(chainId);
794:             
795:             
796:             
797:             
798:             
799:             
800:             
801:             feeTokenBalanceForChain =
802:                 (((feeTokenBalance * WAD) / totalLockedTokens) * 
803:                 crossChainLockData[i].lockAmount) /
804:                 WAD;
805: 
806:             messagingHub.sendFees(
807:                 chainId,
808:                 chainData.messagingHub,
809:                 feeTokenBalanceForChain
810:             );
811: 
812:             unchecked {
813:                 ++i;
814:             }
815:         }
816: 
817:         
818:         
819:         
820:         
821:         
822:         
823:         
824:         feeTokenBalanceForChain =
825:         (((feeTokenBalance * WAD) / totalLockedTokens) * lockedTokens) /
826:             WAD;
827:         uint256 epochRewardsPerCVE = (feeTokenBalance * WAD) /
828:             totalLockedTokens;
829: 
830:         ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
831: 
832:         
833:         
834:         if (locker.isShutdown() == 2) {
835:             SafeTransferLib.safeTransfer(
836:                 feeToken,
837:                 centralRegistry.daoAddress(),
838:                 feeTokenBalanceForChain
839:             );
840:             return epochRewardsPerCVE;
841:         }
842: 
843:         
844:         SafeTransferLib.safeTransfer(
845:             feeToken,
846:             address(locker),
847:             feeTokenBalanceForChain
848:         );
849:         ICVELocker(locker).recordEpochRewards(epochRewardsPerCVE);
850: 
851:         return epochRewardsPerCVE;
852:     }

['856']

856:     function _convertETHUSD(
857:         uint240 currentPrice,
858:         uint256 conversionRate,
859:         bool currentlyInUSD
860:     ) internal pure returns (uint256) {
861:         if (!currentlyInUSD) {
862:             
863:             return (currentPrice * conversionRate) / WAD;
864:         }
865: 
866:         return (currentPrice * WAD) / conversionRate;
867:     }

['224']

224:     function _optimalDeposit(
225:         address factory,
226:         address lpToken,
227:         uint256 amount0,
228:         uint256 reserve0,
229:         uint256 reserve1,
230:         uint256 decimals0,
231:         uint256 decimals1,
232:         bool stable
233:     ) internal view returns (uint256) {
234:         
235:         uint256 swapFee = IVeloPairFactory(factory).getFee(lpToken, stable);
236:         uint256 a;
237: 
238:         
239:         if (stable) {
240:             a = (((amount0 * 10000) / (10000 - swapFee)) * 1e18) / decimals0; // <= FOUND
241: 
242:             uint256 x = (reserve0 * 1e18) / decimals0;
243:             uint256 y = (reserve1 * 1e18) / decimals1; // <= FOUND
244:             uint256 x2 = (x * x) / 1e18;
245:             uint256 y2 = (y * y) / 1e18;
246:             uint256 p = (y * (((x2 * 3 + y2) * 1e18) / (y2 * 3 + x2))) / x; // <= FOUND
247: 
248:             uint256 num = a * y;
249:             uint256 den = ((a + x) * p) / 1e18 + y;
250: 
251:             return ((num / den) * decimals0) / 1e18;
252:         }
253: 
254:         
255:         uint256 swapFeeFactor = 10000 - swapFee;
256:         
257:         a = (10000 + swapFeeFactor) * reserve0;
258:         uint256 b = amount0 * 10000 * reserve0 * 4 * swapFeeFactor;
259:         uint256 c = FixedPointMathLib.sqrt(a * a + b);
260:         uint256 d = swapFeeFactor * 2;
261:         return (c - a) / d; // <= FOUND
262:         
263:     }

[Low-2] Division operations should always be performed after multiplication operations

Resolution

Perform multiplication operations first

Num of instances: 3

Findings

Click to show findings

['72']

72:     function getPrice(
73:         address asset,
74:         bool inUSD,
75:         bool getLower
76:     ) external view override returns (PriceReturnData memory pData) {
77:         
78:         if (!isSupportedAsset[asset]) {
79:             revert BalancerStablePoolAdaptor__AssetIsNotSupported();
80:         }
81: 
82:         
83:         _ensureNotInVaultContext(balancerVault);
84: 
85:         
86:         AdaptorData memory data = adaptorData[asset];
87:         IBalancerPool pool = IBalancerPool(asset);
88: 
89:         pData.inUSD = inUSD;
90:         IOracleRouter oracleRouter = IOracleRouter(centralRegistry.oracleRouter());
91: 
92:         
93:         uint256 numUnderlyingOrConstituent = data
94:             .underlyingOrConstituent
95:             .length;
96:         uint256 averagePrice;
97:         uint256 numPrices;
98: 
99:         uint256 price;
100:         uint256 errorCode;
101:         for (uint256 i; i < numUnderlyingOrConstituent; ++i) {
102:             
103:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
104:                 break;
105:             }
106: 
107:             (price, errorCode) = oracleRouter.getPrice(
108:                 data.underlyingOrConstituent[i],
109:                 inUSD,
110:                 getLower
111:             );
112: 
113:             
114:             if (errorCode > 0) {
115:                 pData.hadError = true;
116:                 return pData;
117:             } 
118:             
119:             
120:             
121:             averagePrice += price;
122:             ++numPrices;
123:             
124:         }
125: 
126:         
127:         if (averagePrice == 0) {
128:             pData.hadError = true;
129:             return pData;
130:         } 
131: 
132:         averagePrice = ((averagePrice / numPrices) * pool.getRate()) / WAD;
133:         
134:         
135:         if (_checkOracleOverflow(averagePrice)) {
136:             pData.hadError = true;
137:             return pData;
138:         }
139: 
140:         pData.price = uint240(averagePrice);
141:     }

['217']

217:     function _getFairPrice(
218:         uint256 reserve0,
219:         uint256 reserve1,
220:         uint256 price0,
221:         uint256 price1,
222:         uint256 totalSupply
223:     ) internal pure returns (uint256) {
224:         
225:         uint256 sqrtReserve = FixedPointMathLib.sqrt(
226:             FixedPointMathLib.sqrt(reserve0 * reserve1) *
227:                 FixedPointMathLib.sqrt(
228:                     reserve0 * reserve0 + reserve1 * reserve1
229:                 )
230:         );
231:         uint256 ratio = ((1e18) * price0) / price1;
232:         uint256 sqrtPrice = FixedPointMathLib.sqrt(
233:             FixedPointMathLib.sqrt((1e18) * ratio) *
234:                 FixedPointMathLib.sqrt(1e36 + ratio * ratio)
235:         );
236:         return
237:             ((((1e18) * sqrtReserve) / sqrtPrice) * price0 * 2) / totalSupply;
238:     }

['748']

748:     function _executeEpochFeeRouter(
749:         ChainData memory chainData,
750:         uint256 numChains,
751:         uint256 epoch
752:     ) internal returns (uint256) {
753:         IProtocolMessagingHub messagingHub = IProtocolMessagingHub(
754:             centralRegistry.protocolMessagingHub()
755:         );
756: 
757:         IVeCVE veCVE = IVeCVE(centralRegistry.veCVE());
758:         uint256 lockedTokens = (veCVE.chainPoints() -
759:             veCVE.chainUnlocksByEpoch(epoch));
760: 
761:         uint256 totalLockedTokens = lockedTokens;
762: 
763:         
764:         
765:         for (uint256 i; i < numChains; ) {
766:             totalLockedTokens += crossChainLockData[i].lockAmount;
767: 
768:             unchecked {
769:                 ++i;
770:             }
771:         }
772: 
773:         uint256 feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
774: 
775:         
776:         
777:         SafeTransferLib.safeTransfer(
778:             feeToken,
779:             oneBalanceFeeManager,
780:             (feeTokenBalance * vaultCompoundFee()) /
781:                 centralRegistry.protocolHarvestFee()
782:         );
783: 
784:         feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
785: 
786:         uint256 chainId;
787:         uint256 feeTokenBalanceForChain;
788: 
789:         
790:         
791:         for (uint256 i; i < numChains; ) {
792:             chainId = crossChainLockData[i].chainId;
793:             chainData = centralRegistry.supportedChainData(chainId);
794:             
795:             
796:             
797:             
798:             
799:             
800:             
801:             feeTokenBalanceForChain =
802:                 (((feeTokenBalance * WAD) / totalLockedTokens) * 
803:                 crossChainLockData[i].lockAmount) /
804:                 WAD;
805: 
806:             messagingHub.sendFees(
807:                 chainId,
808:                 chainData.messagingHub,
809:                 feeTokenBalanceForChain
810:             );
811: 
812:             unchecked {
813:                 ++i;
814:             }
815:         }
816: 
817:         
818:         
819:         
820:         
821:         
822:         
823:         
824:         feeTokenBalanceForChain =
825:         (((feeTokenBalance * WAD) / totalLockedTokens) * lockedTokens) /
826:             WAD;
827:         uint256 epochRewardsPerCVE = (feeTokenBalance * WAD) /
828:             totalLockedTokens;
829: 
830:         ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
831: 
832:         
833:         
834:         if (locker.isShutdown() == 2) {
835:             SafeTransferLib.safeTransfer(
836:                 feeToken,
837:                 centralRegistry.daoAddress(),
838:                 feeTokenBalanceForChain
839:             );
840:             return epochRewardsPerCVE;
841:         }
842: 
843:         
844:         SafeTransferLib.safeTransfer(
845:             feeToken,
846:             address(locker),
847:             feeTokenBalanceForChain
848:         );
849:         ICVELocker(locker).recordEpochRewards(epochRewardsPerCVE);
850: 
851:         return epochRewardsPerCVE;
852:     }

[Low-3] Function with two array parameter missing a length check

Resolution

In Solidity, if two array parameters are used within a function and one of their lengths is used as the for-loop range, it's essential to have a length check. If the arrays are not the same length, you could experience out-of-bounds errors or unintended behavior. This could happen if the function tries to access an index that doesn't exist in the shorter array.

Resolution: Always validate that the lengths of both arrays are the same before entering the loop. Add a require statement at the start of the function to check that both arrays are of equal length. This helps maintain the integrity of the function and prevents potential errors due to differing array lengths. This requirement ensures the function fails early if the arrays don't match, rather than failing unpredictably or silently during execution.

Num of instances: 4

Findings

Click to show findings

['454']

454:     function registerWormholeChainIDs(
455:         uint256[] calldata chainIds,
456:         uint16[] calldata wormholeChainIds
457:     ) external {
458:         _checkElevatedPermissions();
459: 
460:         uint256 numChainIds = chainIds.length;
461:         for (uint256 i; i < numChainIds; ++i) { // <= FOUND
462:             wormholeChainId[chainIds[i]] = wormholeChainIds[i];
463:         }
464:         emit WormholeChainIDsSet(chainIds, wormholeChainIds);
465:     }

['472']

472:     function registerCCTPDomains(
473:         uint256[] calldata chainIds,
474:         uint32[] calldata cctpDomains
475:     ) external {
476:         _checkElevatedPermissions();
477: 
478:         uint256 numChainIds = chainIds.length;
479: 
480:         for (uint256 i; i < numChainIds; ++i) { // <= FOUND
481:             cctpDomain[chainIds[i]] = cctpDomains[i];
482:         }
483:         emit CCTPDomainsSet(chainIds, cctpDomains);
484:     }

['78']

78:     function setEmissionRates(
79:         uint256 epoch,
80:         address[] calldata tokens,
81:         uint256[] calldata poolWeights
82:     ) external override {
83:         if (msg.sender != centralRegistry.protocolMessagingHub()) {
84:             revert GaugeErrors.Unauthorized();
85:         }
86: 
87:         
88:         
89:         if (
90:             !(epoch == 0 && (startTime == 0 || block.timestamp < startTime)) &&
91:             epoch != currentEpoch() + 1
92:         ) {
93:             revert GaugeErrors.InvalidEpoch();
94:         }
95: 
96:         uint256 numTokens = tokens.length;
97: 
98:         
99:         if (numTokens != poolWeights.length) {
100:             revert GaugeErrors.InvalidLength();
101:         }
102: 
103:         Epoch storage info = _epochInfo[epoch];
104:         address priorAddress;
105:         for (uint256 i; i < numTokens; ) { // <= FOUND
106:             
107:             
108:             if (priorAddress > tokens[i]) {
109:                 revert GaugeErrors.InvalidToken();
110:             }
111: 
112:             info.totalWeights =
113:                 info.totalWeights +
114:                 poolWeights[i] -
115:                 info.poolWeights[tokens[i]];
116:             info.poolWeights[tokens[i]] = poolWeights[i];
117:             unchecked {
118:                 
119:                 priorAddress = tokens[i++];
120:             }
121:         }
122:     }

['1086']

1086:     function setCTokenCollateralCaps(
1087:         address[] calldata mTokens,
1088:         uint256[] calldata newCollateralCaps
1089:     ) external {
1090:         if (!centralRegistry.hasDaoPermissions(msg.sender)) {
1091:             _revert(_UNAUTHORIZED_SELECTOR);
1092:         }
1093: 
1094:         uint256 numTokens = mTokens.length;
1095: 
1096:         assembly {
1097:             if iszero(numTokens) {
1098:                 
1099:                 mstore(0x0, _INVALID_PARAMETER_SELECTOR)
1100:                 
1101:                 revert(0x1c, 0x04)
1102:             }
1103:         }
1104: 
1105:         if (numTokens != newCollateralCaps.length) {
1106:             _revert(_INVALID_PARAMETER_SELECTOR);
1107:         }
1108: 
1109:         for (uint256 i; i < numTokens; ++i) { // <= FOUND
1110:             
1111:             if (!IMToken(mTokens[i]).isCToken()) {
1112:                 _revert(_INVALID_PARAMETER_SELECTOR);
1113:             }
1114: 
1115:             
1116:             
1117:             if (tokenData[mTokens[i]].collRatio == 0) {
1118:                 _revert(_INVALID_PARAMETER_SELECTOR);
1119:             }
1120: 
1121:             collateralCaps[mTokens[i]] = newCollateralCaps[i];
1122:             emit NewCollateralCap(mTokens[i], newCollateralCaps[i]);
1123:         }
1124:     }

[Low-4] Missing checks for address(0x0) when updating address state variables

Num of instances: 1

Findings

Click to show findings

['97']

97:     function start(
98:         uint256 startTimestamp,
99:         uint256 softPriceInUSD,
100:         uint256 cveAmountInLBP,
101:         address paymentTokenAddress
102:     ) external {
103:         if (!centralRegistry.hasDaoPermissions(msg.sender)) {
104:             revert CurvanceDAOLBP__Unauthorized();
105:         }
106: 
107:         if (startTime != 0) {
108:             revert CurvanceDAOLBP__AlreadyStarted();
109:         }
110: 
111:         if (startTimestamp < block.timestamp) {
112:             revert CurvanceDAOLBP__InvalidStartTime();
113:         }
114: 
115:         uint256 errorCode;
116:         (paymentTokenPrice, errorCode) = IOracleRouter(centralRegistry.oracleRouter())
117:             .getPrice(paymentTokenAddress, true, true);
118: 
119:         
120:         
121:         if (errorCode == 2) {
122:             revert CurvanceDAOLBP__InvalidPriceSource();
123:         }
124: 
125:         startTime = startTimestamp;
126:         softPriceInpaymentToken = (softPriceInUSD * WAD) / paymentTokenPrice;
127:         cveAmountForSale = cveAmountInLBP;
128:         paymentToken = paymentTokenAddress; // <= FOUND
129:         paymentTokenDecimals = IERC20(paymentTokenAddress).decimals();
130: 
131:         emit LBPStarted(startTimestamp);
132:     }

[Low-5] Missing empty bytes check when assigning bytes/string state variable

Resolution

In programming, especially when dealing with dynamically sized types like bytes and strings in Solidity, omitting a check for empty values when assigning to a state variable can lead to unexpected behavior or vulnerabilities. This oversight may allow zero-length or null values to be stored, which could be problematic in contexts where valid data is expected. Implementing explicit checks for empty bytes or strings before assignment ensures data integrity and can prevent potential logic errors or exploits in smart contracts. It's a crucial practice in robust smart contract development to validate data thoroughly before state modifications.

Num of instances: 1

Findings

Click to show findings

['247']

234:     function setMerkleRoot(bytes32 newRoot) external {
235:         _checkDaoPermissions();
236: 
237:         if (newRoot == bytes32(0)) {
238:             revert CVEInitialDistribution__ParametersAreInvalid();
239:         }
240: 
241:         if (merkleRoot == bytes32(0)) {
242:             if (!centralRegistry.hasElevatedPermissions(msg.sender)) {
243:                 revert CVEInitialDistribution__Unauthorized();
244:             }
245:         }
246: 
247:         merkleRoot = newRoot; // <= FOUND
248:     }

[Low-6] Unchecked blocks with subtractions may underflow

Resolution

Within unchecked blocks in Solidity, arithmetic operations bypass overflow and underflow checks. When subtractions occur without proper bounds validation, they may underflow. An underflow in an unsigned integer subtraction can wrap the value around to its maximum, leading to unintended contract behavior or potential vulnerabilities. To prevent such scenarios, developers should either avoid unchecked blocks for subtraction operations or manually implement checks to ensure operands' validity before subtraction.

Num of instances: 2

Findings

Click to show findings

['86']

86:         unchecked {
87:             gasSpent = gasStart - gasleft(); // <= FOUND
88:         }

['1212']

1212:         unchecked {
1213:             totalSupply = totalSupply - tokens; // <= FOUND
1214:         }

[Low-7] Incorrect comparison against a max value

Resolution

Comparing a value using >= MAX_VALUE is conceptually incorrect when MAX_VALUE is defined as the upper limit that a variable can take. According to the definition of a maximum value, the condition should only revert if the variable is greater than MAX_VALUE, not equal to it. Using >= can introduce unintended behavior, as the condition will trigger a revert even when the variable reaches its legitimate upper bound. This can lead to bugs and vulnerabilities, as well as hamper the contract's intended functionality. The resolution is to strictly use > when making comparisons against a defined MAX_VALUE to align with its conceptual definition and to prevent unintended reverts or vulnerabilities.

Num of instances: 2

Findings

Click to show findings

['308']

308: 
309:         
310:         
311:         
312:         if (maxCautionDivergence >= maxBadSourceDivergence) { // <= FOUND

['308']

308:         if (maxCautionDivergence >= maxBadSourceDivergence) { // <= FOUND
309:             _revert(_INVALID_PARAMETER_SELECTOR); // <= FOUND
310:         }

[Low-8] Getting a bool return value does not confirm the existence of a function in an external call

Resolution

External calls to contracts using address.call() might return a boolean indicating success or failure. However, this boolean doesn't guarantee the existence of a called function. If a function isn't present, the call won't revert but will simply return false. This behavior might lead developers into mistakenly believing they're interacting with a legitimate or expected function, whereas it might not exist at all—a scenario sometimes termed as "phantom functions". Resolution: Instead of solely relying on the boolean, further validate the contract you're interacting with, or use interfaces or abstract contracts to enforce the existence of expected functions.

Num of instances: 2

Findings

Click to show findings

['50']

50:     function swap(
51:         ICentralRegistry centralRegistry,
52:         Swap memory swapData
53:     ) internal returns (uint256) {
54:         address callDataChecker = centralRegistry.externalCallDataChecker(
55:             swapData.target
56:         );
57: 
58:         
59:         if (callDataChecker == address(0)) {
60:             revert SwapperLib__UnknownCalldata();
61:         }
62: 
63:         
64:         IExternalCallDataChecker(callDataChecker).checkCallData(
65:             swapData,
66:             address(this)
67:         );
68: 
69:         
70:         _approveTokenIfNeeded(
71:             swapData.inputToken,
72:             swapData.target,
73:             swapData.inputAmount
74:         );
75: 
76:         
77:         address outputToken = swapData.outputToken;
78:         uint256 balance = CommonLib.getTokenBalance(outputToken);
79: 
80:         uint256 value = CommonLib.isETH(swapData.inputToken)
81:             ? swapData.inputAmount
82:             : 0;
83: 
84:         
85:         (bool success, bytes memory auxData) = swapData.target.call{
86:             value: value
87:         }(swapData.call);
88: 
89:         propagateError(success, auxData, "SwapperLib: swap");
90: 
91:         
92:         if (!success) {
93:             revert SwapperLib__SwapError();
94:         }
95: 
96:         
97:         _removeApprovalIfNeeded(swapData.inputToken, swapData.target);
98: 
99:         return CommonLib.getTokenBalance(outputToken) - balance;
100:     }

['111']

111:     function zap(ZapperCall memory zapperCall) internal { // <= FOUND
112:         
113:         _approveTokenIfNeeded(
114:             zapperCall.inputToken,
115:             zapperCall.target,
116:             zapperCall.inputAmount
117:         );
118: 
119:         
120:         uint256 value = 0;
121:         if (CommonLib.isETH(zapperCall.inputToken)) {
122:             value = zapperCall.inputAmount;
123:         }
124: 
125:         
126:         (bool success, bytes memory auxData) = zapperCall.target.call{
127:             value: value
128:         }(zapperCall.call);
129: 
130:         
131:         _removeApprovalIfNeeded(zapperCall.inputToken, zapperCall.target);
132: 
133:         SwapperLib.propagateError(success, auxData, "SwapperLib: zapper");
134:     }

[Low-9] Low level calls in solidity versions preceding 0.8.14 can result in an optimiser bug

Resolution

In Solidity versions 0.8.13 and 0.8.14, a known optimizer bug presents potential risks when a variable is used in a separate assembly block from the one in which it was stored. Specifically, the 'mstore' operation could be optimized out due to this bug, leading to the use of uninitialized memory. Although the current code does not exhibit this risky pattern of execution, it does utilize 'mstore' within assembly blocks, which introduces a vulnerability risk for future code modifications. As a preventative measure, it is advisable to avoid the usage of the afflicted Solidity versions, 0.8.13 and 0.8.14. Instead, consider utilizing a version that is not impacted by this optimizer bug to prevent potential memory initialization issues in your smart contract.

Num of instances: 13

Findings

Click to show findings

['312']

312:         assembly { // <= FOUND
313:             
314:             mstore(0x00, assets)
315:             mstore(0x20, shares)
316:             let m := shr(96, not(0))
317:             log3(0x00, 0x40, _DEPOSIT_EVENT_SIGNATURE, and(m, by), and(m, to))
318:         }

['128']

128:         assembly { // <= FOUND
129:             
130:             mstore(0x00, 0x313ce567)
131:             
132:             success :=
133:                 and(
134:                     
135:                     and(lt(mload(0x00), 0x100), gt(returndatasize(), 0x1f)),
136:                     
137:                     staticcall(gas(), underlying, 0x1c, 0x04, 0x00, 0x20)
138:                 )
139:             result := mul(mload(0x00), success)
140:         }

['301']

301:         assembly { // <= FOUND
302:             result := or(iszero(a), iszero(b))
303:         }

['312']

312:         assembly { // <= FOUND
313:             
314:             mstore(0x00, assets)
315:             mstore(0x20, shares)
316:             let m := shr(96, not(0))
317:             log3(0x00, 0x40, _DEPOSIT_EVENT_SIGNATURE, and(m, by), and(m, to))
318:         }

['481']

481:         assembly { // <= FOUND
482:             
483:             mstore(0x00, assets)
484:             mstore(0x20, shares)
485:             let m := shr(96, not(0))
486:             log4(0x00, 0x40, _WITHDRAW_EVENT_SIGNATURE, and(m, by), and(m, to), and(m, owner))
487:         }

['29']

29:         assembly { // <= FOUND
30:             for {} 1 {} {
31:                 
32:                 
33:                 
34:                 
35:                 
36: 
37:                 
38:                 result := mul(x, y) 
39:                 let mm := mulmod(x, y, not(0))
40:                 
41:                 let p1 := sub(mm, add(result, lt(mm, result)))
42: 
43:                 
44:                 if iszero(p1) {
45:                     if iszero(d) {
46:                         mstore(0x00, 0xae47f702) 
47:                         revert(0x1c, 0x04)
48:                     }
49:                     result := div(result, d)
50:                     break
51:                 }
52: 
53:                 
54:                 if iszero(gt(d, p1)) {
55:                     mstore(0x00, 0xae47f702) 
56:                     revert(0x1c, 0x04)
57:                 }
58: 
59:                 
60: 
61:                 
62:                 
63:                 let r := mulmod(x, y, d)
64:                 
65:                 
66:                 let t := and(d, sub(0, d))
67:                 
68:                 d := div(d, t)
69:                 
70:                 
71:                 
72:                 
73:                 
74:                 let inv := xor(2, mul(3, d))
75:                 
76:                 
77:                 
78:                 inv := mul(inv, sub(2, mul(d, inv))) 

['107']

107:         assembly { // <= FOUND
108:             if mulmod(x, y, d) {
109:                 result := add(result, 1)
110:                 if iszero(result) {
111:                     mstore(0x00, 0xae47f702) 
112:                     revert(0x1c, 0x04)
113:                 }
114:             }
115:         }

['122']

122:         assembly { // <= FOUND
123:             
124:             if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
125:                 mstore(0x00, 0xad251c27) 
126:                 revert(0x1c, 0x04)
127:             }
128:             z := div(mul(x, y), d)
129:         }

['136']

136:         assembly { // <= FOUND
137:             
138:             if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
139:                 mstore(0x00, 0xad251c27) 
140:                 revert(0x1c, 0x04)
141:             }
142:             z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
143:         }

['149']

149:         assembly { // <= FOUND
150:             
151:             z := 181 
152: 
153:             
154:             
155: 
156:             
157:             
158:             let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
159:             r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
160:             r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
161:             r := or(r, shl(4, lt(0xffffff, shr(r, x))))
162:             z := shl(shr(1, r), z)
163: 
164:             
165:             
166:             
167:             
168: 
169:             
170:             
171:             
172: 
173:             
174:             
175:             
176: 
177:             
178:             
179:             
180: 
181:             
182:             z := shr(18, mul(z, add(shr(r, x), 65536))) 
183: 
184:             
185:             z := shr(1, add(z, div(x, z)))
186:             z := shr(1, add(z, div(x, z)))
187:             z := shr(1, add(z, div(x, z)))
188:             z := shr(1, add(z, div(x, z)))
189:             z := shr(1, add(z, div(x, z)))
190:             z := shr(1, add(z, div(x, z)))
191:             z := shr(1, add(z, div(x, z)))
192: 
193:             
194:             
195:             
196:             z := sub(z, lt(div(x, z), z))
197:         }

['30']

30:         assembly { // <= FOUND
31:             if eq(sload(_REENTRANCY_GUARD_SLOT), 2) {
32:                 mstore(0x00, 0xab143c06) 
33:                 revert(0x1c, 0x04)
34:             }
35:             sstore(_REENTRANCY_GUARD_SLOT, 2)
36:         }

['39']

39:         assembly { // <= FOUND
40:             sstore(_REENTRANCY_GUARD_SLOT, 1)
41:         }

['30']

30:         assembly { // <= FOUND
31:             if eq(sload(_REENTRANCY_GUARD_SLOT), 2) {
32:                 mstore(0x00, 0xab143c06) 
33:                 revert(0x1c, 0x04)
34:             }
35:         }

[Low-10] Mint function with no access control

Resolution

Having a mint function without proper access control in a smart contract is a critical vulnerability. It allows anyone to issue new tokens at will, potentially leading to unlimited inflation, devaluation of the token, and erosion of trust in the project. Proper access control mechanisms, such as specifying roles with the OpenZeppelin Ownable or AccessControl contracts, ensure that only authorized addresses can execute sensitive functions like minting. Implementing rigorous access control is fundamental to maintaining the integrity, security, and economic stability of token ecosystems, preventing unauthorized token creation and protecting against malicious actors.

Num of instances: 3

Findings

Click to show findings

['371']

371:     function mint( // <= FOUND
372:         uint256 shares,
373:         address receiver
374:     ) public override nonReentrant returns (uint256 assets) {
375:         assets = _mint(shares, receiver); // <= FOUND
376:     }

['569']

569:     function mint(uint256 amount) external nonReentrant returns (bool) { // <= FOUND
570:         _mint(msg.sender, msg.sender, amount); // <= FOUND
571:         return true;
572:     }

['393']

393:     function mint(uint256 shares, address to) public virtual returns (uint256 assets) { // <= FOUND
394:         if (shares > maxMint(to)) _revert(0x6a695959); 
395:         assets = previewMint(shares);
396:         _deposit(msg.sender, to, assets, shares);
397:     }

[Low-11] Important function with no access control

Num of instances: 2

Findings

Click to show findings

['385']

385:     function withdraw( // <= FOUND
386:         uint256 assets,
387:         address receiver,
388:         address owner
389:     ) public override nonReentrant returns (uint256 shares) 

['401']

401:     function withdraw( // <= FOUND
402:         address token,
403:         address user,
404:         uint256 amount
405:     ) external nonReentrant 

[Low-12] Chainlink price feeds are not validated

Resolution

Chainlink's latestRoundData function returns the latest price information from a specific oracle feed. However, not validating these prices could be dangerous, particularly if they're used in further arithmetic operations. If the price is 0 or negative, and it's involved in calculations with unsigned integers (uint), it can cause underflows. Underflows with uint can cause the value to wrap around to a very large number (due to uint's inability to represent negative numbers), resulting in unexpected and potentially catastrophic outcomes in the contract logic. To avoid this, always validate oracle prices to ensure they are positive, non-zero, and within expected bounds before using them in any arithmetic operations.

Num of instances: 1

Findings

Click to show findings

['823']

823:     function _isSequencerValid() internal view returns (bool) {
824:         address sequencer = centralRegistry.sequencer();
825: 
826:         if (sequencer != address(0)) {
827:             (, int256 answer, uint256 startedAt, , ) = IChainlink(sequencer)
828:                 .latestRoundData();
829: 
830:             
831:             
832:             
833:             if (
834:                 answer != 0 || block.timestamp < startedAt + GRACE_PERIOD_TIME
835:             ) {
836:                 return false;
837:             }
838:         }
839: 
840:         return true;
841:     }

[Low-13] Return values not checked for approve()

Resolution

The ERC-20 token standard does not dictate that the approve function must return a value. The function signature in the ERC-20 standard is function approve(address spender, uint tokens) public returns (bool success);. However, a well-implemented ERC-20 token contract will typically have approve return a boolean value indicating whether or not the operation was successful.

It's crucial to note that not all token contracts follow this practice. Some might not return a value, or they might return a value in a non-standard way. This inconsistency among token contracts is one reason why it's important to handle token interactions carefully in your smart contracts and to check the return value of approve when possible.

Num of instances: 2

Findings

Click to show findings

['592']

592: 
593:         IERC20(cve).approve(address(veCVE), lockAmount); // <= FOUND

['267']

267:             cve.approve(veCVE, amount); // <= FOUND

[Low-14] Empty receive functions can cause gas issues

Resolution

In Solidity, functions that receive Ether without corresponding functionality to utilize or withdraw these funds can inadvertently lead to a permanent loss of value. This is because if someone sends Ether to the contract, they may be unable to retrieve it. To avoid this, functions receiving Ether should be accompanied by additional methods that process or allow the withdrawal of these funds. If the intent is to use the received Ether, it should trigger a separate function; if not, it should revert the transaction (for instance, via require(msg.sender == address(weth))). Access control checks can also prevent unintended Ether transfers, despite the slight gas cost they entail. If concerns over gas costs persist, at minimum, include a rescue function to recover unused Ether. Missteps in handling Ether in smart contracts can lead to irreversible financial losses, hence these precautions are crucial.

Num of instances: 4

Findings

Click to show findings

['71']

71:     receive() external payable {}

['71']

71:     receive() external payable {}

['71']

71:     receive() external payable {}

['71']

71:     receive() external payable {}

[Low-15] The call abi.encodeWithSignature is not safe from typographical errors

Resolution

When you use abi.encodeWithSignature in Solidity, you're creating an ABI-encoded string representing a function call. This method relies on you, the programmer, correctly typing the function signature (i.e., the function name and types of arguments) as a string.

For instance, if you want to call a function foo(uint256, address), you'd write abi.encodeWithSignature("foo(uint256,address)", arg1, arg2). If there's a typographical error like abi.encodeWithSignature("foo(uinnt256,address)", arg1, arg2), the Solidity compiler will not raise an error because the string is syntactically correct. However, this will fail at runtime because there's no function with that incorrect signature.

This makes abi.encodeWithSignature less safe than directly calling the function or using interfaces to call functions on other contracts. An effective resolution would be using a Solidity interface. An interface provides compile-time type checking, ensuring that you're calling existing functions with the correct parameters.

Num of instances: 1

Findings

Click to show findings

['81']

71:         
72:         
73:         
74:         
75:         
76:         
77:         
78: 
79:         
80:         bytes32 REENTRANCY_ERROR_HASH = keccak256(
81:             abi.encodeWithSignature("Error(string)", "BAL#400") // <= FOUND
82:         );

[Low-16] The call abi.encodeWithSelector is not type safe

Resolution

In Solidity, abi.encodeWithSelector is a function used for encoding data along with a function selector, but it is not type-safe. This means it does not enforce type checking at compile time, potentially leading to errors if arguments do not match the expected types. Starting from version 0.8.13, Solidity introduced abi.encodeCall, which offers a safer alternative. abi.encodeCall ensures type safety by performing a full type check, aligning the types of the arguments with the function signature. This reduces the risk of bugs caused by typographical errors or mismatched types. Using abi.encodeCall enhances the reliability and security of the code by ensuring that the encoded data strictly conforms to the specified types, making it a preferable choice in Solidity versions 0.8.13 and above.

Num of instances: 2

Findings

Click to show findings

['80']

61:     function _ensureNotInVaultContext(IVault vault) internal view {
62:         
63:         
64:         
65:         
66:         
67:         
68:         
69: 
70:         
71:         bytes32 REENTRANCY_ERROR_HASH = keccak256(
72:             abi.encodeWithSignature("Error(string)", "BAL#400")
73:         );
74: 
75:         
76:         
77:         
78:         
79:         (, bytes memory revertData) = address(vault).staticcall{ gas: GAS_LIMIT }(
80:             abi.encodeWithSelector( // <= FOUND
81:                 vault.manageUserBalance.selector,
82:                 new address[](0)
83:             )
84:         );
85: 
86:         if (keccak256(revertData) == REENTRANCY_ERROR_HASH) {
87:             revert BalancerBaseAdaptor__Reentrancy();
88:         }
89:     }

['154']

110:     function harvest(
111:         bytes calldata
112:     ) external override returns (uint256 yield) {
113:         
114:         _canCompound();
115: 
116:         
117:         _vestIfNeeded();
118: 
119:         
120:         if (_checkVestStatus(_vaultData)) {
121:             _updateVestingPeriodIfNeeded();
122: 
123:             
124:             uint256[] memory rewardAmounts = _claimReward();
125: 
126:             
127:             
128:             address feeAccumulator = centralRegistry.feeAccumulator();
129:             uint256 harvestFee = centralRegistry.protocolHarvestFee();
130: 
131:             for (uint256 i; i < 2; ++i) {
132:                 
133:                 
134:                 if (rewardAmounts[i] > 0) {
135:                     
136:                     
137:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
138:                         rewardAmounts[i],
139:                         harvestFee,
140:                         1e18
141:                     );
142:                     rewardAmounts[i] -= protocolFee;
143:                     SafeTransferLib.safeTransfer(
144:                         underlyingTokens[i],
145:                         feeAccumulator,
146:                         protocolFee
147:                     );
148:                 }
149:             }
150: 
151:             
152:             bytes[] memory data = new bytes[](4);
153: 
154:             data[0] = abi.encodeWithSelector( // <= FOUND
155:                 IGMXExchangeRouter.sendWnt.selector,
156:                 gmxDepositVault,
157:                 0.01e18
158:             );
159: 
160:             uint256 rewardAmount;
161:             for (uint256 i = 0; i < 2; ) {
162:                 rewardAmount = rewardAmounts[i];
163:                 SafeTransferLib.safeApprove(
164:                     underlyingTokens[i],
165:                     gmxRouter,
166:                     rewardAmount
167:                 );
168:                 data[++i] = abi.encodeWithSelector( // <= FOUND
169:                     IGMXExchangeRouter.sendTokens.selector,
170:                     underlyingTokens[i],
171:                     gmxDepositVault,
172:                     rewardAmount
173:                 );
174:             }
175:             data[3] = abi.encodeWithSelector( // <= FOUND
176:                 IGMXExchangeRouter.createDeposit.selector,
177:                 IGMXExchangeRouter.CreateDepositParams(
178:                     address(this),
179:                     address(this),
180:                     address(0),
181:                     asset(),
182:                     underlyingTokens[0],
183:                     underlyingTokens[1],
184:                     new address[](0),
185:                     new address[](0),
186:                     0,
187:                     false,
188:                     0.01e18,
189:                     500000
190:                 )
191:             );
192: 
193:             bytes[] memory results = IGMXExchangeRouter(gmxExchangeRouter)
194:                 .multicall{ value: 0.01e18 }(data);
195:             _isDepositKey[bytes32(results[3])] = true;
196:             
197:             yield = 1;
198:         }
199:     }

[Low-17] Token supply should not be centralised at deployment

Resolution

Avoid minting tokens to a single address on deployment, rather have them distributed as intended within the constructor (i.e liquidity pools)

Num of instances: 2

Findings

Click to show findings

['92']

57:     constructor(ICentralRegistry centralRegistry_, address builder_) {
58:         if (
59:             !ERC165Checker.supportsInterface(
60:                 address(centralRegistry_),
61:                 type(ICentralRegistry).interfaceId
62:             )
63:         ) {
64:             revert CVE__ParametersAreInvalid();
65:         }
66: 
67:         if (builder_ == address(0)) {
68:             builder_ = msg.sender;
69:         }
70: 
71:         centralRegistry = centralRegistry_;
72:         tokenGenerationEventTimestamp = block.timestamp;
73:         builderAddress = builder_;
74: 
75:         
76: 
77:         
78:         daoTreasuryAllocation = 60900010e18;
79:         
80:         initialCommunityAllocation = 1575000259e16;
81:         
82:         builderAllocation = 44100007245e15;
83:         
84:         builderAllocationPerMonth = builderAllocation / 48;
85: 
86:         
87:         
88:         
89:         
90:         uint256 initialTokenMint = 50400008285e15;
91: 
92:         _mint(msg.sender, initialTokenMint); // <= FOUND
93:     }

['83']

56:     constructor(ICentralRegistry centralRegistry_, address paymentToken_) {
57:         _name = "CVE Options";
58:         _symbol = "oCVE";
59: 
60:         if (
61:             !ERC165Checker.supportsInterface(
62:                 address(centralRegistry_),
63:                 type(ICentralRegistry).interfaceId
64:             )
65:         ) {
66:             revert OCVE__ParametersAreInvalid();
67:         }
68: 
69:         if (paymentToken_ == address(0)) {
70:             revert OCVE__ParametersAreInvalid();
71:         }
72: 
73:         centralRegistry = centralRegistry_;
74:         paymentToken = paymentToken_;
75:         if (paymentToken_ == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
76:             paymentTokenDecimals = 18;
77:         } else {
78:             paymentTokenDecimals = ERC20(paymentToken_).decimals();
79:         }
80:         cve = centralRegistry.cve();
81: 
82:         
83:         _mint(msg.sender, 15750002.59 ether); // <= FOUND
84:     }

[Low-18] Use SafeCast to safely downcast variables

Resolution

Downcasting int/uints in Solidity can be unsafe due to the potential for data loss and unintended behavior. When downcasting a larger integer type to a smaller one (e.g., uint256 to uint128), the value may exceed the range of the target type, leading to truncation and loss of significant digits. This data loss can result in unexpected state changes, incorrect calculations, or other contract vulnerabilities, ultimately compromising the contracts functionality and reliability. To prevent these risks, developers should carefully consider the range of values their variables may hold and ensure that proper checks are in place to prevent out-of-range values before performing downcasting. Also consider using OZ SafeCast functionality.

Num of instances: 32

Findings

Click to show findings

['142']

123:     function _transferFeeTokenViaCCTP(
124:         ITokenMessenger circleTokenMessenger,
125:         uint256 dstChainId,
126:         address to,
127:         uint256 amount,
128:         uint256 wormholeFee
129:     ) internal {
130:         IWormholeRelayer wormholeRelayer = centralRegistry.wormholeRelayer();
131:         uint16 wormholeChainId = centralRegistry.wormholeChainId(dstChainId);
132: 
133:         SwapperLib._approveTokenIfNeeded(
134:             feeToken,
135:             address(circleTokenMessenger),
136:             amount
137:         );
138: 
139:         uint64 nonce = circleTokenMessenger.depositForBurnWithCaller(
140:             amount,
141:             centralRegistry.cctpDomain(dstChainId),
142:             bytes32(uint256(uint160(to))), // <= FOUND
143:             feeToken,
144:             bytes32(uint256(uint160(to))) // <= FOUND
145:         );
146: 
147:         IWormholeRelayer.MessageKey[]
148:             memory messageKeys = new IWormholeRelayer.MessageKey[](1);
149:         messageKeys[0] = IWormholeRelayer.MessageKey(
150:             2, 
151:             abi.encodePacked(centralRegistry.cctpDomain(block.chainid), nonce)
152:         );
153: 
154:         address defaultDeliveryProvider = wormholeRelayer
155:             .getDefaultDeliveryProvider();
156: 
157:         wormholeRelayer.sendToEvm{ value: wormholeFee }(
158:             wormholeChainId,
159:             to,
160:             abi.encode(uint8(1), feeToken, amount), // <= FOUND
161:             0,
162:             0,
163:             _GAS_LIMIT,
164:             wormholeChainId,
165:             address(0),
166:             defaultDeliveryProvider,
167:             messageKeys,
168:             15
169:         );
170:     }

['193']

179:     function _transferTokenViaWormhole(
180:         address token,
181:         uint256 dstChainId,
182:         address to,
183:         uint256 amount,
184:         uint256 wormholeFee
185:     ) internal returns (uint64) {
186:         ITokenBridge tokenBridge = centralRegistry.tokenBridge();
187:         uint16 wormholeChainId = centralRegistry.wormholeChainId(dstChainId);
188:         IWormhole wormholeCore = centralRegistry.wormholeCore();
189:         uint256 messageFee = wormholeCore.messageFee();
190: 
191:         SwapperLib._approveTokenIfNeeded(token, address(tokenBridge), amount);
192: 
193:         bytes memory payload = abi.encode(uint8(1), feeToken, amount); // <= FOUND
194: 
195:         uint64 sequence = tokenBridge.transferTokensWithPayload{
196:             value: messageFee
197:         }(
198:             token,
199:             amount,
200:             wormholeChainId,
201:             bytes32(uint256(uint160(to))), // <= FOUND
202:             0,
203:             payload
204:         );
205: 
206:         IWormholeRelayer.VaaKey[]
207:             memory vaaKeys = new IWormholeRelayer.VaaKey[](1);
208:         vaaKeys[0] = IWormholeRelayer.VaaKey({
209:             emitterAddress: bytes32(uint256(uint160(address(tokenBridge)))), // <= FOUND
210:             chainId: wormholeCore.chainId(),
211:             sequence: sequence
212:         });
213: 
214:         return
215:             centralRegistry.wormholeRelayer().sendVaasToEvm{
216:                 value: wormholeFee - messageFee
217:             }(wormholeChainId, to, payload, 0, _GAS_LIMIT, vaaKeys);
218:     }

['733']

707:     function _validateAndRecordChainData(
708:         uint256 value,
709:         uint256 chainId,
710:         uint256 numChainData,
711:         uint256 epoch
712:     ) internal {
713:         if (numChainData > 0) {
714:             for (uint256 i; i < numChainData; ) {
715:                 
716:                 
717:                 if (
718:                     crossChainLockData[i].epoch < epoch ||
719:                     crossChainLockData[i].chainId == chainId
720:                 ) {
721:                     delete crossChainLockData;
722:                     break;
723:                 }
724: 
725:                 unchecked {
726:                     ++i;
727:                 }
728:             }
729:         }
730: 
731:         
732:         crossChainLockData.push() = LockData({
733:             lockAmount: uint224(value), // <= FOUND
734:             epoch: uint16(epoch), // <= FOUND
735:             chainId: uint16(chainId) // <= FOUND
736:         });
737:     }

['1015']

977:     function accrueInterest() public {
978:         
979:         MarketData memory cachedData = marketData;
980: 
981:         
982:         if (
983:             cachedData.lastTimestampUpdated + cachedData.compoundRate >
984:             block.timestamp
985:         ) {
986:             return;
987:         }
988: 
989:         
990:         uint256 borrowsPrior = totalBorrows;
991:         uint256 reservesPrior = totalReserves;
992:         uint256 exchangeRatePrior = cachedData.exchangeRate;
993: 
994:         
995:         uint256 borrowRate = interestRateModel.getBorrowRateWithUpdate(
996:             marketUnderlyingHeld(),
997:             borrowsPrior,
998:             reservesPrior
999:         );
1000: 
1001:         
1002:         
1003:         uint256 interestCompounds = (block.timestamp -
1004:             cachedData.lastTimestampUpdated) / cachedData.compoundRate;
1005:         
1006:         uint256 interestAccumulated = borrowRate * interestCompounds;
1007:         uint256 debtAccumulated = (interestAccumulated * borrowsPrior) / WAD;
1008:         
1009:         
1010:         uint256 totalBorrowsNew = debtAccumulated + borrowsPrior;
1011:         uint256 exchangeRateNew = ((interestAccumulated * exchangeRatePrior) /
1012:             WAD) + exchangeRatePrior;
1013:         
1014:         
1015:         marketData.lastTimestampUpdated = uint40( // <= FOUND
1016:             cachedData.lastTimestampUpdated +
1017:                 (interestCompounds * cachedData.compoundRate)
1018:         );
1019:         marketData.exchangeRate = uint216(exchangeRateNew); // <= FOUND
1020:         totalBorrows = totalBorrowsNew;
1021: 
1022:         
1023:         
1024:         uint256 newReserves = ((interestFactor * debtAccumulated) / WAD);
1025:         if (newReserves > 0) {
1026:             totalReserves = newReserves + reservesPrior;
1027: 
1028:             
1029:             _gaugePool().deposit(
1030:                 address(this),
1031:                 centralRegistry.daoAddress(),
1032:                 newReserves
1033:             );
1034:         }
1035: 
1036:         emit InterestAccrued(
1037:             debtAccumulated,
1038:             exchangeRateNew,
1039:             totalBorrowsNew
1040:         );
1041:     }

['649']

646:     function _unpackedVaultData(
647:         uint256 packedVaultData
648:     ) internal pure returns (VaultData memory vault) {
649:         vault.rewardRate = uint128(packedVaultData); // <= FOUND
650:         vault.vestingPeriodEnd = uint64(packedVaultData >> _BITPOS_VEST_END); // <= FOUND
651:         vault.lastVestClaim = uint64(packedVaultData >> _BITPOS_LAST_VEST); // <= FOUND
652:     }

['404']

401:     function currentRatesData() external view returns (uint256, uint256) {
402:         uint256 currentRates = _currentRates;
403:         return (
404:             uint192(currentRates), // <= FOUND
405:             uint64(currentRates >> _BITPOS_UPDATE_TIMESTAMP) // <= FOUND
406:         );
407:     }

['147']

111:     function getPrice(
112:         address asset,
113:         bool inUSD,
114:         bool getLower
115:     ) external view override returns (PriceReturnData memory pData) {
116:         AdaptorData memory data = adaptorData[asset];
117:         
118:         
119:         
120:         if (isLocked(data.pool, 2)) {
121:             revert Curve2PoolAssetAdaptor__Reentrant();
122:         }
123: 
124:         
125:         ICurvePool pool = ICurvePool(data.pool);
126: 
127:         
128:         uint256 virtualPrice = pool.get_virtual_price();
129:         _enforceBounds(virtualPrice, data.lowerBound, data.upperBound);
130: 
131:         
132:         IOracleRouter oracleRouter = IOracleRouter(
133:             centralRegistry.oracleRouter()
134:         );
135:         (uint256 basePrice, uint256 errorCode) = oracleRouter.getPrice(
136:             data.baseToken,
137:             inUSD,
138:             getLower
139:         );
140:         if (errorCode > 0) {
141:             pData.hadError = true;
142:             return pData;
143:         }
144: 
145:         
146:         uint256 sample = pool.balances(
147:             uint256(uint128(data.quoteTokenIndex)) // <= FOUND
148:         ) / 100;
149: 
150:         uint256 out = pool.get_dy(
151:             data.quoteTokenIndex,
152:             data.baseTokenIndex,
153:             sample
154:         );
155: 
156:         uint256 price = (out * WAD * (10 ** data.quoteTokenDecimals)) /
157:             sample /
158:             (10 ** data.baseTokenDecimals); 
159: 
160:         price = (price * basePrice) / WAD;
161: 
162:         if (_checkOracleOverflow(price)) {
163:             pData.hadError = true;
164:             return pData;
165:         }
166: 
167:         pData.inUSD = inUSD;
168:         pData.price = uint240(price); // <= FOUND
169:     }

['206']

177:     function addAsset(address asset, AdaptorData memory data) external {
178:         _checkElevatedPermissions();
179: 
180:         
181:         
182:         if (isLocked(data.pool, 2)) {
183:             revert Curve2PoolAssetAdaptor__UnsupportedPool();
184:         }
185: 
186:         
187:         if (asset == data.baseToken) {
188:             revert Curve2PoolAssetAdaptor__InvalidAsset();
189:         }
190: 
191:         address oracleRouter = centralRegistry.oracleRouter();
192: 
193:         
194:         
195:         if (!IOracleRouter(oracleRouter).isSupportedAsset(data.baseToken)) {
196:             revert Curve2PoolAssetAdaptor__BaseAssetIsNotSupported();
197:         }
198: 
199:         
200:         if (data.lowerBound >= data.upperBound) {
201:             revert Curve2PoolAssetAdaptor__InvalidBounds();
202:         }
203: 
204:         ICurvePool pool = ICurvePool(data.pool);
205:         
206:         if (pool.coins(uint256(uint128(data.quoteTokenIndex))) != asset) { // <= FOUND
207:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
208:         }
209:         if (
210:             pool.coins(uint256(uint128(data.baseTokenIndex))) != data.baseToken // <= FOUND
211:         ) {
212:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
213:         }
214: 
215:         data.quoteTokenDecimals = ERC20(asset).decimals();
216:         
217:         if (CommonLib.isETH(data.baseToken)) {
218:             data.baseTokenDecimals = 18;
219:         } else {
220:             data.baseTokenDecimals = ERC20(data.baseToken).decimals();
221:         }
222: 
223:         
224:         
225:         
226:         
227:         data.lowerBound = _bpToWad(data.lowerBound);
228:         data.upperBound = _bpToWad(data.upperBound);
229: 
230:         
231:         if (_MAX_BOUND_RANGE + data.lowerBound < data.upperBound) {
232:             revert Curve2PoolAssetAdaptor__InvalidBounds();
233:         }
234: 
235:         
236:         uint256 testVirtualPrice = pool.get_virtual_price();
237: 
238:         
239:         _enforceBounds(testVirtualPrice, data.lowerBound, data.upperBound);
240: 
241:         
242:         adaptorData[asset] = data;
243: 
244:         
245:         bool isUpdate;
246:         if (isSupportedAsset[asset]) {
247:             isUpdate = true;
248:         }
249: 
250:         isSupportedAsset[asset] = true;
251:         emit CurvePoolAssetAdded(asset, data, isUpdate);
252:     }

['176']

121:     function exitCurve(
122:         address lpMinter,
123:         address lpToken,
124:         address[] calldata tokens,
125:         uint256 lpAmount,
126:         uint256 singleAssetWithdraw,
127:         uint256 singleAssetIndex
128:     ) internal {
129:         
130:         SwapperLib._approveTokenIfNeeded(lpToken, lpMinter, lpAmount);
131: 
132:         uint256 numTokens = tokens.length;
133:         if (singleAssetWithdraw == 0) {
134:             
135:             
136:             
137:             if (numTokens > 4 || numTokens < 2) {
138:                 revert CurveLib__InvalidPoolType();
139:             }
140: 
141:             if (numTokens == 4) {
142:                 uint256[4] memory fourPoolAmounts;
143:                 return ICurveSwap(lpMinter).remove_liquidity(
144:                     lpAmount, 
145:                     fourPoolAmounts
146:                 );
147:             }
148: 
149:             if (numTokens == 3) {
150:                 uint256[3] memory threePoolAmounts;
151:                 return ICurveSwap(lpMinter).remove_liquidity(
152:                     lpAmount, 
153:                     threePoolAmounts
154:                 );
155:             }
156: 
157:             uint256[2] memory twoPoolAmounts;
158:             return ICurveSwap(lpMinter).remove_liquidity(
159:                 lpAmount,
160:                 twoPoolAmounts
161:             );
162:         }
163: 
164:         
165:         if (singleAssetWithdraw == 1) {
166:             return ICurveSwap(lpMinter).remove_liquidity_one_coin(
167:                 lpAmount,
168:                 singleAssetIndex,
169:                 0
170:             );
171:         }
172: 
173:         
174:         ICurveSwap(lpMinter).remove_liquidity_one_coin(
175:             lpAmount,
176:             int128(uint128(singleAssetIndex)), // <= FOUND
177:             0
178:         );
179:     }

['124']

94:     function receiveWormholeMessages(
95:         bytes memory payload,
96:         bytes[] memory ,
97:         bytes32 ,
98:         uint16 ,
99:         bytes32 deliveryHash
100:     ) external payable {
101:         if (block.chainid != _POLYGON_CHAIN_ID) {
102:             return;
103:         }
104: 
105:         if (isDeliveredMessageHash[deliveryHash]) {
106:             revert OneBalanceFeeManager__MessageHashIsAlreadyDelivered(
107:                 deliveryHash
108:             );
109:         }
110: 
111:         isDeliveredMessageHash[deliveryHash] = true;
112: 
113:         address wormholeRelayer = address(centralRegistry.wormholeRelayer());
114: 
115:         if (msg.sender != wormholeRelayer) {
116:             revert OneBalanceFeeManager__Unauthorized();
117:         }
118: 
119:         uint8 payloadId = abi.decode(payload, (uint8));
120: 
121:         if (payloadId == 1) {
122:             (, bytes32 token) = abi.decode(payload, (uint8, bytes32));
123: 
124:             if (address(uint160(uint256(token))) == feeToken) { // <= FOUND
125:                 _depositOneBalanceFee();
126:             }
127:         }
128:     }

['124']

97:     function receiveWormholeMessages(
98:         bytes memory payload,
99:         bytes[] memory ,
100:         bytes32 srcAddress,
101:         uint16 srcChainId,
102:         bytes32 deliveryHash
103:     ) external payable {
104:         _checkMessagingHubStatus();
105: 
106:         
107:         if (isDeliveredMessageHash[deliveryHash]) {
108:             revert ProtocolMessagingHub__MessageHashIsAlreadyDelivered(
109:                 deliveryHash
110:             );
111:         }
112: 
113:         isDeliveredMessageHash[deliveryHash] = true;
114:         address wormholeRelayer = address(centralRegistry.wormholeRelayer());
115: 
116:         
117:         if (msg.sender != wormholeRelayer) {
118:             _revert(_UNAUTHORIZED_SELECTOR);
119:         }
120: 
121:         uint256 gethChainId = centralRegistry.messagingToGETHChainId(
122:             srcChainId
123:         );
124:         address srcAddr = address(uint160(uint256(srcAddress))); // <= FOUND
125: 
126:         OmnichainData memory operator = centralRegistry.getOmnichainOperators(
127:             srcAddr,
128:             gethChainId
129:         );
130: 
131:         
132:         if (
133:             centralRegistry.supportedChainData(gethChainId).messagingHub !=
134:             srcAddr
135:         ) {
136:             return;
137:         }
138: 
139:         
140:         if (operator.isAuthorized < 2) {
141:             return;
142:         }
143: 
144:         uint8 payloadId = abi.decode(payload, (uint8));
145: 
146:         
147:         
148:         if (payloadId == 1) {
149:             (, address token, uint256 amount) = abi.decode(
150:                 payload,
151:                 (uint8, address, uint256)
152:             );
153: 
154:             address feeToken = centralRegistry.feeToken();
155: 
156:             
157:             
158:             
159:             if (token == feeToken) {
160:                 ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
161: 
162:                 
163:                 
164:                 if (locker.isShutdown() == 2) {
165:                     SafeTransferLib.safeTransfer(
166:                         feeToken,
167:                         centralRegistry.daoAddress(),
168:                         amount
169:                     );
170:                     return;
171:                 }
172: 
173:                 

['135']

93:     function addAsset(
94:         address asset, 
95:         bool inUSD,
96:         uint8 decimals
97:     ) external {
98:         _checkElevatedPermissions();
99: 
100:         bytes32 symbolHash;
101:         if (inUSD) {
102:             
103:             
104:             symbolHash = Bytes32Helper._toBytes32(asset);
105:         } else {
106:             
107:             
108:             symbolHash = Bytes32Helper._toBytes32WithETH(asset);
109:         }
110: 
111:         AdaptorData storage data;
112: 
113:         if (inUSD) {
114:             data = adaptorDataUSD[asset];
115:         } else {
116:             data = adaptorDataNonUSD[asset];
117:         }
118: 
119:         
120:         
121:         if (decimals == 0) {
122:             data.decimals = 8;
123:         } else {
124:             
125:             
126:             data.decimals = uint256(decimals);
127:         }
128: 
129:         
130:         
131:         
132:         
133:         
134:         
135:         data.max = uint192(uint256(type(uint192).max) * 9 / 10); // <= FOUND
136:         data.symbolHash = symbolHash;
137:         data.isConfigured = true;
138: 
139:         
140:         bool isUpdate;
141:         if (isSupportedAsset[asset]) {
142:             isUpdate = true;
143:         }
144: 
145:         isSupportedAsset[asset] = true;
146:         emit RedstoneCoreAssetAdded(asset, data, isUpdate);
147:     }

['24']

22:     function maxAnswer() external view returns (int192) {
23:         uint256 max = uint256(
24:             uint192( // <= FOUND
25:                 IChainlink(
26:                     IChainlink(underlyingAssetAggregator()).aggregator()
27:                 ).maxAnswer()
28:             )
29:         );
30: 
31:         max = FixedPointMathLib.fullMulDiv(max, getWrappedAssetWeight(), WAD);
32:         int256 intMax = _toInt256(max);
33:         if (intMax > type(int192).max) {
34:             return type(int192).max;
35:         }
36:         if (intMax < type(int192).min) {
37:             return type(int192).min;
38:         }
39: 
40:         return _toInt192(intMax);
41:     }

['46']

44:     function minAnswer() external view returns (int192) {
45:         uint256 min = uint256(
46:             uint192( // <= FOUND
47:                 IChainlink(
48:                     IChainlink(underlyingAssetAggregator()).aggregator()
49:                 ).minAnswer()
50:             )
51:         );
52: 
53:         min = FixedPointMathLib.fullMulDiv(min, getWrappedAssetWeight(), WAD);
54:         int256 intMin = _toInt256(min);
55: 
56:         if (intMin > type(int192).max) {
57:             return type(int192).max;
58:         }
59:         if (intMin < type(int192).min) {
60:             return type(int192).min;
61:         }
62: 
63:         return _toInt192(intMin);
64:     }

['134']

113:     function addAsset(
114:         address asset, 
115:         address aggregator, 
116:         uint256 heartbeat, 
117:         bool inUSD
118:     ) external {
119:         _checkElevatedPermissions();
120: 
121:         if (heartbeat != 0) {
122:             if (heartbeat > DEFAULT_HEART_BEAT) {
123:                 revert ChainlinkAdaptor__InvalidHeartbeat();
124:             }
125:         }
126: 
127:         
128:         IChainlink feedAggregator = IChainlink(
129:             IChainlink(aggregator).aggregator()
130:         );
131: 
132:         
133:         uint256 maxFromChainlink = uint256(
134:             uint192(feedAggregator.maxAnswer()) // <= FOUND
135:         );
136:         uint256 minFromChainklink = uint256(
137:             uint192(feedAggregator.minAnswer()) // <= FOUND
138:         );
139: 
140:         
141:         
142:         
143:         uint256 bufferedMaxPrice = (maxFromChainlink * 9) / 10;
144:         uint256 bufferedMinPrice = (minFromChainklink * 11) / 10;
145: 
146:         
147:         
148:         
149:         
150:         if (bufferedMaxPrice > type(uint240).max) {
151:             bufferedMaxPrice = type(uint240).max;
152:         }
153: 
154:         if (bufferedMinPrice >= bufferedMaxPrice) {
155:             revert ChainlinkAdaptor__InvalidMinMaxConfig();
156:         }
157: 
158:         AdaptorData storage data;
159: 
160:         if (inUSD) {
161:             data = adaptorDataUSD[asset];
162:         } else {
163:             data = adaptorDataNonUSD[asset];
164:         }
165: 
166:         
167:         data.decimals = feedAggregator.decimals();
168:         data.max = bufferedMaxPrice;
169:         data.min = bufferedMinPrice;
170:         data.heartbeat = heartbeat != 0
171:             ? heartbeat
172:             : DEFAULT_HEART_BEAT;
173:         data.aggregator = IChainlink(aggregator);
174:         data.isConfigured = true;
175: 
176:         
177:         bool isUpdate;
178:         if (isSupportedAsset[asset]) {
179:             isUpdate = true;
180:         }
181: 
182:         isSupportedAsset[asset] = true;
183:         emit ChainlinkAssetAdded(asset, data, isUpdate);
184:     }

['603']

540:     function combineAllLocks(
541:         bool continuousLock,
542:         RewardsData calldata rewardsData,
543:         bytes calldata params,
544:         uint256 aux
545:     ) external nonReentrant {
546:         if (isShutdown == 2) {
547:             _revert(_VECVE_SHUTDOWN_SELECTOR);
548:         }
549: 
550:         
551:         _claimRewards(msg.sender, rewardsData, params, aux);
552: 
553:         
554:         
555:         Lock[] storage locks = userLocks[msg.sender];
556:         uint256 numLocks = locks.length;
557: 
558:         
559:         if (numLocks < 2) {
560:             _revert(_INVALID_LOCK_SELECTOR);
561:         }
562: 
563:         uint256 lockedAmount;
564:         Lock storage lock;
565: 
566:         for (uint256 i; i < numLocks; ) {
567:             lock = locks[i];
568: 
569:             if (lock.unlockTime != CONTINUOUS_LOCK_VALUE) {
570:                 
571:                 _reduceTokenUnlocks(
572:                     msg.sender,
573:                     currentEpoch(lock.unlockTime),
574:                     lock.amount
575:                 );
576:             }
577: 
578:             unchecked {
579:                 
580:                 
581:                 lockedAmount += locks[i++].amount;
582:             }
583:         }
584: 
585:         
586:         delete userLocks[msg.sender];
587:         uint256 veBalanceOf = balanceOf(msg.sender);
588: 
589:         
590:         
591:         if (veBalanceOf != lockedAmount) {
592:             revert VeCVE__InvariantError();
593:         }
594: 
595:         
596:         uint256 currentPoints = userPoints[msg.sender];
597: 
598:         
599:         if (continuousLock) {
600:             
601:             userLocks[msg.sender].push(
602:                 Lock({
603:                     amount: uint216(lockedAmount), // <= FOUND
604:                     unlockTime: CONTINUOUS_LOCK_VALUE
605:                 })
606:             );
607: 
608:             
609: 
610:             
611:             
612:             
613:             veBalanceOf = veBalanceOf * CL_POINT_MULTIPLIER;
614: 
615:             
616:             
617:             if (veBalanceOf != currentPoints) {
618:                 _incrementPoints(msg.sender, veBalanceOf - currentPoints);
619:             }
620: 
621:             
622:             
623:             return;
624:         }
625: 
626:         
627: 
628:         
629:         userLocks[msg.sender].push(
630:             Lock({
631:                 amount: uint216(lockedAmount), // <= FOUND
632:                 unlockTime: freshLockTimestamp()
633:             })
634:         );
635: 
636:         
637: 
638:         
639:         
640:         if (veBalanceOf != currentPoints) {
641:             
642:             
643:             if (veBalanceOf > currentPoints) {
644:                 _incrementPoints(msg.sender, veBalanceOf - currentPoints);
645:             } else {
646:                 
647:                 
648:                 
649:                 _reducePoints(msg.sender, currentPoints - veBalanceOf);
650:             }
651:         }
652: 
653:         
654:         _incrementTokenUnlocks(msg.sender, freshLockEpoch(), lockedAmount);
655:     }

['1160']

1145:     function _lock(
1146:         address recipient,
1147:         uint256 amount,
1148:         bool continuousLock
1149:     ) internal {
1150:         if (userLocks[recipient].length == 0) {
1151:             cveLocker.updateUserClaimIndex(
1152:                 recipient,
1153:                 currentEpoch(block.timestamp)
1154:             );
1155:         }
1156: 
1157:         if (continuousLock) {
1158:             userLocks[recipient].push(
1159:                 Lock({
1160:                     amount: uint216(amount), // <= FOUND
1161:                     unlockTime: CONTINUOUS_LOCK_VALUE
1162:                 })
1163:             );
1164:             
1165:             _incrementPoints(recipient, _getCLPoints(amount));
1166:         } else {
1167:             userLocks[recipient].push(
1168:                 Lock({
1169:                     amount: uint216(amount), // <= FOUND
1170:                     unlockTime: freshLockTimestamp()
1171:                 })
1172:             );
1173:             
1174:             _incrementPoints(recipient, amount);
1175:             _incrementTokenUnlocks(recipient, freshLockEpoch(), amount);
1176:         }
1177: 
1178:         _mint(recipient, amount);
1179:     }

['1211']

1187:     function _increaseAmountAndExtendLockFor(
1188:         address recipient,
1189:         uint256 amount,
1190:         uint256 lockIndex,
1191:         bool continuousLock
1192:     ) internal {
1193:         Lock[] storage user = userLocks[recipient];
1194: 
1195:         
1196:         if (lockIndex >= user.length) {
1197:             _revert(_INVALID_LOCK_SELECTOR);
1198:         }
1199: 
1200:         uint40 unlockTimestamp = user[lockIndex].unlockTime;
1201: 
1202:         if (unlockTimestamp == CONTINUOUS_LOCK_VALUE) {
1203:             if (!continuousLock) {
1204:                 _revert(_INVALID_LOCK_SELECTOR);
1205:             }
1206: 
1207:             
1208:             _incrementPoints(recipient, _getCLPoints(amount));
1209: 
1210:             
1211:             user[lockIndex].amount = uint216(user[lockIndex].amount + amount); // <= FOUND
1212:         } else {
1213:             
1214:             
1215:             if (unlockTimestamp < block.timestamp) {
1216:                 _revert(_INVALID_LOCK_SELECTOR);
1217:             }
1218: 
1219:             uint256 previousAmount = user[lockIndex].amount;
1220:             uint256 newAmount = previousAmount + amount;
1221:             uint256 priorEpoch = currentEpoch(user[lockIndex].unlockTime);
1222: 
1223:             if (continuousLock) {
1224:                 user[lockIndex].unlockTime = CONTINUOUS_LOCK_VALUE;
1225: 
1226:                 
1227:                 
1228:                 _updateDataToContinuousOn(
1229:                     recipient,
1230:                     priorEpoch,
1231:                     _getCLPoints(newAmount) - previousAmount,
1232:                     previousAmount
1233:                 );
1234:             } else {
1235:                 user[lockIndex].unlockTime = freshLockTimestamp();
1236: 
1237:                 
1238:                 
1239:                 _updateUnlockDataToExtendedLock(
1240:                     recipient,
1241:                     priorEpoch,
1242:                     freshLockEpoch(),
1243:                     previousAmount,
1244:                     newAmount
1245:                 );
1246: 
1247:                 
1248:                 _incrementPoints(recipient, amount);
1249:             }
1250: 
1251:             user[lockIndex].amount = uint216(newAmount); // <= FOUND
1252:         }
1253: 
1254:         _mint(recipient, amount);
1255: 
1256:         emit Locked(recipient, amount);
1257:     }

['240']

228:     function _parseData(
229:         AdaptorData memory data,
230:         bool inUSD
231:     ) internal view returns (PriceReturnData memory pData) {
232:         (int256 price, uint256 updatedAt) = data.proxyFeed.read();
233: 
234:         
235:         if (price <= 0) {
236:             pData.hadError = true;
237:             return pData;
238:         }
239: 
240:         pData.price = uint240(uint256(price)); // <= FOUND
241:         pData.hadError = _verifyData(
242:                         uint256(price),
243:                         updatedAt,
244:                         data.max,
245:                         data.heartbeat
246:                     );
247:         pData.inUSD = inUSD;
248:     }

['140']

72:     function getPrice(
73:         address asset,
74:         bool inUSD,
75:         bool getLower
76:     ) external view override returns (PriceReturnData memory pData) {
77:         
78:         if (!isSupportedAsset[asset]) {
79:             revert BalancerStablePoolAdaptor__AssetIsNotSupported();
80:         }
81: 
82:         
83:         _ensureNotInVaultContext(balancerVault);
84: 
85:         
86:         AdaptorData memory data = adaptorData[asset];
87:         IBalancerPool pool = IBalancerPool(asset);
88: 
89:         pData.inUSD = inUSD;
90:         IOracleRouter oracleRouter = IOracleRouter(centralRegistry.oracleRouter());
91: 
92:         
93:         uint256 numUnderlyingOrConstituent = data
94:             .underlyingOrConstituent
95:             .length;
96:         uint256 averagePrice;
97:         uint256 numPrices;
98: 
99:         uint256 price;
100:         uint256 errorCode;
101:         for (uint256 i; i < numUnderlyingOrConstituent; ++i) {
102:             
103:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
104:                 break;
105:             }
106: 
107:             (price, errorCode) = oracleRouter.getPrice(
108:                 data.underlyingOrConstituent[i],
109:                 inUSD,
110:                 getLower
111:             );
112: 
113:             
114:             if (errorCode > 0) {
115:                 pData.hadError = true;
116:                 return pData;
117:             } 
118:             
119:             
120:             
121:             averagePrice += price;
122:             ++numPrices;
123:             
124:         }
125: 
126:         
127:         if (averagePrice == 0) {
128:             pData.hadError = true;
129:             return pData;
130:         } 
131: 
132:         averagePrice = ((averagePrice / numPrices) * pool.getRate()) / WAD;
133:         
134:         
135:         if (_checkOracleOverflow(averagePrice)) {
136:             pData.hadError = true;
137:             return pData;
138:         }
139: 
140:         pData.price = uint240(averagePrice); // <= FOUND
141:     }

['236']

212:     function _parseData(
213:         AdaptorData memory data,
214:         bool inUSD
215:     ) internal view returns (PriceReturnData memory pData) {
216:         uint256 price = _extractPrice(data.symbolHash);
217: 
218:         
219:         uint256 quoteDecimals = data.decimals;
220:         if (quoteDecimals != 18) {
221:             
222:             
223:             if (quoteDecimals < 18) {
224:                 price = price * (10 ** (18 - quoteDecimals));
225:             } else {
226:                 
227:                 
228:                 price = price / (10 ** (quoteDecimals - 18));
229:             }
230:         }
231: 
232:         pData.hadError = _verifyData(price, data.max);
233: 
234:         if (!pData.hadError) {
235:             pData.inUSD = inUSD;
236:             pData.price = uint240(price); // <= FOUND
237:         }
238:     }

['164']

91:     function _getPrice(
92:         address asset,
93:         bool inUSD,
94:         bool getLower
95:     ) internal view returns (PriceReturnData memory pData) {
96:         
97:         if (!isSupportedAsset[asset]) {
98:             revert BaseStableLPAdaptor__AssetIsNotSupported();
99:         }
100: 
101:         
102:         AdaptorData memory data = adaptorData[asset];
103:         IUniswapV2Pair pool = IUniswapV2Pair(asset);
104: 
105:         
106:         uint256 totalSupply = pool.totalSupply();
107:         
108:         (uint256 reserve0, uint256 reserve1, ) = pool.getReserves();
109:         
110:         if (data.decimals0 != 18) {
111:             reserve0 = (reserve0 * 1e18) / (10 ** data.decimals0);
112:         }
113: 
114:         if (data.decimals1 != 18) {
115:             reserve1 = (reserve1 * 1e18) / (10 ** data.decimals1);
116:         }
117: 
118:         uint256 price0;
119:         uint256 price1;
120:         uint256 errorCode;
121: 
122:         IOracleRouter oracleRouter = IOracleRouter(
123:             centralRegistry.oracleRouter()
124:         );
125:         (price0, errorCode) = oracleRouter.getPrice(
126:             data.token0,
127:             inUSD,
128:             getLower
129:         );
130: 
131:         
132:         if (errorCode > 0) {
133:             pData.hadError = true;
134:             return pData;
135:         }
136: 
137:         (price1, errorCode) = oracleRouter.getPrice(
138:             data.token1,
139:             inUSD,
140:             getLower
141:         );
142: 
143:         
144:         if (errorCode > 0) {
145:             pData.hadError = true;
146:             return pData;
147:         }
148: 
149:         uint256 finalPrice = _getFairPrice(
150:             reserve0,
151:             reserve1,
152:             price0,
153:             price1,
154:             totalSupply
155:         );
156: 
157:         
158:         if (_checkOracleOverflow(finalPrice)) {
159:             pData.hadError = true;
160:             return pData;
161:         }
162: 
163:         pData.inUSD = inUSD;
164:         pData.price = uint240(finalPrice); // <= FOUND
165:     }

['159']

91:     function _getPrice(
92:         address asset,
93:         bool inUSD,
94:         bool getLower
95:     ) internal view returns (PriceReturnData memory pData) {
96:         
97:         if (!isSupportedAsset[asset]) {
98:             revert BaseVolatileLPAdaptor__AssetIsNotSupported();
99:         }
100: 
101:         
102:         AdaptorData memory data = adaptorData[asset];
103:         IUniswapV2Pair pool = IUniswapV2Pair(asset);
104: 
105:         
106:         uint256 totalSupply = pool.totalSupply();
107:         
108:         (uint256 reserve0, uint256 reserve1, ) = pool.getReserves();
109:         
110:         reserve0 = (reserve0 * 1e18) / (10 ** data.decimals0);
111:         reserve1 = (reserve1 * 1e18) / (10 ** data.decimals1);
112: 
113:         
114:         uint256 sqrtReserve = FixedPointMathLib.sqrt(reserve0 * reserve1);
115: 
116:         uint256 price0;
117:         uint256 price1;
118:         uint256 errorCode;
119: 
120:         IOracleRouter oracleRouter = IOracleRouter(
121:             centralRegistry.oracleRouter()
122:         );
123:         (price0, errorCode) = oracleRouter.getPrice(
124:             data.token0,
125:             inUSD,
126:             getLower
127:         );
128: 
129:         
130:         if (errorCode > 0) {
131:             pData.hadError = true;
132:             return pData;
133:         }
134: 
135:         (price1, errorCode) = oracleRouter.getPrice(
136:             data.token1,
137:             inUSD,
138:             getLower
139:         );
140: 
141:         
142:         if (errorCode > 0) {
143:             pData.hadError = true;
144:             return pData;
145:         }
146: 
147:         
148:         uint256 finalPrice = (2 *
149:             sqrtReserve *
150:             FixedPointMathLib.sqrt(price0 * price1)) / totalSupply;
151: 
152:         
153:         if (_checkOracleOverflow(finalPrice)) {
154:             pData.hadError = true;
155:             return pData;
156:         }
157: 
158:         pData.inUSD = inUSD;
159:         pData.price = uint240(finalPrice); // <= FOUND
160:     }

['270']

249:     function _parseData(
250:         AdaptorData memory data,
251:         bool inUSD
252:     ) internal view returns (PriceReturnData memory pData) {
253:         pData.inUSD = inUSD;
254:         if (!IOracleRouter(centralRegistry.oracleRouter()).isSequencerValid()) {
255:             pData.hadError = true;
256:             return pData;
257:         }
258: 
259:         (, int256 price, , uint256 updatedAt, ) = IChainlink(data.aggregator)
260:             .latestRoundData();
261: 
262:         
263:         if (price <= 0) {
264:             pData.hadError = true;
265:             return pData;
266:         }
267: 
268:         uint256 newPrice = (uint256(price) * WAD) / (10 ** data.decimals);
269: 
270:         pData.price = uint240(newPrice); // <= FOUND
271:         pData.hadError = _verifyData(
272:                         uint256(price),
273:                         updatedAt,
274:                         data.max,
275:                         data.min,
276:                         data.heartbeat
277:                     );
278:     }

['188']

106:     function getPrice(
107:         address asset,
108:         bool inUSD,
109:         bool getLower
110:     ) external view override returns (PriceReturnData memory pData) {
111:         AdaptorData memory data = adaptorData[asset];
112:         
113:         
114:         
115:         if (isLocked(data.pool, 2)) {
116:             revert Curve2PoolLPAdaptor__Reentrant();
117:         }
118: 
119:         
120:         ICurvePool pool = ICurvePool(data.pool);
121: 
122:         
123:         uint256 virtualPrice = pool.get_virtual_price();
124:         _enforceBounds(virtualPrice, data.lowerBound, data.upperBound);
125: 
126:         
127:         IOracleRouter oracleRouter = IOracleRouter(
128:             centralRegistry.oracleRouter()
129:         );
130:         uint256 price0;
131:         uint256 price1;
132:         uint256 errorCode;
133:         (price0, errorCode) = oracleRouter.getPrice(
134:             data.underlying0,
135:             inUSD,
136:             getLower
137:         );
138:         if (errorCode > 0) {
139:             pData.hadError = true;
140:             return pData;
141:         }
142:         (price1, errorCode) = oracleRouter.getPrice(
143:             data.underlying1,
144:             inUSD,
145:             getLower
146:         );
147:         if (errorCode > 0) {
148:             pData.hadError = true;
149:             return pData;
150:         }
151: 
152:         
153:         uint256 price;
154:         if (data.isCorrelated) {
155:             
156:             if (data.divideRate0 || data.divideRate1) {
157:                 uint256[2] memory rates = pool.stored_rates();
158:                 if (data.divideRate0) {
159:                     price0 = (price0 * WAD) / rates[0];
160:                 }
161:                 if (data.divideRate1) {
162:                     price1 = (price1 * WAD) / rates[1];
163:                 }
164:             }
165: 
166:             if (getLower) {
167:                 
168:                 uint256 minPrice = price0 < price1 ? price0 : price1;
169:                 price = (minPrice * virtualPrice) / WAD;
170:             } else {
171:                 
172:                 uint256 maxPrice = price0 < price1 ? price1 : price0;
173:                 price = (maxPrice * virtualPrice) / WAD;
174:             }
175:         } else {
176:             price =
177:                 (2 * virtualPrice * FixedPointMathLib.sqrt(price0)) /
178:                 FixedPointMathLib.sqrt(price1);
179:             price = (price * price0) / WAD;
180:         }
181: 
182:         if (_checkOracleOverflow(price)) {
183:             pData.hadError = true;
184:             return pData;
185:         }
186: 
187:         pData.inUSD = inUSD;
188:         pData.price = uint240(price); // <= FOUND
189:     }

['121']

86:     function getPrice(
87:         address asset,
88:         bool inUSD,
89:         bool getLower
90:     ) external view override returns (PriceReturnData memory pData) {
91:         
92:         if (!isSupportedAsset[asset]) {
93:             revert PendleLPTokenAdaptor__AssetIsNotSupported();
94:         }
95: 
96:         AdaptorData memory data = adaptorData[asset];
97:         
98:         uint256 lpRate = IPMarket(asset).getLpToAssetRate(data.twapDuration);
99: 
100:         (uint256 price, uint256 errorCode) = IOracleRouter(
101:             centralRegistry.oracleRouter()
102:         ).getPrice(data.quoteAsset, inUSD, getLower);
103: 
104:         
105:         if (errorCode > 0) {
106:             pData.hadError = true;
107:             return pData;
108:         }
109: 
110:         
111:         
112:         price = (price * lpRate) / WAD;
113: 
114:         
115:         if (_checkOracleOverflow(price)) {
116:             pData.hadError = true;
117:             return pData;
118:         }
119: 
120:         pData.inUSD = inUSD;
121:         pData.price = uint240(price); // <= FOUND
122:     }

['122']

87:     function getPrice(
88:         address asset,
89:         bool inUSD,
90:         bool getLower
91:     ) external view override returns (PriceReturnData memory pData) {
92:         
93:         if (!isSupportedAsset[asset]) {
94:             revert PendlePrincipalTokenAdaptor__AssetIsNotSupported();
95:         }
96: 
97:         AdaptorData memory data = adaptorData[asset];
98:         
99:         uint256 ptRate = data.market.getPtToAssetRate(data.twapDuration);
100: 
101:         (uint256 price, uint256 errorCode) = IOracleRouter(
102:             centralRegistry.oracleRouter()
103:         ).getPrice(data.quoteAsset, inUSD, getLower);
104: 
105:         
106:         if (errorCode > 0) {
107:             pData.hadError = true;
108:             return pData;
109:         }
110: 
111:         
112:         
113:         price = (price * ptRate) / WAD;
114: 
115:         
116:         if (_checkOracleOverflow(price)) {
117:             pData.hadError = true;
118:             return pData;
119:         }
120: 
121:         pData.inUSD = inUSD;
122:         pData.price = uint240(price); // <= FOUND
123:     }

['159']

97:     function getPrice(
98:         address asset,
99:         bool ,
100:         bool 
101:     ) external view override returns (PriceReturnData memory pData) {
102:         
103:         if (!isSupportedAsset[asset]) {
104:             revert GMAdaptor__AssetIsNotSupported();
105:         }
106: 
107:         
108:         IOracleRouter oracleRouter = IOracleRouter(
109:             centralRegistry.oracleRouter()
110:         );
111: 
112:         uint256[] memory prices = new uint256[](3);
113:         address[] memory tokens = marketData[asset];
114:         uint256 errorCode;
115:         address token;
116: 
117:         
118:         
119:         for (uint256 i; i < 3; ++i) {
120:             token = tokens[i];
121: 
122:             (prices[i], errorCode) = oracleRouter.getPrice(token, true, false);
123:             if (errorCode > 0) {
124:                 pData.hadError = true;
125:                 return pData;
126:             }
127: 
128:             prices[i] = (prices[i] * 1e30) / _priceUnit[token];
129:         }
130: 
131:         
132:         (int256 price, ) = gmxReader.getMarketTokenPrice(
133:             gmxDataStore,
134:             IReader.MarketProps(asset, tokens[3], tokens[1], tokens[2]),
135:             IReader.PriceProps(prices[0], prices[0]),
136:             IReader.PriceProps(prices[1], prices[1]),
137:             IReader.PriceProps(prices[2], prices[2]),
138:             PNL_FACTOR_TYPE,
139:             true
140:         );
141: 
142:         
143:         
144:         if (price <= 0) {
145:             pData.hadError = true;
146:             return pData;
147:         }
148: 
149:         
150:         uint256 newPrice = uint256(price) / 1e12;
151: 
152:         
153:         if (_checkOracleOverflow(newPrice)) {
154:             pData.hadError = true;
155:             return pData;
156:         }
157: 
158:         pData.inUSD = true;
159:         pData.price = uint240(newPrice); // <= FOUND
160:     }

['373']

350:     function getPricesForAsset(
351:         address asset,
352:         bool inUSD
353:     ) external view returns (FeedData[] memory) {
354:         bool isMToken = mTokenAssets[asset].isMToken;
355:         if (isMToken) {
356:             asset = mTokenAssets[asset].underlying;
357:         }
358: 
359:         uint256 numFeeds = assetPriceFeeds[asset].length;
360:         if (numFeeds == 0) {
361:             _revert(_NOT_SUPPORTED_SELECTOR);
362:         }
363: 
364:         FeedData[] memory data = new FeedData[](numFeeds * 2);
365: 
366:         
367:         
368:         if (numFeeds < 2) {
369:             data[0] = _getPriceFromFeed(asset, 0, inUSD, true);
370:             data[1] = _getPriceFromFeed(asset, 0, inUSD, false);
371:             if (isMToken) {
372:                 uint256 exchangeRate = IMToken(asset).exchangeRateCached();
373:                 data[0].price = uint240((data[0].price * exchangeRate) / WAD); // <= FOUND
374:                 data[1].price = uint240((data[1].price * exchangeRate) / WAD); // <= FOUND
375:             }
376: 
377:             return data;
378:         }
379: 
380:         
381:         
382:         data[0] = _getPriceFromFeed(asset, 0, inUSD, true);
383:         data[1] = _getPriceFromFeed(asset, 0, inUSD, false);
384:         data[2] = _getPriceFromFeed(asset, 1, inUSD, true);
385:         data[3] = _getPriceFromFeed(asset, 1, inUSD, false);
386: 
387:         if (isMToken) {
388:             uint256 exchangeRate = IMToken(asset).exchangeRateCached();
389:             data[0].price = uint240((data[0].price * exchangeRate) / WAD); // <= FOUND
390:             data[1].price = uint240((data[1].price * exchangeRate) / WAD); // <= FOUND
391:             data[2].price = uint240((data[2].price * exchangeRate) / WAD); // <= FOUND
392:             data[3].price = uint240((data[3].price * exchangeRate) / WAD); // <= FOUND
393:         }
394: 
395:         return data;
396:     }

['722']

694:     function _getPriceSingleFeed(
695:         address asset,
696:         bool inUSD,
697:         bool getLower
698:     ) internal view returns (uint256, uint256) {
699:         address adapter = assetPriceFeeds[asset][0];
700:         if (!isApprovedAdaptor[adapter]) {
701:             revert OracleRouter__AdaptorIsNotApproved();
702:         }
703: 
704:         PriceReturnData memory data = IOracleAdaptor(adapter).getPrice(
705:             asset,
706:             inUSD,
707:             getLower
708:         );
709:         
710:         if (data.hadError) {
711:             return (0, BAD_SOURCE);
712:         }
713: 
714:         
715:         if (data.inUSD != inUSD) {
716:             uint256 newPrice;
717:             (newPrice, data.hadError) = _getETHUSD(getLower);
718:             if (data.hadError) {
719:                 return (0, BAD_SOURCE);
720:             }
721: 
722:             data.price = uint240( // <= FOUND
723:                 _convertETHUSD(data.price, newPrice, data.inUSD)
724:             );
725:         }
726: 
727:         return (data.price, NO_ERROR);
728:     }

['773']

744:     function _getPriceFromFeed(
745:         address asset,
746:         uint256 feedNumber,
747:         bool inUSD,
748:         bool getLower
749:     ) internal view returns (FeedData memory) {
750:         address adapter = assetPriceFeeds[asset][feedNumber];
751:         if (!isApprovedAdaptor[adapter]) {
752:             revert OracleRouter__AdaptorIsNotApproved();
753:         }
754: 
755:         PriceReturnData memory data = IOracleAdaptor(adapter).getPrice(
756:             asset,
757:             inUSD,
758:             getLower
759:         );
760:         
761:         if (data.hadError) {
762:             return FeedData({ price: 0, hadError: true });
763:         }
764: 
765:         
766:         if (data.inUSD != inUSD) {
767:             uint256 newPrice;
768:             (newPrice, data.hadError) = _getETHUSD(getLower);
769:             if (data.hadError) {
770:                 return FeedData({ price: 0, hadError: true });
771:             }
772: 
773:             data.price = uint240( // <= FOUND
774:                 _convertETHUSD(data.price, newPrice, data.inUSD)
775:             );
776:         }
777: 
778:         return FeedData({ price: data.price, hadError: data.hadError });
779:     }

['163']

85:     function getPrice(
86:         address asset,
87:         bool inUSD,
88:         bool getLower
89:     ) external view override returns (PriceReturnData memory pData) {
90:         
91:         if (!isSupportedAsset[asset]) {
92:             revert UniswapV3Adaptor__AssetIsNotSupported();
93:         }
94: 
95:         AdaptorData memory data = adaptorData[asset];
96: 
97:         address[] memory pools = new address[](1);
98:         pools[0] = data.priceSource;
99:         uint256 twapPrice;
100: 
101:         
102:         (bool success, bytes memory returnData) = address(uniswapOracleRouter)
103:             .staticcall(
104:                 abi.encodePacked(
105:                     uniswapOracleRouter
106:                         .quoteSpecificPoolsWithTimePeriod
107:                         .selector,
108:                     abi.encode(
109:                         10 ** data.baseDecimals,
110:                         asset,
111:                         data.quoteToken,
112:                         pools,
113:                         data.secondsAgo
114:                     )
115:                 )
116:             );
117: 
118:         if (success) {
119:             
120:             twapPrice = abi.decode(returnData, (uint256));
121:         } else {
122:             
123:             pData.hadError = true;
124:             return pData;
125:         }
126: 
127:         IOracleRouter OracleRouter = IOracleRouter(
128:             centralRegistry.oracleRouter()
129:         );
130:         pData.inUSD = inUSD;
131: 
132:         
133:         
134:         
135:         if (inUSD) {
136:             if (!OracleRouter.isSupportedAsset(data.quoteToken)) {
137:                 
138:                 
139:                 pData.hadError = true;
140:                 return pData;
141:             }
142: 
143:             (uint256 quoteTokenDenominator, uint256 errorCode) = OracleRouter
144:                 .getPrice(data.quoteToken, true, getLower);
145: 
146:             
147:             if (errorCode > 0) {
148:                 pData.hadError = true;
149:                 return pData;
150:             }
151: 
152:             
153:             
154:             uint256 newPrice = (twapPrice * quoteTokenDenominator) /
155:                 (10 ** data.quoteDecimals);
156: 
157:             
158:             if (_checkOracleOverflow(newPrice)) {
159:                 pData.hadError = true;
160:                 return pData;
161:             }
162: 
163:             pData.price = uint240(newPrice); // <= FOUND
164:             return pData;
165:         }
166: 
167:         if (data.quoteToken != WETH) {
168:             if (!OracleRouter.isSupportedAsset(data.quoteToken)) {
169:                 
170:                 
171:                 pData.hadError = true;
172:                 return pData;
173:             }
174: 
175:             (uint256 quoteTokenDenominator, uint256 errorCode) = OracleRouter
176:                 .getPrice(data.quoteToken, false, getLower);
177: 
178:             
179:             if (errorCode > 0) {
180:                 pData.hadError = true;
181:                 return pData;
182:             }
183: 
184:             
185:             uint256 newPrice = (twapPrice * quoteTokenDenominator) /
186:                 (10 ** data.quoteDecimals);
187: 
188:             
189:             if (_checkOracleOverflow(newPrice)) {
190:                 pData.hadError = true;
191:                 return pData;
192:             }
193: 
194:             
195:             
196:             pData.price = uint240(newPrice); // <= FOUND
197:             return pData;
198:         }
199: 
200:         
201:         if (_checkOracleOverflow(twapPrice)) {
202:             pData.hadError = true;
203:             return pData;
204:         }
205: 
206:         pData.price = uint240(twapPrice); // <= FOUND
207:     }

[Low-19] The nonReentrant modifier should be first in a function declaration

Resolution

In Solidity, the nonReentrant modifier is essential for securing smart contracts against reentrancy attacks. It should be positioned first in a function declaration. This placement is critical because it ensures that the modifier's protection is applied before any other code or modifiers in the function. If placed later, other modifiers could potentially be executed in a reentrant manner before nonReentrant has a chance to lock the function, leaving the contract vulnerable. Thus, prioritizing nonReentrant as the first modifier is a best practice for robust smart contract security, effectively guarding against unintended reentries and related exploits.

Num of instances: 4

Findings

Click to show findings

['174']

174:     function leverage(
175:         LeverageStruct calldata leverageData,
176:         uint256 slippage
177:     ) external checkSlippage(msg.sender, slippage) nonReentrant { // <= FOUND
178:         _leverage(leverageData, msg.sender);
179:     }

['194']

194:     function leverageFor(
195:         LeverageStruct calldata leverageData,
196:         address account,
197:         uint256 slippage
198:     ) external checkSlippage(account, slippage) nonReentrant { // <= FOUND
199:         if (!_checkIsDelegate(account, msg.sender)) {
200:             _revert(_UNAUTHORIZED_SELECTOR);
201:         }
202: 
203:         _leverage(leverageData, account);
204:     }

['217']

217:     function deleverage(
218:         DeleverageStruct calldata deleverageData,
219:         uint256 slippage
220:     ) external checkSlippage(msg.sender, slippage) nonReentrant { // <= FOUND
221:         _deleverage(deleverageData, msg.sender);
222:     }

['234']

234:     function deleverageFor(
235:         DeleverageStruct calldata deleverageData,
236:         address account,
237:         uint256 slippage
238:     ) external checkSlippage(account, slippage) nonReentrant { // <= FOUND
239:         if (!_checkIsDelegate(account, msg.sender)) {
240:             _revert(_UNAUTHORIZED_SELECTOR);
241:         }
242: 
243:         _deleverage(deleverageData, account);
244:     }

[Low-20] Function calls within for loops

Resolution

Making function calls or external calls within loops in Solidity can lead to inefficient gas usage, potential bottlenecks, and increased vulnerability to attacks. Each function call or external call consumes gas, and when executed within a loop, the gas cost multiplies, potentially causing the transaction to run out of gas or exceed block gas limits. This can result in transaction failure or unpredictable behavior.

Num of instances: 19

Findings

Click to show findings

['147']

147:        for (uint256 i; i < extraRewardsLength; ) { // <= FOUND
148:             unchecked {
149:                 address rewardToken = IStashWrapper( // <= FOUND
150:                     IRewards(rewarder.extraRewards(i++)).rewardToken() // <= FOUND
151:                 ).baseToken(); // <= FOUND
152: 
153:                 if (rewardToken != _AURA && rewardToken != _BAL) { // <= FOUND
154:                     strategyData.rewardTokens.push() = rewardToken; // <= FOUND
155:                 }
156:             }
157:         }

['40']

40:        for (uint256 i; i < numTokens; ++i) { // <= FOUND
41:             balances[i] = CommonLib.getTokenBalance(tokens[i]); // <= FOUND
42:             SwapperLib._approveTokenIfNeeded( // <= FOUND
43:                 tokens[i],
44:                 balancerVault,
45:                 balances[i]
46:             );
47: 
48:             if (CommonLib.isETH(tokens[i])) { // <= FOUND
49:                 
50:                 
51:                 if (containsEth) { // <= FOUND
52:                     revert BalancerLib__InvalidPoolInvariantError(); // <= FOUND
53:                 }
54: 
55:                 value = balances[i];
56:                 containsEth = true;
57:             }
58:         }

['162']

162:        for (uint256 i; i < numUnderlyingOrConstituent; ++i) { // <= FOUND
163:             
164:             if (address(data.underlyingOrConstituent[i]) == address(0)) { // <= FOUND
165:                 continue;
166:             }
167: 
168:             if ( // <= FOUND
169:                 !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
170:                     data.underlyingOrConstituent[i]
171:                 )
172:             ) {
173:                 revert BalancerStablePoolAdaptor__ConfigurationError(); // <= FOUND
174:             }
175: 
176:             if (data.rateProviders[i] != address(0)) { // <= FOUND
177:                 
178:                 if (data.rateProviderDecimals[i] == 0) { // <= FOUND
179:                     revert BalancerStablePoolAdaptor__ConfigurationError(); // <= FOUND
180:                 }
181: 
182:                 
183:                 if (IRateProvider(data.rateProviders[i]).getRate() == 0) { // <= FOUND
184:                     revert BalancerStablePoolAdaptor__ConfigurationError(); // <= FOUND
185:                 }
186:             }
187:         }

['404']

404:        for (uint256 i; i < nonMTokensLength; ++i) { // <= FOUND
405:             yieldClaimed += CHAIN_YIELD_MANAGER.claimMaxGas( // <= FOUND
406:                 nonMTokens[i],
407:                 address(this) // <= FOUND
408:             );
409:         }

['470']

470:        for (uint256 i; i < epochs; ) { // <= FOUND
471:             unchecked {
472:                 rewards += _calculateRewardsForEpoch(user, startEpoch + i++); // <= FOUND
473:             }
474:         }

['647']

647:        for (uint256 i; i < numTokenSwaps; ) { // <= FOUND
648:             
649:             if (!centralRegistry.isSwapper(tokenSwaps[i].target)) { // <= FOUND
650:                 revert ComplexZapper__InvalidSwapper(i, tokenSwaps[i].target); // <= FOUND
651:             }
652: 
653:             
654:             unchecked {
655:                 SwapperLib.swap(centralRegistry, tokenSwaps[i++]); // <= FOUND
656:             }
657:         }

['297']

297:        for (uint256 i; i < 2; ++i) { // <= FOUND
298:             underlyingToken = strategyData.underlyingTokens[i];
299:             amounts[i] = CommonLib.getTokenBalance(underlyingToken); // <= FOUND
300: 
301:             if (CommonLib.isETH(underlyingToken)) { // <= FOUND
302:                 value = amounts[i];
303:             }
304: 
305:             SwapperLib._approveTokenIfNeeded( // <= FOUND
306:                 underlyingToken,
307:                 address(strategyData.curvePool), // <= FOUND
308:                 amounts[i]
309:             );
310: 
311:             if (amounts[i] > 0) { // <= FOUND
312:                 liquidityAvailable = true;
313:             }
314:         }

['297']

297:        for (uint256 i; i < 3; ++i) { // <= FOUND
298:             underlyingToken = strategyData.underlyingTokens[i];
299:             amounts[i] = CommonLib.getTokenBalance(underlyingToken); // <= FOUND
300: 
301:             if (CommonLib.isETH(underlyingToken)) { // <= FOUND
302:                 value = amounts[i];
303:             }
304: 
305:             SwapperLib._approveTokenIfNeeded( // <= FOUND
306:                 underlyingToken,
307:                 address(strategyData.curvePool), // <= FOUND
308:                 amounts[i]
309:             );
310: 
311:             if (amounts[i] > 0) { // <= FOUND
312:                 liquidityAvailable = true;
313:             }
314:         }

['458']

458:            for (uint256 i; i < numChainData; ) { // <= FOUND
459:                 lockData = crossChainLockData[i];
460:                 chainData = centralRegistry.supportedChainData( // <= FOUND
461:                     lockData.chainId
462:                 );
463:                 messagingChainId = centralRegistry.GETHToMessagingChainId( // <= FOUND
464:                     uint256(lockData.chainId) // <= FOUND
465:                 );
466: 
467:                 gas = messagingHub.quoteWormholeFee( // <= FOUND
468:                     uint256(lockData.chainId), // <= FOUND
469:                     false
470:                 );
471: 
472:                 messagingHub.sendWormholeMessages{ value: gas }( // <= FOUND
473:                     uint256(lockData.chainId), // <= FOUND
474:                     chainData.messagingHub,
475:                     abi.encode(epochRewardsPerCVE) // <= FOUND
476:                 );
477: 
478:                 unchecked {
479:                     ++i;
480:                 }
481:             }

['648']

648:        for (uint256 i; i < numTokens; ) { // <= FOUND
649:             tokenBalances[i] = IERC20(currentTokens[i]).balanceOf( // <= FOUND
650:                 address(this) // <= FOUND
651:             );
652: 
653:             unchecked {
654:                 ++i;
655:             }
656:         }

['128']

128:        for (uint256 i; i < numTokens; ) { // <= FOUND
129:             unchecked {
130:                 
131:                 updatePool(tokens[i++]); // <= FOUND
132:             }
133:         }

['325']

325:        for (uint256 i; i < rewardTokensLength; ++i) { // <= FOUND
326:             results[i] = pendingRewards(token, user, rewardTokens[i]); // <= FOUND
327:         }

['692']

692:        for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
693:             
694:             address rewardToken = rewardTokens[i++];
695:             UserRewardInfo storage info = userDebtInfo[token][user][
696:                 rewardToken
697:             ];
698:             info.rewardPending +=
699:                 (balanceOf[token][user] * // <= FOUND
700:                     poolAccRewardPerShare[token][rewardToken]) /
701:                 (WAD_SQUARED) - // <= FOUND
702:                 info.rewardDebt;
703:         }

['712']

712:        for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
713:             
714:             address rewardToken = rewardTokens[i++];
715:             UserRewardInfo storage info = userDebtInfo[token][user][
716:                 rewardToken
717:             ];
718:             info.rewardDebt =
719:                 (balanceOf[token][user] * // <= FOUND
720:                     poolAccRewardPerShare[token][rewardToken]) /
721:                 (WAD_SQUARED); // <= FOUND
722:         }

['209']

209:        for (uint256 i; i < numAssets; ++i) { // <= FOUND
210:             snapshot = snapshots[i];
211: 
212:             if (snapshot.isCToken) { // <= FOUND
213:                 
214:                 
215:                 if (tokenData[snapshot.asset].collRatio != 0) { // <= FOUND
216:                     uint256 collateralValue = _assetValue( // <= FOUND
217:                         ((tokenData[snapshot.asset] // <= FOUND
218:                             .accountPositions[account]
219:                             .collateralPosted * snapshot.exchangeRate) / WAD),
220:                         underlyingPrices[i],
221:                         snapshot.decimals
222:                     );
223:                     accountCollateral += collateralValue;
224:                     maxDebt =
225:                         (collateralValue * // <= FOUND
226:                             tokenData[snapshot.asset].collRatio) /
227:                         WAD;
228:                 }
229:             } else {
230:                 
231:                 if (snapshot.debtBalance > 0) { // <= FOUND
232:                     accountDebt += _assetValue( // <= FOUND
233:                         snapshot.debtBalance,
234:                         underlyingPrices[i],
235:                         snapshot.decimals
236:                     );
237:                 }
238:             }
239:         }

['425']

425:        for (uint256 i; i < numAssets; ++i) { // <= FOUND
426:             snapshot = snapshots[i];
427: 
428:             if (snapshot.isCToken) { // <= FOUND
429:                 
430:                 
431:                 if (tokenData[snapshot.asset].collRatio != 0) { // <= FOUND
432:                     accountCollateral += _assetValue( // <= FOUND
433:                         ((tokenData[snapshot.asset] // <= FOUND
434:                             .accountPositions[account]
435:                             .collateralPosted * snapshot.exchangeRate) / WAD),
436:                         underlyingPrices[i],
437:                         snapshot.decimals
438:                     );
439:                 }
440:             } else {
441:                 
442:                 if (snapshot.debtBalance > 0) { // <= FOUND
443:                     accountDebt += _assetValue( // <= FOUND
444:                         snapshot.debtBalance,
445:                         underlyingPrices[i],
446:                         snapshot.decimals
447:                     );
448:                 }
449:             }
450:         }

['506']

506:        for (uint256 i; i < numAssets; ++i) { // <= FOUND
507:             snapshot = snapshots[i];
508: 
509:             if (snapshot.isCToken) { // <= FOUND
510:                 if (snapshot.asset == collateralToken) { // <= FOUND
511:                     result.collateralTokenPrice = underlyingPrices[i];
512:                 }
513: 
514:                 
515:                 if (tokenData[snapshot.asset].collRatio != 0) { // <= FOUND
516:                     ( // <= FOUND
517:                         accountCollateralSoft,
518:                         accountCollateralHard
519:                     ) = _addLiquidationValues( // <= FOUND
520:                         snapshot,
521:                         account,
522:                         underlyingPrices[i],
523:                         accountCollateralSoft,
524:                         accountCollateralHard
525:                     );
526:                 }
527:             } else {
528:                 if (snapshot.asset == debtToken) { // <= FOUND
529:                     result.debtTokenPrice = underlyingPrices[i];
530:                 }
531: 
532:                 
533:                 
534:                 if (snapshot.debtBalance > 0) { // <= FOUND
535:                     accountDebt += _assetValue( // <= FOUND
536:                         snapshot.debtBalance,
537:                         underlyingPrices[i],
538:                         snapshot.decimals
539:                     );
540:                 }
541:             }
542:         }

['1319']

1319:        for (uint256 i; i < numUserAssets; ++i) { // <= FOUND
1320:             if (userAssetList[i] == mToken) { // <= FOUND
1321:                 assetIndex = i;
1322:                 break;
1323:             }
1324:         }

['1372']

1372:        for (uint256 i = numAssets; i > 0; ) { // <= FOUND
1373:             
1374:             
1375:             if (positionsToClose[--i]) { // <= FOUND
1376:                 
1377:                 
1378:                 if (i != lastAssetIndex) { // <= FOUND
1379:                     
1380:                     
1381:                     storedAssets[i] = storedAssets[lastAssetIndex--];
1382:                     
1383:                     
1384:                     storedAssets.pop(); // <= FOUND
1385:                 } else {
1386:                     
1387:                     
1388:                     if (lastAssetIndex != 0) { // <= FOUND
1389:                         --lastAssetIndex;
1390:                     }
1391:                     
1392:                     storedAssets.pop(); // <= FOUND
1393:                 }
1394: 
1395:                 cachedToken = address(userAssets[i]); // <= FOUND
1396: 
1397:                 
1398:                 tokenData[cachedToken].accountPositions[account].activePosition = 1;
1399:                 emit TokenPositionClosed(cachedToken, account); // <= FOUND
1400:             }
1401:         }

[Low-21] For loops in public or external functions should be avoided due to high gas costs and possible DOS

Resolution

In Solidity, for loops can potentially cause Denial of Service (DoS) attacks if not handled carefully. DoS attacks can occur when an attacker intentionally exploits the gas cost of a function, causing it to run out of gas or making it too expensive for other users to call. Below are some scenarios where for loops can lead to DoS attacks: Nested for loops can become exceptionally gas expensive and should be used sparingly

Num of instances: 45

Findings

Click to show findings

['147']

134:     function reQueryRewardTokens() external {
135:         delete strategyData.rewardTokens;
136: 
137:         
138:         
139:         strategyData.rewardTokens.push() = _BAL;
140:         
141:         
142:         strategyData.rewardTokens.push() = _AURA;
143: 
144:         IBaseRewardPool rewarder = strategyData.rewarder;
145:         uint256 extraRewardsLength = rewarder.extraRewardsLength();
146: 
147:         for (uint256 i; i < extraRewardsLength; ) { // <= FOUND
148:             unchecked {
149:                 address rewardToken = IStashWrapper(
150:                     IRewards(rewarder.extraRewards(i++)).rewardToken()
151:                 ).baseToken();
152: 
153:                 if (rewardToken != _AURA && rewardToken != _BAL) {
154:                     strategyData.rewardTokens.push() = rewardToken;
155:                 }
156:             }
157:         }
158:     }

['169']

163:     function reQueryUnderlyingTokens() external {
164:         address[] memory currentTokens = strategyData.underlyingTokens;
165:         uint256 numCurrentTokens = currentTokens.length;
166: 
167:         
168:         
169:         for (uint256 i; i < numCurrentTokens; ) { // <= FOUND
170:             unchecked {
171:                 isUnderlyingToken[currentTokens[i++]] = false;
172:             }
173:         }
174: 
175:         
176:         (currentTokens, , ) = strategyData.balancerVault.getPoolTokens(
177:             strategyData.balancerPoolId
178:         );
179:         strategyData.underlyingTokens = currentTokens;
180:         numCurrentTokens = currentTokens.length;
181: 
182:         
183:         
184:         for (uint256 i = 0; i < numCurrentTokens; ) { // <= FOUND
185:             unchecked {
186:                 isUnderlyingToken[strategyData.underlyingTokens[i++]] = true;
187:             }
188:         }
189:     }

['245']

212:     function harvest(
213:         bytes calldata data
214:     ) external override returns (uint256 yield) {
215:         
216:         _canCompound();
217: 
218:         
219:         _vestIfNeeded();
220: 
221:         
222:         if (_checkVestStatus(_vaultData)) {
223:             _updateVestingPeriodIfNeeded();
224: 
225:             
226:             StrategyData memory sd = strategyData;
227: 
228:             
229:             sd.rewarder.getReward(address(this), true);
230: 
231:             (SwapperLib.Swap[] memory swapDataArray, uint256 minLPAmount) = abi
232:                 .decode(data, (SwapperLib.Swap[], uint256));
233: 
234:             {
235:                 
236:                 uint256 numRewardTokens = sd.rewardTokens.length;
237:                 address rewardToken;
238:                 uint256 rewardAmount;
239:                 uint256 protocolFee;
240:                 
241:                 
242:                 address feeAccumulator = centralRegistry.feeAccumulator();
243:                 uint256 harvestFee = centralRegistry.protocolHarvestFee();
244: 
245:                 for (uint256 i; i < numRewardTokens; ++i) { // <= FOUND
246:                     rewardToken = sd.rewardTokens[i];
247:                     rewardAmount = IERC20(rewardToken).balanceOf(
248:                         address(this)
249:                     );
250: 
251:                     
252:                     
253:                     if (rewardAmount == 0) {
254:                         continue;
255:                     }
256: 
257:                     
258:                     
259:                     protocolFee = FixedPointMathLib.mulDiv(
260:                         rewardAmount, 
261:                         harvestFee, 
262:                         1e18
263:                     );
264:                     rewardAmount -= protocolFee;
265:                     SafeTransferLib.safeTransfer(
266:                         rewardToken,
267:                         feeAccumulator,
268:                         protocolFee
269:                     );
270: 
271:                     
272:                     if (!isUnderlyingToken[rewardToken]) {
273:                         if (
274:                             !centralRegistry.isSwapper(swapDataArray[i].target)
275:                         ) {
276:                             revert AuraCToken__InvalidSwapper(
277:                                 i,
278:                                 swapDataArray[i].target
279:                             );
280:                         }
281: 
282:                         SwapperLib.swap(centralRegistry, swapDataArray[i]);
283:                     }
284:                 }
285:             }
286: 
287:             
288:             {
289:                 
290:                 uint256 numUnderlyingTokens = sd.underlyingTokens.length;
291:                 address[] memory assets = new address[](numUnderlyingTokens);
292:                 uint256[] memory maxAmountsIn = new uint256[](
293:                     numUnderlyingTokens
294:                 );
295:                 address underlyingToken;
296: 
297:                 for (uint256 i; i < numUnderlyingTokens; ++i) { // <= FOUND
298:                     underlyingToken = sd.underlyingTokens[i];
299:                     assets[i] = underlyingToken;
300:                     maxAmountsIn[i] = IERC20(underlyingToken).balanceOf(
301:                         address(this)
302:                     );
303: 
304:                     SwapperLib._approveTokenIfNeeded(
305:                         underlyingToken,
306:                         address(sd.balancerVault),
307:                         maxAmountsIn[i]
308:                     );
309:                 }
310: 
311:                 
312:                 sd.balancerVault.joinPool(
313:                     sd.balancerPoolId,
314:                     address(this),
315:                     address(this),
316:                     IBalancerVault.JoinPoolRequest(
317:                         assets,
318:                         maxAmountsIn,
319:                         abi.encode(
320:                             IBalancerVault
321:                                 .JoinKind
322:                                 .EXACT_TOKENS_IN_FOR_BPT_OUT,
323:                             maxAmountsIn,
324:                             minLPAmount
325:                         ),
326:                         false 
327:                     )
328:                 );
329:             }
330: 
331:             
332:             yield = IERC20(asset()).balanceOf(address(this));
333:             _afterDeposit(yield, 0);
334: 
335:             
336:             _setNewVaultData(yield, vestPeriod);
337: 
338:             emit Harvest(yield);
339:         }
340:     }

['101']

72:     function getPrice(
73:         address asset,
74:         bool inUSD,
75:         bool getLower
76:     ) external view override returns (PriceReturnData memory pData) {
77:         
78:         if (!isSupportedAsset[asset]) {
79:             revert BalancerStablePoolAdaptor__AssetIsNotSupported();
80:         }
81: 
82:         
83:         _ensureNotInVaultContext(balancerVault);
84: 
85:         
86:         AdaptorData memory data = adaptorData[asset];
87:         IBalancerPool pool = IBalancerPool(asset);
88: 
89:         pData.inUSD = inUSD;
90:         IOracleRouter oracleRouter = IOracleRouter(centralRegistry.oracleRouter());
91: 
92:         
93:         uint256 numUnderlyingOrConstituent = data
94:             .underlyingOrConstituent
95:             .length;
96:         uint256 averagePrice;
97:         uint256 numPrices;
98: 
99:         uint256 price;
100:         uint256 errorCode;
101:         for (uint256 i; i < numUnderlyingOrConstituent; ++i) { // <= FOUND
102:             
103:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
104:                 break;
105:             }
106: 
107:             (price, errorCode) = oracleRouter.getPrice(
108:                 data.underlyingOrConstituent[i],
109:                 inUSD,
110:                 getLower
111:             );
112: 
113:             
114:             if (errorCode > 0) {
115:                 pData.hadError = true;
116:                 return pData;
117:             } 
118:             
119:             
120:             
121:             averagePrice += price;
122:             ++numPrices;
123:             
124:         }
125: 
126:         
127:         if (averagePrice == 0) {
128:             pData.hadError = true;
129:             return pData;
130:         } 
131: 
132:         averagePrice = ((averagePrice / numPrices) * pool.getRate()) / WAD;
133:         
134:         
135:         if (_checkOracleOverflow(averagePrice)) {
136:             pData.hadError = true;
137:             return pData;
138:         }
139: 
140:         pData.price = uint240(averagePrice);
141:     }

['162']

148:     function addAsset(address asset, AdaptorData memory data) external {
149:         _checkElevatedPermissions();
150: 
151:         IBalancerPool pool = IBalancerPool(asset);
152: 
153:         
154:         data.poolId = pool.getPoolId();
155:         data.poolDecimals = pool.decimals();
156: 
157:         uint256 numUnderlyingOrConstituent = data
158:             .underlyingOrConstituent
159:             .length;
160: 
161:         
162:         for (uint256 i; i < numUnderlyingOrConstituent; ++i) { // <= FOUND
163:             
164:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
165:                 continue;
166:             }
167: 
168:             if (
169:                 !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset(
170:                     data.underlyingOrConstituent[i]
171:                 )
172:             ) {
173:                 revert BalancerStablePoolAdaptor__ConfigurationError();
174:             }
175: 
176:             if (data.rateProviders[i] != address(0)) {
177:                 
178:                 if (data.rateProviderDecimals[i] == 0) {
179:                     revert BalancerStablePoolAdaptor__ConfigurationError();
180:                 }
181: 
182:                 
183:                 if (IRateProvider(data.rateProviders[i]).getRate() == 0) {
184:                     revert BalancerStablePoolAdaptor__ConfigurationError();
185:                 }
186:             }
187:         }
188: 
189:         
190:         adaptorData[asset] = data;
191: 
192:         
193:         bool isUpdate;
194:         if (isSupportedAsset[asset]) {
195:             isUpdate = true;
196:         }
197: 
198:         isSupportedAsset[asset] = true;
199:         emit BalancerStablePoolAssetAdded(asset, data, isUpdate);
200:     }

['85']

70:     function withdrawNativeYield(address[] calldata nonMTokens) external {
71:         
72:         _checkDaoPermissions();
73: 
74:         uint256 nonMTokensLength = nonMTokens.length;
75:         if (nonMTokensLength == 0) {
76:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
77:         }
78: 
79:         
80:         IBlastNativeYieldManager yieldManager =IBlastNativeYieldManager(
81:             nativeYieldManager
82:         );
83:         address nonMToken;
84: 
85:         for (uint256 i; i < nonMTokensLength; ) { // <= FOUND
86:             nonMToken = nonMTokens[i++];
87: 
88:             
89:             (bool success, ) = nonMToken.staticcall(
90:                 abi.encodePacked(IMToken(nonMToken).isCToken.selector)
91:             );
92:             
93:             
94:             if (success) {
95:                 revert BlastCentralRegistry__Unauthorized();
96:             }
97:         }
98: 
99:         yieldManager.claimPendingNativeYield(nonMTokens);
100:     }

['404']

398:     function claimPendingNativeYield(address[] calldata nonMTokens) external {
399:         _checkIsCentralRegistry();
400: 
401:         uint256 nonMTokensLength = nonMTokens.length;
402:         uint256 yieldClaimed;
403: 
404:         for (uint256 i; i < nonMTokensLength; ++i) { // <= FOUND
405:             yieldClaimed += CHAIN_YIELD_MANAGER.claimMaxGas(
406:                 nonMTokens[i],
407:                 address(this)
408:             );
409:         }
410: 
411:         if (yieldClaimed == 0) {
412:             revert BlastNativeYieldManager__NoYieldToClaim();
413:         }
414: 
415:         IWETH(address(WETH_YIELD_MANAGER)).deposit{ value: yieldClaimed }();
416:         SafeTransferLib.safeTransfer(address(WETH_YIELD_MANAGER), centralRegistry.daoAddress(), yieldClaimed);
417:     }

['249']

236:     function hypotheticalRewardsClaim(
237:         address user
238:     ) external view returns (uint256) {
239:         uint256 epochs = epochsToClaim(user);
240:         if (epochs == 0) {
241:             return 0;
242:         }
243: 
244:         uint256 startEpoch = userNextClaimIndex[user];
245:         uint256 startPoints = veCVE.userPoints(user);
246:         uint256 rewards;
247:         uint256 pointsOffset;
248: 
249:         for (uint256 i; i < epochs; ++i) { // <= FOUND
250:             pointsOffset = veCVE.userUnlocksByEpoch(user, startEpoch + i);
251:             
252:             
253:             if (pointsOffset > 0) {
254:                 
255:                 startPoints -= pointsOffset;
256:             }
257: 
258:             
259:             if (startPoints == 0) {
260:                 break;
261:             }
262: 
263:             
264:             rewards += startPoints * epochRewardsPerCVE[startEpoch + i];
265:         }
266: 
267:         
268:         return rewards / WAD;
269:     }

['314']

303:     function withdrawReservesMulti(address[] calldata dTokens) external {
304:         
305:         _checkDaoPermissions();
306: 
307:         uint256 dTokenLength = dTokens.length;
308:         if (dTokenLength == 0) {
309:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
310:         }
311: 
312:         IMToken dToken;
313: 
314:         for (uint256 i; i < dTokenLength; ) { // <= FOUND
315:             dToken = IMToken(dTokens[i++]);
316:             
317:             if (dToken.isCToken()) {
318:                 _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
319:             }
320: 
321:             dToken.processWithdrawReserves();
322:         }
323:     }

['461']

454:     function registerWormholeChainIDs(
455:         uint256[] calldata chainIds,
456:         uint16[] calldata wormholeChainIds
457:     ) external {
458:         _checkElevatedPermissions();
459: 
460:         uint256 numChainIds = chainIds.length;
461:         for (uint256 i; i < numChainIds; ++i) { // <= FOUND
462:             wormholeChainId[chainIds[i]] = wormholeChainIds[i];
463:         }
464:         emit WormholeChainIDsSet(chainIds, wormholeChainIds);
465:     }

['480']

472:     function registerCCTPDomains(
473:         uint256[] calldata chainIds,
474:         uint32[] calldata cctpDomains
475:     ) external {
476:         _checkElevatedPermissions();
477: 
478:         uint256 numChainIds = chainIds.length;
479: 
480:         for (uint256 i; i < numChainIds; ++i) { // <= FOUND
481:             cctpDomain[chainIds[i]] = cctpDomains[i];
482:         }
483:         emit CCTPDomainsSet(chainIds, cctpDomains);
484:     }

['210']

177:     function harvest(
178:         bytes calldata data
179:     ) external override returns (uint256 yield) {
180:         
181:         _canCompound();
182: 
183:         
184:         _vestIfNeeded();
185: 
186:         
187:         if (_checkVestStatus(_vaultData)) {
188:             _updateVestingPeriodIfNeeded();
189: 
190:             
191:             StrategyData memory sd = strategyData;
192: 
193:             
194:             sd.rewarder.getReward(address(this), true);
195: 
196:             (SwapperLib.Swap[] memory swapDataArray, uint256 minLPAmount) = abi
197:                 .decode(data, (SwapperLib.Swap[], uint256));
198: 
199:             uint256 numRewardTokens = sd.rewardTokens.length;
200:             address rewardToken;
201:             uint256 rewardAmount;
202:             uint256 protocolFee;
203: 
204:             {
205:                 
206:                 
207:                 address feeAccumulator = centralRegistry.feeAccumulator();
208:                 uint256 harvestFee = centralRegistry.protocolHarvestFee();
209: 
210:                 for (uint256 i; i < numRewardTokens; ++i) { // <= FOUND
211:                     rewardToken = sd.rewardTokens[i];
212:                     rewardAmount = IERC20(rewardToken).balanceOf(
213:                         address(this)
214:                     );
215: 
216:                     
217:                     
218:                     if (rewardAmount == 0) {
219:                         continue;
220:                     }
221: 
222:                     
223:                     
224:                     protocolFee = FixedPointMathLib.mulDiv(
225:                         rewardAmount, 
226:                         harvestFee, 
227:                         1e18
228:                     );
229:                     rewardAmount -= protocolFee;
230:                     SafeTransferLib.safeTransfer(
231:                         address(rewardToken),
232:                         feeAccumulator,
233:                         protocolFee
234:                     );
235:                 }
236:             }
237: 
238:             
239:             {
240:                 uint256 numSwapData = swapDataArray.length;
241:                 for (uint256 i; i < numSwapData; ++i) { // <= FOUND
242:                     if (!centralRegistry.isSwapper(swapDataArray[i].target)) {
243:                         revert Convex2PoolCToken__InvalidSwapper(
244:                             i,
245:                             swapDataArray[i].target
246:                         );
247:                     }
248:                     SwapperLib.swap(centralRegistry, swapDataArray[i]);
249:                 }
250:             }
251: 
252:             
253:             _addLiquidityToCurve(minLPAmount);
254: 
255:             
256:             yield = IERC20(asset()).balanceOf(address(this));
257:             if (yield == 0) {
258:                 revert Convex2PoolCToken__NoYield();
259:             }
260:             _afterDeposit(yield, 0);
261: 
262:             
263:             _setNewVaultData(yield, vestPeriod);
264: 
265:             emit Harvest(yield);
266:         }
267:     }

['210']

177:     function harvest(
178:         bytes calldata data
179:     ) external override returns (uint256 yield) {
180:         
181:         _canCompound();
182: 
183:         
184:         _vestIfNeeded();
185: 
186:         
187:         if (_checkVestStatus(_vaultData)) {
188:             _updateVestingPeriodIfNeeded();
189: 
190:             
191:             StrategyData memory sd = strategyData;
192: 
193:             
194:             sd.rewarder.getReward(address(this), true);
195: 
196:             (SwapperLib.Swap[] memory swapDataArray, uint256 minLPAmount) = abi
197:                 .decode(data, (SwapperLib.Swap[], uint256));
198: 
199:             uint256 numRewardTokens = sd.rewardTokens.length;
200:             address rewardToken;
201:             uint256 rewardAmount;
202:             uint256 protocolFee;
203: 
204:             {
205:                 
206:                 
207:                 address feeAccumulator = centralRegistry.feeAccumulator();
208:                 uint256 harvestFee = centralRegistry.protocolHarvestFee();
209: 
210:                 for (uint256 i; i < numRewardTokens; ++i) { // <= FOUND
211:                     rewardToken = sd.rewardTokens[i];
212:                     rewardAmount = IERC20(rewardToken).balanceOf(
213:                         address(this)
214:                     );
215: 
216:                     
217:                     
218:                     if (rewardAmount == 0) {
219:                         continue;
220:                     }
221: 
222:                     
223:                     
224:                     protocolFee = FixedPointMathLib.mulDiv(
225:                         rewardAmount, 
226:                         harvestFee, 
227:                         1e18
228:                     );
229:                     rewardAmount -= protocolFee;
230:                     SafeTransferLib.safeTransfer(
231:                         address(rewardToken),
232:                         feeAccumulator,
233:                         protocolFee
234:                     );
235:                 }
236:             }
237: 
238:             
239:             {
240:                 uint256 numSwapData = swapDataArray.length;
241:                 for (uint256 i; i < numSwapData; ++i) { // <= FOUND
242:                     if (!centralRegistry.isSwapper(swapDataArray[i].target)) {
243:                         revert Convex3PoolCToken__InvalidSwapper(
244:                             i,
245:                             swapDataArray[i].target
246:                         );
247:                     }
248:                     SwapperLib.swap(centralRegistry, swapDataArray[i]);
249:                 }
250:             }
251: 
252:             
253:             _addLiquidityToCurve(minLPAmount);
254: 
255:             
256:             yield = IERC20(asset()).balanceOf(address(this));
257:             if (yield == 0) {
258:                 revert Convex3PoolCToken__NoYield();
259:             }
260:             _afterDeposit(yield, 0);
261: 
262:             
263:             _setNewVaultData(yield, vestPeriod);
264: 
265:             emit Harvest(yield);
266:         }
267:     }

['249']

196:     function addAsset(address asset, AdaptorData memory data) external {
197:         _checkElevatedPermissions();
198: 
199:         
200:         
201:         if (isLocked(asset, 2)) {
202:             revert Curve2PoolLPAdaptor__UnsupportedPool();
203:         }
204: 
205:         address oracleRouter = centralRegistry.oracleRouter();
206: 
207:         
208:         
209:         if (
210:             !IOracleRouter(oracleRouter).isSupportedAsset(
211:                 data.underlying0
212:             )
213:         ) {
214:             revert Curve2PoolLPAdaptor__QuoteAssetIsNotSupported();
215:         }
216: 
217:         
218:         
219:         if (
220:             !IOracleRouter(oracleRouter).isSupportedAsset(
221:                 data.underlying1
222:             )
223:         ) {
224:             revert Curve2PoolLPAdaptor__QuoteAssetIsNotSupported();
225:         }
226: 
227:         
228:         if (data.lowerBound >= data.upperBound) {
229:             revert Curve2PoolLPAdaptor__InvalidBounds();
230:         }
231: 
232:         ICurvePool pool = ICurvePool(data.pool);
233:         uint256 coinsLength;
234:         
235:         while (true) {
236:             try pool.coins(coinsLength) {
237:                 ++coinsLength;
238:             } catch {
239:                 break;
240:             }
241:         }
242: 
243:         
244:         if (coinsLength != 2) {
245:             revert Curve2PoolLPAdaptor__UnsupportedPool();
246:         }
247: 
248:         address underlying;
249:         for (uint256 i; i < coinsLength; ) { // <= FOUND
250:             underlying = pool.coins(i++);
251: 
252:             
253:             
254:             if (
255:                 underlying != data.underlying0 &&
256:                 underlying != data.underlying1
257:             ) {
258:                 revert Curve2PoolLPAdaptor__UnsupportedPool();
259:             }
260:         }
261: 
262:         
263:         
264:         
265:         
266:         data.lowerBound = _bpToWad(data.lowerBound);
267:         data.upperBound = _bpToWad(data.upperBound);
268: 
269:         
270:         if (_MAX_BOUND_RANGE + data.lowerBound < data.upperBound) {
271:             revert Curve2PoolLPAdaptor__InvalidBounds();
272:         }
273: 
274:         
275:         if (data.isCorrelated) {
276:             
277:             
278:             try pool.lp_price() {
279:                 revert Curve2PoolLPAdaptor__UnsupportedPool();
280:             } catch {}
281:         } else {
282:             
283:             
284:             try pool.lp_price() {} catch {
285:                 revert Curve2PoolLPAdaptor__UnsupportedPool();
286:             }
287:         }
288: 
289:         
290:         uint256 testVirtualPrice = pool.get_virtual_price();
291: 
292:         
293:         _enforceBounds(testVirtualPrice, data.lowerBound, data.upperBound);
294: 
295:         
296:         adaptorData[asset] = data;
297: 
298:         

['203']

181:     function multiSwap(
182:         bytes calldata data,
183:         address[] calldata tokens
184:     ) external nonReentrant {
185:         if (!centralRegistry.isHarvester(msg.sender)) {
186:             revert FeeAccumulator__Unauthorized();
187:         }
188: 
189:         SwapperLib.Swap[] memory swapDataArray = abi.decode(
190:             data,
191:             (SwapperLib.Swap[])
192:         );
193: 
194:         uint256 numTokens = swapDataArray.length;
195:         if (numTokens != tokens.length) {
196:             revert FeeAccumulator__SwapDataAndTokenLengthMismatch(
197:                 numTokens,
198:                 tokens.length
199:             );
200:         }
201:         address currentToken;
202: 
203:         for (uint256 i; i < numTokens; ++i) { // <= FOUND
204:             currentToken = tokens[i];
205:             
206:             if (rewardTokenInfo[currentToken].forOTC == 2) {
207:                 continue;
208:             }
209:             if (rewardTokenInfo[currentToken].isRewardToken != 2) {
210:                 revert FeeAccumulator__SwapDataCurrentTokenIsNotRewardToken(
211:                     i,
212:                     currentToken
213:                 );
214:             }
215:             if (swapDataArray[i].inputToken != currentToken) {
216:                 revert FeeAccumulator__SwapDataInputTokenIsNotCurrentToken(
217:                     i,
218:                     swapDataArray[i].inputToken,
219:                     currentToken
220:                 );
221:             }
222:             if (swapDataArray[i].outputToken != feeToken) {
223:                 revert FeeAccumulator__SwapDataOutputTokenIsNotFeeToken(
224:                     i,
225:                     swapDataArray[i].outputToken,
226:                     feeToken
227:                 );
228:             }
229:             if (!centralRegistry.isSwapper(swapDataArray[i].target)) {
230:                 revert FeeAccumulator__SwapDataInvalidSwapper(
231:                     i,
232:                     swapDataArray[i].target
233:                 );
234:             }
235: 
236:             
237:             
238:             
239:             
240:             
241:             
242:             SwapperLib.swap(centralRegistry, swapDataArray[i]);
243:         }
244: 
245:         SafeTransferLib.safeTransfer(
246:             feeToken,
247:             oneBalanceFeeManager,
248:             (IERC20(feeToken).balanceOf(address(this)) * vaultCompoundFee()) /
249:                 vaultYieldFee()
250:         );
251:     }

['458']

420:     function executeEpochFeeRouter(uint256 chainId) external {
421:         ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
422:         uint256 epoch = locker.nextEpochToDeliver();
423: 
424:         if (locker.currentEpoch(block.timestamp) <= epoch) {
425:             revert FeeAccumulator__CurrentEpochError(
426:                 locker.currentEpoch(block.timestamp),
427:                 epoch
428:             );
429:         }
430: 
431:         ChainData memory chainData = centralRegistry.supportedChainData(
432:             chainId
433:         );
434:         if (chainData.isSupported < 2) {
435:             return;
436:         }
437: 
438:         uint256 numChainData = crossChainLockData.length;
439: 
440:         
441:         
442:         if (numChainData == centralRegistry.supportedChains()) {
443:             
444:             uint256 epochRewardsPerCVE = _executeEpochFeeRouter(
445:                 chainData,
446:                 numChainData,
447:                 epoch
448:             );
449: 
450:             IProtocolMessagingHub messagingHub = IProtocolMessagingHub(
451:                 centralRegistry.protocolMessagingHub()
452:             );
453:             LockData memory lockData;
454:             uint256 gas;
455:             uint16 messagingChainId;
456: 
457:             
458:             for (uint256 i; i < numChainData; ) { // <= FOUND
459:                 lockData = crossChainLockData[i];
460:                 chainData = centralRegistry.supportedChainData(
461:                     lockData.chainId
462:                 );
463:                 messagingChainId = centralRegistry.GETHToMessagingChainId(
464:                     uint256(lockData.chainId)
465:                 );
466: 
467:                 gas = messagingHub.quoteWormholeFee(
468:                     uint256(lockData.chainId),
469:                     false
470:                 );
471: 
472:                 messagingHub.sendWormholeMessages{ value: gas }(
473:                     uint256(lockData.chainId),
474:                     chainData.messagingHub,
475:                     abi.encode(epochRewardsPerCVE)
476:                 );
477: 
478:                 unchecked {
479:                     ++i;
480:                 }
481:             }
482: 
483:             delete crossChainLockData;
484:         }
485:     }

['502']

491:     function migrateFeeAccumulator() external {
492:         address newFeeAccumulator = centralRegistry.feeAccumulator();
493:         if (newFeeAccumulator == address(this)) {
494:             revert FeeAccumulator__NewFeeAccumulatorIsNotChanged();
495:         }
496: 
497:         address[] memory currentRewardTokens = rewardTokens;
498:         uint256 numTokens = currentRewardTokens.length;
499:         uint256 tokenBalance;
500: 
501:         
502:         for (uint256 i; i < numTokens; ) { // <= FOUND
503:             tokenBalance = IERC20(currentRewardTokens[i]).balanceOf(
504:                 address(this)
505:             );
506: 
507:             if (tokenBalance > 0) {
508:                 SafeTransferLib.safeTransfer(
509:                     currentRewardTokens[i],
510:                     newFeeAccumulator,
511:                     tokenBalance
512:                 );
513:             }
514: 
515:             unchecked {
516:                 ++i;
517:             }
518:         }
519: 
520:         tokenBalance = IERC20(feeToken).balanceOf(address(this));
521: 
522:         
523:         if (tokenBalance > 0) {
524:             SafeTransferLib.safeTransfer(
525:                 feeToken,
526:                 newFeeAccumulator,
527:                 tokenBalance
528:             );
529:         }
530:     }

['576']

568:     function addRewardTokens(address[] calldata newTokens) external {
569:         _checkDaoPermissions();
570: 
571:         uint256 numTokens = newTokens.length;
572:         if (numTokens == 0) {
573:             revert FeeAccumulator__TokenLengthIsZero();
574:         }
575: 
576:         for (uint256 i; i < numTokens; ++i) { // <= FOUND
577:             
578:             if (rewardTokenInfo[newTokens[i]].isRewardToken == 2) {
579:                 continue;
580:             }
581: 
582:             
583:             
584:             _addRewardToken(newTokens[i]);
585:         }
586:     }

['606']

592:     function removeRewardToken(address rewardTokenToRemove) external {
593:         _checkDaoPermissions();
594: 
595:         RewardToken storage tokenToRemove = rewardTokenInfo[
596:             rewardTokenToRemove
597:         ];
598:         if (tokenToRemove.isRewardToken != 2) {
599:             revert FeeAccumulator__RemovalTokenIsNotRewardToken();
600:         }
601: 
602:         address[] memory currentTokens = rewardTokens;
603:         uint256 numTokens = currentTokens.length;
604:         uint256 tokenIndex = numTokens;
605: 
606:         for (uint256 i; i < numTokens; ) { // <= FOUND
607:             if (currentTokens[i] == rewardTokenToRemove) {
608:                 
609:                 tokenIndex = i;
610:                 break;
611:             }
612:             unchecked {
613:                 ++i;
614:             }
615:         }
616: 
617:         
618:         if (tokenIndex == numTokens--) {
619:             
620:             
621:             revert FeeAccumulator__RemovalTokenDoesNotExist();
622:         }
623: 
624:         
625:         address[] storage currentList = rewardTokens;
626:         
627:         currentList[tokenIndex] = currentList[numTokens];
628:         
629:         currentList.pop();
630: 
631:         
632:         tokenToRemove.isRewardToken = 1;
633:     }

['648']

639:     function getRewardTokenBalances()
640:         external
641:         view
642:         returns (uint256[] memory)
643:     {
644:         address[] memory currentTokens = rewardTokens;
645:         uint256 numTokens = currentTokens.length;
646:         uint256[] memory tokenBalances = new uint256[](numTokens);
647: 
648:         for (uint256 i; i < numTokens; ) { // <= FOUND
649:             tokenBalances[i] = IERC20(currentTokens[i]).balanceOf(
650:                 address(this)
651:             );
652: 
653:             unchecked {
654:                 ++i;
655:             }
656:         }
657: 
658:         return tokenBalances;
659:     }

['119']

97:     function getPrice(
98:         address asset,
99:         bool ,
100:         bool 
101:     ) external view override returns (PriceReturnData memory pData) {
102:         
103:         if (!isSupportedAsset[asset]) {
104:             revert GMAdaptor__AssetIsNotSupported();
105:         }
106: 
107:         
108:         IOracleRouter oracleRouter = IOracleRouter(
109:             centralRegistry.oracleRouter()
110:         );
111: 
112:         uint256[] memory prices = new uint256[](3);
113:         address[] memory tokens = marketData[asset];
114:         uint256 errorCode;
115:         address token;
116: 
117:         
118:         
119:         for (uint256 i; i < 3; ++i) { // <= FOUND
120:             token = tokens[i];
121: 
122:             (prices[i], errorCode) = oracleRouter.getPrice(token, true, false);
123:             if (errorCode > 0) {
124:                 pData.hadError = true;
125:                 return pData;
126:             }
127: 
128:             prices[i] = (prices[i] * 1e30) / _priceUnit[token];
129:         }
130: 
131:         
132:         (int256 price, ) = gmxReader.getMarketTokenPrice(
133:             gmxDataStore,
134:             IReader.MarketProps(asset, tokens[3], tokens[1], tokens[2]),
135:             IReader.PriceProps(prices[0], prices[0]),
136:             IReader.PriceProps(prices[1], prices[1]),
137:             IReader.PriceProps(prices[2], prices[2]),
138:             PNL_FACTOR_TYPE,
139:             true
140:         );
141: 
142:         
143:         
144:         if (price <= 0) {
145:             pData.hadError = true;
146:             return pData;
147:         }
148: 
149:         
150:         uint256 newPrice = uint256(price) / 1e12;
151: 
152:         
153:         if (_checkOracleOverflow(newPrice)) {
154:             pData.hadError = true;
155:             return pData;
156:         }
157: 
158:         pData.inUSD = true;
159:         pData.price = uint240(newPrice);
160:     }

['210']

169:     function addAsset(address asset, address alteredToken) external {
170:         _checkElevatedPermissions();
171: 
172:         IReader.MarketProps memory market = gmxReader.getMarket(
173:             gmxDataStore,
174:             asset
175:         );
176:         
177:         bool isSynthetic = market.indexToken.code.length == 0;
178: 
179:         
180:         if (
181:             market.indexToken == address(0) ||
182:             market.longToken == address(0) ||
183:             market.shortToken == address(0)
184:         ) {
185:             revert GMAdaptor__MarketIsInvalid();
186:         }
187: 
188:         
189:         
190:         if (
191:             (isSynthetic && alteredToken == address(0)) ||
192:             (!isSynthetic && alteredToken != address(0))
193:         ) {
194:             revert GMAdaptor__AlteredTokenIsInvalid();
195:         }
196: 
197:         IOracleRouter oracleRouter = IOracleRouter(
198:             centralRegistry.oracleRouter()
199:         );
200: 
201:         address[] memory tokens = new address[](4);
202:         tokens[0] = isSynthetic ? alteredToken : market.indexToken;
203:         tokens[1] = market.longToken;
204:         tokens[2] = market.shortToken;
205:         tokens[3] = market.indexToken;
206: 
207:         address token;
208: 
209:         
210:         for (uint256 i; i < 3; ++i) { // <= FOUND
211:             token = tokens[i];
212: 
213:             if (!oracleRouter.isSupportedAsset(token)) {
214:                 revert GMAdaptor__MarketTokenIsNotSupported(token);
215:             }
216: 
217:             if (_priceUnit[token] == 0) {
218:                 _priceUnit[token] = WAD * 10 ** IERC20(token).decimals();
219:             }
220:         }
221: 
222:         
223:         marketData[asset] = tokens;
224: 
225:         
226:         bool isUpdate;
227:         if (isSupportedAsset[asset]) {
228:             isUpdate = true;
229:         }
230: 
231:         isSupportedAsset[asset] = true;
232:         emit GMXGMAssetAdded(
233:             asset,
234:             tokens,
235:             isSynthetic,
236:             alteredToken,
237:             isUpdate
238:         );
239:     }

['131']

110:     function harvest(
111:         bytes calldata
112:     ) external override returns (uint256 yield) {
113:         
114:         _canCompound();
115: 
116:         
117:         _vestIfNeeded();
118: 
119:         
120:         if (_checkVestStatus(_vaultData)) {
121:             _updateVestingPeriodIfNeeded();
122: 
123:             
124:             uint256[] memory rewardAmounts = _claimReward();
125: 
126:             
127:             
128:             address feeAccumulator = centralRegistry.feeAccumulator();
129:             uint256 harvestFee = centralRegistry.protocolHarvestFee();
130: 
131:             for (uint256 i; i < 2; ++i) { // <= FOUND
132:                 
133:                 
134:                 if (rewardAmounts[i] > 0) {
135:                     
136:                     
137:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
138:                         rewardAmounts[i],
139:                         harvestFee,
140:                         1e18
141:                     );
142:                     rewardAmounts[i] -= protocolFee;
143:                     SafeTransferLib.safeTransfer(
144:                         underlyingTokens[i],
145:                         feeAccumulator,
146:                         protocolFee
147:                     );
148:                 }
149:             }
150: 
151:             
152:             bytes[] memory data = new bytes[](4);
153: 
154:             data[0] = abi.encodeWithSelector(
155:                 IGMXExchangeRouter.sendWnt.selector,
156:                 gmxDepositVault,
157:                 0.01e18
158:             );
159: 
160:             uint256 rewardAmount;
161:             for (uint256 i = 0; i < 2; ) { // <= FOUND
162:                 rewardAmount = rewardAmounts[i];
163:                 SafeTransferLib.safeApprove(
164:                     underlyingTokens[i],
165:                     gmxRouter,
166:                     rewardAmount
167:                 );
168:                 data[++i] = abi.encodeWithSelector(
169:                     IGMXExchangeRouter.sendTokens.selector,
170:                     underlyingTokens[i],
171:                     gmxDepositVault,
172:                     rewardAmount
173:                 );
174:             }
175:             data[3] = abi.encodeWithSelector(
176:                 IGMXExchangeRouter.createDeposit.selector,
177:                 IGMXExchangeRouter.CreateDepositParams(
178:                     address(this),
179:                     address(this),
180:                     address(0),
181:                     asset(),
182:                     underlyingTokens[0],
183:                     underlyingTokens[1],
184:                     new address[](0),
185:                     new address[](0),
186:                     0,
187:                     false,
188:                     0.01e18,
189:                     500000
190:                 )
191:             );
192: 
193:             bytes[] memory results = IGMXExchangeRouter(gmxExchangeRouter)
194:                 .multicall{ value: 0.01e18 }(data);
195:             _isDepositKey[bytes32(results[3])] = true;
196:             
197:             yield = 1;
198:         }
199:     }

['105']

78:     function setEmissionRates(
79:         uint256 epoch,
80:         address[] calldata tokens,
81:         uint256[] calldata poolWeights
82:     ) external override {
83:         if (msg.sender != centralRegistry.protocolMessagingHub()) {
84:             revert GaugeErrors.Unauthorized();
85:         }
86: 
87:         
88:         
89:         if (
90:             !(epoch == 0 && (startTime == 0 || block.timestamp < startTime)) &&
91:             epoch != currentEpoch() + 1
92:         ) {
93:             revert GaugeErrors.InvalidEpoch();
94:         }
95: 
96:         uint256 numTokens = tokens.length;
97: 
98:         
99:         if (numTokens != poolWeights.length) {
100:             revert GaugeErrors.InvalidLength();
101:         }
102: 
103:         Epoch storage info = _epochInfo[epoch];
104:         address priorAddress;
105:         for (uint256 i; i < numTokens; ) { // <= FOUND
106:             
107:             
108:             if (priorAddress > tokens[i]) {
109:                 revert GaugeErrors.InvalidToken();
110:             }
111: 
112:             info.totalWeights =
113:                 info.totalWeights +
114:                 poolWeights[i] -
115:                 info.poolWeights[tokens[i]];
116:             info.poolWeights[tokens[i]] = poolWeights[i];
117:             unchecked {
118:                 
119:                 priorAddress = tokens[i++];
120:             }
121:         }
122:     }

['128']

126:     function massUpdatePools(address[] calldata tokens) external {
127:         uint256 numTokens = tokens.length;
128:         for (uint256 i; i < numTokens; ) { // <= FOUND
129:             unchecked {
130:                 
131:                 updatePool(tokens[i++]);
132:             }
133:         }
134:     }

['153']

145:     function addExtraReward(address newReward) external {
146:         _checkDaoPermissions();
147: 
148:         if (newReward == address(0)) {
149:             revert GaugeErrors.InvalidAddress();
150:         }
151: 
152:         uint256 rewardTokensLength = rewardTokens.length;
153:         for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
154:             
155:             if (rewardTokens[i++] == newReward) {
156:                 revert GaugeErrors.InvalidAddress();
157:             }
158:         }
159: 
160:         rewardTokens.push(newReward);
161: 
162:         emit AddExtraReward(newReward);
163:     }

['325']

318:     function pendingRewards(
319:         address token,
320:         address user
321:     ) external view returns (uint256[] memory results) {
322:         uint256 rewardTokensLength = rewardTokens.length;
323:         results = new uint256[](rewardTokensLength);
324: 
325:         for (uint256 i; i < rewardTokensLength; ++i) { // <= FOUND
326:             results[i] = pendingRewards(token, user, rewardTokens[i]);
327:         }
328:     }

['371']

334:     function deposit(
335:         address token,
336:         address user,
337:         uint256 amount
338:     ) external nonReentrant {
339:         if (amount == 0) {
340:             revert GaugeErrors.InvalidAmount();
341:         }
342: 
343:         
344:         
345:         if (
346:             msg.sender != token ||
347:             !IMarketManager(marketManager).isListed(token)
348:         ) {
349:             revert GaugeErrors.InvalidToken();
350:         }
351: 
352:         updatePool(token);
353: 
354:         _calcPending(user, token);
355: 
356:         balanceOf[token][user] += amount;
357:         totalSupply[token] += amount;
358: 
359:         
360:         
361:         if (block.timestamp > startTime) {
362:             
363:             
364:             if (firstDeposit == 0) {
365:                 
366:                 
367:                 firstDeposit = block.timestamp;
368:                 updatePool(token);
369: 
370:                 uint256 rewardTokensLength = rewardTokens.length;
371:                 for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
372:                     
373:                     address rewardToken = rewardTokens[i++];
374:                     uint256 unallocatedRewards = (poolAccRewardPerShare[token][
375:                         rewardToken
376:                     ] * totalSupply[token]) / WAD_SQUARED;
377:                     if (unallocatedRewards > 0) {
378:                         SafeTransferLib.safeTransfer(
379:                             rewardToken,
380:                             centralRegistry.daoAddress(),
381:                             unallocatedRewards
382:                         );
383:                     }
384:                 }
385:             }
386:         }
387: 
388:         _calcDebt(user, token);
389: 
390:         emit Deposit(user, token, amount);
391:     }

['447']

436:     function claim(address token) external nonReentrant {
437:         if (block.timestamp < startTime) {
438:             revert GaugeErrors.NotStarted();
439:         }
440: 
441:         updatePool(token);
442:         _calcPending(msg.sender, token);
443: 
444:         bool hasRewards;
445:         uint256 rewardTokensLength = rewardTokens.length;
446: 
447:         for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
448:             
449:             address rewardToken = rewardTokens[i++];
450:             uint256 rewards = userDebtInfo[token][msg.sender][rewardToken]
451:                 .rewardPending;
452:             
453:             
454:             if (rewards > 0) {
455:                 hasRewards = true;
456:                 SafeTransferLib.safeTransfer(rewardToken, msg.sender, rewards);
457:             }
458: 
459:             
460:             userDebtInfo[token][msg.sender][rewardToken].rewardPending = 0;
461:         }
462:         if (!hasRewards) {
463:             revert GaugeErrors.NoReward();
464:         }
465: 
466:         _calcDebt(msg.sender, token);
467: 
468:         emit Claim(msg.sender, token);
469:     }

['759']

743:     function liquidateAccount(address account) external {
744:         
745:         if (msg.sender == account) {
746:             _revert(_UNAUTHORIZED_SELECTOR);
747:         }
748: 
749:         
750:         if (seizePaused == 2) {
751:             _revert(_PAUSED_SELECTOR);
752:         }
753: 
754:         IMToken[] memory accountAssetsPrior = accountAssets[account].assets;
755:         uint256 numAssetsPrior = accountAssetsPrior.length;
756:         IMToken mToken;
757: 
758:         
759:         for (uint256 i = 0; i < numAssetsPrior; ) { // <= FOUND
760:             
761:             mToken = accountAssetsPrior[i++];
762:             if (!mToken.isCToken()) {
763:                 
764:                 mToken.accrueInterest();
765:             }
766:         }
767: 
768:         (
769:             BadDebtData memory data,
770:             uint256[] memory assetBalances
771:         ) = _BadDebtTermsOf(account);
772: 
773:         
774:         if (data.collateral >= data.debt) {
775:             revert MarketManager__NoLiquidationAvailable();
776:         }
777: 
778:         uint256 repayRatio = (data.debtToPay * WAD) / data.debt;
779:         uint256 debt;
780:         
781:         
782:         for (uint256 i = 0; i < numAssetsPrior; ++i) { // <= FOUND
783:             
784:             mToken = accountAssetsPrior[i];
785:             if (!mToken.isCToken()) {
786:                 debt = mToken.debtBalanceCached(account);
787: 
788:                 
789:                 
790:                 
791:                 if (debt != assetBalances[i]) {
792:                     _revert(_INVARIANT_ERROR_SELECTOR);
793:                 }
794: 
795:                 
796:                 if (debt > 0) {
797:                     
798:                     
799:                     
800:                     
801:                     
802:                     
803:                     
804:                     
805:                     mToken.repayWithBadDebt(msg.sender, account, repayRatio);
806:                 }
807:             }
808:         }
809: 
810:         uint256 collateral;
811: 
812:         
813:         for (uint256 i = 0; i < numAssetsPrior; ++i) { // <= FOUND
814:             
815:             mToken = accountAssetsPrior[i];
816:             if (mToken.isCToken()) {
817:                 AccountPosition storage collateralData = tokenData[address(mToken)]
818:                 .accountPositions[account];
819:                 
820:                 collateral = collateralData.collateralPosted;
821: 
822:                 
823:                 
824:                 
825:                 if (collateral != assetBalances[i]) {
826:                     _revert(_INVARIANT_ERROR_SELECTOR);
827:                 }
828: 
829:                 
830:                 
831:                 
832:                 
833:                 if (collateral > 0) {
834:                     
835:                     
836:                     
837:                     delete collateralData.collateralPosted;
838: 
839:                     
840:                     collateralPosted[address(mToken)] =
841:                         collateralPosted[address(mToken)] -
842:                         collateral;
843:                     emit CollateralRemoved(account, address(mToken), collateral);
844:                     
845:                     mToken.seizeAccountLiquidation(
846:                         msg.sender,
847:                         account,
848:                         collateral
849:                     );
850:                 }  
851:             }
852:         }
853: 
854:         IMToken[] memory accountAssetsPost = accountAssets[account].assets;
855:         uint256 numAssetsPost = accountAssetsPost.length;
856: 
857:         
858:         
859:         
860:         
861:         if (numAssetsPost != numAssetsPrior) {
862:             _revert(_INVARIANT_ERROR_SELECTOR);
863:         }
864: 
865:         for (uint256 i = 0; i < numAssetsPrior; ++i) { // <= FOUND
866:             if (accountAssetsPost[i] != accountAssetsPrior[i]) {
867:                 _revert(_INVARIANT_ERROR_SELECTOR);
868:             }
869:         }
870:     }

['900']

878:     function listToken(address mToken) external {
879:         _checkElevatedPermissions();
880: 
881:         if (tokenData[mToken].isListed) {
882:             _revert(_INVALID_PARAMETER_SELECTOR);
883:         }
884: 
885:         
886:         IMToken(mToken).isCToken();
887: 
888:         
889:         
890:         if (!IMToken(mToken).startMarket(msg.sender)) {
891:             _revert(_INVARIANT_ERROR_SELECTOR);
892:         }
893: 
894:         MarketToken storage token = tokenData[mToken];
895:         token.isListed = true;
896:         token.collRatio = 0;
897: 
898:         uint256 numTokens = tokensListed.length;
899: 
900:         for (uint256 i; i < numTokens; ) { // <= FOUND
901:             unchecked {
902:                 if (tokensListed[i++] == mToken) {
903:                     _revert(_INVALID_PARAMETER_SELECTOR);
904:                 }
905:             }
906:         }
907: 
908:         tokensListed.push(mToken);
909:         emit TokenListed(mToken);
910:     }

['1109']

1086:     function setCTokenCollateralCaps(
1087:         address[] calldata mTokens,
1088:         uint256[] calldata newCollateralCaps
1089:     ) external {
1090:         if (!centralRegistry.hasDaoPermissions(msg.sender)) {
1091:             _revert(_UNAUTHORIZED_SELECTOR);
1092:         }
1093: 
1094:         uint256 numTokens = mTokens.length;
1095: 
1096:         assembly {
1097:             if iszero(numTokens) {
1098:                 
1099:                 mstore(0x0, _INVALID_PARAMETER_SELECTOR)
1100:                 
1101:                 revert(0x1c, 0x04)
1102:             }
1103:         }
1104: 
1105:         if (numTokens != newCollateralCaps.length) {
1106:             _revert(_INVALID_PARAMETER_SELECTOR);
1107:         }
1108: 
1109:         for (uint256 i; i < numTokens; ++i) { // <= FOUND
1110:             
1111:             if (!IMToken(mTokens[i]).isCToken()) {
1112:                 _revert(_INVALID_PARAMETER_SELECTOR);
1113:             }
1114: 
1115:             
1116:             
1117:             if (tokenData[mTokens[i]].collRatio == 0) {
1118:                 _revert(_INVALID_PARAMETER_SELECTOR);
1119:             }
1120: 
1121:             collateralCaps[mTokens[i]] = newCollateralCaps[i];
1122:             emit NewCollateralCap(mTokens[i], newCollateralCaps[i]);
1123:         }
1124:     }

['502']

483:     function getPrices(
484:         address[] calldata assets,
485:         bool[] calldata inUSD,
486:         bool[] calldata getLower
487:     ) external view returns (uint256[] memory, uint256[] memory) {
488:         uint256 numAssets = assets.length;
489:         
490:         if (numAssets == 0) {
491:             _revert(_INVALID_PARAMETER_SELECTOR);
492:         }
493: 
494:         
495:         if (numAssets != inUSD.length || numAssets != getLower.length) {
496:             _revert(_INVALID_PARAMETER_SELECTOR);
497:         }
498: 
499:         uint256[] memory prices = new uint256[](numAssets);
500:         uint256[] memory hadError = new uint256[](numAssets);
501: 
502:         for (uint256 i; i < numAssets; ) { // <= FOUND
503:             (prices[i], hadError[i]) = getPrice(
504:                 assets[i],
505:                 inUSD[i],
506:                 getLower[i]
507:             );
508: 
509:             unchecked {
510:                 ++i;
511:             }
512:         }
513: 
514:         return (prices, hadError);
515:     }

['545']

526:     function getPricesForMarket(
527:         address account,
528:         IMToken[] calldata assets,
529:         uint256 errorCodeBreakpoint
530:     )
531:         external
532:         view
533:         returns (AccountSnapshot[] memory, uint256[] memory, uint256)
534:     {
535:         uint256 numAssets = assets.length;
536:         
537:         if (numAssets == 0) {
538:             _revert(_INVALID_PARAMETER_SELECTOR);
539:         }
540: 
541:         AccountSnapshot[] memory snapshots = new AccountSnapshot[](numAssets);
542:         uint256[] memory underlyingPrices = new uint256[](numAssets);
543:         uint256 hadError;
544: 
545:         for (uint256 i; i < numAssets; ) { // <= FOUND
546:             snapshots[i] = assets[i].getSnapshotPacked(account);
547:             (underlyingPrices[i], hadError) = getPrice(
548:                 assets[i].underlying(),
549:                 true,
550:                 snapshots[i].isCToken
551:             );
552: 
553:             if (hadError >= errorCodeBreakpoint) {
554:                 _revert(_ERROR_CODE_FLAGGED_SELECTOR);
555:             }
556: 
557:             unchecked {
558:                 ++i;
559:             }
560:         }
561: 
562:         return (snapshots, underlyingPrices, numAssets);
563:     }

['107']

101:     function reQueryUnderlyingTokens() external {
102:         address[] memory currentTokens = strategyData.underlyingTokens;
103:         uint256 numCurrentTokens = currentTokens.length;
104:         
105:         
106:         
107:         for (uint256 i; i < numCurrentTokens; ) { // <= FOUND
108:             unchecked {
109:                 isUnderlyingToken[currentTokens[i++]] = false;
110:             }
111:         }
112: 
113:         
114:         strategyData.underlyingTokens = strategyData.sy.getTokensIn();
115:         numCurrentTokens = strategyData.underlyingTokens.length;
116: 
117:         
118:         
119:         for (uint256 i = 0; i < numCurrentTokens; ) { // <= FOUND
120:             unchecked {
121:                 isUnderlyingToken[strategyData.underlyingTokens[i++]] = true;
122:             }
123:         }
124:     }

['184']

147:     function harvest(
148:         bytes calldata data
149:     ) external override returns (uint256 yield) {
150:         
151:         _canCompound();
152: 
153:         
154:         _vestIfNeeded();
155: 
156:         
157:         if (_checkVestStatus(_vaultData)) {
158:             _updateVestingPeriodIfNeeded();
159: 
160:             
161:             StrategyData memory sd = strategyData;
162: 
163:             
164:             sd.lp.redeemRewards(address(this));
165: 
166:             (
167:                 SwapperLib.Swap[] memory swapDataArray,
168:                 uint256 minLPAmount,
169:                 ApproxParams memory approx,
170:                 LimitOrderData memory limit
171:             ) = abi.decode(data, (SwapperLib.Swap[], uint256, ApproxParams, LimitOrderData));
172: 
173:             {
174:                 
175:                 uint256 numRewardTokens = sd.rewardTokens.length;
176:                 address rewardToken;
177:                 uint256 rewardAmount;
178:                 uint256 protocolFee;
179:                 
180:                 
181:                 address feeAccumulator = centralRegistry.feeAccumulator();
182:                 uint256 harvestFee = centralRegistry.protocolHarvestFee();
183: 
184:                 for (uint256 i; i < numRewardTokens; ++i) { // <= FOUND
185:                     rewardToken = sd.rewardTokens[i];
186:                     rewardAmount = IERC20(rewardToken).balanceOf(
187:                         address(this)
188:                     );
189: 
190:                     
191:                     
192:                     if (rewardAmount == 0) {
193:                         continue;
194:                     }
195: 
196:                     
197:                     
198:                     protocolFee = FixedPointMathLib.mulDiv(
199:                         rewardAmount, 
200:                         harvestFee, 
201:                         1e18
202:                     );
203:                     rewardAmount -= protocolFee;
204:                     SafeTransferLib.safeTransfer(
205:                         rewardToken,
206:                         feeAccumulator,
207:                         protocolFee
208:                     );
209: 
210:                     
211:                     if (!isUnderlyingToken[rewardToken]) {
212:                         if (
213:                             !centralRegistry.isSwapper(swapDataArray[i].target)
214:                         ) {
215:                             revert PendleLPCToken__InvalidSwapper(
216:                                 i,
217:                                 swapDataArray[i].target
218:                             );
219:                         }
220: 
221:                         SwapperLib.swap(centralRegistry, swapDataArray[i]);
222:                     }
223:                 }
224:             }
225: 
226:             
227:             {
228:                 uint256 numUnderlyingTokens = sd.underlyingTokens.length;
229:                 address underlyingToken;
230:                 uint256 balance;
231:                 for (uint256 i; i < numUnderlyingTokens; ++i) { // <= FOUND
232:                     underlyingToken = sd.underlyingTokens[i];
233: 
234:                     if (underlyingToken == address(0)) {
235:                         balance = address(this).balance;
236:                         if (balance > 0) {
237:                             
238:                             sd.sy.deposit{ value: balance }(
239:                                 address(this),
240:                                 underlyingToken,
241:                                 balance,
242:                                 0
243:                             );
244:                         }
245:                     } else {
246:                         balance = IERC20(underlyingToken).balanceOf(
247:                             address(this)
248:                         );
249:                         if (balance > 0) {
250:                             SwapperLib._approveTokenIfNeeded(
251:                                 underlyingToken,
252:                                 address(sd.sy),
253:                                 balance
254:                             );
255:                             
256:                             sd.sy.deposit(
257:                                 address(this),
258:                                 underlyingToken,
259:                                 balance,
260:                                 0
261:                             );
262:                         }
263:                     }
264:                 }
265:             }
266: 
267:             {
268:                 uint256 balance = sd.sy.balanceOf(address(this));
269:                 SwapperLib._approveTokenIfNeeded(
270:                     address(sd.sy),
271:                     address(sd.router),
272:                     balance
273:                 );
274: 
275:                 
276:                 (yield, ) = sd.router.addLiquiditySingleSy(
277:                     address(this),
278:                     address(sd.lp),
279:                     balance,
280:                     minLPAmount,

['239']

97:     function receiveWormholeMessages(
98:         bytes memory payload,
99:         bytes[] memory ,
100:         bytes32 srcAddress,
101:         uint16 srcChainId,
102:         bytes32 deliveryHash
103:     ) external payable {
104:         _checkMessagingHubStatus();
105: 
106:         
107:         if (isDeliveredMessageHash[deliveryHash]) {
108:             revert ProtocolMessagingHub__MessageHashIsAlreadyDelivered(
109:                 deliveryHash
110:             );
111:         }
112: 
113:         isDeliveredMessageHash[deliveryHash] = true;
114:         address wormholeRelayer = address(centralRegistry.wormholeRelayer());
115: 
116:         
117:         if (msg.sender != wormholeRelayer) {
118:             _revert(_UNAUTHORIZED_SELECTOR);
119:         }
120: 
121:         uint256 gethChainId = centralRegistry.messagingToGETHChainId(
122:             srcChainId
123:         );
124:         address srcAddr = address(uint160(uint256(srcAddress)));
125: 
126:         OmnichainData memory operator = centralRegistry.getOmnichainOperators(
127:             srcAddr,
128:             gethChainId
129:         );
130: 
131:         
132:         if (
133:             centralRegistry.supportedChainData(gethChainId).messagingHub !=
134:             srcAddr
135:         ) {
136:             return;
137:         }
138: 
139:         
140:         if (operator.isAuthorized < 2) {
141:             return;
142:         }
143: 
144:         uint8 payloadId = abi.decode(payload, (uint8));
145: 
146:         
147:         
148:         if (payloadId == 1) {
149:             (, address token, uint256 amount) = abi.decode(
150:                 payload,
151:                 (uint8, address, uint256)
152:             );
153: 
154:             address feeToken = centralRegistry.feeToken();
155: 
156:             
157:             
158:             
159:             if (token == feeToken) {
160:                 ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
161: 
162:                 
163:                 
164:                 if (locker.isShutdown() == 2) {
165:                     SafeTransferLib.safeTransfer(
166:                         feeToken,
167:                         centralRegistry.daoAddress(),
168:                         amount
169:                     );
170:                     return;
171:                 }
172: 
173:                 
174:                 SafeTransferLib.safeTransfer(
175:                     feeToken,
176:                     address(locker),
177:                     amount
178:                 );
179:                 locker.recordEpochRewards(amount);
180:                 return;
181:             }
182:         
183:         
184:         
185:         } else if (payloadId == 4) {
186:             (, bytes memory emissionData) = abi.decode(
187:                 payload,
188:                 (uint8, bytes)
189:             );
190: 
191:             (
192:                 address[] memory gaugePools,
193:                 uint256[] memory emissionTotals,
194:                 address[][] memory tokens,
195:                 uint256[][] memory emissions,
196:                 uint256 chainLockedAmount,
197:                 uint256 messageType
198:             ) = abi.decode(
199:                     emissionData,
200:                     (
201:                         address[],
202:                         uint256[],
203:                         address[][],
204:                         uint256[][],
205:                         uint256,
206:                         uint256
207:                     )
208:                 );
209: 
210:             
211:             
212:             if (messageType == 1) {
213:                 IFeeAccumulator(centralRegistry.feeAccumulator())
214:                     .receiveCrossChainLockData(
215:                         EpochRolloverData({
216:                             chainId: gethChainId,
217:                             value: chainLockedAmount,
218:                             numChainData: 0,
219:                             epoch: 0
220:                         })
221:                     );
222:                 return;
223:             }
224: 
225:             
226:             if (messageType == 2) {
227:                 IFeeAccumulator(centralRegistry.feeAccumulator())
228:                     .receiveExecutableLockData(chainLockedAmount);
229:                 return;
230:             }
231: 
232:             
233:             
234:             {
235:                 
236:                 uint256 numPools = gaugePools.length;
237:                 GaugeController gaugePool;
238: 
239:                 for (uint256 i; i < numPools; ) { // <= FOUND
240:                     gaugePool = GaugeController(gaugePools[i]);
241:                     
242:                     cve.mintGaugeEmissions(
243:                         address(gaugePool),
244:                         emissionTotals[i]
245:                     );
246:                     
247:                     gaugePool.setEmissionRates(
248:                         gaugePool.currentEpoch() + 1,
249:                         tokens[i],
250:                         emissions[i]
251:                     );
252: 
253:                     unchecked {
254:                         ++i;
255:                     }
256:                 }
257:             }
258:         
259:         
260:         } else if (payloadId == 5) {
261:             (, bytes memory lockData) = abi.decode(payload, (uint8, bytes));
262: 
263:             (address recipient, uint256 amount, bool continuousLock) = abi
264:                 .decode(lockData, (address, uint256, bool));
265: 
266:             cve.mintVeCVELock(amount);
267:             cve.approve(veCVE, amount);
268: 
269:             RewardsData memory rewardData;
270: 
271:             
272:             
273:             
274:             IVeCVE(veCVE).createLockFor(
275:                 recipient,
276:                 amount,
277:                 continuousLock,
278:                 rewardData,
279:                 "",
280:                 0
281:             );
282:         }
283:     }

['229']

220:     function queryUserLocks(
221:         address user
222:     ) external view returns (uint256[] memory, uint256[] memory) {
223:         uint256 numLocks = userLocks[user].length;
224:         Lock[] memory locks = userLocks[user];
225:         Lock memory lock;
226:         uint256[] memory lockAmounts = new uint256[](numLocks);
227:         uint256[] memory lockTimestamps = new uint256[](numLocks);
228: 
229:         for (uint256 i; i < numLocks; ++i) { // <= FOUND
230:             lock = locks[i];
231:             lockAmounts[i] = lock.amount;
232:             lockTimestamps[i] = lock.unlockTime;
233:         }
234: 
235:         return (lockAmounts, lockTimestamps);
236:     }

['566']

540:     function combineAllLocks(
541:         bool continuousLock,
542:         RewardsData calldata rewardsData,
543:         bytes calldata params,
544:         uint256 aux
545:     ) external nonReentrant {
546:         if (isShutdown == 2) {
547:             _revert(_VECVE_SHUTDOWN_SELECTOR);
548:         }
549: 
550:         
551:         _claimRewards(msg.sender, rewardsData, params, aux);
552: 
553:         
554:         
555:         Lock[] storage locks = userLocks[msg.sender];
556:         uint256 numLocks = locks.length;
557: 
558:         
559:         if (numLocks < 2) {
560:             _revert(_INVALID_LOCK_SELECTOR);
561:         }
562: 
563:         uint256 lockedAmount;
564:         Lock storage lock;
565: 
566:         for (uint256 i; i < numLocks; ) { // <= FOUND
567:             lock = locks[i];
568: 
569:             if (lock.unlockTime != CONTINUOUS_LOCK_VALUE) {
570:                 
571:                 _reduceTokenUnlocks(
572:                     msg.sender,
573:                     currentEpoch(lock.unlockTime),
574:                     lock.amount
575:                 );
576:             }
577: 
578:             unchecked {
579:                 
580:                 
581:                 lockedAmount += locks[i++].amount;
582:             }
583:         }
584: 
585:         
586:         delete userLocks[msg.sender];
587:         uint256 veBalanceOf = balanceOf(msg.sender);
588: 
589:         
590:         
591:         if (veBalanceOf != lockedAmount) {
592:             revert VeCVE__InvariantError();
593:         }
594: 
595:         
596:         uint256 currentPoints = userPoints[msg.sender];
597: 
598:         
599:         if (continuousLock) {
600:             
601:             userLocks[msg.sender].push(
602:                 Lock({
603:                     amount: uint216(lockedAmount),
604:                     unlockTime: CONTINUOUS_LOCK_VALUE
605:                 })
606:             );
607: 
608:             
609: 
610:             
611:             
612:             
613:             veBalanceOf = veBalanceOf * CL_POINT_MULTIPLIER;
614: 
615:             

['930']

920:     function getVotes(address user) external view returns (uint256) {
921:         uint256 numLocks = userLocks[user].length;
922: 
923:         if (numLocks == 0) {
924:             return 0;
925:         }
926: 
927:         uint256 currentLockBoost = centralRegistry.voteBoostMultiplier();
928:         uint256 votes;
929: 
930:         for (uint256 i; i < numLocks; ) { // <= FOUND
931:             
932:             unchecked {
933:                 votes += getVotesForSingleLockForTime(
934:                     user,
935:                     i++,
936:                     block.timestamp,
937:                     currentLockBoost
938:                 );
939:             }
940:         }
941: 
942:         return votes;
943:     }

['964']

950:     function getVotesForEpoch(
951:         address user,
952:         uint256 epoch
953:     ) external view returns (uint256) {
954:         uint256 numLocks = userLocks[user].length;
955: 
956:         if (numLocks == 0) {
957:             return 0;
958:         }
959: 
960:         uint256 timestamp = genesisEpoch + (EPOCH_DURATION * epoch);
961:         uint256 currentLockBoost = centralRegistry.voteBoostMultiplier();
962:         uint256 votes;
963: 
964:         for (uint256 i; i < numLocks; ) { // <= FOUND
965:             
966:             unchecked {
967:                 votes += getVotesForSingleLockForTime(
968:                     user,
969:                     i++,
970:                     timestamp,
971:                     currentLockBoost
972:                 );
973:             }
974:         }
975: 
976:         return votes;
977:     }

['1202']

1186:     function removeMarketManager(
1187:         address currentMarketManager
1188:     ) public virtual {
1189:         _checkElevatedPermissions();
1190: 
1191:         
1192:         if (!isMarketManager[currentMarketManager]) {
1193:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1194:         }
1195: 
1196:         delete isMarketManager[currentMarketManager];
1197: 
1198:         
1199:         uint256 numMarkets = marketManagers.length;
1200:         uint256 marketIndex = numMarkets;
1201: 
1202:         for (uint256 i; i < numMarkets; ++i) { // <= FOUND
1203:             if (marketManagers[i] == currentMarketManager) {
1204:                 marketIndex = i;
1205:                 break;
1206:             }
1207:         }
1208: 
1209:         
1210:         
1211:         
1212:         if (marketIndex >= numMarkets--) {
1213:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1214:         }
1215: 
1216:         
1217:         marketManagers[marketIndex] = marketManagers[numMarkets];
1218:         
1219:         
1220:         marketManagers.pop();
1221: 
1222:         emit RemovedCurvanceContract("Market Manager", currentMarketManager);
1223:     }

['149']

140:     function reQueryRewardTokens() public {
141:         delete strategyData.rewardTokens;
142: 
143:         
144:         
145:         strategyData.rewardTokens.push() = _CRV;
146:         IBaseRewardPool rewarder = strategyData.rewarder;
147: 
148:         uint256 extraRewardsLength = rewarder.extraRewardsLength();
149:         for (uint256 i; i < extraRewardsLength; ++i) { // <= FOUND
150:             strategyData.rewardTokens.push() = IRewards(
151:                 rewarder.extraRewards(i)
152:             ).rewardToken();
153:         }
154:     }

['149']

140:     function reQueryRewardTokens() public {
141:         delete strategyData.rewardTokens;
142: 
143:         
144:         
145:         strategyData.rewardTokens.push() = _CRV;
146:         IBaseRewardPool rewarder = strategyData.rewarder;
147: 
148:         uint256 extraRewardsLength = rewarder.extraRewardsLength();
149:         for (uint256 i; i < extraRewardsLength; ++i) { // <= FOUND
150:             strategyData.rewardTokens.push() = IRewards(
151:                 rewarder.extraRewards(i)
152:             ).rewardToken();
153:         }
154:     }

['628']

603:     function updatePool(address token) public override {
604:         
605:         if (block.timestamp <= startTime) {
606:             return;
607:         }
608: 
609:         uint256 _lastRewardTimestamp = poolLastRewardTimestamp[token];
610:         
611:         if (_lastRewardTimestamp == 0) {
612:             _lastRewardTimestamp = startTime;
613:         }
614: 
615:         
616:         if (block.timestamp <= _lastRewardTimestamp) {
617:             return;
618:         }
619: 
620:         
621:         uint256 totalDeposited = totalSupply[token];
622:         if (totalDeposited == 0) {
623:             return;
624:         }
625: 
626:         
627:         uint256 rewardTokensLength = rewardTokens.length;
628:         for (uint256 i; i < rewardTokensLength; ) { // <= FOUND
629:             uint256 lastRewardTimestamp = _lastRewardTimestamp;
630: 
631:             
632:             address rewardToken = rewardTokens[i++];
633:             uint256 accRewardPerShare = poolAccRewardPerShare[token][
634:                 rewardToken
635:             ];
636:             uint256 lastEpoch = epochOfTimestamp(lastRewardTimestamp);
637:             uint256 currentEpoch = currentEpoch();
638:             uint256 reward;
639: 
640:             
641:             while (lastEpoch < currentEpoch) {
642:                 uint256 endTimestamp = epochEndTime(lastEpoch);
643: 
644:                 
645:                 reward =
646:                     ((endTimestamp - lastRewardTimestamp) *
647:                         rewardAllocation(token, lastEpoch, rewardToken)) /
648:                     EPOCH_WINDOW;
649:                 accRewardPerShare =
650:                     accRewardPerShare +
651:                     (reward * (WAD_SQUARED)) /
652:                     totalDeposited;
653: 
654:                 ++lastEpoch;
655:                 lastRewardTimestamp = endTimestamp;
656:             }
657: 
658:             
659:             reward =
660:                 ((block.timestamp - lastRewardTimestamp) *
661:                     rewardAllocation(token, lastEpoch, rewardToken)) /
662:                 EPOCH_WINDOW;
663:             accRewardPerShare =
664:                 accRewardPerShare +
665:                 (reward * (WAD_SQUARED)) /
666:                 totalDeposited;
667: 
668:             poolAccRewardPerShare[token][rewardToken] = accRewardPerShare;
669:         }
670: 
671:         
672:         poolLastRewardTimestamp[token] = block.timestamp;
673:     }

[Low-22] The function decimals() is not part of the ERC20 standard

Resolution

The decimals() function in an ERC20 token contract is used to specify how many decimal places the token can be divided into. However, it should be used with caution because not all ERC20 token contracts implement decimals(), and the function isn't required by the ERC20 standard. Calling decimals() on a contract that doesn't implement it will result in a runtime error. Moreover, even when implemented, the returned value can be manipulated or artificially set, which may cause unexpected behavior. Therefore, always verify the decimal count independently if precision is crucial for your contract logic. When interacting with other ERC20 tokens, consider implementing safeguards or checks to handle potential errors from calling decimals().

Num of instances: 23

Findings

Click to show findings

['104']

104:         strategyData.decimalsA = 10 ** IERC20(strategyData.token0).decimals(); // <= FOUND

['105']

105:         strategyData.decimalsB = 10 ** IERC20(strategyData.token1).decimals(); // <= FOUND

['173']

173:         data.decimals0 = IERC20(data.token0).decimals(); // <= FOUND

['174']

174:         data.decimals1 = IERC20(data.token1).decimals(); // <= FOUND

['129']

129:         paymentTokenDecimals = IERC20(paymentTokenAddress).decimals(); // <= FOUND

['215']

215: 
216:         data.quoteTokenDecimals = ERC20(asset).decimals(); // <= FOUND

['220']

220:             data.baseTokenDecimals = ERC20(data.baseToken).decimals(); // <= FOUND

['918']

918:         return IERC20(underlying).decimals(); // <= FOUND

['103']

103: 
107:     function decimals() public view virtual override(ERC20) returns (uint8) { // <= FOUND

['160']

160:         _feeTokenUnit = 10 ** IERC20(feeToken).decimals(); // <= FOUND

['292']

292:         
293:         
294:         uint256 feeTokenRequiredForOTC = (
295:             ((priceSwap * amountToOTC * _feeTokenUnit) / priceFeeToken)
296:         ) / 10 ** IERC20(tokenToOTC).decimals(); // <= FOUND

['218']

218:                 _priceUnit[token] = WAD * 10 ** IERC20(token).decimals(); // <= FOUND

['1596']

1596: 
1597:         
1598:         uint256 amountAdjusted = (debtAmount *
1599:             (10 ** IERC20(collateralToken).decimals())) / // <= FOUND
1600:             (10 ** IERC20(debtToken).decimals()); // <= FOUND

['78']

78:             paymentTokenDecimals = ERC20(paymentToken_).decimals(); // <= FOUND

['228']

228:             data.baseDecimals = ERC20(asset).decimals(); // <= FOUND

['229']

229:             data.quoteDecimals = ERC20(token1).decimals(); // <= FOUND

['233']

233:             data.quoteDecimals = ERC20(token0).decimals(); // <= FOUND

['57']

57:             
58:             uint256 swapAmount = _optimalDeposit(
59:                 factory,
60:                 lpToken,
61:                 amount0,
62:                 r0,
63:                 r1,
64:                 10 ** ERC20(token0).decimals(), // <= FOUND
65:                 10 ** ERC20(token1).decimals(), // <= FOUND
66:                 stable
67:             );

['99']

99:             
100:             uint256 swapAmount = _optimalDeposit(
101:                 factory,
102:                 lpToken,
103:                 amount1,
104:                 r1,
105:                 r0,
106:                 10 ** ERC20(token1).decimals(), // <= FOUND
107:                 10 ** ERC20(token0).decimals(), // <= FOUND
108:                 stable
109:             );

['155']

155:         data.poolDecimals = pool.decimals(); // <= FOUND

['68']

68:         return IChainlink(underlyingAssetAggregator()).decimals(); // <= FOUND

['107']

107:         _decimals = asset_.decimals(); // <= FOUND

['167']

167: 
168:         
169:         data.decimals = feedAggregator.decimals(); // <= FOUND

[Low-23] Minting to the zero address should be avoided

Resolution

Minting tokens to the zero address in Solidity is a potential pitfall that should be carefully guarded against. The zero address (0x0) is generally used as a default value and represents an uninitialized or null address. Minting tokens to this address effectively burns them, making them inaccessible and permanently removing them from the total supply. This could lead to unintended token loss if performed accidentally. To prevent this, it's important to include a check in the minting function to ensure that the target address is not the zero address. Using a library like OpenZeppelin's Address can provide utility functions like requireNonZero, which simplifies this check and enhances the security of the minting function.

Num of instances: 6

Findings

Click to show findings

['371']

371:     function mint(
372:         uint256 shares,
373:         address receiver
374:     ) public override nonReentrant returns (uint256 assets) {
375:         assets = _mint(shares, receiver);
376:     }

['142']

142:     function _mint(
143:         uint256 shares,
144:         address receiver
145:     ) internal virtual returns (uint256 assets) {}

['320']

320:     function _mint(
321:         uint256 shares,
322:         address receiver
323:     ) internal override returns (uint256 assets) {
324:         if (shares == 0) {
325:             revert CTokenCompounding__ZeroShares();
326:         }
327: 
328:         
329:         
330:         marketManager.canMint(address(this));
331: 
332:         
333:         uint256 pending = _calculatePendingRewards();
334:         uint256 ta = _totalAssets + pending;
335: 
336:         
337:         assets = _previewMint(shares, ta);
338: 
339:         
340:         _processDeposit(msg.sender, receiver, assets, shares, ta, pending);
341:         
342:         _gaugePool().deposit(address(this), receiver, shares);
343:     }

['142']

142:     function _mint(
143:         uint256 shares,
144:         address receiver
145:     ) internal override returns (uint256 assets) {
146:         if (shares == 0) {
147:             revert CTokenPrimitive__ZeroShares();
148:         }
149: 
150:         
151:         
152:         marketManager.canMint(address(this));
153: 
154:         
155:         uint256 ta = _totalAssets;
156: 
157:         
158:         assets = _previewMint(shares, ta);
159: 
160:         
161:         _processDeposit(msg.sender, receiver, assets, shares, ta);
162:         
163:         _gaugePool().deposit(address(this), receiver, shares);
164:     }

['1149']

1149:     function _mint(
1150:         address minter,
1151:         address recipient,
1152:         uint256 amount
1153:     ) internal {
1154:         
1155:         accrueInterest();
1156: 
1157:         
1158:         marketManager.canMint(address(this));
1159: 
1160:         
1161:         uint256 er = exchangeRateCached();
1162: 
1163:         
1164:         SafeTransferLib.safeTransferFrom(
1165:             underlying,
1166:             minter,
1167:             address(this),
1168:             amount
1169:         );
1170: 
1171:         
1172:         uint256 tokens = (amount * WAD) / er;
1173: 
1174:         
1175:         unchecked {
1176:             totalSupply = totalSupply + tokens;
1177:             
1178:             balanceOf[recipient] = balanceOf[recipient] + tokens;
1179:         }
1180: 
1181:         
1182:         _gaugePool().deposit(address(this), recipient, tokens);
1183: 
1184:         emit Transfer(address(0), recipient, tokens);
1185:     }

['393']

393:     function mint(uint256 shares, address to) public virtual returns (uint256 assets) {
394:         if (shares > maxMint(to)) _revert(0x6a695959); 
395:         assets = previewMint(shares);
396:         _deposit(msg.sender, to, assets, shares);
397:     }

[Low-24] No limits when setting fees

Resolution

When settings fees state variables, ensure there a require checks in place to prevent incorrect values from being set. This is particularly important when dealing with fee values as without checks fees can be set to 100%

Num of instances: 1

Findings

Click to show findings

['52']

52:     function setExitFee(uint256 newExitFee) external {
53:         _checkElevatedPermissions();
54:         _setExitFee(newExitFee);
55:     }

[Low-25] Loss of precision

Resolution

Dividing by large numbers in Solidity can cause a loss of precision due to the language's inherent integer division behavior. Solidity does not support floating-point arithmetic, and as a result, division between integers yields an integer result, truncating any fractional part. When dividing by a large number, the resulting value may become significantly smaller, leading to a loss of precision, as the fractional part is discarded.

Num of instances: 40

Findings

Click to show findings

['212']

212:     function _parseData(
213:         AdaptorData memory data,
214:         bool inUSD
215:     ) internal view returns (PriceReturnData memory pData) {
216:         uint256 price = _extractPrice(data.symbolHash);
217: 
218:         
219:         uint256 quoteDecimals = data.decimals;
220:         if (quoteDecimals != 18) {
221:             
222:             
223:             if (quoteDecimals < 18) {
224:                 price = price * (10 ** (18 - quoteDecimals));
225:             } else {
226:                 
227:                 
228:                 price = price / (10 ** (quoteDecimals - 18));
229:             }
230:         }
231: 
232:         pData.hadError = _verifyData(price, data.max);
233: 
234:         if (!pData.hadError) {
235:             pData.inUSD = inUSD;
236:             pData.price = uint240(price);
237:         }
238:     }

['217']

217:     function _getFairPrice(
218:         uint256 reserve0,
219:         uint256 reserve1,
220:         uint256 price0,
221:         uint256 price1,
222:         uint256 totalSupply
223:     ) internal pure returns (uint256) {
224:         
225:         uint256 sqrtReserve = FixedPointMathLib.sqrt(
226:             FixedPointMathLib.sqrt(reserve0 * reserve1) *
227:                 FixedPointMathLib.sqrt(
228:                     reserve0 * reserve0 + reserve1 * reserve1
229:                 )
230:         );
231:         uint256 ratio = ((1e18) * price0) / price1; // <= FOUND
232:         uint256 sqrtPrice = FixedPointMathLib.sqrt(
233:             FixedPointMathLib.sqrt((1e18) * ratio) *
234:                 FixedPointMathLib.sqrt(1e36 + ratio * ratio)
235:         );
236:         return
237:             ((((1e18) * sqrtReserve) / sqrtPrice) * price0 * 2) / totalSupply;
238:     }

['227']

227:     function balanceOfUnderlyingSafe(
228:         address account
229:     ) external view returns (uint256) {
230:         return ((convertToAssetsSafe(WAD) * balanceOf(account)) / WAD); // <= FOUND
231:     }

['236']

236:     function balanceOfUnderlying(
237:         address account
238:     ) external view returns (uint256) {
239:         return ((convertToAssets(WAD) * balanceOf(account)) / WAD); // <= FOUND
240:     }

['204']

204:     function currentEpoch(uint256 time) external view returns (uint256) { // <= FOUND
205:         if (time < genesisEpoch) {
206:             return 0;
207:         }
208: 
209:         return ((time - genesisEpoch) / EPOCH_DURATION);
210:     }

['463']

463:     function _calculateRewards(
464:         address user,
465:         uint256 epochs
466:     ) internal returns (uint256) {
467:         uint256 startEpoch = userNextClaimIndex[user];
468:         uint256 rewards;
469: 
470:         for (uint256 i; i < epochs; ) {
471:             unchecked {
472:                 rewards += _calculateRewardsForEpoch(user, startEpoch + i++);
473:             }
474:         }
475: 
476:         
477:         
478:         unchecked {
479:             userNextClaimIndex[user] += epochs;
480:         }
481: 
482:         
483:         return rewards / WAD;
484:     }

['249']

249:     function _parseData(
250:         AdaptorData memory data,
251:         bool inUSD
252:     ) internal view returns (PriceReturnData memory pData) {
253:         pData.inUSD = inUSD;
254:         if (!IOracleRouter(centralRegistry.oracleRouter()).isSequencerValid()) {
255:             pData.hadError = true;
256:             return pData;
257:         }
258: 
259:         (, int256 price, , uint256 updatedAt, ) = IChainlink(data.aggregator)
260:             .latestRoundData();
261: 
262:         
263:         if (price <= 0) {
264:             pData.hadError = true;
265:             return pData;
266:         }
267: 
268:         uint256 newPrice = (uint256(price) * WAD) / (10 ** data.decimals);
269: 
270:         pData.price = uint240(newPrice);
271:         pData.hadError = _verifyData(
272:                         uint256(price),
273:                         updatedAt,
274:                         data.max,
275:                         data.min,
276:                         data.heartbeat
277:                     );
278:     }

['178']

178:     function claim() external returns (uint256 amount) { // <= FOUND
179:         SaleStatus saleStatus = currentStatus();
180:         if (saleStatus == SaleStatus.NotStarted) {
181:             revert CurvanceDAOLBP__NotStarted();
182:         }
183:         if (saleStatus == SaleStatus.InSale) {
184:             revert CurvanceDAOLBP__InSale();
185:         }
186: 
187:         uint256 payAmount = userCommitted[msg.sender];
188:         userCommitted[msg.sender] = 0;
189: 
190:         uint256 price = currentPrice();
191:         uint256 adjustedPayAmount = _adjustDecimals(
192:             payAmount,
193:             paymentTokenDecimals,
194:             18
195:         );
196:         amount = (adjustedPayAmount * WAD) / price; // <= FOUND
197: 
198:         SafeTransferLib.safeTransfer(cve, msg.sender, amount);
199: 
200:         emit Claimed(msg.sender, amount);
201:     }

['232']

232:     function softCap() public view returns (uint256) { // <= FOUND
233:         return (softPriceInpaymentToken * cveAmountForSale) / WAD; // <= FOUND
234:     }

['392']

392:     function repayWithBadDebt(
393:         address liquidator,
394:         address account,
395:         uint256 repayRatio
396:     ) external nonReentrant {
397:         
398:         
399: 
400:         
401:         
402:         if (msg.sender != address(marketManager)) {
403:             _revert(_UNAUTHORIZED_SELECTOR);
404:         }
405: 
406:         
407:         
408:         
409: 
410:         
411:         uint256 accountDebt = debtBalanceCached(account);
412:         uint256 repayAmount = (accountDebt * repayRatio) / WAD; // <= FOUND
413: 
414:         
415:         
416:         
417: 
418:         
419:         
420:         SafeTransferLib.safeTransferFrom(
421:             underlying,
422:             liquidator,
423:             address(this),
424:             repayAmount
425:         );
426: 
427:         
428:         
429:         delete _debtOf[account].principal;
430:         totalBorrows -= accountDebt;
431: 
432:         emit Repay(liquidator, account, repayAmount);
433:         emit BadDebtRecognized(liquidator, account, accountDebt - repayAmount);
434:     }

['472']

472:     function redeem(uint256 tokens) external nonReentrant { // <= FOUND
473:         
474:         accrueInterest();
475: 
476:         
477:         
478:         marketManager.canRedeem(address(this), msg.sender, tokens);
479: 
480:         _redeem(
481:             msg.sender,
482:             msg.sender,
483:             tokens,
484:             (exchangeRateCached() * tokens) / WAD
485:         );
486:     }

['497']

497:     function redeemFor(
498:         address account,
499:         address recipient,
500:         uint256 tokens
501:     ) external nonReentrant {
502:         if (!_checkIsDelegate(account, msg.sender)) {
503:             _revert(_UNAUTHORIZED_SELECTOR);
504:         }
505: 
506:         
507:         accrueInterest();
508: 
509:         
510:         
511:         marketManager.canRedeem(address(this), account, tokens);
512: 
513:         _redeem(
514:             account,
515:             recipient,
516:             tokens,
517:             (exchangeRateCached() * tokens) / WAD
518:         );
519:     }

['532']

532:     function redeemUnderlyingForPositionFolding(
533:         address account,
534:         uint256 amount,
535:         bytes calldata params
536:     ) external nonReentrant {
537:         if (msg.sender != marketManager.positionFolding()) {
538:             _revert(_UNAUTHORIZED_SELECTOR);
539:         }
540: 
541:         
542:         accrueInterest();
543: 
544:         _redeem(
545:             account,
546:             msg.sender,
547:             (amount * WAD) / exchangeRateCached(),
548:             amount
549:         );
550: 
551:         IPositionFolding(msg.sender).onRedeem(
552:             address(this),
553:             account,
554:             amount,
555:             params
556:         );
557: 
558:         
559:         
560:         marketManager.canRedeem(address(this), account, 0);
561:     }

['594']

594:     function depositReserves(uint256 amount) external nonReentrant { // <= FOUND
595:         _checkDaoPermissions();
596: 
597:         
598:         accrueInterest();
599: 
600:         
601:         uint256 tokens = (amount * WAD) / exchangeRateCached();
602: 
603:         
604:         SafeTransferLib.safeTransferFrom(
605:             underlying,
606:             msg.sender,
607:             address(this),
608:             amount
609:         );
610: 
611:         
612:         address daoAddress = centralRegistry.daoAddress();
613: 
614:         
615:         _gaugePool().deposit(address(this), daoAddress, tokens);
616: 
617:         
618:         totalReserves = totalReserves + tokens;
619:     }

['627']

627:     function withdrawReserves(uint256 amount) external nonReentrant { // <= FOUND
628:         _checkDaoPermissions();
629: 
630:         
631:         accrueInterest();
632: 
633:         
634:         if (marketUnderlyingHeld() < amount) {
635:             revert DToken__InsufficientUnderlyingHeld();
636:         }
637: 
638:         
639:         
640:         uint256 tokens = (amount * WAD) / exchangeRateCached();
641: 
642:         
643:         totalReserves = totalReserves - tokens;
644: 
645:         
646:         address daoAddress = centralRegistry.daoAddress();
647: 
648:         
649:         _gaugePool().withdraw(address(this), daoAddress, tokens);
650:         
651:         SafeTransferLib.safeTransfer(underlying, daoAddress, amount);
652:     }

['659']

659:     function processWithdrawReserves() external { // <= FOUND
660:         
661:         if (msg.sender != address(centralRegistry)) {
662:             _revert(_UNAUTHORIZED_SELECTOR);
663:         }
664: 
665:         
666:         accrueInterest();
667: 
668:         uint256 totalReservesCached = totalReserves;
669:         uint256 amount = (totalReservesCached * exchangeRateCached()) / WAD; // <= FOUND
670: 
671:         
672:         if (marketUnderlyingHeld() < amount) {
673:             revert DToken__InsufficientUnderlyingHeld();
674:         }
675: 
676:         
677:         delete totalReserves;
678: 
679:         
680:         address daoAddress = centralRegistry.daoAddress();
681:         
682:         _gaugePool().withdraw(address(this), daoAddress, totalReservesCached);
683: 
684:         
685:         SafeTransferLib.safeTransfer(underlying, daoAddress, amount);
686:     }

['764']

764:     function balanceOfUnderlyingSafe(
765:         address account
766:     ) external returns (uint256) {
767:         return ((exchangeRateWithUpdateSafe() * balanceOf[account]) / WAD); // <= FOUND
768:     }

['1149']

1149:     function _mint(
1150:         address minter,
1151:         address recipient,
1152:         uint256 amount
1153:     ) internal {
1154:         
1155:         accrueInterest();
1156: 
1157:         
1158:         marketManager.canMint(address(this));
1159: 
1160:         
1161:         uint256 er = exchangeRateCached();
1162: 
1163:         
1164:         SafeTransferLib.safeTransferFrom(
1165:             underlying,
1166:             minter,
1167:             address(this),
1168:             amount
1169:         );
1170: 
1171:         
1172:         uint256 tokens = (amount * WAD) / er;
1173: 
1174:         
1175:         unchecked {
1176:             totalSupply = totalSupply + tokens;
1177:             
1178:             balanceOf[recipient] = balanceOf[recipient] + tokens;
1179:         }
1180: 
1181:         
1182:         _gaugePool().deposit(address(this), recipient, tokens);
1183: 
1184:         emit Transfer(address(0), recipient, tokens);
1185:     }

['363']

363:     function getBorrowRatePerYear(
364:         uint256 cash,
365:         uint256 borrows,
366:         uint256 reserves
367:     ) external view returns (uint256) {
368:         return
369:             _SECONDS_PER_YEAR *
370:             (getBorrowRate(cash, borrows, reserves) / INTEREST_COMPOUND_RATE);
371:     }

['436']

436:     function getPredictedBorrowRate(
437:         uint256 cash,
438:         uint256 borrows,
439:         uint256 reserves
440:     ) public view returns (uint256) {
441:         uint256 util = utilizationRate(cash, borrows, reserves);
442:         RatesConfiguration memory config = ratesConfig;
443:         uint256 vertexPoint = config.vertexStartingPoint;
444: 
445:         
446:         
447:         if (util <= vertexPoint) {
448:             return _getBaseInterestRate(util);
449:         }
450: 
451:         if (vertexMultiplier() == WAD && util < config.increaseThreshold) {
452:             return (_getVertexInterestRate(util - vertexPoint) +
453:                 _getBaseInterestRate(vertexPoint));
454:         }
455: 
456:         uint256 vertexInterestRate = ratesConfig.vertexInterestRate;
457:         uint256 newMultiplier = _updateForAboveVertex(config, util);
458: 
459:         return (util * vertexInterestRate * newMultiplier) / WAD_SQUARED;
460:     }

['496']

496:     function getSupplyRate(
497:         uint256 cash,
498:         uint256 borrows,
499:         uint256 reserves,
500:         uint256 interestFee
501:     ) public view returns (uint256) {
502:         
503:         uint256 rateToPool = (getBorrowRate(cash, borrows, reserves) *
504:             (WAD - interestFee)) / WAD;
505: 
506:         
507:         return (utilizationRate(cash, borrows, reserves) * rateToPool) / WAD;
508:     }

['536']

536:     function _getBaseInterestRate(
537:         uint256 util
538:     ) internal view returns (uint256) {
539:         return (util * ratesConfig.baseInterestRate) / WAD; // <= FOUND
540:     }

['696']

696:     function _updateForAboveVertex(
697:         RatesConfiguration memory config,
698:         uint256 util
699:     ) internal view returns (uint256) {
700:         uint256 currentMultiplier = vertexMultiplier();
701:         
702:         uint256 decay = (currentMultiplier * config.decayRate) / WAD;
703:         uint256 newMultiplier;
704: 
705:         if (util <= config.increaseThreshold) {
706:             newMultiplier = currentMultiplier - decay;
707: 
708:             
709:             return newMultiplier < WAD ? WAD : newMultiplier;
710:         }
711: 
712:         
713:         
714:         
715:         newMultiplier = _getPositiveCFactorResult(
716:             currentMultiplier, 
717:             config.adjustmentVelocity, 
718:             decay, 
719:             util, 
720:             config.increaseThreshold, 
721:             config.increaseThresholdMax 
722:         );
723: 
724:         
725:         
726:         
727:         if (newMultiplier < WAD) {
728:             return WAD;
729:         }
730: 
731:         
732:         
733:         return
734:             newMultiplier < config.vertexMultiplierMax
735:                 ? newMultiplier
736:                 : config.vertexMultiplierMax;
737:     }

['758']

758:     function _updateForBelowVertex(
759:         RatesConfiguration memory config,
760:         uint256 util
761:     ) internal view returns (uint256) {
762:         uint256 currentMultiplier = vertexMultiplier();
763:         
764:         uint256 decay = (currentMultiplier * config.decayRate) / WAD;
765:         uint256 newMultiplier;
766: 
767:         if (util <= config.decreaseThresholdMax) {
768:             
769:             
770:             
771:             
772:             newMultiplier =
773:                 ((currentMultiplier * WAD) /
774:                     (WAD + config.adjustmentVelocity)) -
775:                 decay;
776: 
777:             
778:             return newMultiplier < WAD ? WAD : newMultiplier;
779:         }
780: 
781:         
782:         
783:         
784:         newMultiplier = _getNegativeCFactorResult(
785:             currentMultiplier, 
786:             config.adjustmentVelocity, 
787:             decay, 
788:             util, 
789:             config.decreaseThreshold, 
790:             config.decreaseThresholdMax 
791:         );
792: 
793:         
794:         
795:         return newMultiplier < WAD ? WAD : newMultiplier;
796:     }

['821']

821:     function _getPositiveCFactorResult(
822:         uint256 multiplier,
823:         uint256 adjustmentVelocity,
824:         uint256 decay,
825:         uint256 current, 
826:         uint256 start, 
827:         uint256 end 
828:     ) internal pure returns (uint256) {
829:         
830:         
831:         
832:         
833:         uint256 cFactor = ((current - start) * WAD) / (end - start);
834: 
835:         
836:         
837:         cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
838: 
839:         
840:         
841:         return ((multiplier * cFactor) / WAD_SQUARED) - decay;
842:     }

['867']

867:     function _getNegativeCFactorResult(
868:         uint256 multiplier,
869:         uint256 adjustmentVelocity,
870:         uint256 decay,
871:         uint256 current, 
872:         uint256 start, 
873:         uint256 end 
874:     ) internal pure returns (uint256) {
875:         
876:         
877:         
878:         uint256 cFactor = ((start - current) * WAD) / (start - end);
879: 
880:         
881:         
882:         cFactor = WAD_SQUARED + (cFactor * adjustmentVelocity);
883: 
884:         
885:         
886:         return ((multiplier * WAD_SQUARED) / cFactor) - decay;
887:     }

['260']

260:     function executeOTC(
261:         address tokenToOTC,
262:         uint256 amountToOTC
263:     ) external nonReentrant {
264:         _checkDaoPermissions();
265: 
266:         
267:         if (rewardTokenInfo[tokenToOTC].forOTC < 2) {
268:             revert FeeAccumulator__TokenIsNotEarmarked();
269:         }
270: 
271:         
272:         IOracleRouter oracleRouter = IOracleRouter(
273:             centralRegistry.oracleRouter()
274:         );
275: 
276:         (uint256 priceSwap, uint256 errorCodeSwap) = oracleRouter.getPrice(
277:             tokenToOTC,
278:             true,
279:             true
280:         );
281:         (uint256 priceFeeToken, uint256 errorCodeFeeToken) = oracleRouter
282:             .getPrice(feeToken, true, true);
283: 
284:         
285:         if (errorCodeFeeToken == 2 || errorCodeSwap == 2) {
286:             revert FeeAccumulator__ConfigurationError();
287:         }
288: 
289:         address daoAddress = centralRegistry.daoAddress();
290:         
291:         
292:         uint256 feeTokenRequiredForOTC = (
293:             ((priceSwap * amountToOTC * _feeTokenUnit) / priceFeeToken)
294:         ) / 10 ** IERC20(tokenToOTC).decimals();
295: 
296:         SafeTransferLib.safeTransferFrom(
297:             feeToken,
298:             msg.sender,
299:             address(this),
300:             feeTokenRequiredForOTC
301:         );
302: 
303:         SafeTransferLib.safeTransfer(
304:             feeToken,
305:             oneBalanceFeeManager,
306:             (feeTokenRequiredForOTC * vaultCompoundFee()) / vaultYieldFee()
307:         );
308: 
309:         
310:         SafeTransferLib.safeTransfer(tokenToOTC, daoAddress, amountToOTC);
311:     }

['748']

748:     function _executeEpochFeeRouter(
749:         ChainData memory chainData,
750:         uint256 numChains,
751:         uint256 epoch
752:     ) internal returns (uint256) {
753:         IProtocolMessagingHub messagingHub = IProtocolMessagingHub(
754:             centralRegistry.protocolMessagingHub()
755:         );
756: 
757:         IVeCVE veCVE = IVeCVE(centralRegistry.veCVE());
758:         uint256 lockedTokens = (veCVE.chainPoints() -
759:             veCVE.chainUnlocksByEpoch(epoch));
760: 
761:         uint256 totalLockedTokens = lockedTokens;
762: 
763:         
764:         
765:         for (uint256 i; i < numChains; ) {
766:             totalLockedTokens += crossChainLockData[i].lockAmount;
767: 
768:             unchecked {
769:                 ++i;
770:             }
771:         }
772: 
773:         uint256 feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
774: 
775:         
776:         
777:         SafeTransferLib.safeTransfer(
778:             feeToken,
779:             oneBalanceFeeManager,
780:             (feeTokenBalance * vaultCompoundFee()) /
781:                 centralRegistry.protocolHarvestFee()
782:         );
783: 
784:         feeTokenBalance = IERC20(feeToken).balanceOf(address(this));
785: 
786:         uint256 chainId;
787:         uint256 feeTokenBalanceForChain;
788: 
789:         
790:         
791:         for (uint256 i; i < numChains; ) {
792:             chainId = crossChainLockData[i].chainId;
793:             chainData = centralRegistry.supportedChainData(chainId);
794:             
795:             
796:             
797:             
798:             
799:             
800:             
801:             feeTokenBalanceForChain =
802:                 (((feeTokenBalance * WAD) / totalLockedTokens) * 
803:                 crossChainLockData[i].lockAmount) /
804:                 WAD;
805: 
806:             messagingHub.sendFees(
807:                 chainId,
808:                 chainData.messagingHub,
809:                 feeTokenBalanceForChain
810:             );
811: 
812:             unchecked {
813:                 ++i;
814:             }
815:         }
816: 
817:         
818:         
819:         
820:         
821:         
822:         
823:         
824:         feeTokenBalanceForChain =
825:         (((feeTokenBalance * WAD) / totalLockedTokens) * lockedTokens) /
826:             WAD;
827:         uint256 epochRewardsPerCVE = (feeTokenBalance * WAD) /
828:             totalLockedTokens;
829: 
830:         ICVELocker locker = ICVELocker(centralRegistry.cveLocker());
831: 
832:         
833:         
834:         if (locker.isShutdown() == 2) {
835:             SafeTransferLib.safeTransfer(
836:                 feeToken,
837:                 centralRegistry.daoAddress(),
838:                 feeTokenBalanceForChain
839:             );
840:             return epochRewardsPerCVE;
841:         }
842: 
843:         
844:         SafeTransferLib.safeTransfer(
845:             feeToken,
846:             address(locker),
847:             feeTokenBalanceForChain
848:         );
849:         ICVELocker(locker).recordEpochRewards(epochRewardsPerCVE);
850: 
851:         return epochRewardsPerCVE;
852:     }

['145']

145:     function epochOfTimestamp(
146:         uint256 timestamp
147:     ) public view returns (uint256) {
148:         _checkGaugeHasStarted();
149:         return (timestamp - startTime) / EPOCH_WINDOW; // <= FOUND
150:     }

['670']

670:     function _assetValue(
671:         uint256 amount,
672:         uint256 price,
673:         uint256 decimals
674:     ) internal pure returns (uint256) {
675:         return (amount * price) / (10 ** decimals); // <= FOUND
676:     }

['687']

687:     function _redemptionValue(
688:         uint256 amount,
689:         uint256 exchangeRate,
690:         uint256 price,
691:         uint256 decimals,
692:         uint256 collRatio
693:     ) internal pure returns (uint256) {
694:         uint256 assetValue = _assetValue(
695:             (amount * exchangeRate) / WAD,
696:             price,
697:             decimals
698:         );
699: 
700:         
701:         return ((assetValue * collRatio) / WAD);
702:     }

['715']

715:     function _liquidityValue(
716:         uint256 liqForBorrowPrior,
717:         uint256 posted,
718:         uint256 exchangeRate,
719:         uint256 price,
720:         uint256 decimals,
721:         uint256 collRatio
722:     ) internal pure returns (uint256) {
723:         uint256 assetValue = _assetValue(
724:             ((posted * exchangeRate) / WAD),
725:             price,
726:             decimals
727:         );
728: 
729:         return (liqForBorrowPrior + (assetValue * collRatio) / WAD);
730:     }

['744']

744:     function _addLiquidationValues(
745:         AccountSnapshot memory snapshot,
746:         address account,
747:         uint256 price,
748:         uint256 softSumPrior,
749:         uint256 hardSumPrior
750:     ) internal view returns (uint256, uint256) {
751:         uint256 assetValue = _assetValue(
752:             ((tokenData[snapshot.asset].accountPositions[account].collateralPosted *
753:                 snapshot.exchangeRate) / WAD),
754:             price,
755:             snapshot.decimals
756:         ) * WAD;
757: 
758:         return (
759:             softSumPrior +
760:                 (assetValue / tokenData[snapshot.asset].collReqSoft),
761:             hardSumPrior + (assetValue / tokenData[snapshot.asset].collReqHard)
762:         );
763:     }

['856']

856:     function _convertETHUSD(
857:         uint240 currentPrice,
858:         uint256 conversionRate,
859:         bool currentlyInUSD
860:     ) internal pure returns (uint256) {
861:         if (!currentlyInUSD) {
862:             
863:             return (currentPrice * conversionRate) / WAD;
864:         }
865: 
866:         return (currentPrice * WAD) / conversionRate;
867:     }

['877']

877:     function _calculateLowerPrice(
878:         uint256 a,
879:         uint256 b
880:     ) internal view returns (uint256, uint256) {
881:         if (a <= b) {
882:             
883:             
884:             if (((a * cautionDivergenceFlag) / DENOMINATOR) < b) {
885:                 
886:                 
887:                 
888:                 if (((a * badSourceDivergenceFlag) / DENOMINATOR) < b) {
889:                     return (a, BAD_SOURCE);
890:                 }
891: 
892:                 
893:                 
894:                 
895:                 return (a, CAUTION);
896:             }
897: 
898:             return (a, NO_ERROR);
899:         }
900: 
901:         
902:         
903:         if (((b * cautionDivergenceFlag) / DENOMINATOR) < a) {
904:             
905:             
906:             
907:             if (((b * badSourceDivergenceFlag) / DENOMINATOR) < a) {
908:                 return (b, BAD_SOURCE);
909:             }
910: 
911:             
912:             
913:             
914:             return (b, CAUTION);
915:         }
916: 
917:         return (b, NO_ERROR);
918:     }

['928']

928:     function _calculateHigherPrice(
929:         uint256 a,
930:         uint256 b
931:     ) internal view returns (uint256, uint256) {
932:         if (a >= b) {
933:             
934:             
935:             if (((b * cautionDivergenceFlag) / DENOMINATOR) < a) {
936:                 
937:                 
938:                 
939:                 if (((b * badSourceDivergenceFlag) / DENOMINATOR) < a) {
940:                     return (a, BAD_SOURCE);
941:                 }
942: 
943:                 
944:                 
945:                 
946:                 return (a, CAUTION);
947:             }
948: 
949:             return (a, NO_ERROR);
950:         }
951: 
952:         
953:         
954:         if (((a * cautionDivergenceFlag) / DENOMINATOR) < b) {
955:             
956:             
957:             
958:             if (((a * badSourceDivergenceFlag) / DENOMINATOR) < b) {
959:                 return (b, BAD_SOURCE);
960:             }
961: 
962:             
963:             
964:             
965:             return (b, CAUTION);
966:         }
967: 
968:         return (b, NO_ERROR);
969:     }

['994']

994:     function currentEpoch(uint256 time) public view returns (uint256) { // <= FOUND
995:         if (time < genesisEpoch) {
996:             return 0;
997:         }
998: 
999:         
1000:         return ((time - genesisEpoch) / EPOCH_DURATION);
1001:     }

['1040']

1040:     function getVotesForSingleLockForTime(
1041:         address user,
1042:         uint256 lockIndex,
1043:         uint256 time,
1044:         uint256 currentLockBoost
1045:     ) public view returns (uint256) {
1046:         Lock storage lock = userLocks[user][lockIndex];
1047: 
1048:         if (lock.unlockTime < time) {
1049:             return 0;
1050:         }
1051: 
1052:         if (lock.unlockTime == CONTINUOUS_LOCK_VALUE) {
1053:             unchecked {
1054:                 return ((lock.amount * currentLockBoost) / 10000);
1055:             }
1056:         }
1057: 
1058:         
1059:         
1060:         return
1061:             (lock.amount * ((lock.unlockTime - time) / EPOCH_DURATION)) /
1062:             LOCK_DURATION_EPOCHS;
1063:     }

['1408']

1408:     function _getUnlockPenalty(
1409:         uint256 amount,
1410:         uint256 penalty,
1411:         uint256 unlockTime
1412:     ) internal view returns (uint256) {
1413:         
1414:         
1415:         
1416:         return
1417:             (amount *
1418:                 ((penalty * (LOCK_DURATION - (unlockTime - block.timestamp))) /
1419:                     LOCK_DURATION)) / WAD;
1420:     }

['224']

224:     function _optimalDeposit(
225:         address factory,
226:         address lpToken,
227:         uint256 amount0,
228:         uint256 reserve0,
229:         uint256 reserve1,
230:         uint256 decimals0,
231:         uint256 decimals1,
232:         bool stable
233:     ) internal view returns (uint256) {
234:         
235:         uint256 swapFee = IVeloPairFactory(factory).getFee(lpToken, stable);
236:         uint256 a;
237: 
238:         
239:         if (stable) {
240:             a = (((amount0 * 10000) / (10000 - swapFee)) * 1e18) / decimals0; // <= FOUND
241: 
242:             uint256 x = (reserve0 * 1e18) / decimals0;
243:             uint256 y = (reserve1 * 1e18) / decimals1; // <= FOUND
244:             uint256 x2 = (x * x) / 1e18;
245:             uint256 y2 = (y * y) / 1e18;
246:             uint256 p = (y * (((x2 * 3 + y2) * 1e18) / (y2 * 3 + x2))) / x; // <= FOUND
247: 
248:             uint256 num = a * y;
249:             uint256 den = ((a + x) * p) / 1e18 + y;
250: 
251:             return ((num / den) * decimals0) / 1e18;
252:         }
253: 
254:         
255:         uint256 swapFeeFactor = 10000 - swapFee;
256:         
257:         a = (10000 + swapFeeFactor) * reserve0;
258:         uint256 b = amount0 * 10000 * reserve0 * 4 * swapFeeFactor;
259:         uint256 c = FixedPointMathLib.sqrt(a * a + b);
260:         uint256 d = swapFeeFactor * 2;
261:         return (c - a) / d; // <= FOUND
262:         
263:     }

[Low-26] Missing zero address check in constructor

Resolution

In Solidity, constructors often take address parameters to initialize important components of a contract, such as owner or linked contracts. However, without a check, there's a risk that an address parameter could be mistakenly set to the zero address (0x0). This could occur due to a mistake or oversight during contract deployment. A zero address in a crucial role can cause serious issues, as it cannot perform actions like a normal address, and any funds sent to it are irretrievable. Therefore, it's crucial to include a zero address check in constructors to prevent such potential problems. If a zero address is detected, the constructor should revert the transaction.

Num of instances: 22

Findings

Click to show findings

['69']

67:     constructor(
68:         ICentralRegistry centralRegistry_,
69:         IERC20 asset_, // <= FOUND
70:         address marketManager_, // <= FOUND
71:         IVeloGauge gauge,
72:         IVeloPairFactory pairFactory,
73:         IVeloRouter router
74:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
75:         if (block.chainid != 8453) {
76:             revert AerodromeStableCToken__ChainIsNotSupported();
77:         }
78: 
79:         
80:         address _asset = asset(); // <= FOUND
81:         
82:         
83:         if (gauge.stakingToken() != _asset) {
84:             revert AerodromeStableCToken__StakingTokenIsNotAsset(
85:                 gauge.stakingToken()
86:             );
87:         }
88: 
89:         
90:         if (!IVeloPool(_asset).stable()) {
91:             revert AerodromeStableCToken__AssetIsNotStable();
92:         }
93: 
94:         
95:         strategyData.token0 = IVeloPool(_asset).token0();
96:         strategyData.token1 = IVeloPool(_asset).token1();
97:         
98:         
99:         if (strategyData.token1 == address(rewardToken)) {
100:             strategyData.token1 = strategyData.token0;
101:             strategyData.token0 = address(rewardToken);
102:         }
103:         strategyData.decimalsA = 10 ** IERC20(strategyData.token0).decimals();
104:         strategyData.decimalsB = 10 ** IERC20(strategyData.token1).decimals();
105: 
106:         strategyData.gauge = gauge;
107:         strategyData.router = router;
108:         strategyData.pairFactory = pairFactory;
109: 
110:         isUnderlyingToken[strategyData.token0] = true;
111:         isUnderlyingToken[strategyData.token1] = true;
112: 
113:         rewardTokenIsUnderlying = (address(rewardToken) ==
114:             strategyData.token0 ||
115:             address(rewardToken) == strategyData.token1);
116:     }

['67']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IERC20 asset_, // <= FOUND
68:         address marketManager_, // <= FOUND
69:         IVeloGauge gauge,
70:         IVeloPairFactory pairFactory,
71:         IVeloRouter router
72:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73:         if (block.chainid != 8453) {
74:             revert AerodromeVolatileCToken__ChainIsNotSupported();
75:         }
76: 
77:         
78:         address _asset = asset(); // <= FOUND
79:         
80:         
81:         if (gauge.stakingToken() != _asset) {
82:             revert AerodromeVolatileCToken__StakingTokenIsNotAsset(
83:                 gauge.stakingToken()
84:             );
85:         }
86: 
87:         
88:         if (IVeloPool(_asset).stable()) {
89:             revert AerodromeVolatileCToken__AssetIsNotStable();
90:         }
91: 
92:         
93:         strategyData.token0 = IVeloPool(_asset).token0();
94:         strategyData.token1 = IVeloPool(_asset).token1();
95:         
96:         
97:         if (strategyData.token1 == address(rewardToken)) {
98:             strategyData.token1 = strategyData.token0;
99:             strategyData.token0 = address(rewardToken);
100:         }
101:         strategyData.gauge = gauge;
102:         strategyData.router = router;
103:         strategyData.pairFactory = pairFactory;
104: 
105:         isUnderlyingToken[strategyData.token0] = true;
106:         isUnderlyingToken[strategyData.token1] = true;
107: 
108:         rewardTokenIsUnderlying = (address(rewardToken) ==
109:             strategyData.token0 ||
110:             address(rewardToken) == strategyData.token1);
111:     }

['65']

63:     constructor(
64:         ICentralRegistry centralRegistry_,
65:         IERC20 asset_, // <= FOUND
66:         address marketManager_, // <= FOUND
67:         uint256 pid_,
68:         address rewarder_, // <= FOUND
69:         address booster_ // <= FOUND
70:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
71:         strategyData.pid = pid_;
72:         strategyData.booster = IBooster(booster_);
73: 
74:         
75:         (address pidToken, , , address balRewards, , bool shutdown) = IBooster( // <= FOUND
76:             booster_
77:         ).poolInfo(strategyData.pid);
78: 
79:         
80:         
81:         if (pidToken != asset() || shutdown || balRewards != rewarder_) {
82:             revert AuraCToken__InvalidVaultConfig();
83:         }
84: 
85:         strategyData.rewarder = IBaseRewardPool(rewarder_);
86:         strategyData.balancerVault = IBalancerVault(
87:             IBalancerPool(pidToken).getVault()
88:         );
89:         strategyData.balancerPoolId = IBalancerPool(pidToken).getPoolId();
90: 
91:         
92:         
93:         strategyData.rewardTokens.push() = _BAL;
94:         
95:         
96:         strategyData.rewardTokens.push() = _AURA;
97: 
98:         uint256 extraRewardsLength = IBaseRewardPool(rewarder_)
99:             .extraRewardsLength();
100:         for (uint256 i; i < extraRewardsLength; ) {
101:             unchecked {
102:                 address rewardToken = IStashWrapper( // <= FOUND
103:                     IRewards(IBaseRewardPool(rewarder_).extraRewards(i++))
104:                         .rewardToken()
105:                 ).baseToken();
106: 
107:                 if (rewardToken != _AURA && rewardToken != _BAL) {
108:                     strategyData.rewardTokens.push() = rewardToken;
109:                 }
110:             }
111:         }
112: 
113:         
114:         (address[] memory queriedTokens, , ) = strategyData
115:             .balancerVault
116:             .getPoolTokens(strategyData.balancerPoolId);
117:         strategyData.underlyingTokens = queriedTokens;
118: 
119:         uint256 numUnderlyingTokens = strategyData.underlyingTokens.length;
120:         for (uint256 i; i < numUnderlyingTokens; ) {
121:             unchecked {
122:                 isUnderlyingToken[strategyData.underlyingTokens[i++]] = true;
123:             }
124:         }
125:     }

['30']

28:     constructor(
29:         ICentralRegistry centralRegistry_,
30:         IERC20 asset_, // <= FOUND
31:         address marketManager_ // <= FOUND
32:     ) CTokenCompounding(
33:         centralRegistry_,
34:         asset_,
35:         marketManager_
36:     ) {
37:         nativeYieldManager = IBlastCentralRegistry(address(centralRegistry_)).nativeYieldManager();
38: 
39:         
40:         
41:         CHAIN_YIELD_MANAGER.configureClaimableGas();
42:     }

['101']

99:     constructor(
100:         ICentralRegistry centralRegistry_,
101:         IERC20 asset_, // <= FOUND
102:         address MarketManager_ // <= FOUND
103:     ) Delegable(centralRegistry_) {
104:         _asset = asset_;
105:         _name = string.concat("Curvance collateralized ", asset_.name());
106:         _symbol = string.concat("c", asset_.symbol());
107:         _decimals = asset_.decimals();
108: 
109:         if (
110:             !ERC165Checker.supportsInterface(
111:                 address(centralRegistry_),
112:                 type(ICentralRegistry).interfaceId
113:             )
114:         ) {
115:             revert CTokenBase__InvalidCentralRegistry();
116:         }
117: 
118:         
119:         if (!centralRegistry.isMarketManager(MarketManager_)) {
120:             revert CTokenBase__InvalidMarketManager();
121:         }
122: 
123:         
124:         marketManager = IMarketManager(MarketManager_);
125: 
126:         
127:         
128:         if (asset_.totalSupply() >= type(uint232).max) {
129:             revert CTokenBase__UnderlyingAssetTotalSupplyExceedsMaximum();
130:         }
131:     }

['37']

35:     constructor(
36:         ICentralRegistry centralRegistry_,
37:         IERC20 asset_, // <= FOUND
38:         address marketManager_, // <= FOUND
39:         uint256 exitFee_
40:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
41:         _setExitFee(exitFee_);
42:     }

['21']

21:     constructor(address _target) { // <= FOUND
22:         target = _target;
23:     }

['91']

89:     constructor(
90:         ICentralRegistry centralRegistry_,
91:         address marketManager_, // <= FOUND
92:         address WETH_ // <= FOUND
93:     ) {
94:         if (
95:             !ERC165Checker.supportsInterface(
96:                 address(centralRegistry_),
97:                 type(ICentralRegistry).interfaceId
98:             )
99:         ) {
100:             revert ComplexZapper__InvalidCentralRegistry();
101:         }
102: 
103:         centralRegistry = centralRegistry_;
104: 
105:         
106:         
107:         if (!centralRegistry.isMarketManager(marketManager_)) {
108:             revert ComplexZapper__InvalidMarketManager();
109:         }
110: 
111:         marketManager = IMarketManager(marketManager_);
112:         WETH = WETH_;
113:     }

['67']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IERC20 asset_, // <= FOUND
68:         address marketManager_, // <= FOUND
69:         uint256 pid_,
70:         address rewarder_, // <= FOUND
71:         address booster_ // <= FOUND
72:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73:         
74:         
75:         if (pid_ <= 176) {
76:             revert Convex2PoolCToken__UnsafePool();
77:         }
78: 
79:         strategyData.pid = pid_;
80:         strategyData.booster = IBooster(booster_);
81: 
82:         
83:         (address pidToken, , , address crvRewards, , bool shutdown) = IBooster( // <= FOUND
84:             booster_
85:         ).poolInfo(strategyData.pid);
86: 
87:         
88:         
89:         if (
90:             pidToken != address(asset_) || shutdown || crvRewards != rewarder_
91:         ) {
92:             revert Convex2PoolCToken__InvalidVaultConfig();
93:         }
94: 
95:         strategyData.curvePool = ICurveFi(pidToken); // <= FOUND
96: 
97:         uint256 coinsLength;
98:         address token; // <= FOUND
99: 
100:         
101:         while (true) {
102:             try ICurveFi(pidToken).coins(coinsLength) {
103:                 ++coinsLength;
104:             } catch {
105:                 break;
106:             }
107:         }
108: 
109:         
110:         if (coinsLength != 2) {
111:             revert Convex2PoolCToken__InvalidCoinLength();
112:         }
113: 
114:         strategyData.rewarder = IBaseRewardPool(rewarder_);
115: 
116:         
117:         
118:         reQueryRewardTokens();
119: 
120:         
121:         strategyData.underlyingTokens = new address[](coinsLength);
122:         for (uint256 i; i < coinsLength; ) {
123:             token = ICurveFi(pidToken).coins(i);
124:             strategyData.underlyingTokens[i] = token;
125:             isUnderlyingToken[token] = true;
126: 
127:             unchecked {
128:                 ++i;
129:             }
130:         }
131:     }

['67']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IERC20 asset_, // <= FOUND
68:         address marketManager_, // <= FOUND
69:         uint256 pid_,
70:         address rewarder_, // <= FOUND
71:         address booster_ // <= FOUND
72:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73:         
74:         
75:         if (pid_ <= 176) {
76:             revert Convex3PoolCToken__UnsafePool();
77:         }
78: 
79:         strategyData.pid = pid_;
80:         strategyData.booster = IBooster(booster_);
81: 
82:         
83:         (address pidToken, , , address crvRewards, , bool shutdown) = IBooster( // <= FOUND
84:             booster_
85:         ).poolInfo(strategyData.pid);
86: 
87:         
88:         
89:         if (
90:             pidToken != address(asset_) || shutdown || crvRewards != rewarder_
91:         ) {
92:             revert Convex3PoolCToken__InvalidVaultConfig();
93:         }
94: 
95:         strategyData.curvePool = ICurveFi(pidToken); // <= FOUND
96: 
97:         uint256 coinsLength;
98:         address token; // <= FOUND
99: 
100:         
101:         while (true) {
102:             try ICurveFi(pidToken).coins(coinsLength) {
103:                 ++coinsLength;
104:             } catch {
105:                 break;
106:             }
107:         }
108: 
109:         
110:         if (coinsLength != 3) {
111:             revert Convex3PoolCToken__InvalidCoinLength();
112:         }
113: 
114:         strategyData.rewarder = IBaseRewardPool(rewarder_);
115: 
116:         
117:         
118:         reQueryRewardTokens();
119: 
120:         
121:         strategyData.underlyingTokens = new address[](coinsLength);
122:         for (uint256 i; i < coinsLength; ) {
123:             token = ICurveFi(pidToken).coins(i);
124:             strategyData.underlyingTokens[i] = token;
125:             isUnderlyingToken[token] = true;
126: 
127:             unchecked {
128:                 ++i;
129:             }
130:         }
131:     }

['162']

160:     constructor(
161:         ICentralRegistry centralRegistry_,
162:         address underlying_, // <= FOUND
163:         address marketManager_, // <= FOUND
164:         address interestRateModel_ // <= FOUND
165:     ) Delegable(centralRegistry_) {
166:         if (
167:             !ERC165Checker.supportsInterface(
168:                 address(centralRegistry_),
169:                 type(ICentralRegistry).interfaceId
170:             )
171:         ) {
172:             revert DToken__InvalidCentralRegistry();
173:         }
174: 
175:         
176:         
177:         if (!centralRegistry.isMarketManager(marketManager_)) {
178:             revert DToken__MarketManagerIsNotLendingMarket();
179:         }
180: 
181:         
182:         marketManager = IMarketManager(marketManager_);
183: 
184:         
185:         marketData.lastTimestampUpdated = uint40(block.timestamp);
186:         marketData.exchangeRate = uint216(WAD);
187: 
188:         _setInterestRateModel(IInterestRateModel(interestRateModel_));
189: 
190:         
191:         
192:         uint256 newInterestFactor = centralRegistry.protocolInterestFactor(
193:             marketManager_
194:         );
195:         interestFactor = newInterestFactor;
196: 
197:         emit NewInterestFactor(0, newInterestFactor);
198: 
199:         underlying = underlying_;
200:         name = string.concat(
201:             "Curvance interest bearing ",
202:             IERC20(underlying_).name()
203:         );
204:         symbol = string.concat("c", IERC20(underlying_).symbol());
205: 
206:         
207:         
208:         if (IERC20(underlying).totalSupply() >= type(uint232).max) {
209:             revert DToken__UnderlyingAssetTotalSupplyExceedsMaximum();
210:         }
211:     }

['78']

76:     constructor(
77:         ICentralRegistry centralRegistry_,
78:         address gmxReader_, // <= FOUND
79:         address gmxDataStore_ // <= FOUND
80:     ) BaseOracleAdaptor(centralRegistry_) {
81:         if (block.chainid != 42161) {
82:             revert GMAdaptor__ChainIsNotSupported();
83:         }
84: 
85:         _setGMXReader(gmxReader_);
86:         _setGMXDataStore(gmxDataStore_);
87:     }

['185']

183:     constructor(
184:         ICentralRegistry centralRegistry_,
185:         address gaugePool_ // <= FOUND
186:     ) LiquidityManager(centralRegistry_) {
187:         if (
188:             !ERC165Checker.supportsInterface(
189:                 address(gaugePool_),
190:                 type(IGaugePool).interfaceId
191:             )
192:         ) {
193:             _revert(_INVALID_PARAMETER_SELECTOR);
194:         }
195: 
196:         gaugePool = IGaugePool(gaugePool_); // <= FOUND
197:     }

['61']

59:     constructor(
60:         ICentralRegistry centralRegistry_,
61:         IERC20 asset_, // <= FOUND
62:         address marketManager_, // <= FOUND
63:         IPendleRouter router_
64:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
65:         strategyData.router = router_;
66:         strategyData.lp = IPMarket(address(asset_));
67:         
68:         (strategyData.sy, strategyData.pt, strategyData.yt) = strategyData
69:             .lp
70:             .readTokens();
71: 
72:         strategyData.rewardTokens = strategyData.lp.getRewardTokens();
73: 
74:         
75:         
76:         strategyData.underlyingTokens = strategyData.sy.getTokensIn();
77:         uint256 numUnderlyingTokens = strategyData.underlyingTokens.length;
78:         for (uint256 i; i < numUnderlyingTokens; ) {
79:             unchecked {
80:                 isUnderlyingToken[strategyData.underlying