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.underlyingTokens[i++]] = true;
81:             }
82:         }
83:     }

['139']

137:     constructor(
138:         ICentralRegistry centralRegistry_,
139:         address marketManager_ // <= FOUND
140:     ) Delegable(centralRegistry_) {
141:         if (
142:             !ERC165Checker.supportsInterface(
143:                 address(centralRegistry_),
144:                 type(ICentralRegistry).interfaceId
145:             )
146:         ) {
147:             revert PositionFolding__InvalidCentralRegistry();
148:         }
149: 
150:         
151:         
152:         if (!centralRegistry_.isMarketManager(marketManager_)) {
153:             revert PositionFolding__InvalidMarketManager();
154:         }
155: 
156:         marketManager = IMarketManager(marketManager_);
157:     }

['15']

15:     constructor(address _sDai, address _dai, address _daiAggregator) { // <= FOUND
16:         sDai = _sDai;
17:         dai = _dai;
18:         daiAggregator = _daiAggregator;
19:     }

['55']

53:     constructor(
54:         ICentralRegistry centralRegistry_,
55:         address marketManager_, // <= FOUND
56:         address WETH_ // <= FOUND
57:     ) {
58:         if (
59:             !ERC165Checker.supportsInterface(
60:                 address(centralRegistry_),
61:                 type(ICentralRegistry).interfaceId
62:             )
63:         ) {
64:             revert SimpleZapper__InvalidCentralRegistry();
65:         }
66: 
67:         centralRegistry = centralRegistry_;
68: 
69:         
70:         
71:         if (!centralRegistry.isMarketManager(marketManager_)) {
72:             revert SimpleZapper__InvalidMarketManager();
73:         }
74: 
75:         marketManager = IMarketManager(marketManager_);
76:         WETH = WETH_;
77:     }

['15']

15:     constructor(address _sFrax, address _frax, address _fraxAggregator) { // <= FOUND
16:         sFrax = _sFrax;
17:         frax = _frax;
18:         fraxAggregator = _fraxAggregator;
19:     }

['68']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IStaticOracle oracleAddress_,
68:         address WETH_ // <= FOUND
69:     ) BaseOracleAdaptor(centralRegistry_) {
70:         uniswapOracleRouter = oracleAddress_;
71:         WETH = WETH_;
72:     }

['70']

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

['60']

58:     constructor(
59:         ICentralRegistry centralRegistry_,
60:         IERC20 asset_, // <= FOUND
61:         address marketManager_, // <= FOUND
62:         IVeloGauge gauge,
63:         IVeloPairFactory pairFactory,
64:         IVeloRouter router
65:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
66:         if (block.chainid != 10) {
67:             revert VelodromeVolatileCToken__ChainIsNotSupported();
68:         }
69: 
70:         
71:         address _asset = asset(); // <= FOUND
72:         
73:         
74:         if (gauge.stakingToken() != _asset) {
75:             revert VelodromeVolatileCToken__StakingTokenIsNotAsset(
76:                 gauge.stakingToken()
77:             );
78:         }
79: 
80:         
81:         if (IVeloPool(_asset).stable()) {
82:             revert VelodromeVolatileCToken__AssetIsNotStable();
83:         }
84: 
85:         
86:         strategyData.token0 = IVeloPool(_asset).token0();
87:         strategyData.token1 = IVeloPool(_asset).token1();
88:         
89:         
90:         if (strategyData.token1 == address(rewardToken)) {
91:             strategyData.token1 = strategyData.token0;
92:             strategyData.token0 = address(rewardToken);
93:         }
94:         strategyData.gauge = gauge;
95:         strategyData.router = router;
96:         strategyData.pairFactory = pairFactory;
97: 
98:         isUnderlyingToken[strategyData.token0] = true;
99:         isUnderlyingToken[strategyData.token1] = true;
100: 
101:         rewardTokenIsUnderlying = (address(rewardToken) ==
102:             strategyData.token0 ||
103:             address(rewardToken) == strategyData.token1);
104:     }

['15']

15:     constructor(address _wstETH, address _stETH, address _stETHAggregator) { // <= FOUND
16:         wstETH = _wstETH;
17:         stETH = _stETH;
18:         stETHAggregator = _stETHAggregator;
19:     }

[Low-27] Using zero as a parameter

Resolution

Taking 0 as a valid argument in Solidity without checks can lead to severe security issues. A historical example is the infamous 0x0 address bug where numerous tokens were lost. This happens because '0' can be interpreted as an uninitialized address, leading to transfers to the '0x0' address, effectively burning tokens. Moreover, 0 as a denominator in division operations would cause a runtime exception. It's also often indicative of a logical error in the caller's code. It's important to always validate input and handle edge cases like 0 appropriately. Use require() statements to enforce conditions and provide clear error messages to facilitate debugging and safer code.

Num of instances: 16

Findings

Click to show findings

['129']

129:     function harvest(
130:         bytes calldata data
131:     ) external override returns (uint256 yield) {
132:         
133:         _canCompound();
134: 
135:         
136:         _vestIfNeeded();
137: 
138:         
139:         if (_checkVestStatus(_vaultData)) {
140:             _updateVestingPeriodIfNeeded();
141: 
142:             
143:             StrategyData memory sd = strategyData;
144: 
145:             
146:             sd.gauge.getReward(address(this));
147: 
148:             {
149:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
150: 
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount,
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert AerodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert AerodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0); // <= FOUND
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['124']

124:     function harvest(
125:         bytes calldata data
126:     ) external override returns (uint256 yield) {
127:         
128:         _canCompound();
129: 
130:         
131:         _vestIfNeeded();
132: 
133:         
134:         if (_checkVestStatus(_vaultData)) {
135:             _updateVestingPeriodIfNeeded();
136: 
137:             
138:             StrategyData memory sd = strategyData;
139: 
140:             
141:             sd.gauge.getReward(address(this));
142: 
143:             {
144:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
145:                 
146:                 if (rewardAmount > 0) {
147:                     
148:                     
149:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
150:                         rewardAmount,
151:                         centralRegistry.protocolHarvestFee(),
152:                         1e18
153:                     );
154:                     rewardAmount -= protocolFee;
155:                     SafeTransferLib.safeTransfer(
156:                         address(rewardToken),
157:                         centralRegistry.feeAccumulator(),
158:                         protocolFee
159:                     );
160: 
161:                     
162:                     if (!rewardTokenIsUnderlying) {
163:                         SwapperLib.Swap memory swapData = abi.decode(
164:                             data,
165:                             (SwapperLib.Swap)
166:                         );
167: 
168:                         if (!centralRegistry.isSwapper(swapData.target)) {
169:                             revert AerodromeVolatileCToken__InvalidSwapper(
170:                                 swapData.target
171:                             );
172:                         }
173: 
174:                         SwapperLib.swap(centralRegistry, swapData);
175:                     }
176:                 }
177:             }
178: 
179:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
180:             
181:             if (totalAmountA == 0) {
182:                 revert AerodromeVolatileCToken__SlippageError();
183:             }
184: 
185:             
186:             address _asset = asset();
187:             
188:             
189:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
190:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0()
191:                 ? r0
192:                 : r1;
193: 
194:             
195:             
196:             
197:             uint256 swapAmount = VelodromeLib._optimalDeposit(
198:                 address(sd.pairFactory),
199:                 _asset,
200:                 totalAmountA,
201:                 reserveA,
202:                 0,
203:                 0,
204:                 0,
205:                 false
206:             );
207:             
208:             VelodromeLib._swapExactTokensForTokens(
209:                 address(sd.router),
210:                 _asset,
211:                 sd.token0,
212:                 sd.token1,
213:                 swapAmount,
214:                 false
215:             );
216:             totalAmountA -= swapAmount;
217: 
218:             
219:             yield = VelodromeLib._addLiquidity(
220:                 address(sd.router),
221:                 sd.token0,
222:                 sd.token1,
223:                 false,
224:                 totalAmountA,
225:                 IERC20(sd.token1).balanceOf(address(this)), 
226:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
227:             );
228: 
229:             
230:             
231:             _afterDeposit(yield, 0); // <= FOUND
232: 
233:             
234:             _setNewVaultData(yield, vestPeriod);
235: 
236:             emit Harvest(yield);
237:         }
238:         
239:     }

['70']

70:     function harvest(
71:         bytes calldata data
72:     ) external override returns (uint256 yield) {
73:         
74:         _canCompound();
75: 
76:         
77:         _vestIfNeeded();
78: 
79:         
80:         if (_checkVestStatus(_vaultData)) {
81:             _updateVestingPeriodIfNeeded();
82: 
83:             
84:             rewardRouter.handleRewards(
85:                 true,
86:                 true,
87:                 true,
88:                 true,
89:                 true,
90:                 true,
91:                 false
92:             );
93:             uint256 rewardAmount = WETH.balanceOf(address(this));
94: 
95:             
96:             if (rewardAmount > 0) {
97:                 
98:                     
99:                 uint256 protocolFee = FixedPointMathLib.mulDiv(
100:                     rewardAmount,
101:                     centralRegistry.protocolHarvestFee(),
102:                     1e18
103:                 );
104:                 rewardAmount -= protocolFee;
105:                 SafeTransferLib.safeTransfer(
106:                     address(WETH),
107:                     centralRegistry.feeAccumulator(),
108:                     protocolFee
109:                 );
110: 
111:                 SwapperLib.Swap memory swapData = abi.decode(
112:                     data,
113:                     (SwapperLib.Swap)
114:                 );
115: 
116:                 if (!centralRegistry.isSwapper(swapData.target)) {
117:                     revert StakedGMXCToken__InvalidSwapper(swapData.target);
118:                 }
119: 
120:                 yield = SwapperLib.swap(centralRegistry, swapData);
121: 
122:                 
123:                 if (yield == 0) {
124:                     revert StakedGMXCToken__SlippageError();
125:                 }
126:             }
127: 
128:             
129:             
130:             _afterDeposit(yield, 0); // <= FOUND
131: 
132:             
133:             _setNewVaultData(yield, vestPeriod);
134: 
135:             emit Harvest(yield);
136:         }
137:     }

['130']

130:     function harvest(
131:         bytes calldata data
132:     ) external override returns (uint256 yield) {
133:         
134:         _canCompound();
135: 
136:         
137:         _vestIfNeeded();
138: 
139:         
140:         if (_checkVestStatus(_vaultData)) {
141:             _updateVestingPeriodIfNeeded();
142: 
143:             
144:             StrategyData memory sd = strategyData;
145: 
146:             
147:             sd.gauge.getReward(address(this));
148: 
149:             {
150:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount, 
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert VelodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert VelodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0); // <= FOUND
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['117']

117:     function harvest(
118:         bytes calldata data
119:     ) external override returns (uint256 yield) {
120:         
121:         _canCompound();
122: 
123:         
124:         _vestIfNeeded();
125: 
126:         
127:         if (_checkVestStatus(_vaultData)) {
128:             _updateVestingPeriodIfNeeded();
129: 
130:             
131:             StrategyData memory sd = strategyData;
132: 
133:             
134:             sd.gauge.getReward(address(this));
135: 
136:             {
137:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
138:                 
139:                 if (rewardAmount > 0) {
140:                     
141:                     
142:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
143:                         rewardAmount, 
144:                         centralRegistry.protocolHarvestFee(),
145:                         1e18
146:                     );
147:                     rewardAmount -= protocolFee;
148:                     SafeTransferLib.safeTransfer(
149:                         address(rewardToken),
150:                         centralRegistry.feeAccumulator(),
151:                         protocolFee
152:                     );
153: 
154:                     
155:                     if (!rewardTokenIsUnderlying) {
156:                         SwapperLib.Swap memory swapData = abi.decode(
157:                             data,
158:                             (SwapperLib.Swap)
159:                         );
160: 
161:                         if (!centralRegistry.isSwapper(swapData.target)) {
162:                             revert VelodromeVolatileCToken__InvalidSwapper(
163:                                 swapData.target
164:                             );
165:                         }
166: 
167:                         SwapperLib.swap(centralRegistry, swapData);
168:                     }
169:                 }
170:             }
171: 
172:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
173: 
174:             
175:             if (totalAmountA == 0) {
176:                 revert VelodromeVolatileCToken__SlippageError();
177:             }
178: 
179:             
180:             address _asset = asset();
181:             
182:             
183:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
184:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0()
185:                 ? r0
186:                 : r1;
187: 
188:             
189:             
190:             
191:             uint256 swapAmount = VelodromeLib._optimalDeposit(
192:                 address(sd.pairFactory),
193:                 _asset,
194:                 totalAmountA,
195:                 reserveA,
196:                 0,
197:                 0,
198:                 0,
199:                 false
200:             );
201:             
202:             VelodromeLib._swapExactTokensForTokens(
203:                 address(sd.router),
204:                 _asset,
205:                 sd.token0,
206:                 sd.token1,
207:                 swapAmount,
208:                 false
209:             );
210:             totalAmountA -= swapAmount;
211: 
212:             
213:             yield = VelodromeLib._addLiquidity(
214:                 address(sd.router),
215:                 sd.token0,
216:                 sd.token1,
217:                 false,
218:                 totalAmountA,
219:                 IERC20(sd.token1).balanceOf(address(this)), 
220:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
221:             );
222: 
223:             
224:             
225:             _afterDeposit(yield, 0); // <= FOUND
226: 
227:             
228:             _setNewVaultData(yield, vestPeriod);
229: 
230:             emit Harvest(yield);
231:         }
232:     }

['212']

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) {
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) {
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); // <= FOUND
334: 
335:             
336:             _setNewVaultData(yield, vestPeriod);
337: 
338:             emit Harvest(yield);
339:         }
340:     }

['177']

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) {
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) {
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); // <= FOUND
261: 
262:             
263:             _setNewVaultData(yield, vestPeriod);
264: 
265:             emit Harvest(yield);
266:         }
267:     }

['177']

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) {
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) {
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); // <= FOUND
261: 
262:             
263:             _setNewVaultData(yield, vestPeriod);
264: 
265:             emit Harvest(yield);
266:         }
267:     }

['100']

100:     function withdrawByPositionFolding(
101:         address owner,
102:         uint256 assets,
103:         bytes calldata params
104:     ) external nonReentrant {
105:         
106:         if (msg.sender != marketManager.positionFolding()) {
107:             _revert(_UNAUTHORIZED_SELECTOR);
108:         }
109: 
110:         
111:         uint256 pending = _calculatePendingRewards();
112:         uint256 ta = _totalAssets + pending;
113:         uint256 balancePrior = balanceOf(owner);
114: 
115:         
116:         if (assets > _convertToAssets(balancePrior, ta)) {
117:             
118:             _revert(0x2735eaab);
119:         }
120: 
121:         
122:         uint256 shares = _previewWithdraw(assets, ta);
123: 
124:         
125:         _gaugePool().withdraw(address(this), owner, shares);
126:         
127:         
128:         
129:         _processWithdraw(
130:             msg.sender,
131:             msg.sender,
132:             owner,
133:             assets,
134:             shares,
135:             ta,
136:             pending
137:         );
138: 
139:         
140:         IPositionFolding(msg.sender).onRedeem(
141:             address(this),
142:             owner,
143:             assets,
144:             params
145:         );
146: 
147:         
148:         marketManager.reduceCollateralIfNecessary(
149:             owner,
150:             address(this),
151:             balancePrior,
152:             shares
153:         );
154: 
155:         
156:         marketManager.canRedeemWithPrune(address(this), owner, 0);
157:     }

['41']

41:     function withdrawByPositionFolding(
42:         address owner,
43:         uint256 assets,
44:         bytes calldata params
45:     ) external nonReentrant {
46:         
47:         if (msg.sender != marketManager.positionFolding()) {
48:             _revert(_UNAUTHORIZED_SELECTOR);
49:         }
50: 
51:         
52:         uint256 ta = _totalAssets;
53:         uint256 balancePrior = balanceOf(owner);
54: 
55:         
56:         
57:         if (assets > _convertToAssets(balancePrior, ta)) {
58:             
59:             _revert(0xc6e63cc0);
60:         }
61: 
62:         
63:         uint256 shares = _previewWithdraw(assets, ta);
64: 
65:         
66:         _gaugePool().withdraw(address(this), owner, shares);
67:         
68:         
69:         
70:         _processWithdraw(msg.sender, msg.sender, owner, assets, shares, ta);
71: 
72:         
73:         IPositionFolding(msg.sender).onRedeem(
74:             address(this),
75:             owner,
76:             assets,
77:             params
78:         );
79: 
80:         
81:         marketManager.reduceCollateralIfNecessary(
82:             owner,
83:             address(this),
84:             balancePrior,
85:             shares
86:         );
87:         
88:         marketManager.canRedeemWithPrune(address(this), owner, 0);
89:     }

['52']

52:     function isLocked(
53:         address curvePool,
54:         uint256 coinsLength
55:     ) public view returns (bool) {
56:         uint256 gasLimit = reentrancyConfig[coinsLength];
57: 
58:         if (gasLimit == 0) {
59:             revert CurveBaseAdaptor__PoolNotFound();
60:         }
61: 
62:         uint256 gasStart = gasleft();
63: 
64:         ICurveRemoveLiquidity pool = ICurveRemoveLiquidity(curvePool);
65: 
66:         if (coinsLength == 2) {
67:             uint256[2] memory amounts;
68:             try pool.remove_liquidity{ gas: gasLimit }(0, amounts) {} catch ( // <= FOUND
69:                 bytes memory
70:             ) {}
71:         } else if (coinsLength == 3) {
72:             uint256[3] memory amounts;
73:             try pool.remove_liquidity{ gas: gasLimit }(0, amounts) {} catch ( // <= FOUND
74:                 bytes memory
75:             ) {}
76:         }
77:         if (coinsLength == 4) {
78:             uint256[4] memory amounts;
79:             try pool.remove_liquidity{ gas: gasLimit }(0, amounts) {} catch ( // <= FOUND
80:                 bytes memory
81:             ) {}
82:         }
83: 
84:         uint256 gasSpent;
85:         
86:         unchecked {
87:             gasSpent = gasStart - gasleft();
88:         }
89: 
90:         return
91:             gasSpent > gasLimit
92:                 ? false 
93:                 : true ;
94:     }

['326']

326:     function borrowForPositionFolding(
327:         address account,
328:         uint256 amount,
329:         bytes calldata params
330:     ) external nonReentrant {
331:         if (msg.sender != marketManager.positionFolding()) {
332:             _revert(_UNAUTHORIZED_SELECTOR);
333:         }
334: 
335:         
336:         accrueInterest();
337: 
338:         
339:         
340:         marketManager.notifyBorrow(address(this), account);
341: 
342:         _borrow(account, amount, msg.sender);
343: 
344:         
345:         IPositionFolding(msg.sender).onBorrow(
346:             address(this),
347:             account,
348:             amount,
349:             params
350:         );
351: 
352:         
353:         
354:         marketManager.canBorrowWithPrune(address(this), account, 0);
355:     }

['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:     }

['179']

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);
194: 
195:         uint64 sequence = tokenBridge.transferTokensWithPayload{
196:             value: messageFee
197:         }(
198:             token,
199:             amount,
200:             wormholeChainId,
201:             bytes32(uint256(uint160(to))),
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)))),
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:     }

['350']

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); // <= FOUND
370:             data[1] = _getPriceFromFeed(asset, 0, inUSD, false); // <= FOUND
371:             if (isMToken) {
372:                 uint256 exchangeRate = IMToken(asset).exchangeRateCached();
373:                 data[0].price = uint240((data[0].price * exchangeRate) / WAD);
374:                 data[1].price = uint240((data[1].price * exchangeRate) / WAD);
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); // <= FOUND
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);
390:             data[1].price = uint240((data[1].price * exchangeRate) / WAD);
391:             data[2].price = uint240((data[2].price * exchangeRate) / WAD);
392:             data[3].price = uint240((data[3].price * exchangeRate) / WAD);
393:         }
394: 
395:         return data;
396:     }

['661']

661:     function _getPriceDualFeed(
662:         address asset,
663:         bool inUSD,
664:         bool getLower
665:     ) internal view returns (uint256, uint256) {
666:         FeedData memory feed0 = _getPriceFromFeed(asset, 0, inUSD, getLower); // <= FOUND
667:         FeedData memory feed1 = _getPriceFromFeed(asset, 1, inUSD, getLower);
668: 
669:         
670:         
671:         if (feed0.hadError && feed1.hadError) return (0, BAD_SOURCE);
672: 
673:         
674:         if (feed0.hadError || feed1.hadError) {
675:             return (_getWorkingPrice(feed0, feed1), CAUTION);
676:         }
677:         if (getLower) {
678:             return _calculateLowerPrice(feed0.price, feed1.price);
679:         }
680: 
681:         return _calculateHigherPrice(feed0.price, feed1.price);
682:     }

[Low-28] Constant decimal values

Resolution

The use of fixed decimal values such as 1e18 or 1e8 in Solidity contracts can lead to inaccuracies, bugs, and vulnerabilities, particularly when interacting with tokens having different decimal configurations. Not all ERC20 tokens follow the standard 18 decimal places, and assumptions about decimal places can lead to miscalculations.

Resolution: Always retrieve and use the decimals() function from the token contract itself when performing calculations involving token amounts. This ensures that your contract correctly handles tokens with any number of decimal places, mitigating the risk of numerical errors or under/overflows that could jeopardize contract integrity and user funds.

Num of instances: 32

Findings

Click to show findings

['160']

155:                     
156:                     
157:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
158:                         rewardAmount,
159:                         centralRegistry.protocolHarvestFee(),
160:                         1e18 // <= FOUND
161:                     );

['230']

224: 
225:                     
226:                     
227:                     protocolFee = FixedPointMathLib.mulDiv(
228:                         rewardAmount, 
229:                         harvestFee, 
230:                         1e18 // <= FOUND
231:                     );

['111']

111:             reserve0 = (reserve0 * 1e18) / (10 ** data.decimals0); // <= FOUND

['115']

115:             reserve1 = (reserve1 * 1e18) / (10 ** data.decimals1); // <= FOUND

['231']

231:         uint256 ratio = ((1e18) * price0) / price1; // <= FOUND

['233']

232:         uint256 sqrtPrice = FixedPointMathLib.sqrt(
233:             FixedPointMathLib.sqrt((1e18) * ratio) * // <= FOUND
234:                 FixedPointMathLib.sqrt(1e36 + ratio * ratio) // <= FOUND
235:         );

['237']

236:         return
237:             ((((1e18) * sqrtReserve) / sqrtPrice) * price0 * 2) / totalSupply; // <= FOUND

['111']

110:         
111:         reserve0 = (reserve0 * 1e18) / (10 ** data.decimals0); // <= FOUND

['111']

111:         reserve1 = (reserve1 * 1e18) / (10 ** data.decimals1); // <= FOUND

['612']

609:         
610:         
611:         _vaultData = _packVaultData(
612:             FixedPointMathLib.mulDiv(yieldToVest, 1e18, periodToVest), // <= FOUND
613:             block.timestamp + periodToVest
614:             );

['728']

711:             
712:             
713:             
714:             
715:             
716:             
717:             
718:             
719:             pendingRewards =
720:                 (
721:                     block.timestamp < vaultData.vestingPeriodEnd
722:                         ? (vaultData.rewardRate *
723:                             (block.timestamp - vaultData.lastVestClaim))
724:                         : (vaultData.rewardRate *
725:                             (vaultData.vestingPeriodEnd -
726:                                 vaultData.lastVestClaim))
727:                 ) /
728:                 1e18; // <= FOUND

['68']

66:         
67:         
68:         return assets - FixedPointMathLib.mulDivUp(exitFee, assets, 1e18); // <= FOUND

['10']

8: 
10: uint256 constant WAD = 1e18; // <= FOUND

['142']

137:                     
138:                     
139:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
140:                         rewardAmounts[i],
141:                         harvestFee,
142:                         1e18 // <= FOUND
143:                     );

['158']

154: 
155:             data[0] = abi.encodeWithSelector(
156:                 IGMXExchangeRouter.sendWnt.selector,
157:                 gmxDepositVault,
158:                 0.01e18 // <= FOUND
159:             );

['188']

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, // <= FOUND
189:                     500000
190:                 )
191:             );

['195']

193: 
194:             bytes[] memory results = IGMXExchangeRouter(gmxExchangeRouter)
195:                 .multicall{ value: 0.01e18 }(data); // <= FOUND

['77']

75:     
77:     uint256 public constant MAX_COLLATERALIZATION_RATIO = .91e18; // <= FOUND

['83']

81:     
83:     uint256 public constant MIN_LIQUIDATION_INCENTIVE = .01e18; // <= FOUND

['92']

90:     
92:     uint256 public constant MIN_BASE_CFACTOR = .1e18; // <= FOUND

['578']

577: 
578:         return ((maxLeverage - sumDebt) * 1e18) / price; // <= FOUND

['160']

155:                 
156:                     
157:                 uint256 protocolFee = FixedPointMathLib.mulDiv(
158:                     rewardAmount,
159:                     centralRegistry.protocolHarvestFee(),
160:                     1e18 // <= FOUND
161:                 );

['240']

240:             a = (((amount0 * 10000) / (10000 - swapFee)) * 1e18) / decimals0; // <= FOUND

['243']

242: 
243:             uint256 x = (reserve0 * 1e18) / decimals0; // <= FOUND

['243']

243:             uint256 y = (reserve1 * 1e18) / decimals1; // <= FOUND

['244']

244:             uint256 x2 = (x * x) / 1e18; // <= FOUND

['245']

245:             uint256 y2 = (y * y) / 1e18; // <= FOUND

['246']

246:             uint256 p = (y * (((x2 * 3 + y2) * 1e18) / (y2 * 3 + x2))) / x; // <= FOUND

['249']

249:             uint256 den = ((a + x) * p) / 1e18 + y; // <= FOUND

['252']

251: 
252:             return ((num / den) * decimals0) / 1e18; // <= FOUND

['160']

155:                     
156:                     
157:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
158:                         rewardAmount, 
159:                         centralRegistry.protocolHarvestFee(),
160:                         1e18 // <= FOUND
161:                     );

['36']

35:         
36:         return IWstETH(wstETH).getStETHByWstETH(1e18); // <= FOUND

[Low-29] Calculation will revert when totalSupply() returns zero

Resolution

In the instance where the function totalSupply() returns zero, it will inevitably lead to a division by zero error when used in mathematical operations, causing the transaction to fail and potentially disrupting contract functionality. This situation can inadvertently serve as a blocking mechanism, preventing valid transactions and operations. It's crucial to accommodate this special scenario in your code. One resolution could be implementing condition checks in your function to detect a zero totalSupply() and handle it differently, perhaps by returning a specific value or altering the operational flow, thus ensuring that transactions do not revert and the contract functions smoothly even in this edge case.

Num of instances: 2

Findings

Click to show findings

['91']

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);
160:     }

['952']

952:     function exchangeRateCached() public view returns (uint256) { // <= FOUND
953:         
954:         
955:         
956:         
957:         return
958:             ((marketUnderlyingHeld() + totalBorrows - totalReserves) * WAD) /
959:             totalSupply;
960:     }

[Low-30] Bridge function must check for native token

Resolution

Functions that bridge ERC20 tokens often do not interact with Ether (ETH), the native cryptocurrency of Ethereum. In these functions, msg.value represents the amount of Ether sent in the transaction.

When a function is marked as payable, it indicates that the function is allowed to receive Ether. However, if these functions are not designed to handle Ether, and someone unintentionally or intentionally sends Ether to these functions, the Ether could be locked in the contract forever, leading to a loss of funds. This is because if the contract does not have a specific function to withdraw or refund this Ether, there's no way to retrieve it.

To mitigate this issue, these functions should explicitly require that msg.value == 0. This statement ensures that no Ether is being sent in the transaction. If msg.value is not equal to zero, the transaction will revert, thereby preventing accidental loss of Ether.

In conclusion, requiring msg.value == 0 in functions that bridge ERC20 tokens ensures that only token transactions are processed, and prevents inadvertent loss of Ether, thereby increasing the security of the contract.

Num of instances: 1

Findings

Click to show findings

['37']

37:     function borrowAndBridge(
38:         address dToken,
39:         uint256 borrowAmount,
40:         SwapperLib.Swap memory swapData,
41:         uint256 dstChainId
42:     ) external payable nonReentrant {
43:         uint256 balancePrior = IERC20(feeToken).balanceOf(address(this));
44: 
45:         
46:         DToken(dToken).borrowFor(msg.sender, address(this), borrowAmount);
47: 
48:         address underlying = DToken(dToken).underlying();
49: 
50:         
51:         if (underlying != feeToken) {
52:             if (
53:                 swapData.target == address(0) ||
54:                 swapData.inputToken != underlying ||
55:                 swapData.outputToken != feeToken ||
56:                 swapData.inputAmount != borrowAmount
57:             ) {
58:                 revert BorrowZapper__InvalidSwapData();
59:             }
60: 
61:             
62:             if (!centralRegistry.isSwapper(swapData.target)) {
63:                 revert BorrowZapper__InvalidSwapper(swapData.target);
64:             }
65:             unchecked {
66:                 SwapperLib.swap(centralRegistry, swapData);
67:             }
68:         } else {
69:             if (swapData.target != address(0)) {
70:                 revert BorrowZapper__InvalidSwapData();
71:             }
72:         }
73: 
74:         
75:         _sendFeeToken(
76:             dstChainId,
77:             msg.sender,
78:             IERC20(feeToken).balanceOf(address(this)) - balancePrior
79:         );
80:     }

[Low-31] A year is not always 365/364 days

Resolution

Assuming a year is always 365/364 days in a Solidity contract could lead to inaccuracies, especially when dealing with time-sensitive functionalities or calculations. The actual length of a year can vary due to the presence of leap years, which contain an extra day. By not accounting for this variation, contracts may exhibit incorrect behavior or calculations, potentially leading to significant discrepancies over time or vulnerabilities. Therefore, it's crucial to use accurate time calculation methods or libraries that account for such variations.

Num of instances: 1

Findings

Click to show findings

['116']

115:     
116:     uint256 public constant LOCK_DURATION = 52 weeks; // <= FOUND

[Low-32] Chainlink answer is not compared against min/max values

Resolution

Chainlink oracle provides reliable, real-time data feeds to smart contracts. However, in order to enhance security and minimize the potential impact of an oracle failure or manipulation, it's a good practice to establish minimum and maximum thresholds for these data inputs. Without this safeguard, an erroneous or maliciously manipulated data point from the oracle could potentially lead to severe consequences in contract behavior. Therefore, the values retrieved from Chainlink's oracle should be cross-verified against preset min/max boundaries to ensure they fall within the expected range. This extra layer of validation adds robustness and reduces the risk of oracle-related issues.

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-33] Unsafe uint to int conversion

Resolution

Unsafe conversion from uint to int in Solidity can lead to unexpected overflows if the unsigned integer value exceeds the positive limit of the corresponding signed integer type. This is due to the fact that signed integers use one bit for the sign, effectively halving the positive range. An example is converting a uint256 number greater than type(uint128).max to an int256, which can result in an overflow. To mitigate this risk, consider using libraries like SafeCast, which include functions specifically designed to safely cast between different numerical types. They perform necessary checks and revert the transaction if an unsafe cast is attempted, thereby enhancing the security and robustness of the code.

Num of instances: 2

Findings

Click to show findings

['140']

140:         return int256(value); // <= FOUND

['174']

174: 
175:         
176:         ICurveSwap(lpMinter).remove_liquidity_one_coin(
177:             lpAmount,
178:             int128(uint128(singleAssetIndex)), // <= FOUND
179:             0
180:         );

[Low-34] Calling internal _grantRole bypasses role access checks

Resolution

OpenZeppelin's AccessControl library is designed with security in mind, encapsulating common access control patterns. Within this library, the grantRole function is public and ensures proper access checks before the role is granted. However, by calling the internal _grantRole function directly, developers bypass these pre-defined access controls. While this may be intentional, allowing for custom access controls, it risks introducing vulnerabilities if not handled with caution. Bypassing the native checks goes against the library's design philosophy, which emphasizes secure defaults. To uphold security, developers should consistently use public functions provided by OpenZeppelin, or, if choosing to call internal functions, ensure they implement and rigorously test their own access controls.

Num of instances: 1

Findings

Click to show findings

['57']

57:     function updateDaoAddress() external {
58:         address daoAddress = centralRegistry.daoAddress();
59:         if (daoAddress != _DAO_ADDRESS) {
60:             _revokeRole(PROPOSER_ROLE, _DAO_ADDRESS);
61:             _revokeRole(EXECUTOR_ROLE, _DAO_ADDRESS);
62: 
63:             _grantRole(PROPOSER_ROLE, daoAddress); // <= FOUND
64:             _grantRole(EXECUTOR_ROLE, daoAddress); // <= FOUND
65:             _DAO_ADDRESS = daoAddress;
66:         }
67:     }

[Low-35] Direct supportsInterface() calls may cause caller to revert

Resolution

When invoking supportsInterface() on a contract, if the addressed contract doesn't adhere to the ERC-165 standard, the call may revert. This potential reversion isn't solely because of non-compliance; a contract could be designed maliciously to consume the entire transaction's gas. To circumvent these pitfalls, opt for a low-level staticcall() when calling supportsInterface(), setting a defined gas limit, and subsequently evaluating the return code. Alternatively, leverage OpenZeppelin's ERC165Checker.supportsInterface() for a safer approach.

Num of instances: 4

Findings

Click to show findings

['699']

699:     function transferDaoOwnership(address newDaoAddress) external { // <= FOUND
700:         _checkElevatedPermissions();
701: 
702:         
703:         address previousDaoAddress = daoAddress;
704:         daoAddress = newDaoAddress;
705: 
706:         
707:         delete hasDaoPermissions[previousDaoAddress];
708:         
709:         hasDaoPermissions[newDaoAddress] = true;
710:         emit OwnershipTransferred(previousDaoAddress, newDaoAddress);
711: 
712:         
713:         if (timelock != address(0)) {
714:             if (
715:                 ERC165Checker.supportsInterface(
716:                     timelock,
717:                     type(ITimelock).interfaceId
718:                 )
719:             ) {
720:                 ITimelock(timelock).updateDaoAddress();
721:             }
722:         }
723:     }

['1141']

1141:     function addMarketManager(
1142:         address newMarketManager,
1143:         uint256 marketInterestFactor
1144:     ) public virtual {
1145:         _checkElevatedPermissions();
1146: 
1147:         
1148:         if (isMarketManager[newMarketManager]) {
1149:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1150:         }
1151: 
1152:         
1153:         if (
1154:             !ERC165Checker.supportsInterface(
1155:                 newMarketManager,
1156:                 type(IMarketManager).interfaceId
1157:             )
1158:         ) {
1159:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1160:         }
1161: 
1162:         
1163:         if (marketInterestFactor > 5000) {
1164:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1165:         }
1166: 
1167:         isMarketManager[newMarketManager] = true;
1168:         
1169:         marketManagers.push(newMarketManager);
1170:         
1171:         
1172:         protocolInterestFactor[newMarketManager] = _bpToWad(
1173:             marketInterestFactor
1174:         );
1175: 
1176:         emit NewCurvanceContract("Market Manager", newMarketManager);
1177:         emit InterestFeeSet(newMarketManager, marketInterestFactor);
1178:     }

['1049']

1049:     function _setInterestRateModel(
1050:         IInterestRateModel newInterestRateModel
1051:     ) internal {
1052:         
1053:         if (
1054:             !ERC165Checker.supportsInterface(
1055:                 address(newInterestRateModel),
1056:                 type(IInterestRateModel).interfaceId
1057:             )
1058:         ) {
1059:             revert DToken__ValidationFailed();
1060:         }
1061: 
1062:         
1063:         address oldInterestRateModel = address(interestRateModel);
1064: 
1065:         
1066:         interestRateModel = newInterestRateModel;
1067:         marketData.compoundRate = newInterestRateModel.compoundRate();
1068: 
1069:         emit NewMarketInterestRateModel(
1070:             oldInterestRateModel,
1071:             address(newInterestRateModel),
1072:             marketData.compoundRate
1073:         );
1074:     }

['1196']

1196:     function setPositionFolding(address newPositionFolding) external { // <= FOUND
1197:         _checkElevatedPermissions();
1198: 
1199:         if (
1200:             !ERC165Checker.supportsInterface(
1201:                 newPositionFolding,
1202:                 type(IPositionFolding).interfaceId
1203:             )
1204:         ) {
1205:             _revert(_INVALID_PARAMETER_SELECTOR);
1206:         }
1207: 
1208:         
1209:         address oldPositionFolding = positionFolding;
1210: 
1211:         
1212:         positionFolding = newPositionFolding;
1213: 
1214:         emit NewPositionFoldingContract(
1215:             oldPositionFolding,
1216:             newPositionFolding
1217:         );
1218:     }

[Low-36] SafeTransferLib does not ensure that the token contract exists

Resolution

SafeTransferLib as similarly named function as OpenZepelins SafeERC20 module however it's functions such as safeTransferFrom don't check if the contract exists which can result in silent failures when performing such operations. As such it is recommended to perform a contract existence check beforehand.

Num of instances: 6

Findings

Click to show findings

['4']

4: import { CTokenCompounding, FixedPointMathLib, SafeTransferLib, IERC20, ICentralRegistry } from "contracts/market/collateral/CTokenCompounding.sol"; // <= FOUND

['11']

11: import { SafeTransferLib } from "contracts/libraries/external/SafeTransferLib.sol"; // <= FOUND

['6']

6: import { ERC4626, SafeTransferLib } from "contracts/libraries/ERC4626.sol"; // <= FOUND

['4']

4: import { CTokenBase, FixedPointMathLib, SafeTransferLib, ERC4626 } from "contracts/market/collateral/CTokenBase.sol"; // <= FOUND

['4']

4: import { CTokenBase, SafeTransferLib, ERC4626 } from "contracts/market/collateral/CTokenBase.sol"; // <= FOUND

['4']

4: import { CTokenCompounding, SafeTransferLib, IERC20, FixedPointMathLib, ICentralRegistry } from "contracts/market/collateral/CTokenCompounding.sol"; // <= FOUND

[Low-37] External calls in modifiers should be avoided

Resolution

External calls within modifiers can introduce unintended reentrancy risks and obscure the flow of a contract's logic. Modifiers are designed to perform checks before executing function logic, and using external calls can make the flow unpredictable due to the potential for state changes or reentrancy by the called contract. Such ambiguity makes code harder to audit and understand. To ensure clarity and security, avoid external calls in modifiers. Instead, place them in the function body, where their execution order and effects are more explicit. This practice enhances contract readability, aids auditors, and minimizes unexpected behaviors.

Num of instances: 1

Findings

Click to show findings

['110']

110:     modifier checkSlippage(address account, uint256 slippage) { // <= FOUND
111:         (uint256 collateralBefore, uint256 debtBefore) = marketManager
112:             .solvencyOf(account);
113:         uint256 liquidityBefore = collateralBefore - debtBefore;
114: 
115:         _;
116: 
117:         (uint256 sumCollateral, uint256 sumDebt) = marketManager.solvencyOf(
118:             account
119:         );
120: 
121:         uint256 liquidityAfter = sumCollateral - sumDebt;
122:         
123:         if (liquidityBefore > liquidityAfter) {
124:             if (
125:                 liquidityBefore - liquidityAfter >=
126:                 (liquidityBefore * slippage) / DENOMINATOR
127:             ) {
128:                 revert PositionFolding__InvalidSlippage();
129:             }
130:         }
131:     }

[Low-38] Using a immutable/constant state variable value as a gas limit can be dangerous

Resolution

Using an immutable or constant state variable as a gas limit in a contract can introduce risks, primarily because gas costs on the Ethereum network can change due to various factors like network upgrades. If the contract relies on a hardcoded gas limit, it may either waste gas if the limit is too high or fail to execute if the limit is too low. Moreover, a fixed gas limit doesn't adapt to different scenarios the contract might encounter, potentially becoming a point of failure or inefficiency. It's often better to allow flexibility in setting gas limits to adapt to real-time network conditions.

Num of instances: 1

Findings

Click to show findings

['84']

79: 
80:         
81:         
82:         
83:         
84:         (, bytes memory revertData) = address(vault).staticcall{ gas: GAS_LIMIT }( // <= FOUND

[Low-39] Prefer skip over revert model in iteration

Resolution

It is preferable to skip operations on an array index when a condition is not met rather than reverting the whole transaction as reverting can introduce the possiblity of malicous actors purposefully introducing array objects which fail conditional checks within for/while loops so group operations fail. As such it is recommended to simply skip such array indices over reverting unless there is a valid security or logic reason behind not doing so.

Num of instances: 1

Findings

Click to show findings

['647']

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

[Low-40] Constructors missing validation

Resolution

In Solidity, when values are being assigned in constructors to unsigned or integer variables, it's crucial to ensure the provided values adhere to the protocol's specific operational boundaries as laid out in the project specifications and documentation. If the constructors lack appropriate validation checks, there's a risk of setting state variables with values that could cause unexpected and potentially detrimental behavior within the contract's operations, violating the intended logic of the protocol. This can compromise the contract's security and impact the maintainability and reliability of the system. In order to avoid such issues, it is recommended to incorporate rigorous validation checks in constructors. These checks should align with the project's defined rules and constraints, making use of Solidity's built-in require function to enforce these conditions. If the validation checks fail, the require function will cause the transaction to revert, ensuring the integrity and adherence to the protocol's expected behavior.

Num of instances: 35

Findings

Click to show findings

['67']

67:     constructor(
68:         ICentralRegistry centralRegistry_,
69:         IERC20 asset_,
70:         address marketManager_,
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();
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; // <= FOUND
107:         strategyData.router = router; // <= FOUND
108:         strategyData.pairFactory = pairFactory; // <= FOUND
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:     }

['65']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IERC20 asset_,
68:         address marketManager_,
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();
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; // <= FOUND
102:         strategyData.router = router; // <= FOUND
103:         strategyData.pairFactory = pairFactory; // <= FOUND
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:     }

['63']

63:     constructor(
64:         ICentralRegistry centralRegistry_,
65:         IERC20 asset_,
66:         address marketManager_,
67:         uint256 pid_,
68:         address rewarder_,
69:         address booster_
70:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
71:         strategyData.pid = pid_; // <= FOUND
72:         strategyData.booster = IBooster(booster_);
73: 
74:         
75:         (address pidToken, , , address balRewards, , bool shutdown) = IBooster(
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(
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; ) {

['29']

29:     constructor(
30:         ICentralRegistry centralRegistry_,
31:         IVault balancerVault_
32:     ) BaseOracleAdaptor(centralRegistry_) {
33:         balancerVault = balancerVault_; // <= FOUND
34:     }

['28']

28:     constructor(ICentralRegistry centralRegistry_) {
29:         if (
30:             !ERC165Checker.supportsInterface(
31:                 address(centralRegistry_),
32:                 type(ICentralRegistry).interfaceId
33:             )
34:         ) {
35:             revert BaseOracleAdaptor__InvalidCentralRegistry();
36:         }
37: 
38:         centralRegistry = centralRegistry_; // <= FOUND
39:     }

['82']

82:     constructor(IBlastCentralRegistry centralRegistry_) {
83:         if (
84:             !ERC165Checker.supportsInterface(
85:                 address(centralRegistry_),
86:                 type(IBlastCentralRegistry).interfaceId
87:             )
88:         ) {
89:             revert BlastNativeYieldManager__InvalidCentralRegistry();
90:         }
91: 
92:         centralRegistry = centralRegistry_; // <= FOUND
93: 
94:         address[] memory marketManagers = centralRegistry_.marketManagers();
95:         uint256 numMarkets = marketManagers.length;
96: 
97:         
98:         for (uint256 i; i < numMarkets; ) {
99:             isMarketManager[marketManagers[i++]] = true;
100:         }
101: 
102:         
103:         
104:         CHAIN_YIELD_MANAGER.configureClaimableGas();
105:         CHAIN_YIELD_MANAGER.configureGovernor(centralRegistry_.daoAddress());
106: 
107:     }

['99']

99:     constructor(
100:         ICentralRegistry centralRegistry_,
101:         IERC20 asset_,
102:         address MarketManager_
103:     ) Delegable(centralRegistry_) {
104:         _asset = asset_; // <= FOUND
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:     }

['57']

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_; // <= FOUND
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);
93:     }

['56']

56:     constructor(
57:         ICentralRegistry centralRegistry_,
58:         uint256 maximumClaimAmount_
59:     ) {
60:         if (
61:             !ERC165Checker.supportsInterface(
62:                 address(centralRegistry_),
63:                 type(ICentralRegistry).interfaceId
64:             )
65:         ) {
66:             revert CVEInitialDistribution__InvalidCentralRegistry();
67:         }
68:         centralRegistry = centralRegistry_; // <= FOUND
69: 
70:         
71:         
72:         
73:         
74:         if (maximumClaimAmount_ * lockedClaimMultiplier > 15750002.59 ether) {
75:             revert CVEInitialDistribution__InvalidlockedClaimMultiplier();
76:         }
77: 
78:         cve = centralRegistry.cve();
79:         veCVE = IVeCVE(centralRegistry.veCVE());
80:         maximumClaimAmount = maximumClaimAmount_; // <= FOUND
81:     }

['21']

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

['248']

248:     constructor(
249:         address daoAddress_,
250:         address timelock_,
251:         address emergencyCouncil_,
252:         uint256 genesisEpoch_,
253:         address sequencer_,
254:         address feeToken_
255:     ) {
256:         if (feeToken_ == address(0)) {
257:             revert CentralRegistry__InvalidFeeToken();
258:         }
259: 
260:         if (daoAddress_ == address(0)) {
261:             daoAddress_ = msg.sender;
262:         }
263: 
264:         if (timelock_ == address(0)) {
265:             timelock_ = msg.sender;
266:         }
267: 
268:         if (emergencyCouncil_ == address(0)) {
269:             emergencyCouncil_ = msg.sender;
270:         }
271: 
272:         
273:         daoAddress = daoAddress_;
274:         timelock = timelock_;
275:         emergencyCouncil = emergencyCouncil_;
276: 
277:         
278:         
279:         hasDaoPermissions[daoAddress] = true;
280:         hasDaoPermissions[timelock] = true;
281:         hasDaoPermissions[emergencyCouncil] = true;
282: 
283:         
284:         
285:         hasElevatedPermissions[timelock] = true;
286:         hasElevatedPermissions[emergencyCouncil] = true;
287: 
288:         genesisEpoch = genesisEpoch_; // <= FOUND
289:         sequencer = sequencer_; // <= FOUND
290: 
291:         feeToken = feeToken_;
292: 
293:         emit OwnershipTransferred(address(0), daoAddress_);
294:         emit NewTimelockConfiguration(address(0), timelock_);
295:         emit EmergencyCouncilTransferred(address(0), emergencyCouncil_);
296:     }

['30']

30:     constructor(ICentralRegistry centralRegistry_) {
31:         if (
32:             !ERC165Checker.supportsInterface(
33:                 address(centralRegistry_),
34:                 type(ICentralRegistry).interfaceId
35:             )
36:         ) {
37:             revert CVE__ParametersAreInvalid();
38:         }
39:         centralRegistry = centralRegistry_; // <= FOUND
40:     }

['89']

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

['76']

76:     constructor(ICentralRegistry centralRegistry_) {
77:         if (
78:             !ERC165Checker.supportsInterface(
79:                 address(centralRegistry_),
80:                 type(ICentralRegistry).interfaceId
81:             )
82:         ) {
83:             revert CurvanceDAOLBP__InvalidCentralRegistry();
84:         }
85: 
86:         centralRegistry = centralRegistry_; // <= FOUND
87:         cve = centralRegistry.cve();
88:     }

['28']

28:     constructor(
29:         ICentralRegistry centralRegistry_
30:     )
31:         TimelockController(
32:             MINIMUM_DELAY,
33:             new address[](0),
34:             new address[](0),
35:             address(0)
36:         )
37:     {
38:         if (
39:             !ERC165Checker.supportsInterface(
40:                 address(centralRegistry_),
41:                 type(ICentralRegistry).interfaceId
42:             )
43:         ) {
44:             revert Timelock__InvalidCentralRegistry(address(centralRegistry_));
45:         }
46: 
47:         centralRegistry = centralRegistry_; // <= FOUND
48: 
49:         
50:         _DAO_ADDRESS = centralRegistry.daoAddress();
51:         _grantRole(PROPOSER_ROLE, _DAO_ADDRESS);
52:         _grantRole(EXECUTOR_ROLE, _DAO_ADDRESS);
53:     }

['160']

160:     constructor(
161:         ICentralRegistry centralRegistry_,
162:         address underlying_,
163:         address marketManager_,
164:         address interestRateModel_
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_; // <= FOUND
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:     }

['45']

45:     constructor(ICentralRegistry centralRegistry_) {
46:         if (
47:             !ERC165Checker.supportsInterface(
48:                 address(centralRegistry_),
49:                 type(ICentralRegistry).interfaceId
50:             )
51:         ) {
52:             revert Delegable__InvalidCentralRegistry();
53:         }
54: 
55:         centralRegistry = centralRegistry_; // <= FOUND
56:     }

['206']

206:     constructor(
207:         ICentralRegistry centralRegistry_,
208:         uint256 baseRatePerYear,
209:         uint256 vertexRatePerYear,
210:         uint256 vertexUtilStart,
211:         uint256 adjustmentRate,
212:         uint256 adjustmentVelocity,
213:         uint256 vertexMultiplierMax,
214:         uint256 decayRate
215:     ) {
216:         if (
217:             !ERC165Checker.supportsInterface(
218:                 address(centralRegistry_),
219:                 type(ICentralRegistry).interfaceId
220:             )
221:         ) {
222:             revert DynamicInterestRateModel__InvalidCentralRegistry();
223:         }
224: 
225:         centralRegistry = centralRegistry_; // <= FOUND
226: 
227:         _updateDynamicInterestRateModel(
228:             baseRatePerYear,
229:             vertexRatePerYear,
230:             vertexUtilStart,
231:             adjustmentRate,
232:             adjustmentVelocity,
233:             vertexMultiplierMax,
234:             decayRate,
235:             true
236:         );
237:     }

['141']

141:     constructor(
142:         ICentralRegistry centralRegistry_,
143:         address oneBalanceFeeManager_
144:     ) {
145:         if (
146:             !ERC165Checker.supportsInterface(
147:                 address(centralRegistry_),
148:                 type(ICentralRegistry).interfaceId
149:             )
150:         ) {
151:             revert FeeAccumulator__InvalidCentralRegistry();
152:         }
153:         if (oneBalanceFeeManager_ == address(0)) {
154:             revert FeeAccumulator__OneBalanceFeeManagerIsZeroAddress();
155:         }
156: 
157:         centralRegistry = centralRegistry_; // <= FOUND
158:         feeToken = centralRegistry.feeToken();
159:         oneBalanceFeeManager = oneBalanceFeeManager_;
160:         _feeTokenUnit = 10 ** IERC20(feeToken).decimals();
161:         
162:         
163:         _messagingHubStored = centralRegistry.protocolMessagingHub();
164: 
165:         
166:         
167:         SafeTransferLib.safeApprove(
168:             feeToken,
169:             _messagingHubStored,
170:             type(uint256).max
171:         );
172:     }

['40']

40:     constructor(ICentralRegistry centralRegistry_) {
41:         if (
42:             !ERC165Checker.supportsInterface(
43:                 address(centralRegistry_),
44:                 type(ICentralRegistry).interfaceId
45:             )
46:         ) {
47:             revert FeeTokenBridgingHub__InvalidCentralRegistry();
48:         }
49: 
50:         centralRegistry = centralRegistry_; // <= FOUND
51: 
52:         feeToken = centralRegistry.feeToken();
53:     }

['42']

42:     constructor(ICentralRegistry centralRegistry_) {
43:         if (
44:             !ERC165Checker.supportsInterface(
45:                 address(centralRegistry_),
46:                 type(ICentralRegistry).interfaceId
47:             )
48:         ) {
49:             revert GaugeErrors.InvalidAddress();
50:         }
51:         centralRegistry = centralRegistry_; // <= FOUND
52:         
53:         cve = centralRegistry.cve();
54:         veCVE = IVeCVE(centralRegistry.veCVE());
55:     }

['170']

170:     constructor(ICentralRegistry centralRegistry_) {
171:         if (
172:             !ERC165Checker.supportsInterface(
173:                 address(centralRegistry_),
174:                 type(ICentralRegistry).interfaceId
175:             )
176:         ) {
177:             
178:             _revert(0x78eefdcc);
179:         }
180: 
181:         centralRegistry = centralRegistry_; // <= FOUND
182:     }

['56']

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_; // <= FOUND
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);
84:     }

['132']

132:     constructor(ICentralRegistry centralRegistry_) {
133:         if (
134:             !ERC165Checker.supportsInterface(
135:                 address(centralRegistry_),
136:                 type(ICentralRegistry).interfaceId
137:             )
138:         ) {
139:             _revert(_INVALID_PARAMETER_SELECTOR);
140:         }
141: 
142:         centralRegistry = centralRegistry_; // <= FOUND
143:     }

['59']

59:     constructor(
60:         ICentralRegistry centralRegistry_,
61:         IERC20 asset_,
62:         address marketManager_,
63:         IPendleRouter router_
64:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
65:         strategyData.router = router_; // <= FOUND
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.underlyingTokens[i++]] = true;
81:             }
82:         }
83:     }

['69']

69:     constructor(
70:         ICentralRegistry centralRegistry_,
71:         IPendlePTOracle ptOracle_
72:     ) BaseOracleAdaptor(centralRegistry_) {
73:         ptOracle = ptOracle_; // <= FOUND
74:     }

['15']

15:     constructor(address _sDai, address _dai, address _daiAggregator) {
16:         sDai = _sDai; // <= FOUND
17:         dai = _dai; // <= FOUND
18:         daiAggregator = _daiAggregator; // <= FOUND
19:     }

['62']

62:     constructor(ICentralRegistry centralRegistry_, address WETH_) {
63:         if (
64:             !ERC165Checker.supportsInterface(
65:                 address(centralRegistry_),
66:                 type(ICentralRegistry).interfaceId
67:             )
68:         ) {
69:             revert SimpleRewardZapper__InvalidCentralRegistry();
70:         }
71: 
72:         address locker = centralRegistry_.cveLocker();
73: 
74:         
75:         
76:         if (locker == address(0)) {
77:             revert SimpleRewardZapper__InvalidCVELocker();
78:         }
79: 
80:         centralRegistry = centralRegistry_; // <= FOUND
81:         cveLocker = ICVELocker(locker);
82:         rewardToken = ICVELocker(locker).rewardToken();
83:         WETH = WETH_; // <= FOUND
84:     }

['53']

53:     constructor(
54:         ICentralRegistry centralRegistry_,
55:         address marketManager_,
56:         address WETH_
57:     ) {
58:         if (
59:             !ERC165Checker.supportsInterface(
60:                 address(centralRegistry_),
61:                 type(ICentralRegistry).interfaceId
62:             )
63:         ) {
64:             revert SimpleZapper__InvalidCentralRegistry();
65:         }
66: 
67:         centralRegistry = centralRegistry_; // <= FOUND
68: 
69:         
70:         
71:         if (!centralRegistry.isMarketManager(marketManager_)) {
72:             revert SimpleZapper__InvalidMarketManager();
73:         }
74: 
75:         marketManager = IMarketManager(marketManager_);
76:         WETH = WETH_; // <= FOUND
77:     }

['15']

15:     constructor(address _sFrax, address _frax, address _fraxAggregator) {
16:         sFrax = _sFrax; // <= FOUND
17:         frax = _frax; // <= FOUND
18:         fraxAggregator = _fraxAggregator; // <= FOUND
19:     }

['65']

65:     constructor(
66:         ICentralRegistry centralRegistry_,
67:         IStaticOracle oracleAddress_,
68:         address WETH_
69:     ) BaseOracleAdaptor(centralRegistry_) {
70:         uniswapOracleRouter = oracleAddress_; // <= FOUND
71:         WETH = WETH_; // <= FOUND
72:     }

['196']

196:     constructor(ICentralRegistry centralRegistry_) {
197:         _name = "Vote Escrowed CVE";
198:         _symbol = "veCVE";
199: 
200:         if (
201:             !ERC165Checker.supportsInterface(
202:                 address(centralRegistry_),
203:                 type(ICentralRegistry).interfaceId
204:             )
205:         ) {
206:             revert VeCVE__ParametersAreInvalid();
207:         }
208: 
209:         centralRegistry = centralRegistry_; // <= FOUND
210:         genesisEpoch = centralRegistry.genesisEpoch();
211:         cve = centralRegistry.cve();
212:         cveLocker = ICVELocker(centralRegistry.cveLocker());
213:     }

['68']

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

['58']

58:     constructor(
59:         ICentralRegistry centralRegistry_,
60:         IERC20 asset_,
61:         address marketManager_,
62:         IVeloGauge gauge,
63:         IVeloPairFactory pairFactory,
64:         IVeloRouter router
65:     ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
66:         if (block.chainid != 10) {
67:             revert VelodromeVolatileCToken__ChainIsNotSupported();
68:         }
69: 
70:         
71:         address _asset = asset();
72:         
73:         
74:         if (gauge.stakingToken() != _asset) {
75:             revert VelodromeVolatileCToken__StakingTokenIsNotAsset(
76:                 gauge.stakingToken()
77:             );
78:         }
79: 
80:         
81:         if (IVeloPool(_asset).stable()) {
82:             revert VelodromeVolatileCToken__AssetIsNotStable();
83:         }
84: 
85:         
86:         strategyData.token0 = IVeloPool(_asset).token0();
87:         strategyData.token1 = IVeloPool(_asset).token1();
88:         
89:         
90:         if (strategyData.token1 == address(rewardToken)) {
91:             strategyData.token1 = strategyData.token0;
92:             strategyData.token0 = address(rewardToken);
93:         }
94:         strategyData.gauge = gauge; // <= FOUND
95:         strategyData.router = router; // <= FOUND
96:         strategyData.pairFactory = pairFactory; // <= FOUND
97: 
98:         isUnderlyingToken[strategyData.token0] = true;
99:         isUnderlyingToken[strategyData.token1] = true;
100: 
101:         rewardTokenIsUnderlying = (address(rewardToken) ==
102:             strategyData.token0 ||
103:             address(rewardToken) == strategyData.token1);
104:     }

['15']

15:     constructor(address _wstETH, address _stETH, address _stETHAggregator) {
16:         wstETH = _wstETH; // <= FOUND
17:         stETH = _stETH; // <= FOUND
18:         stETHAggregator = _stETHAggregator; // <= FOUND
19:     }

[Low-41] Division in comparison

Resolution

To ensure accuracy in comparisons within programming, especially when dealing with integers, it's often more efficient to use multiplication rather than division. This approach stems from the fact that division operations are generally slower and more complex than multiplication. And in the context of solidity they can cause precision errors.

Suppose you want to compare if a/b is greater than c/d (where a, b, c, and d are integers). Instead of performing division, which is prone to precision errors, you can cross-multiply to avoid division. The comparison a/b > c/d is equivalent to ad > bc. This way, you only use multiplication, which is faster and avoids potential inaccuracies or complexities associated with division.

Num of instances: 9

Findings

Click to show findings

['884']

884:             
885:             
886:             if (((a * cautionDivergenceFlag) / DENOMINATOR) < b) { // <= FOUND

['888']

888:                 
889:                 
890:                 
891:                 if (((a * badSourceDivergenceFlag) / DENOMINATOR) < b) { // <= FOUND

['903']

903: 
904:         
905:         
906:         if (((b * cautionDivergenceFlag) / DENOMINATOR) < a) { // <= FOUND

['907']

907:             
908:             
909:             
910:             if (((b * badSourceDivergenceFlag) / DENOMINATOR) < a) { // <= FOUND

['935']

935:             
936:             
937:             if (((b * cautionDivergenceFlag) / DENOMINATOR) < a) { // <= FOUND

['939']

939:                 
940:                 
941:                 
942:                 if (((b * badSourceDivergenceFlag) / DENOMINATOR) < a) { // <= FOUND

['884']

884: 
885:         
886:         
887:         if (((a * cautionDivergenceFlag) / DENOMINATOR) < b) { // <= FOUND

['888']

888:             
889:             
890:             
891:             if (((a * badSourceDivergenceFlag) / DENOMINATOR) < b) { // <= FOUND

['1018']

1018: 
1019:         
1020:         
1021:         if (collRatio > (WAD_SQUARED / (WAD + collReqSoft))) { // <= FOUND

[Low-42] Contract contains payable functions but no withdraw/sweep function

Resolution

In smart contract development, particularly for Ethereum, having payable functions without a corresponding withdraw or sweep function can lead to potential issues. Payable functions allow the contract to receive Ether, but without a mechanism to withdraw these funds, the Ether can become locked within the contract indefinitely. This situation might be intentional in some cases (like a burn function), but generally, it’s a design oversight. A withdraw or sweep function is necessary to transfer Ether out of the contract to a specific address, typically the owner's or a designated recipient. Without this, the contract lacks flexibility in managing its funds, potentially leading to lost or inaccessible Ether.

Num of instances: 14

Findings

Click to show findings

['26']

26: contract BlastNativeYieldManager is ReentrancyGuard 

['12']

12: contract BorrowZapper is FeeTokenBridgingHub 

['12']

12: contract CVE is ERC20 

['43']

43: contract CVELocker is Delegable, ReentrancyGuard 

['12']

12: contract CVE is ERC20 

['21']

21: contract ComplexZapper is ReentrancyGuard 

['46']

46: contract FeeAccumulator is ReentrancyGuard 

['11']

11: contract GMCToken is CTokenCompounding 

['12']

12: contract OneBalanceFeeManager is FeeTokenBridgingHub 

['27']

27: contract PositionFolding is IPositionFolding, Delegable, ERC165, ReentrancyGuard 

['18']

18: contract SimpleRewardZapper is ReentrancyGuard 

['17']

17: contract SimpleZapper is ReentrancyGuard 

['9']

9: contract StakedGMXCToken is CTokenCompounding 

['93']

93: contract VeCVE is ERC20, ReentrancyGuard 

[NonCritical-1] Some if-statement can be converted to a ternary

Resolution

Improving code readability and compactness is an integral part of optimal programming practices. The use of ternary operators in place of if-else conditions is one such measure. Ternary operators allow us to write conditional statements in a more concise manner, thereby enhancing readability and simplicity. They follow the syntax condition ? exprIfTrue : exprIfFalse, which interprets as "if the condition is true, evaluate to exprIfTrue, else evaluate to exprIfFalse". By adopting this approach, we make our code more streamlined and intuitive, which could potentially aid in better understanding and maintenance of the codebase.

Num of instances: 71

Findings

Click to show findings

['113']

113:         if (inUSD) {
114:             data = adaptorDataUSD[asset]; // <= FOUND
115:         }

['242']

242:         if (isSupportedAsset[asset]) {
243:             isUpdate = true; // <= FOUND
244:         }

['101']

101:         if (inUSD) {
102:             
103:             
104:             symbolHash = Bytes32Helper._toBytes32(asset); // <= FOUND
105:         }

['121']

121:         if (decimals == 0) {
122:             data.decimals = 8; // <= FOUND
123:         }

['110']

110:         if (data.decimals0 != 18) {
111:             reserve0 = (reserve0 * 1e18) / (10 ** data.decimals0); // <= FOUND
112:         }

['114']

114:         if (data.decimals1 != 18) {
115:             reserve1 = (reserve1 * 1e18) / (10 ** data.decimals1); // <= FOUND
116:         }

['152']

152:         if (yieldDestination == address(0)) {
153:             yieldDestination = msg.sender; // <= FOUND
154:         }

['165']

165:             if (pendingWETH > 0) {
166:                 WETHYield += pendingWETH; // <= FOUND
167:             }

['161']

161:         if (claimWETHYield) {
162:             uint256 pendingWETH = pendingWETHYield[msg.sender]; // <= FOUND
163: 
164:             
165:             if (pendingWETH > 0) {
166:                 WETHYield += pendingWETH; // <= FOUND
167:             }

['175']

175:             if (pendingUSDB > 0) {
176:                 USDBYield += pendingUSDB; // <= FOUND
177:             }

['171']

171:         if (claimUSDBYield) {
172:             uint256 pendingUSDB = pendingUSDBYield[msg.sender]; // <= FOUND
173: 
174:             
175:             if (pendingUSDB > 0) {
176:                 USDBYield += pendingUSDB; // <= FOUND
177:             }

['246']

246:            if (msg.sender != owner) {
247:                 uint256 allowed = allowance(owner, msg.sender); // <= FOUND
248: 
249:                 if (allowed != type(uint256).max) {
250:                     _spendAllowance(owner, msg.sender, allowed - shares);
251:                 }
252:             }

['67']

67:         if (builder_ == address(0)) {
68:             builder_ = msg.sender; // <= FOUND
69:         }

['197']

197:         if (builderAllocation <= _builderAllocationMinted + amount) {
198:             amount = builderAllocation - builderAllocationMinted; // <= FOUND
199:         }

['715']

715:         if (token == address(0)) {
716:             if (amount == 0) {
717:                 amount = address(this).balance; // <= FOUND
718:             }
719: 
720:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
721:         }

['260']

260:         if (isPaused == 1 && currentState == 2) {
261:             endClaimTimestamp = block.timestamp + (6 weeks); // <= FOUND
262:         }

['260']

260:         if (daoAddress_ == address(0)) {
261:             daoAddress_ = msg.sender; // <= FOUND
262:         }

['264']

264:         if (timelock_ == address(0)) {
265:             timelock_ = msg.sender; // <= FOUND
266:         }

['268']

268:         if (emergencyCouncil_ == address(0)) {
269:             emergencyCouncil_ = msg.sender; // <= FOUND
270:         }

['150']

150:         if (bufferedMaxPrice > type(uint240).max) {
151:             bufferedMaxPrice = type(uint240).max; // <= FOUND
152:         }

['590']

590:         if (forceRedeemCollateral) {
591:             assets = cToken.redeemCollateralFor( // <= FOUND
592:                 shares,
593:                 address(this),
594:                 msg.sender
595:             );
596:         }

['217']

217:         if (CommonLib.isETH(data.baseToken)) {
218:             data.baseTokenDecimals = 18; // <= FOUND
219:         }

['1119']

1119:         if (spender != from) {
1120:             
1121:             
1122:             allowance[from][spender] = allowance[from][spender] - tokens; // <= FOUND
1123:         }

['302']

302:         if (belowVertex) {
303:             unchecked {
304:                 borrowRate = _getBaseInterestRate(util); // <= FOUND
305:             }
306:         }

['238']

238:         if (transferToken) {
239:             
240:             nativeFee += wormholeCore.messageFee(); // <= FOUND
241:         }

['183']

183:         if (index != (rewardTokensLength - 1)) {
184:             rewardTokens[index] = rewardTokens[rewardTokensLength - 1]; // <= FOUND
185:         }

['271']

271:         if (lastRewardTimestamp == 0) {
272:             lastRewardTimestamp = startTime; // <= FOUND
273:         }

['611']

611:         if (_lastRewardTimestamp == 0) {
612:             _lastRewardTimestamp = startTime; // <= FOUND
613:         }

['564']

564:         if (result.lFactor == 0) {
565:             
566:             result.lFactor = 1; // <= FOUND
567:         }

['1591']

1591:         if (!liquidateExact) {
1592:             debtAmount = maxAmount; // <= FOUND
1593:         }

['75']

75:         if (paymentToken_ == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) {
76:             paymentTokenDecimals = 18; // <= FOUND
77:         }

['355']

355:         if (isMToken) {
356:             asset = mTokenAssets[asset].underlying; // <= FOUND
357:         }

['454']

454:         if (numFeeds < 2) {
455:             (price, errorCode) = _getPriceSingleFeed(asset, inUSD, getLower); // <= FOUND
456:         }

['462']

462:         if (price == 0 && errorCode < BAD_SOURCE) {
463:             errorCode = BAD_SOURCE; // <= FOUND
464:         }

['806']

806:         if (numFeeds < 2) {
807:             (price, errorCode) = _getPriceSingleFeed(ETH, true, getLower); // <= FOUND
808:         }

['826']

826:         if (sequencer != address(0)) {
827:             (, int256 answer, uint256 startedAt, , ) = IChainlink(sequencer) // <= FOUND
828:                 .latestRoundData();
829: 
830:             
831:             
832:             
833:             if (
834:                 answer != 0 || block.timestamp < startedAt + GRACE_PERIOD_TIME
835:             ) {
836:                 return false;
837:             }
838:         }

['265']

265:         if (forceRedeemCollateral && mToken.isCToken()) {
266:             assets = mToken.redeemCollateralFor( // <= FOUND
267:                 shares,
268:                 address(this),
269:                 msg.sender
270:             );
271:         }

['80']

80:         if (_checkVestStatus(_vaultData)) {
81:             _updateVestingPeriodIfNeeded();
82: 
83:             
84:             rewardRouter.handleRewards(
85:                 true,
86:                 true,
87:                 true,
88:                 true,
89:                 true,
90:                 true,
91:                 false
92:             );
93:             uint256 rewardAmount = WETH.balanceOf(address(this)); // <= FOUND
94: 
95:             
96:             if (rewardAmount > 0) {
97:                 
98:                     
99:                 uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
100:                     rewardAmount,
101:                     centralRegistry.protocolHarvestFee(),
102:                     1e18
103:                 );
104:                 rewardAmount -= protocolFee; // <= FOUND
105:                 SafeTransferLib.safeTransfer(
106:                     address(WETH),
107:                     centralRegistry.feeAccumulator(),
108:                     protocolFee
109:                 );
110: 
111:                 SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
112:                     data,
113:                     (SwapperLib.Swap)
114:                 );
115: 
116:                 if (!centralRegistry.isSwapper(swapData.target)) {
117:                     revert StakedGMXCToken__InvalidSwapper(swapData.target);
118:                 }
119: 
120:                 yield = SwapperLib.swap(centralRegistry, swapData); // <= FOUND
121: 
122:                 
123:                 if (yield == 0) {
124:                     revert StakedGMXCToken__SlippageError();
125:                 }
126:             }
127: 
128:             
129:             
130:             _afterDeposit(yield, 0);
131: 
132:             
133:             _setNewVaultData(yield, vestPeriod);
134: 
135:             emit Harvest(yield);
136:         }

['121']

121:         if (CommonLib.isETH(zapperCall.inputToken)) {
122:             value = zapperCall.inputAmount; // <= FOUND
123:         }

['118']

118:         if (success) {
119:             
120:             twapPrice = abi.decode(returnData, (uint256)); // <= FOUND
121:         }

['716']

716:            if (amount == 0) {
717:                 amount = address(this).balance; // <= FOUND
718:             }

['715']

715:         if (token == address(0)) {
716:             if (amount == 0) {
717:                 amount = address(this).balance; // <= FOUND
718:             }

['716']

716:           if (amount == 0) {
717:                 amount = address(this).balance; // <= FOUND
718:             }

['1265']

1265:         if (lockIndex != lastLockIndex) {
1266:             user[lockIndex] = user[lastLockIndex]; // <= FOUND
1267:         }

['107']

107:                if (rewardToken != _AURA && rewardToken != _BAL) {
108:                     strategyData.rewardTokens.push() = rewardToken; // <= FOUND
109:                 }

['107']

107:               if (rewardToken != _AURA && rewardToken != _BAL) {
108:                     strategyData.rewardTokens.push() = rewardToken; // <= FOUND
109:                 }

['220']

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:         }

['223']

223:             if (quoteDecimals < 18) {
224:                 price = price * (10 ** (18 - quoteDecimals)); // <= FOUND
225:             }

['197']

197:         if (msg.sender != owner) {
198:             uint256 allowed = allowance(owner, msg.sender); // <= FOUND
199: 
200:             if (allowed != type(uint256).max) {
201:                 _spendAllowance(owner, msg.sender, allowed - shares);
202:             }

['726']

726:             if (amount == 0) {
727:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
728:             }

['531']

531:         if (rewardsData.asCVE) {
532:             SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
533:                 params,
534:                 (SwapperLib.Swap)
535:             );
536: 
537:             
538:             if (
539:                 swapData.call.length == 0 ||
540:                 swapData.inputToken != rewardToken ||
541:                 swapData.outputToken != cve ||
542:                 swapData.inputAmount != rewards ||
543:                 !centralRegistry.isSwapper(swapData.target)
544:             ) {
545:                 revert CVELocker__SwapDataIsInvalid();
546:             }

['311']

311:             if (amounts[i] > 0) {
312:                 liquidityAvailable = true; // <= FOUND
313:             }

['156']

156:             if (data.divideRate0 || data.divideRate1) {
157:                 uint256[2] memory rates = pool.stored_rates(); // <= FOUND
158:                 if (data.divideRate0) {
159:                     price0 = (price0 * WAD) / rates[0]; // <= FOUND
160:                 }
161:                 if (data.divideRate1) {
162:                     price1 = (price1 * WAD) / rates[1]; // <= FOUND
163:                 }
164:             }

['158']

158:                if (data.divideRate0) {
159:                     price0 = (price0 * WAD) / rates[0]; // <= FOUND
160:                 }

['154']

154:         if (data.isCorrelated) {
155:             
156:             if (data.divideRate0 || data.divideRate1) {
157:                 uint256[2] memory rates = pool.stored_rates(); // <= FOUND
158:                 if (data.divideRate0) {
159:                     price0 = (price0 * WAD) / rates[0]; // <= FOUND
160:                 }

['217']

217:             if (_priceUnit[token] == 0) {
218:                 _priceUnit[token] = WAD * 10 ** IERC20(token).decimals(); // <= FOUND
219:             }

['393']

393:         if (maxDebt > newDebt) {
394:             unchecked {
395:                 result.collateralSurplus = maxDebt - newDebt; // <= FOUND
396:             }

['121']

121:         if (payloadId == 1) {
122:             (, bytes32 token) = abi.decode(payload, (uint8, bytes32)); // <= FOUND
123: 
124:             if (address(uint160(uint256(token))) == feeToken) {
125:                 _depositOneBalanceFee();
126:             }

['634']

634:             if (assetPriceFeeds[asset][0] == feed) {
635:                 assetPriceFeeds[asset][0] = assetPriceFeeds[asset][1]; // <= FOUND
636:             }

['715']

715:         if (data.inUSD != inUSD) {
716:             uint256 newPrice;
717:             (newPrice, data.hadError) = _getETHUSD(getLower); // <= FOUND
718:             if (data.hadError) {
719:                 return (0, BAD_SOURCE);
720:             }

['96']

96:             if (rewardAmount > 0) {
97:                 
98:                     
99:                 uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
100:                     rewardAmount,
101:                     centralRegistry.protocolHarvestFee(),
102:                     1e18
103:                 );
104:                 rewardAmount -= protocolFee; // <= FOUND
105:                 SafeTransferLib.safeTransfer(
106:                     address(WETH),
107:                     centralRegistry.feeAccumulator(),
108:                     protocolFee
109:                 );
110: 
111:                 SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
112:                     data,
113:                     (SwapperLib.Swap)
114:                 );
115: 
116:                 if (!centralRegistry.isSwapper(swapData.target)) {
117:                     revert StakedGMXCToken__InvalidSwapper(swapData.target);
118:                 }
119: 
120:                 yield = SwapperLib.swap(centralRegistry, swapData); // <= FOUND
121: 
122:                 
123:                 if (yield == 0) {
124:                     revert StakedGMXCToken__SlippageError();
125:                 }
126:             }

['139']

139:         if (_checkVestStatus(_vaultData)) {
140:             _updateVestingPeriodIfNeeded();
141: 
142:             
143:             StrategyData memory sd = strategyData; // <= FOUND
144: 
145:             
146:             sd.gauge.getReward(address(this));
147: 
148:             {
149:                 uint256 rewardAmount = rewardToken.balanceOf(address(this)); // <= FOUND
150: 
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
156:                         rewardAmount,
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee; // <= FOUND
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert AerodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert AerodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset(); // <= FOUND
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 == // <= FOUND
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit( // <= FOUND
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount; // <= FOUND
223: 
224:             
225:             yield = VelodromeLib._addLiquidity( // <= FOUND
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }

['134']

134:         if (_checkVestStatus(_vaultData)) {
135:             _updateVestingPeriodIfNeeded();
136: 
137:             
138:             StrategyData memory sd = strategyData; // <= FOUND
139: 
140:             
141:             sd.gauge.getReward(address(this));
142: 
143:             {
144:                 uint256 rewardAmount = rewardToken.balanceOf(address(this)); // <= FOUND
145:                 
146:                 if (rewardAmount > 0) {
147:                     
148:                     
149:                     uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
150:                         rewardAmount,
151:                         centralRegistry.protocolHarvestFee(),
152:                         1e18
153:                     );
154:                     rewardAmount -= protocolFee; // <= FOUND
155:                     SafeTransferLib.safeTransfer(
156:                         address(rewardToken),
157:                         centralRegistry.feeAccumulator(),
158:                         protocolFee
159:                     );
160: 
161:                     
162:                     if (!rewardTokenIsUnderlying) {
163:                         SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
164:                             data,
165:                             (SwapperLib.Swap)
166:                         );
167: 
168:                         if (!centralRegistry.isSwapper(swapData.target)) {
169:                             revert AerodromeVolatileCToken__InvalidSwapper(
170:                                 swapData.target
171:                             );
172:                         }
173: 
174:                         SwapperLib.swap(centralRegistry, swapData);
175:                     }
176:                 }
177:             }
178: 
179:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
180:             
181:             if (totalAmountA == 0) {
182:                 revert AerodromeVolatileCToken__SlippageError();
183:             }
184: 
185:             
186:             address _asset = asset(); // <= FOUND
187:             
188:             
189:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
190:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
191:                 ? r0
192:                 : r1;
193: 
194:             
195:             
196:             
197:             uint256 swapAmount = VelodromeLib._optimalDeposit( // <= FOUND
198:                 address(sd.pairFactory),
199:                 _asset,
200:                 totalAmountA,
201:                 reserveA,
202:                 0,
203:                 0,
204:                 0,
205:                 false
206:             );
207:             
208:             VelodromeLib._swapExactTokensForTokens(
209:                 address(sd.router),
210:                 _asset,
211:                 sd.token0,
212:                 sd.token1,
213:                 swapAmount,
214:                 false
215:             );
216:             totalAmountA -= swapAmount; // <= FOUND
217: 
218:             
219:             yield = VelodromeLib._addLiquidity( // <= FOUND
220:                 address(sd.router),
221:                 sd.token0,
222:                 sd.token1,
223:                 false,
224:                 totalAmountA,
225:                 IERC20(sd.token1).balanceOf(address(this)), 
226:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
227:             );
228: 
229:             
230:             
231:             _afterDeposit(yield, 0);
232: 
233:             
234:             _setNewVaultData(yield, vestPeriod);
235: 
236:             emit Harvest(yield);
237:         }

['161']

161:                 if (data.divideRate1) {
162:                     price1 = (price1 * WAD) / rates[1]; // <= FOUND
163:                 }

['309']

309:                           if (result.updateNeeded == 0) {
310:                                 result.updateNeeded = 1; // <= FOUND
311:                             }

['431']

431:                 if (tokenData[snapshot.asset].collRatio != 0) {
432:                     accountCollateral += _assetValue( // <= FOUND
433:                         ((tokenData[snapshot.asset]
434:                             .accountPositions[account]
435:                             .collateralPosted * snapshot.exchangeRate) / WAD),
436:                         underlyingPrices[i],
437:                         snapshot.decimals
438:                     );
439:                 }

['510']

510:              if (snapshot.asset == collateralToken) {
511:                     result.collateralTokenPrice = underlyingPrices[i]; // <= FOUND
512:                 }

['515']

515:                 if (tokenData[snapshot.asset].collRatio != 0) {
516:                     (
517:                         accountCollateralSoft,
518:                         accountCollateralHard
519:                     ) = _addLiquidationValues( // <= FOUND
520:                         snapshot,
521:                         account,
522:                         underlyingPrices[i],
523:                         accountCollateralSoft,
524:                         accountCollateralHard
525:                     );
526:                 }

['231']

231:                 if (snapshot.debtBalance > 0) {
232:                     accountDebt += _assetValue( // <= FOUND
233:                         snapshot.debtBalance,
234:                         underlyingPrices[i],
235:                         snapshot.decimals
236:                     );
237:                 }

['140']

140:         if (_checkVestStatus(_vaultData)) {
141:             _updateVestingPeriodIfNeeded();
142: 
143:             
144:             StrategyData memory sd = strategyData; // <= FOUND
145: 
146:             
147:             sd.gauge.getReward(address(this));
148: 
149:             {
150:                 uint256 rewardAmount = rewardToken.balanceOf(address(this)); // <= FOUND
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
156:                         rewardAmount, 
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee; // <= FOUND
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert VelodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert VelodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset(); // <= FOUND
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 == // <= FOUND
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit( // <= FOUND
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount; // <= FOUND
223: 
224:             
225:             yield = VelodromeLib._addLiquidity( // <= FOUND
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }

['127']

127:         if (_checkVestStatus(_vaultData)) {
128:             _updateVestingPeriodIfNeeded();
129: 
130:             
131:             StrategyData memory sd = strategyData; // <= FOUND
132: 
133:             
134:             sd.gauge.getReward(address(this));
135: 
136:             {
137:                 uint256 rewardAmount = rewardToken.balanceOf(address(this)); // <= FOUND
138:                 
139:                 if (rewardAmount > 0) {
140:                     
141:                     
142:                     uint256 protocolFee = FixedPointMathLib.mulDiv( // <= FOUND
143:                         rewardAmount, 
144:                         centralRegistry.protocolHarvestFee(),
145:                         1e18
146:                     );
147:                     rewardAmount -= protocolFee; // <= FOUND
148:                     SafeTransferLib.safeTransfer(
149:                         address(rewardToken),
150:                         centralRegistry.feeAccumulator(),
151:                         protocolFee
152:                     );
153: 
154:                     
155:                     if (!rewardTokenIsUnderlying) {
156:                         SwapperLib.Swap memory swapData = abi.decode( // <= FOUND
157:                             data,
158:                             (SwapperLib.Swap)
159:                         );
160: 
161:                         if (!centralRegistry.isSwapper(swapData.target)) {
162:                             revert VelodromeVolatileCToken__InvalidSwapper(
163:                                 swapData.target
164:                             );
165:                         }
166: 
167:                         SwapperLib.swap(centralRegistry, swapData);
168:                     }
169:                 }
170:             }
171: 
172:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
173: 
174:             
175:             if (totalAmountA == 0) {
176:                 revert VelodromeVolatileCToken__SlippageError();
177:             }
178: 
179:             
180:             address _asset = asset(); // <= FOUND
181:             
182:             
183:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
184:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
185:                 ? r0
186:                 : r1;
187: 
188:             
189:             
190:             
191:             uint256 swapAmount = VelodromeLib._optimalDeposit( // <= FOUND
192:                 address(sd.pairFactory),
193:                 _asset,
194:                 totalAmountA,
195:                 reserveA,
196:                 0,
197:                 0,
198:                 0,
199:                 false
200:             );
201:             
202:             VelodromeLib._swapExactTokensForTokens(
203:                 address(sd.router),
204:                 _asset,
205:                 sd.token0,
206:                 sd.token1,
207:                 swapAmount,
208:                 false
209:             );
210:             totalAmountA -= swapAmount; // <= FOUND
211: 
212:             
213:             yield = VelodromeLib._addLiquidity( // <= FOUND
214:                 address(sd.router),
215:                 sd.token0,
216:                 sd.token1,
217:                 false,
218:                 totalAmountA,
219:                 IERC20(sd.token1).balanceOf(address(this)), 
220:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
221:             );
222: 
223:             
224:             
225:             _afterDeposit(yield, 0);
226: 
227:             
228:             _setNewVaultData(yield, vestPeriod);
229: 
230:             emit Harvest(yield);
231:         }

[NonCritical-2] Addresses shouldn't be hard-coded

Num of instances: 8

Findings

Click to show findings

['33']

33: 
37:     IERC20 public constant rewardToken =
38:         IERC20(0x940181a94A35A4569E4529A3CDfB74e38FD98631); // <= FOUND

['17']

17: 
21:     IBlast public constant CHAIN_YIELD_MANAGER = IBlast(0x4300000000000000000000000000000000000002); // <= FOUND

['19']

19:     
20:     IERC20Rebasing public constant WETH_YIELD_MANAGER = IERC20Rebasing(0x4300000000000000000000000000000000000004); // <= FOUND

['18']

18: 
22:     IWETH public constant WETH = IWETH(0x4300000000000000000000000000000000000004); // <= FOUND

['33']

33: 
34:         IBlast yieldConfiguration = IBlast(0x4300000000000000000000000000000000000002); // <= FOUND

['17']

17:     
22:     IBlast public constant CHAIN_YIELD_MANAGER = IBlast(0x4300000000000000000000000000000000000002); // <= FOUND

['36']

36:     
37:     IERC20Rebasing public constant USDB_YIELD_MANAGER = IERC20Rebasing(0x4300000000000000000000000000000000000003); // <= FOUND

['38']

38: 
42:     IERC20 public constant rewardToken =
43:         IERC20(0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db); // <= FOUND

[NonCritical-3] Code does not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern is a best practice in smart contract development, emphasizing the order of operations in functions to prevent reentrancy attacks. Violations arise when a function interacts with external contracts before settling internal state changes or checks. This misordering can expose the contract to potential threats. To adhere to this pattern, first ensure all conditions or checks are satisfied, then update any internal states, and only after these steps, interact with external contracts or addresses. Rearranging operations to this recommended sequence bolsters contract security and aligns with established best practices in the Ethereum community.

Num of instances: 49

Findings

Click to show findings

['148']

148:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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) {
163:             
164:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
165:                 continue;
166:             }
167: 
168:             if (
169:                 !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
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) { // <= FOUND
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:     }

['177']

177:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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)) { // <= FOUND
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) {
207:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
208:         }
209:         if (
210:             pool.coins(uint256(uint128(data.baseTokenIndex))) != data.baseToken
211:         ) {
212:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
213:         }
214: 
215:         data.quoteTokenDecimals = ERC20(asset).decimals(); // <= FOUND
216:         
217:         if (CommonLib.isETH(data.baseToken)) {
218:             data.baseTokenDecimals = 18;
219:         } else {
220:             data.baseTokenDecimals = ERC20(data.baseToken).decimals(); // <= FOUND
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:     }

['196']

196:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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( // <= FOUND
211:                 data.underlying0
212:             )
213:         ) {
214:             revert Curve2PoolLPAdaptor__QuoteAssetIsNotSupported();
215:         }
216: 
217:         
218:         
219:         if (
220:             !IOracleRouter(oracleRouter).isSupportedAsset( // <= FOUND
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; ) {
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:         

['130']

130:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
131:         _checkElevatedPermissions();
132: 
133:         
134:         (IStandardizedYield sy, IPPrincipalToken pt, ) = IPMarket(asset)
135:             .readTokens();
136: 
137:         
138:         if (address(pt) != data.pt) {
139:             revert PendleLPTokenAdaptor__WrongMarket();
140:         }
141: 
142:         
143:         if (data.twapDuration < MINIMUM_TWAP_DURATION) {
144:             revert PendleLPTokenAdaptor__TwapDurationIsLessThanMinimum();
145:         }
146: 
147:         
148:         (, address assetAddress, ) = sy.assetInfo();
149:         if (assetAddress != data.quoteAsset) {
150:             revert PendleLPTokenAdaptor__WrongQuote();
151:         }
152: 
153:         
154:         _checkPtTwap(asset, data.twapDuration);
155: 
156:         
157:         if (
158:             !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
159:                 data.quoteAsset
160:             )
161:         ) {
162:             revert PendleLPTokenAdaptor__QuoteAssetIsNotSupported();
163:         }
164: 
165:         
166:         adaptorData[asset] = data;
167: 
168:         
169:         bool isUpdate;
170:         if (isSupportedAsset[asset]) {
171:             isUpdate = true;
172:         }
173: 
174:         isSupportedAsset[asset] = true;
175:         emit PendleLPAssetAdded(asset, data, isUpdate);
176:     }

['131']

131:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
132:         _checkElevatedPermissions();
133: 
134:         
135:         (IStandardizedYield sy, IPPrincipalToken pt, ) = data
136:             .market
137:             .readTokens();
138: 
139:         
140:         if (address(pt) != asset) {
141:             revert PendlePrincipalTokenAdaptor__WrongMarket();
142:         }
143: 
144:         
145:         if (data.twapDuration < MINIMUM_TWAP_DURATION) {
146:             revert PendlePrincipalTokenAdaptor__TwapDurationIsLessThanMinimum();
147:         }
148: 
149:         
150:         (, address assetAddress, ) = sy.assetInfo();
151:         if (assetAddress != data.quoteAsset) {
152:             revert PendlePrincipalTokenAdaptor__WrongQuote();
153:         }
154: 
155:         
156:         _checkPtTwap(address(data.market), data.twapDuration);
157: 
158:         
159:         if (
160:             !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
161:                 data.quoteAsset
162:             )
163:         ) {
164:             revert PendlePrincipalTokenAdaptor__QuoteAssetIsNotSupported();
165:         }
166: 
167:         
168:         adaptorData[asset] = data;
169: 
170:         
171:         bool isUpdate;
172:         if (isSupportedAsset[asset]) {
173:             isUpdate = true;
174:         }
175: 
176:         isSupportedAsset[asset] = true;
177:         emit PendlePTAssetAdded(asset, data, isUpdate);
178:     }

['214']

214:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
215:         _checkElevatedPermissions();
216: 
217:         
218:         if (data.secondsAgo < MINIMUM_SECONDS_AGO) {
219:             revert UniswapV3Adaptor__SecondsAgoIsLessThanMinimum();
220:         }
221: 
222:         UniswapV3Pool pool = UniswapV3Pool(data.priceSource);
223: 
224:         
225:         address token0 = pool.token0();
226:         address token1 = pool.token1();
227:         if (token0 == asset) {
228:             data.baseDecimals = ERC20(asset).decimals(); // <= FOUND
229:             data.quoteDecimals = ERC20(token1).decimals(); // <= FOUND
230:             data.quoteToken = token1;
231:         } else if (token1 == asset) {
232:             data.baseDecimals = ERC20(asset).decimals(); // <= FOUND
233:             data.quoteDecimals = ERC20(token0).decimals(); // <= FOUND
234:             data.quoteToken = token0;
235:         } else revert UniswapV3Adaptor__AssetIsNotSupported();
236: 
237:         
238:         adaptorData[asset] = data;
239: 
240:         
241:         bool isUpdate;
242:         if (isSupportedAsset[asset]) {
243:             isUpdate = true;
244:         }
245: 
246:         isSupportedAsset[asset] = true;
247:         emit UniswapV3AssetAdded(asset, data, isUpdate);
248:     }

['129']

129:     function harvest(
130:         bytes calldata data
131:     ) external override returns (uint256 yield) {
132:         
133:         _canCompound();
134: 
135:         
136:         _vestIfNeeded();
137: 
138:         
139:         if (_checkVestStatus(_vaultData)) {
140:             _updateVestingPeriodIfNeeded();
141: 
142:             
143:             StrategyData memory sd = strategyData;
144: 
145:             
146:             sd.gauge.getReward(address(this));
147: 
148:             {
149:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
150: 
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount,
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert AerodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert AerodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0() // <= FOUND
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['124']

124:     function harvest(
125:         bytes calldata data
126:     ) external override returns (uint256 yield) {
127:         
128:         _canCompound();
129: 
130:         
131:         _vestIfNeeded();
132: 
133:         
134:         if (_checkVestStatus(_vaultData)) {
135:             _updateVestingPeriodIfNeeded();
136: 
137:             
138:             StrategyData memory sd = strategyData;
139: 
140:             
141:             sd.gauge.getReward(address(this));
142: 
143:             {
144:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
145:                 
146:                 if (rewardAmount > 0) {
147:                     
148:                     
149:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
150:                         rewardAmount,
151:                         centralRegistry.protocolHarvestFee(),
152:                         1e18
153:                     );
154:                     rewardAmount -= protocolFee;
155:                     SafeTransferLib.safeTransfer(
156:                         address(rewardToken),
157:                         centralRegistry.feeAccumulator(),
158:                         protocolFee
159:                     );
160: 
161:                     
162:                     if (!rewardTokenIsUnderlying) {
163:                         SwapperLib.Swap memory swapData = abi.decode(
164:                             data,
165:                             (SwapperLib.Swap)
166:                         );
167: 
168:                         if (!centralRegistry.isSwapper(swapData.target)) {
169:                             revert AerodromeVolatileCToken__InvalidSwapper(
170:                                 swapData.target
171:                             );
172:                         }
173: 
174:                         SwapperLib.swap(centralRegistry, swapData);
175:                     }
176:                 }
177:             }
178: 
179:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
180:             
181:             if (totalAmountA == 0) {
182:                 revert AerodromeVolatileCToken__SlippageError();
183:             }
184: 
185:             
186:             address _asset = asset();
187:             
188:             
189:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
190:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
191:                 ? r0
192:                 : r1;
193: 
194:             
195:             
196:             
197:             uint256 swapAmount = VelodromeLib._optimalDeposit(
198:                 address(sd.pairFactory),
199:                 _asset,
200:                 totalAmountA,
201:                 reserveA,
202:                 0,
203:                 0,
204:                 0,
205:                 false
206:             );
207:             
208:             VelodromeLib._swapExactTokensForTokens(
209:                 address(sd.router),
210:                 _asset,
211:                 sd.token0,
212:                 sd.token1,
213:                 swapAmount,
214:                 false
215:             );
216:             totalAmountA -= swapAmount;
217: 
218:             
219:             yield = VelodromeLib._addLiquidity(
220:                 address(sd.router),
221:                 sd.token0,
222:                 sd.token1,
223:                 false,
224:                 totalAmountA,
225:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
226:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
227:             );
228: 
229:             
230:             
231:             _afterDeposit(yield, 0);
232: 
233:             
234:             _setNewVaultData(yield, vestPeriod);
235: 
236:             emit Harvest(yield);
237:         }
238:         
239:     }

['130']

130:     function harvest(
131:         bytes calldata data
132:     ) external override returns (uint256 yield) {
133:         
134:         _canCompound();
135: 
136:         
137:         _vestIfNeeded();
138: 
139:         
140:         if (_checkVestStatus(_vaultData)) {
141:             _updateVestingPeriodIfNeeded();
142: 
143:             
144:             StrategyData memory sd = strategyData;
145: 
146:             
147:             sd.gauge.getReward(address(this));
148: 
149:             {
150:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount, 
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert VelodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert VelodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0() // <= FOUND
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['117']

117:     function harvest(
118:         bytes calldata data
119:     ) external override returns (uint256 yield) {
120:         
121:         _canCompound();
122: 
123:         
124:         _vestIfNeeded();
125: 
126:         
127:         if (_checkVestStatus(_vaultData)) {
128:             _updateVestingPeriodIfNeeded();
129: 
130:             
131:             StrategyData memory sd = strategyData;
132: 
133:             
134:             sd.gauge.getReward(address(this));
135: 
136:             {
137:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
138:                 
139:                 if (rewardAmount > 0) {
140:                     
141:                     
142:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
143:                         rewardAmount, 
144:                         centralRegistry.protocolHarvestFee(),
145:                         1e18
146:                     );
147:                     rewardAmount -= protocolFee;
148:                     SafeTransferLib.safeTransfer(
149:                         address(rewardToken),
150:                         centralRegistry.feeAccumulator(),
151:                         protocolFee
152:                     );
153: 
154:                     
155:                     if (!rewardTokenIsUnderlying) {
156:                         SwapperLib.Swap memory swapData = abi.decode(
157:                             data,
158:                             (SwapperLib.Swap)
159:                         );
160: 
161:                         if (!centralRegistry.isSwapper(swapData.target)) {
162:                             revert VelodromeVolatileCToken__InvalidSwapper(
163:                                 swapData.target
164:                             );
165:                         }
166: 
167:                         SwapperLib.swap(centralRegistry, swapData);
168:                     }
169:                 }
170:             }
171: 
172:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
173: 
174:             
175:             if (totalAmountA == 0) {
176:                 revert VelodromeVolatileCToken__SlippageError();
177:             }
178: 
179:             
180:             address _asset = asset();
181:             
182:             
183:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
184:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
185:                 ? r0
186:                 : r1;
187: 
188:             
189:             
190:             
191:             uint256 swapAmount = VelodromeLib._optimalDeposit(
192:                 address(sd.pairFactory),
193:                 _asset,
194:                 totalAmountA,
195:                 reserveA,
196:                 0,
197:                 0,
198:                 0,
199:                 false
200:             );
201:             
202:             VelodromeLib._swapExactTokensForTokens(
203:                 address(sd.router),
204:                 _asset,
205:                 sd.token0,
206:                 sd.token1,
207:                 swapAmount,
208:                 false
209:             );
210:             totalAmountA -= swapAmount;
211: 
212:             
213:             yield = VelodromeLib._addLiquidity(
214:                 address(sd.router),
215:                 sd.token0,
216:                 sd.token1,
217:                 false,
218:                 totalAmountA,
219:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
220:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
221:             );
222: 
223:             
224:             
225:             _afterDeposit(yield, 0);
226: 
227:             
228:             _setNewVaultData(yield, vestPeriod);
229: 
230:             emit Harvest(yield);
231:         }
232:     }

['108']

108:     function addAsset(
109:         address asset,
110:         string memory ticker, 
111:         address proxyFeed, 
112:         uint256 heartbeat, 
113:         bool inUSD
114:     ) external {
115:         _checkElevatedPermissions();
116: 
117:         if (heartbeat != 0) {
118:             if (heartbeat > DEFAULT_HEART_BEAT) {
119:                 revert Api3Adaptor__InvalidHeartbeat();
120:             }
121:         }
122: 
123:         bytes32 dapiName = Bytes32Helper.stringToBytes32(ticker);
124:         bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
125: 
126:         
127:         
128:         if (dapiNameHash != IProxy(proxyFeed).dapiNameHash()) { // <= FOUND
129:             revert Api3Adaptor__DAPINameHashError();
130:         }
131: 
132:         AdaptorData storage data;
133: 
134:         if (inUSD) {
135:             data = adaptorDataUSD[asset];
136:         } else {
137:             data = adaptorDataNonUSD[asset];
138:         }
139: 
140:         data.heartbeat = heartbeat != 0
141:             ? heartbeat
142:             : DEFAULT_HEART_BEAT;
143: 
144:         
145: 
146:         
147:         
148:         
149:         
150:         data.max = (uint256(int256(type(int224).max)) * 9) / 10; // <= FOUND
151:         data.dapiNameHash = dapiNameHash;
152:         data.proxyFeed = IProxy(proxyFeed);
153:         data.isConfigured = true;
154: 
155:         
156:         bool isUpdate;
157:         if (isSupportedAsset[asset]) {
158:             isUpdate = true;
159:         }
160: 
161:         isSupportedAsset[asset] = true;
162:         emit Api3AssetAdded(asset, data, isUpdate);
163:     }

['27']

27:     function enterBalancer(
28:         address balancerVault,
29:         bytes32 balancerPoolId,
30:         address lpToken,
31:         address[] calldata tokens,
32:         uint256 lpMinOutAmount
33:     ) internal returns (uint256 lpOutAmount) {
34:         uint256 numTokens = tokens.length;
35:         uint256[] memory balances = new uint256[](numTokens);
36:         uint256 value;
37:         bool containsEth;
38: 
39:         
40:         for (uint256 i; i < numTokens; ++i) {
41:             balances[i] = CommonLib.getTokenBalance(tokens[i]);
42:             SwapperLib._approveTokenIfNeeded(
43:                 tokens[i],
44:                 balancerVault,
45:                 balances[i]
46:             );
47: 
48:             if (CommonLib.isETH(tokens[i])) {
49:                 
50:                 
51:                 if (containsEth) {
52:                     revert BalancerLib__InvalidPoolInvariantError();
53:                 }
54: 
55:                 value = balances[i];
56:                 containsEth = true;
57:             }
58:         }
59: 
60:         
61:         IBalancerVault(balancerVault).joinPool{ value: value }( // <= FOUND
62:             balancerPoolId,
63:             address(this),
64:             address(this),
65:             IBalancerVault.JoinPoolRequest(
66:                 tokens,
67:                 balances,
68:                 abi.encode(
69:                     IBalancerVault.JoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT,
70:                     balances,
71:                     1
72:                 ),
73:                 false 
74:             )
75:         );
76: 
77:         lpOutAmount = IERC20(lpToken).balanceOf(address(this)); // <= FOUND
78:         
79:         if (lpOutAmount < lpMinOutAmount) {
80:             revert BalancerLib__ReceivedAmountIsLessThanMinimum(
81:                 lpOutAmount,
82:                 lpMinOutAmount
83:             );
84:         }
85:     }

['119']

119:     function claimYieldForGauge(
120:         address marketManager,
121:         bool claimWETHYield,
122:         bool claimUSDBYield
123:     ) external nonReentrant returns (
124:         uint256 WETHYield,
125:         uint256 USDBYield
126:     ) {
127:         
128:         
129:         if (!isMarketManager[marketManager]) {
130:             _revert(_UNAUTHORIZED_SELECTOR);
131:         }
132: 
133:         
134:         
135:         if (!IMarketManager(marketManager).isListed(msg.sender)) { // <= FOUND
136:             _revert(_UNAUTHORIZED_SELECTOR);
137:         }
138: 
139:         uint256 gasYield = CHAIN_YIELD_MANAGER.claimMaxGas(
140:             msg.sender,
141:             address(this)
142:         );
143:         uint256 WETHPrior = WETH_YIELD_MANAGER.balanceOf(address(this));
144:         uint256 USDBPrior = USDB_YIELD_MANAGER.balanceOf(address(this));
145:         uint256 WETHPerSecond;
146:         uint256 USDBPerSecond;
147: 
148:         address yieldDestination = cTokenToDTokenYieldRouted[msg.sender];
149: 
150:         
151:         
152:         if (yieldDestination == address(0)) {
153:             yieldDestination = msg.sender;
154:         }
155: 
156:         if (gasYield > 0) {
157:             IWETH(address(WETH_YIELD_MANAGER)).deposit{ value: gasYield }(); // <= FOUND
158:             WETHYield += gasYield;
159:         }
160: 
161:         if (claimWETHYield) {
162:             uint256 pendingWETH = pendingWETHYield[msg.sender];
163: 
164:             
165:             if (pendingWETH > 0) {
166:                 WETHYield += pendingWETH;
167:                 WETHPerSecond = WETHYield / EPOCH_WINDOW;
168:             }
169:         }
170: 
171:         if (claimUSDBYield) {
172:             uint256 pendingUSDB = pendingUSDBYield[msg.sender];
173: 
174:             
175:             if (pendingUSDB > 0) {
176:                 USDBYield += pendingUSDB;
177:                 USDBPerSecond = USDBYield / EPOCH_WINDOW;
178:             }
179:         }
180: 
181:         
182:         if (USDBYield == 0 && WETHYield == 0) {
183:             revert BlastNativeYieldManager__NoYieldToClaim();
184:         }
185: 
186:         
187:         IGaugePool gaugePool = IMarketManager(marketManager).gaugePool(); // <= FOUND
188:         uint256 nextEpoch = gaugePool.currentEpoch() + 1;
189: 
190:         
191:         
192:         if (epochReported[msg.sender][nextEpoch]) {
193:             _revert(_UNAUTHORIZED_SELECTOR);
194:         }
195: 
196:         
197:         
198:         
199:         
200: 
202:         if (WETHPerSecond > 0){
203:             
204:             SwapperLib._approveTokenIfNeeded(
205:                 address(WETH_YIELD_MANAGER),
206:                 address(gaugePool),
207:                 WETHYield
208:             );
209: 
210:             gaugePool.setRewardPerSec(
211:                 yieldDestination,
212:                 nextEpoch,
213:                 address(WETH_YIELD_MANAGER),
214:                 WETHPerSecond
215:             );
216: 
217:             
218:             SwapperLib._removeApprovalIfNeeded(
219:                 address(WETH_YIELD_MANAGER),
220:                 address(gaugePool)
221:             );
222:         }
223: 
224:         
225:         
226:         
227:         uint256 WETHAfter = WETH_YIELD_MANAGER.balanceOf(address(this));
228: 
229:         
230:         
231:         WETHPrior -= WETHYield;
232: 
233:         if (WETHAfter != WETHPrior) {
234:             
235:             
236:             if (WETHAfter < WETHPrior) {
237:                 revert BlastNativeYieldManager__InvariantError();

['307']

307:     function claimYieldForAutoCompounding(
308:         address marketManager,
309:         bool claimWETHYield,
310:         bool claimUSDBYield
311:     ) external nonReentrant returns (
312:         uint256 WETHYield,
313:         uint256 USDBYield
314:     ) {
315:         
316:         
317:         if (!isMarketManager[marketManager]) {
318:             _revert(_UNAUTHORIZED_SELECTOR);
319:         }
320: 
321:         
322:         
323:         if (!IMarketManager(marketManager).isListed(msg.sender)) { // <= FOUND
324:             _revert(_UNAUTHORIZED_SELECTOR);
325:         }
326: 
327:         if (claimWETHYield) {
328:             uint256 pendingWETH = pendingWETHYield[msg.sender];
329: 
330:             
331:             if (pendingWETH > 0) {
332:                 WETHYield += pendingWETH;
333:             }
334:         }
335: 
336:         if (claimUSDBYield) {
337:             uint256 pendingUSDB = pendingUSDBYield[msg.sender];
338: 
339:             
340:             if (pendingUSDB > 0) {
341:                 USDBYield += pendingUSDB;
342:             }
343:         }
344: 
345:         
346:         if (USDBYield == 0 && WETHYield == 0) {
347:             revert BlastNativeYieldManager__NoYieldToClaim();
348:         }
349: 
350:         if (USDBYield != 0) {
351:             SafeTransferLib.safeTransfer(
352:                 address(USDB_YIELD_MANAGER),
353:                 msg.sender,
354:                 USDBYield
355:             );
356:         }
357: 
358:         if (WETHYield != 0) {
359:             SafeTransferLib.safeTransfer(
360:                 address(WETH_YIELD_MANAGER),
361:                 msg.sender,
362:                 WETHYield
363:             );
364:         }
365:     }

['372']

372:     function setCTokenToDTokenYieldDonation(
373:         address cToken,
374:         address dToken
375:     ) external {
376:         _checkElevatedPermissions();
377: 
378:         if (
379:             IMToken(cToken).marketManager() !=  // <= FOUND
380:             IMToken(dToken).marketManager() // <= FOUND
381:             ) {
382:                 revert BlastNativeYieldManager__MarketManagerMismatch();
383:             }
384:         
385:         if (
386:             !IMToken(cToken).isCToken() || // <= FOUND
387:             IMToken(dToken).isCToken() // <= FOUND
388:             ) {
389:                 revert BlastNativeYieldManager__InvalidTokenTypes();
390:             }
391:         
392:         cTokenToDTokenYieldRouted[cToken] = dToken;
393:     }

['427']

427:     function notifyRewards(
428:         address marketManager,
429:         bool isWETH,
430:         uint256 amount
431:     ) external {
432:         
433:         
434:         if (!isMarketManager[marketManager]) {
435:             _revert(_UNAUTHORIZED_SELECTOR);
436:         }
437: 
438:         
439:         
440:         if (!IMarketManager(marketManager).isListed(msg.sender)) { // <= FOUND
441:             _revert(_UNAUTHORIZED_SELECTOR);
442:         }
443: 
444:         if (isWETH) {
445:             pendingWETHYield[msg.sender] = pendingWETHYield[msg.sender] + amount;
446:             return;
447:         }
448: 
449:         pendingUSDBYield[msg.sender] = pendingUSDBYield[msg.sender] + amount;
450:     }

['189']

189:     function rescueToken(address token, uint256 amount) external { // <= FOUND
190:         _checkDaoPermissions();
191:         address daoOperator = centralRegistry.daoAddress();
192: 
193:         if (token == address(0)) {
194:             if (amount == 0) {
195:                 amount = address(this).balance; // <= FOUND
196:             }
197: 
198:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
199:         } else {
200:             if (token == cve) {
201:                 revert CVEInitialDistribution__TransferError();
202:             }
203: 
204:             if (amount == 0) {
205:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
206:             }
207: 
208:             SafeTransferLib.safeTransfer(token, daoOperator, amount);
209:         }
210:     }

['164']

164:     function rescueToken(address token, uint256 amount) external { // <= FOUND
165:         _checkDaoPermissions();
166:         address daoOperator = centralRegistry.daoAddress();
167: 
168:         if (token == address(0)) {
169:             if (amount == 0) {
170:                 amount = address(this).balance; // <= FOUND
171:             }
172: 
173:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
174:         } else {
175:             if (token == rewardToken) {
176:                 _revert(_UNAUTHORIZED_SELECTOR);
177:             }
178: 
179:             if (amount == 0) {
180:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
181:             }
182: 
183:             SafeTransferLib.safeTransfer(token, daoOperator, amount);
184:         }
185:     }

['711']

711:     function rescueToken(address token, uint256 amount) external { // <= FOUND
712:         _checkDaoPermissions();
713:         address daoOperator = centralRegistry.daoAddress();
714: 
715:         if (token == address(0)) {
716:             if (amount == 0) {
717:                 amount = address(this).balance; // <= FOUND
718:             }
719: 
720:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
721:         } else {
722:             if (token == underlying) {
723:                 revert DToken__TransferError();
724:             }
725: 
726:             if (amount == 0) {
727:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
728:             }
729: 
730:             SafeTransferLib.safeTransfer(token, daoOperator, amount);
731:         }
732:     }

['91']

91:     function rescueToken(address token, uint256 amount) external { // <= FOUND
92:         _checkDaoPermissions();
93:         address daoOperator = centralRegistry.daoAddress();
94: 
95:         if (token == address(0)) {
96:             if (amount == 0) {
97:                 amount = address(this).balance; // <= FOUND
98:             }
99: 
100:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
101:         } else {
102:             if (token == cve) {
103:                 revert OCVE__TransferError();
104:             }
105: 
106:             if (amount == 0) {
107:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
108:             }
109: 
110:             SafeTransferLib.safeTransfer(token, daoOperator, amount);
111:         }
112:     }

['241']

241:     function rescueToken(address token, uint256 amount) external { // <= FOUND
242:         if (!centralRegistry.hasDaoPermissions(msg.sender)) {
243:             _revert(_UNAUTHORIZED_SELECTOR);
244:         }
245: 
246:         address daoOperator = centralRegistry.daoAddress();
247: 
248:         if (token == address(0)) {
249:             if (amount == 0) {
250:                 amount = address(this).balance; // <= FOUND
251:             }
252: 
253:             SafeTransferLib.forceSafeTransferETH(daoOperator, amount);
254:         } else {
255:             if (token == address(cve)) {
256:                 revert VeCVE__NonTransferrable();
257:             }
258: 
259:             if (amount == 0) {
260:                 amount = IERC20(token).balanceOf(address(this)); // <= FOUND
261:             }
262: 
263:             SafeTransferLib.safeTransfer(token, daoOperator, amount);
264:         }
265:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (!ICamelotPair(asset).stableSwap()) { // <= FOUND
42:             revert CamelotStableLPAdaptor__AssetIsNotStableLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotStableLPAssetAdded(asset, data, isUpdate);
53:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (ICamelotPair(asset).stableSwap()) { // <= FOUND
42:             revert CamelotVolatileLPAdaptor__AssetIsNotVolatileLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotVolatileLPAssetAdded(asset, data, isUpdate);
53:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (!IVeloPool(asset).stable()) { // <= FOUND
42:             revert VelodromeStableLPAdaptor__AssetIsNotStableLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50:         
51:         AdaptorData memory data = _addAsset(asset);
52:         emit VelodromeStableLPAssetAdded(asset, data, isUpdate);
53:     }

['38']

38:     function addAsset(
39:         address asset
40:     ) external override {
41:         _checkElevatedPermissions();
42: 
43:         if (IVeloPool(asset).stable()) { // <= FOUND
44:             revert VelodromeVolatileLPAdaptor__AssetIsNotVolatileLP();
45:         }
46: 
47:         
48:         bool isUpdate;
49:         if (isSupportedAsset[asset]) {
50:             isUpdate = true;
51:         }
52: 
53:         AdaptorData memory data = _addAsset(asset);
54:         emit VelodromeVolatileLPAssetAdded(asset, data, isUpdate);
55:     }

['113']

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() // <= FOUND
130:         );
131: 
132:         
133:         uint256 maxFromChainlink = uint256(
134:             uint192(feedAggregator.maxAnswer())
135:         );
136:         uint256 minFromChainklink = uint256(
137:             uint192(feedAggregator.minAnswer())
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) { // <= FOUND
151:             bufferedMaxPrice = type(uint240).max; // <= FOUND
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:     }

['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;
129:         paymentTokenDecimals = IERC20(paymentTokenAddress).decimals(); // <= FOUND
130: 
131:         emit LBPStarted(startTimestamp);
132:     }

['27']

27:     function enterCurve(
28:         address lpMinter,
29:         address lpToken,
30:         address[] calldata tokens,
31:         uint256 lpMinOutAmount
32:     ) internal returns (uint256 lpOutAmount) {
33:         uint256 numTokens = tokens.length;
34:         
35:         
36:         if (numTokens > 4 || numTokens < 2) {
37:             revert CurveLib__InvalidPoolType();
38:         }
39: 
40:         uint256[] memory balances = new uint256[](numTokens);
41:         uint256 value;
42:         bool containsEth;
43:         
44:         
45:         for (uint256 i; i < numTokens; ++i) {
46:             balances[i] = CommonLib.getTokenBalance(tokens[i]);
47:             SwapperLib._approveTokenIfNeeded(
48:                 tokens[i], 
49:                 lpMinter, 
50:                 balances[i]
51:             );
52: 
53:             if (CommonLib.isETH(tokens[i])) {
54:                 
55:                 
56:                 if (containsEth) {
57:                     revert CurveLib__InvalidPoolInvariantError();
58:                 }
59: 
60:                 value = balances[i];
61:                 containsEth = true;
62:             }
63:         }
64: 
65:         
66:         if (numTokens == 4) {
67:             uint256[4] memory fourPoolAmounts;
68:             fourPoolAmounts[0] = balances[0];
69:             fourPoolAmounts[1] = balances[1];
70:             fourPoolAmounts[2] = balances[2];
71:             fourPoolAmounts[3] = balances[3];
72: 
73:             ICurveSwap(lpMinter).add_liquidity{ value: value }( // <= FOUND
74:                 fourPoolAmounts,
75:                 0
76:             );
77:         } else if (numTokens == 3) {
78:             uint256[3] memory threePoolAmounts;
79:             threePoolAmounts[0] = balances[0];
80:             threePoolAmounts[1] = balances[1];
81:             threePoolAmounts[2] = balances[2];
82: 
83:             ICurveSwap(lpMinter).add_liquidity{ value: value }( // <= FOUND
84:                 threePoolAmounts,
85:                 0
86:             );
87:         } else {
88:             uint256[2] memory twoPoolAmounts;
89:             twoPoolAmounts[0] = balances[0];
90:             twoPoolAmounts[1] = balances[1];
91: 
92:             ICurveSwap(lpMinter).add_liquidity{ value: value }( // <= FOUND
93:                 twoPoolAmounts,
94:                 0
95:             );
96:         }
97: 
98:         lpOutAmount = IERC20(lpToken).balanceOf(address(this)); // <= FOUND
99:         
100:         if (lpOutAmount < lpMinOutAmount) {
101:             revert CurveLib__ReceivedAmountIsLessThanMinimum(
102:                 lpOutAmount,
103:                 lpMinOutAmount
104:             );
105:         }
106:     }

['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(); // <= FOUND
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:     }

['221']

221:     function tokenDataOf(
222:         address account,
223:         address mToken
224:     ) external view returns (
225:         bool hasPosition, 
226:         uint256 balanceOf, 
227:         uint256 collateralPostedOf
228:     ) {
229:         AccountPosition memory accountPositions = tokenData[mToken].accountPositions[account];
230:         hasPosition = accountPositions.activePosition == 2;
231:         balanceOf = IMToken(mToken).balanceOf(account); // <= FOUND
232:         collateralPostedOf = accountPositions.collateralPosted;
233:     }

['285']

285:     function hypotheticalLiquidityOf(
286:         address account,
287:         address mTokenModified,
288:         uint256 redeemTokens, 
289:         uint256 borrowAmount 
290:     ) external view returns (uint256, uint256) {
291:         
292:         
293:         if (IMToken(mTokenModified).isCToken() && borrowAmount > 0) { // <= FOUND
294:             _revert(_INVALID_PARAMETER_SELECTOR);
295:         }
296: 
297:         (HypotheticalData memory result,) = _hypotheticalLiquidityOf(
298:             account,
299:             HypotheticalAction({
300:                     mTokenModified: mTokenModified,
301:                     redeemTokens: redeemTokens,
302:                     borrowAmount: borrowAmount,
303:                     errorCodeBreakpoint: 2
304:             })
305:         );
306:         return (result.collateralSurplus, result.liquidityDeficit);
307:     }

['315']

315:     function postCollateral(
316:         address account,
317:         address cToken,
318:         uint256 tokens
319:     ) external {
320:         if (tokens == 0) {
321:             _revert(_INVALID_PARAMETER_SELECTOR);
322:         }
323: 
324:         
325:         
326:         if (msg.sender != account) {
327:             _checkIsToken(cToken);
328:         }
329: 
330:         if (!tokenData[cToken].isListed) {
331:             _revert(_TOKEN_NOT_LISTED_SELECTOR);
332:         }
333: 
334:         if (!IMToken(cToken).isCToken()) { // <= FOUND
335:             _revert(_INVALID_PARAMETER_SELECTOR);
336:         }
337: 
338:         AccountPosition storage accountPositions = tokenData[cToken].accountPositions[
339:             account
340:         ];
341: 
342:         
343:         if (
344:             accountPositions.collateralPosted + tokens >
345:             IMToken(cToken).balanceOf(account) // <= FOUND
346:         ) {
347:             revert MarketManager__InsufficientCollateral();
348:         }
349: 
350:         _postCollateral(account, accountPositions, cToken, tokens);
351:     }

['357']

357:     function removeCollateral(
358:         address cToken,
359:         uint256 tokens
360:     ) external {
361:         if (tokens == 0) {
362:             _revert(_INVALID_PARAMETER_SELECTOR);
363:         }
364: 
365:         AccountPosition storage accountPositions = tokenData[cToken].accountPositions[
366:             msg.sender
367:         ];
368: 
369:         
370:         
371:         
372:         if (accountPositions.activePosition != 2) {
373:             _revert(_INVARIANT_ERROR_SELECTOR);
374:         }
375: 
376:         if (!IMToken(cToken).isCToken()) { // <= FOUND
377:             _revert(_INVALID_PARAMETER_SELECTOR);
378:         }
379: 
380:         if (accountPositions.collateralPosted < tokens) {
381:             revert MarketManager__InsufficientCollateral();
382:         }
383: 
384:         
385:         
386:         _canRedeem(cToken, msg.sender, tokens);
387:         _removeCollateral(
388:             msg.sender,
389:             accountPositions,
390:             cToken,
391:             tokens
392:         );
393:     }

['878']

878:     function listToken(address mToken) external { // <= FOUND
879:         _checkElevatedPermissions();
880: 
881:         if (tokenData[mToken].isListed) {
882:             _revert(_INVALID_PARAMETER_SELECTOR);
883:         }
884: 
885:         
886:         IMToken(mToken).isCToken(); // <= FOUND
887: 
888:         
889:         
890:         if (!IMToken(mToken).startMarket(msg.sender)) { // <= FOUND
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; ) {
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:     }

['1536']

1536:     function _canLiquidate(
1537:         address debtToken,
1538:         address collateralToken,
1539:         address account,
1540:         uint256 debtAmount,
1541:         bool liquidateExact
1542:     ) internal view returns (uint256, uint256, uint256) {
1543:         if (!tokenData[debtToken].isListed) {
1544:             _revert(_TOKEN_NOT_LISTED_SELECTOR);
1545:         }
1546: 
1547:         MarketToken storage cToken = tokenData[collateralToken];
1548: 
1549:         if (!cToken.isListed) {
1550:             _revert(_TOKEN_NOT_LISTED_SELECTOR);
1551:         }
1552: 
1553:         
1554:         if (cToken.collRatio == 0) {
1555:             _revert(_INVALID_PARAMETER_SELECTOR);
1556:         }
1557: 
1558:         
1559:         LiqData memory data = _LiquidationStatusOf(
1560:             account,
1561:             debtToken,
1562:             collateralToken
1563:         );
1564: 
1565:         
1566:         if (data.lFactor == 0) {
1567:             revert MarketManager__NoLiquidationAvailable();
1568:         }
1569: 
1570:         uint256 maxAmount;
1571:         uint256 debtToCollateralRatio;
1572:         {
1573:             uint256 cFactor = cToken.baseCFactor +
1574:                 ((cToken.cFactorCurve * data.lFactor) / WAD);
1575:             uint256 incentive = cToken.liqBaseIncentive +
1576:                 ((cToken.liqCurve * data.lFactor) / WAD);
1577:             maxAmount =
1578:                 (cFactor * IMToken(debtToken).debtBalanceCached(account)) / // <= FOUND
1579:                 WAD;
1580: 
1581:             
1582:             
1583:             debtToCollateralRatio =
1584:                 (incentive * data.debtTokenPrice * WAD) /
1585:                 (data.collateralTokenPrice *
1586:                     IMToken(collateralToken).exchangeRateCached()); // <= FOUND
1587:         }
1588: 
1589:         
1590:         
1591:         if (!liquidateExact) {
1592:             debtAmount = maxAmount;
1593:         }
1594: 
1595:         
1596:         uint256 amountAdjusted = (debtAmount *
1597:             (10 ** IERC20(collateralToken).decimals())) / // <= FOUND
1598:             (10 ** IERC20(debtToken).decimals()); // <= FOUND
1599:         
1600:         uint256 liquidatedTokens = (amountAdjusted * debtToCollateralRatio) /
1601:             WAD;
1602: 
1603:         
1604:         uint256 collateralAvailable = cToken
1605:             .accountPositions[account]
1606:             .collateralPosted;
1607:         
1608:         
1609:         
1610:         if (liquidateExact) {
1611:             if (
1612:                 debtAmount > maxAmount ||
1613:                 liquidatedTokens > collateralAvailable
1614:             ) {
1615:                 
1616:                 
1617:                 _revert(_INVALID_PARAMETER_SELECTOR);
1618:             }
1619:         } else {
1620:             if (liquidatedTokens > collateralAvailable) {
1621:                 debtAmount =
1622:                     (debtAmount * collateralAvailable) /
1623:                     liquidatedTokens;
1624:                 liquidatedTokens = collateralAvailable;
1625:             }
1626:         }
1627: 
1628:         
1629:         
1630:         return (
1631:             debtAmount,
1632:             liquidatedTokens,
1633:             (liquidatedTokens * cToken.liqFee) / WAD
1634:         );
1635:     }

['205']

205:     function addMTokenSupport(address newMToken) external { // <= FOUND
206:         _checkElevatedPermissions();
207: 
208:         if (mTokenAssets[newMToken].isMToken) {
209:             _revert(_INVALID_PARAMETER_SELECTOR);
210:         }
211: 
212:         
213:         IMToken(newMToken).isCToken(); // <= FOUND
214: 
215:         mTokenAssets[newMToken].isMToken = true;
216:         mTokenAssets[newMToken].underlying = IMToken(newMToken).underlying(); // <= FOUND
217:     }

['350']

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(); // <= FOUND
373:                 data[0].price = uint240((data[0].price * exchangeRate) / WAD);
374:                 data[1].price = uint240((data[1].price * exchangeRate) / WAD);
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(); // <= FOUND
389:             data[0].price = uint240((data[0].price * exchangeRate) / WAD);
390:             data[1].price = uint240((data[1].price * exchangeRate) / WAD);
391:             data[2].price = uint240((data[2].price * exchangeRate) / WAD);
392:             data[3].price = uint240((data[3].price * exchangeRate) / WAD);
393:         }
394: 
395:         return data;
396:     }

['571']

571:     function _addFeed(address asset, address feed) internal { // <= FOUND
572:         
573:         if (!isApprovedAdaptor[feed]) {
574:             _revert(_INVALID_PARAMETER_SELECTOR);
575:         }
576: 
577:         
578:         if (!IOracleAdaptor(feed).isSupportedAsset(asset)) { // <= FOUND
579:             _revert(_INVALID_PARAMETER_SELECTOR);
580:         }
581: 
582:         uint256 numPriceFeeds = assetPriceFeeds[asset].length;
583: 
584:         
585:         if (numPriceFeeds >= 2) {
586:             _revert(_INVALID_PARAMETER_SELECTOR);
587:         }
588: 
589:         
590:         
591:         if (numPriceFeeds != 0 && assetPriceFeeds[asset][0] == feed) {
592:             _revert(_INVALID_PARAMETER_SELECTOR);
593:         }
594: 
595:         
596:         
597:         PriceReturnData memory sampleData = IOracleAdaptor(feed).getPrice( // <= FOUND
598:             asset,
599:             true,
600:             true
601:         );
602: 
603:         if (sampleData.price == 0 || sampleData.hadError) {
604:             _revert(_INVALID_PARAMETER_SELECTOR);
605:         }
606: 
607:         assetPriceFeeds[asset].push(feed);
608:     }

['694']

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( // <= FOUND
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(
723:                 _convertETHUSD(data.price, newPrice, data.inUSD)
724:             );
725:         }
726: 
727:         return (data.price, NO_ERROR);
728:     }

['744']

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( // <= FOUND
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(
774:                 _convertETHUSD(data.price, newPrice, data.inUSD)
775:             );
776:         }
777: 
778:         return FeedData({ price: data.price, hadError: data.hadError });
779:     }

['86']

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); // <= FOUND
99: 
100:         (uint256 price, uint256 errorCode) = IOracleRouter(
101:             centralRegistry.oracleRouter()
102:         ).getPrice(data.quoteAsset, inUSD, getLower); // <= FOUND
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);
122:     }

['256']

256:     function onBorrow(
257:         address borrowToken,
258:         address borrower,
259:         uint256 borrowAmount,
260:         bytes calldata params
261:     ) external override {
262:         
263:         
264:         if (msg.sender != borrowToken) {
265:             _revert(_UNAUTHORIZED_SELECTOR);
266:         }
267: 
268:         
269:         
270:         if (!marketManager.isListed(borrowToken)) {
271:             _revert(_UNAUTHORIZED_SELECTOR);
272:         }
273: 
274:         LeverageStruct memory leverageData = abi.decode(
275:             params,
276:             (LeverageStruct)
277:         );
278: 
279:         if (
280:             borrowToken != address(leverageData.borrowToken) ||
281:             borrowAmount != leverageData.borrowAmount
282:         ) {
283:             revert PositionFolding__InvalidParam();
284:         }
285: 
286:         address borrowUnderlying = CTokenPrimitive(borrowToken).underlying(); // <= FOUND
287: 
288:         if (IERC20(borrowUnderlying).balanceOf(address(this)) < borrowAmount) { // <= FOUND
289:             revert PositionFolding__InvalidAmount();
290:         }
291: 
292:         
293:         uint256 fee = (borrowAmount * getProtocolLeverageFee()) / WAD;
294:         if (fee > 0) {
295:             SafeTransferLib.safeTransfer(
296:                 borrowUnderlying,
297:                 centralRegistry.daoAddress(),
298:                 fee
299:             );
300:         }
301: 
302:         
303:         if (leverageData.swapData.call.length > 0) {
304:             
305:             if (!centralRegistry.isSwapper(leverageData.swapData.target)) {
306:                 revert PositionFolding__InvalidSwapper(
307:                     leverageData.swapData.target
308:                 );
309:             }
310: 
311:             
312:             SwapperLib.swap(centralRegistry, leverageData.swapData);
313:         }
314: 
315:         
316:         SwapperLib.ZapperCall memory zapperCall = leverageData.zapperCall;
317: 
318:         
319:         if (zapperCall.call.length > 0) {
320:             
321:             if (!centralRegistry.isZapper(leverageData.zapperCall.target)) {
322:                 revert PositionFolding__InvalidZapper(
323:                     leverageData.zapperCall.target
324:                 );
325:             }
326: 
327:             
328:             
329:             SwapperLib.zap(zapperCall);
330:         }
331: 
332:         
333:         
334:         
335:         
336:         CTokenPrimitive collateralToken = leverageData.collateralToken;
337:         
338:         
339:         address collateralUnderlying = collateralToken.underlying();
340:         uint256 amount = IERC20(collateralUnderlying).balanceOf(address(this)); // <= FOUND
341: 
342:         
343:         SwapperLib._approveTokenIfNeeded(
344:             collateralUnderlying,
345:             address(collateralToken),
346:             amount
347:         );
348: 
349:         
350:         collateralToken.depositAsCollateral(amount, borrower);
351: 
352:         uint256 remaining = IERC20(zapperCall.inputToken).balanceOf( // <= FOUND
353:             address(this)
354:         );
355: 
356:         
357:         if (remaining > 0) {
358:             SafeTransferLib.safeTransfer(
359:                 zapperCall.inputToken,
360:                 borrower,
361:                 remaining
362:             );
363:         }
364: 
365:         remaining = IERC20(borrowUnderlying).balanceOf(address(this)); // <= FOUND
366: 
367:         
368:         if (remaining > 0) {
369:             SafeTransferLib.safeTransfer(
370:                 borrowUnderlying,
371:                 borrower,
372:                 remaining
373:             );
374:         }
375: 
376:         
377:         SwapperLib._removeApprovalIfNeeded(
378:             borrowUnderlying,
379:             address(borrowToken)
380:         );
381:     }

['394']

394:     function onRedeem(
395:         address collateralToken,
396:         address redeemer,
397:         uint256 collateralAmount,
398:         bytes calldata params
399:     ) external override {
400:         
401:         
402:         if (msg.sender != collateralToken) {
403:             _revert(_UNAUTHORIZED_SELECTOR);
404:         }
405: 
406:         
407:         
408:         if (!marketManager.isListed(collateralToken)) {
409:             _revert(_UNAUTHORIZED_SELECTOR);
410:         }
411: 
412:         DeleverageStruct memory deleverageData = abi.decode(
413:             params,
414:             (DeleverageStruct)
415:         );
416: 
417:         if (
418:             collateralToken != address(deleverageData.collateralToken) ||
419:             collateralAmount != deleverageData.collateralAmount
420:         ) {
421:             revert PositionFolding__InvalidParam();
422:         }
423: 
424:         
425:         
426:         address collateralUnderlying = CTokenPrimitive(collateralToken)
427:             .underlying();
428: 
429:         if (
430:             IERC20(collateralUnderlying).balanceOf(address(this)) < // <= FOUND
431:             collateralAmount
432:         ) {
433:             revert PositionFolding__InvalidAmount();
434:         }
435: 
436:         
437:         uint256 fee = (collateralAmount * getProtocolLeverageFee()) / 10000;
438:         if (fee > 0) {
439:             collateralAmount -= fee;
440:             SafeTransferLib.safeTransfer(
441:                 collateralUnderlying,
442:                 centralRegistry.daoAddress(),
443:                 fee
444:             );
445:         }
446: 
447:         SwapperLib.ZapperCall memory zapperCall = deleverageData.zapperCall;
448: 
449:         
450:         if (zapperCall.call.length > 0) {
451:             if (collateralUnderlying != zapperCall.inputToken) {
452:                 revert PositionFolding__InvalidZapperParam();
453:             }
454: 
455:             
456:             if (!centralRegistry.isZapper(deleverageData.zapperCall.target)) {
457:                 revert PositionFolding__InvalidZapper(
458:                     deleverageData.zapperCall.target
459:                 );
460:             }
461: 
462:             
463:             SwapperLib.zap(zapperCall);
464:         }
465: 
466:         
467:         if (deleverageData.swapData.call.length > 0) {
468:             
469:             if (!centralRegistry.isSwapper(deleverageData.swapData.target)) {
470:                 revert PositionFolding__InvalidSwapper(
471:                     deleverageData.swapData.target
472:                 );
473:             }
474: 
475:             
476:             SwapperLib.swap(centralRegistry, deleverageData.swapData);
477:         }
478: 
479:         
480:         
481:         
482:         
483:         DToken borrowToken = deleverageData.borrowToken;
484: 
485:         
486:         address borrowUnderlying = borrowToken.underlying();
487:         uint256 repayAmount = deleverageData.repayAmount;
488:         uint256 remaining = IERC20(borrowUnderlying).balanceOf(address(this)) - // <= FOUND
489:             repayAmount;
490: 
491:         
492:         SwapperLib._approveTokenIfNeeded(
493:             borrowUnderlying,
494:             address(borrowToken),
495:             repayAmount
496:         );
497: 
498:         
499:         borrowToken.repayFor(redeemer, repayAmount);
500: 
501:         
502:         if (remaining > 0) {
503:             SafeTransferLib.safeTransfer(
504:                 borrowUnderlying,
505:                 redeemer,
506:                 remaining
507:             );
508:         }
509: 
510:         remaining = IERC20(collateralUnderlying).balanceOf(address(this)); // <= FOUND
511: 
512:         
513:         if (remaining > 0) {
514:             SafeTransferLib.safeTransfer(
515:                 collateralUnderlying,
516:                 redeemer,
517:                 remaining
518:             );
519:         }
520: 
521:         
522:         SwapperLib._removeApprovalIfNeeded(
523:             borrowUnderlying,
524:             address(borrowToken)
525:         );
526:     }

['389']

389:     function _repayDebt(
390:         address dToken,
391:         address dTokenUnderlying,
392:         uint256 repayAmount,
393:         address recipient
394:     ) internal returns (uint256 outAmount) {
395:         
396:         
397:         
398:         
399:         
400:         outAmount = IERC20(dTokenUnderlying).balanceOf(address(this)); // <= FOUND
401: 
402:         
403:         if (outAmount < repayAmount) {
404:             revert SimpleRewardZapper__InsufficientToRepay();
405:         }
406: 
407:         
408:         SwapperLib._approveTokenIfNeeded(
409:             dTokenUnderlying,
410:             dToken,
411:             repayAmount
412:         );
413: 
414:         
415:         DToken(dToken).repayFor(recipient, repayAmount); // <= FOUND
416: 
417:         
418:         SwapperLib._removeApprovalIfNeeded(dTokenUnderlying, dToken);
419: 
420:         outAmount -= repayAmount;
421: 
422:         
423:         if (outAmount > 0) {
424:             _transferToRecipient(dTokenUnderlying, recipient, outAmount);
425:         }
426:     }

['292']

292:     function _repayDebt(
293:         address dToken,
294:         uint256 repayAmount,
295:         address recipient
296:     ) internal returns (uint256 outAmount) {
297:         address dTokenUnderlying = DToken(dToken).underlying(); // <= FOUND
298:         
299:         
300:         
301:         outAmount = IERC20(dTokenUnderlying).balanceOf(address(this)); // <= FOUND
302:         
303:         
304:         if (outAmount < repayAmount) {
305:             revert SimpleZapper__InsufficientToRepay();
306:         }
307: 
308:         
309:         SwapperLib._approveTokenIfNeeded(
310:             dTokenUnderlying,
311:             dToken,
312:             repayAmount
313:         );
314: 
315:         
316:         DToken(dToken).repayFor(recipient, repayAmount); // <= FOUND
317: 
318:         
319:         SwapperLib._removeApprovalIfNeeded(dTokenUnderlying, dToken);
320: 
321:         outAmount -= repayAmount;
322: 
323:         
324:         if (outAmount > 0) {
325:             _transferToRecipient(
326:                 dTokenUnderlying, 
327:                 recipient, 
328:                 outAmount
329:             );
330:         }
331:     }

['759']

759:     function bridgeVeCVELock(
760:         uint256 lockIndex,
761:         uint256 dstChainId,
762:         bool continuousLock,
763:         RewardsData calldata rewardsData,
764:         bytes calldata params,
765:         uint256 aux
766:     ) external payable nonReentrant returns (uint64 sequence) {
767:         if (isShutdown == 2) {
768:             _revert(_VECVE_SHUTDOWN_SELECTOR);
769:         }
770: 
771:         
772:         _claimRewards(msg.sender, rewardsData, params, aux);
773: 
774:         
775:         
776:         Lock[] storage locks = userLocks[msg.sender];
777: 
778:         
779:         if (lockIndex >= locks.length) {
780:             _revert(_INVALID_LOCK_SELECTOR);
781:         }
782: 
783:         
784:         if (block.timestamp >= locks[lockIndex].unlockTime) {
785:             _revert(_INVALID_LOCK_SELECTOR);
786:         }
787: 
788:         Lock memory lock = locks[lockIndex];
789:         uint256 amount = lock.amount;
790: 
791:         
792:         _updateDataFromEarlyUnlock(msg.sender, amount, lock.unlockTime);
793: 
794:         
795:         _burn(msg.sender, amount);
796:         
797:         _removeLock(locks, lockIndex);
798:         
799:         ICVE(cve).burnVeCVELock(amount); // <= FOUND
800: 
801:         address messagingHub = centralRegistry.protocolMessagingHub();
802: 
803:         sequence = IProtocolMessagingHub(messagingHub).bridgeVeCVELock{ // <= FOUND
804:             value: msg.value
805:         }(dstChainId, msg.sender, amount, continuousLock);
806: 
807:         
808:         
809:         
810:         if (locks.length == 0 && isShutdown != 2) {
811:             cveLocker.resetUserClaimIndex(msg.sender);
812:         }
813:     }

['41']

41:     function enterVelodrome(
42:         address router,
43:         address factory,
44:         address lpToken,
45:         uint256 amount0,
46:         uint256 amount1,
47:         uint256 lpMinOutAmount
48:     ) internal returns (uint256 lpOutAmount) {
49:         address token0 = IVeloPair(lpToken).token0(); // <= FOUND
50:         address token1 = IVeloPair(lpToken).token1(); // <= FOUND
51:         bool stable = IVeloPool(lpToken).stable(); // <= FOUND
52: 
53:         
54:         if (amount0 > 0) {
55:             (uint256 r0, uint256 r1, ) = IVeloPair(lpToken).getReserves(); // <= FOUND
56:             
57:             uint256 swapAmount = _optimalDeposit(
58:                 factory,
59:                 lpToken,
60:                 amount0,
61:                 r0,
62:                 r1,
63:                 10 ** ERC20(token0).decimals(), // <= FOUND
64:                 10 ** ERC20(token1).decimals(), // <= FOUND
65:                 stable
66:             );
67: 
68:             
69:             amount1 = _swapExactTokensForTokens(
70:                 router,
71:                 lpToken,
72:                 token0,
73:                 token1,
74:                 swapAmount,
75:                 stable
76:             );
77:             amount0 -= swapAmount;
78: 
79:             
80:             uint256 newLpOutAmount = _addLiquidity(
81:                 router,
82:                 token0,
83:                 token1,
84:                 stable,
85:                 amount0,
86:                 amount1,
87:                 VELODROME_ADD_LIQUIDITY_SLIPPAGE
88:             );
89: 
90:             lpOutAmount += newLpOutAmount;
91:         }
92: 
93:         amount1 = CommonLib.getTokenBalance(token1);
94: 
95:         
96:         if (amount1 > 0) {
97:             (uint256 r0, uint256 r1, ) = IVeloPair(lpToken).getReserves(); // <= FOUND
98:             
99:             uint256 swapAmount = _optimalDeposit(
100:                 factory,
101:                 lpToken,
102:                 amount1,
103:                 r1,
104:                 r0,
105:                 10 ** ERC20(token1).decimals(), // <= FOUND
106:                 10 ** ERC20(token0).decimals(), // <= FOUND
107:                 stable
108:             );
109: 
110:             
111:             amount0 = _swapExactTokensForTokens(
112:                 router,
113:                 lpToken,
114:                 token1,
115:                 token0,
116:                 swapAmount,
117:                 stable
118:             );
119:             amount1 -= swapAmount;
120: 
121:             
122:             uint256 newLpOutAmount = _addLiquidity(
123:                 router,
124:                 token0,
125:                 token1,
126:                 stable,
127:                 amount0,
128:                 amount1,
129:                 VELODROME_ADD_LIQUIDITY_SLIPPAGE
130:             );
131: 
132:             lpOutAmount += newLpOutAmount;
133:         }
134: 
135:         
136:         if (lpOutAmount < lpMinOutAmount) {
137:             revert VelodromeLib__ReceivedAmountIsLessThanMinimum(
138:                 lpOutAmount,
139:                 lpMinOutAmount
140:             );
141:         }
142:     }

['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); // <= FOUND
236:         uint256 a;
237: 
238:         
239:         if (stable) {
240:             a = (((amount0 * 10000) / (10000 - swapFee)) * 1e18) / decimals0;
241: 
242:             uint256 x = (reserve0 * 1e18) / decimals0;
243:             uint256 y = (reserve1 * 1e18) / decimals1;
244:             uint256 x2 = (x * x) / 1e18;
245:             uint256 y2 = (y * y) / 1e18;
246:             uint256 p = (y * (((x2 * 3 + y2) * 1e18) / (y2 * 3 + x2))) / x;
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;
262:         
263:     }

['273']

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(); // <= FOUND
289: 
290:         
291:         uint256[] memory amountsOut = IVeloRouter(router)
292:             .swapExactTokensForTokens(
293:                 amount,
294:                 0,
295:                 routes,
296:                 address(this),
297:                 block.timestamp
298:             );
299: 
300:         
301:         SwapperLib._removeApprovalIfNeeded(tokenIn, router);
302: 
303:         return amountsOut[amountsOut.length - 1];
304:     }

[NonCritical-4] Events may be emitted out of order due to code not follow the best practice of check-effects-interaction

Resolution

The "check-effects-interaction" pattern also impacts event ordering. When a contract doesn't adhere to this pattern, events might be emitted in a sequence that doesn't reflect the actual logical flow of operations. This can cause confusion during event tracking, potentially leading to erroneous off-chain interpretations. To rectify this, always ensure that checks are performed first, state modifications come next, and interactions with external contracts or addresses are done last. This will ensure events are emitted in a logical, consistent manner, providing a clear and accurate chronological record of on-chain actions for off-chain systems and observers.

Num of instances: 18

Findings

Click to show findings

['148']

148:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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) {
163:             
164:             if (address(data.underlyingOrConstituent[i]) == address(0)) {
165:                 continue;
166:             }
167: 
168:             if (
169:                 !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
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) { // <= FOUND
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); // <= FOUND
200:     }

['177']

177:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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)) { // <= FOUND
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) {
207:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
208:         }
209:         if (
210:             pool.coins(uint256(uint128(data.baseTokenIndex))) != data.baseToken
211:         ) {
212:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
213:         }
214: 
215:         data.quoteTokenDecimals = ERC20(asset).decimals(); // <= FOUND
216:         
217:         if (CommonLib.isETH(data.baseToken)) {
218:             data.baseTokenDecimals = 18;
219:         } else {
220:             data.baseTokenDecimals = ERC20(data.baseToken).decimals(); // <= FOUND
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); // <= FOUND
252:     }

['196']

196:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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( // <= FOUND
211:                 data.underlying0
212:             )
213:         ) {
214:             revert Curve2PoolLPAdaptor__QuoteAssetIsNotSupported();
215:         }
216: 
217:         
218:         
219:         if (
220:             !IOracleRouter(oracleRouter).isSupportedAsset( // <= FOUND
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; ) {
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:         
299:         bool isUpdate;
300:         if (isSupportedAsset[asset]) {
301:             isUpdate = true;
302:         }
303: 
304:         isSupportedAsset[asset] = true;
305:         emit CurvePoolAssetAdded(asset, data, isUpdate); // <= FOUND
306:     }

['130']

130:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
131:         _checkElevatedPermissions();
132: 
133:         
134:         (IStandardizedYield sy, IPPrincipalToken pt, ) = IPMarket(asset)
135:             .readTokens();
136: 
137:         
138:         if (address(pt) != data.pt) {
139:             revert PendleLPTokenAdaptor__WrongMarket();
140:         }
141: 
142:         
143:         if (data.twapDuration < MINIMUM_TWAP_DURATION) {
144:             revert PendleLPTokenAdaptor__TwapDurationIsLessThanMinimum();
145:         }
146: 
147:         
148:         (, address assetAddress, ) = sy.assetInfo();
149:         if (assetAddress != data.quoteAsset) {
150:             revert PendleLPTokenAdaptor__WrongQuote();
151:         }
152: 
153:         
154:         _checkPtTwap(asset, data.twapDuration);
155: 
156:         
157:         if (
158:             !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
159:                 data.quoteAsset
160:             )
161:         ) {
162:             revert PendleLPTokenAdaptor__QuoteAssetIsNotSupported();
163:         }
164: 
165:         
166:         adaptorData[asset] = data;
167: 
168:         
169:         bool isUpdate;
170:         if (isSupportedAsset[asset]) {
171:             isUpdate = true;
172:         }
173: 
174:         isSupportedAsset[asset] = true;
175:         emit PendleLPAssetAdded(asset, data, isUpdate); // <= FOUND
176:     }

['131']

131:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
132:         _checkElevatedPermissions();
133: 
134:         
135:         (IStandardizedYield sy, IPPrincipalToken pt, ) = data
136:             .market
137:             .readTokens();
138: 
139:         
140:         if (address(pt) != asset) {
141:             revert PendlePrincipalTokenAdaptor__WrongMarket();
142:         }
143: 
144:         
145:         if (data.twapDuration < MINIMUM_TWAP_DURATION) {
146:             revert PendlePrincipalTokenAdaptor__TwapDurationIsLessThanMinimum();
147:         }
148: 
149:         
150:         (, address assetAddress, ) = sy.assetInfo();
151:         if (assetAddress != data.quoteAsset) {
152:             revert PendlePrincipalTokenAdaptor__WrongQuote();
153:         }
154: 
155:         
156:         _checkPtTwap(address(data.market), data.twapDuration);
157: 
158:         
159:         if (
160:             !IOracleRouter(centralRegistry.oracleRouter()).isSupportedAsset( // <= FOUND
161:                 data.quoteAsset
162:             )
163:         ) {
164:             revert PendlePrincipalTokenAdaptor__QuoteAssetIsNotSupported();
165:         }
166: 
167:         
168:         adaptorData[asset] = data;
169: 
170:         
171:         bool isUpdate;
172:         if (isSupportedAsset[asset]) {
173:             isUpdate = true;
174:         }
175: 
176:         isSupportedAsset[asset] = true;
177:         emit PendlePTAssetAdded(asset, data, isUpdate); // <= FOUND
178:     }

['214']

214:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
215:         _checkElevatedPermissions();
216: 
217:         
218:         if (data.secondsAgo < MINIMUM_SECONDS_AGO) {
219:             revert UniswapV3Adaptor__SecondsAgoIsLessThanMinimum();
220:         }
221: 
222:         UniswapV3Pool pool = UniswapV3Pool(data.priceSource);
223: 
224:         
225:         address token0 = pool.token0();
226:         address token1 = pool.token1();
227:         if (token0 == asset) {
228:             data.baseDecimals = ERC20(asset).decimals(); // <= FOUND
229:             data.quoteDecimals = ERC20(token1).decimals(); // <= FOUND
230:             data.quoteToken = token1;
231:         } else if (token1 == asset) {
232:             data.baseDecimals = ERC20(asset).decimals(); // <= FOUND
233:             data.quoteDecimals = ERC20(token0).decimals(); // <= FOUND
234:             data.quoteToken = token0;
235:         } else revert UniswapV3Adaptor__AssetIsNotSupported();
236: 
237:         
238:         adaptorData[asset] = data;
239: 
240:         
241:         bool isUpdate;
242:         if (isSupportedAsset[asset]) {
243:             isUpdate = true;
244:         }
245: 
246:         isSupportedAsset[asset] = true;
247:         emit UniswapV3AssetAdded(asset, data, isUpdate); // <= FOUND
248:     }

['129']

129:     function harvest(
130:         bytes calldata data
131:     ) external override returns (uint256 yield) {
132:         
133:         _canCompound();
134: 
135:         
136:         _vestIfNeeded();
137: 
138:         
139:         if (_checkVestStatus(_vaultData)) {
140:             _updateVestingPeriodIfNeeded();
141: 
142:             
143:             StrategyData memory sd = strategyData;
144: 
145:             
146:             sd.gauge.getReward(address(this));
147: 
148:             {
149:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
150: 
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount,
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert AerodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert AerodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0() // <= FOUND
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield); // <= FOUND
243:         }
244:     }

['124']

124:     function harvest(
125:         bytes calldata data
126:     ) external override returns (uint256 yield) {
127:         
128:         _canCompound();
129: 
130:         
131:         _vestIfNeeded();
132: 
133:         
134:         if (_checkVestStatus(_vaultData)) {
135:             _updateVestingPeriodIfNeeded();
136: 
137:             
138:             StrategyData memory sd = strategyData;
139: 
140:             
141:             sd.gauge.getReward(address(this));
142: 
143:             {
144:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
145:                 
146:                 if (rewardAmount > 0) {
147:                     
148:                     
149:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
150:                         rewardAmount,
151:                         centralRegistry.protocolHarvestFee(),
152:                         1e18
153:                     );
154:                     rewardAmount -= protocolFee;
155:                     SafeTransferLib.safeTransfer(
156:                         address(rewardToken),
157:                         centralRegistry.feeAccumulator(),
158:                         protocolFee
159:                     );
160: 
161:                     
162:                     if (!rewardTokenIsUnderlying) {
163:                         SwapperLib.Swap memory swapData = abi.decode(
164:                             data,
165:                             (SwapperLib.Swap)
166:                         );
167: 
168:                         if (!centralRegistry.isSwapper(swapData.target)) {
169:                             revert AerodromeVolatileCToken__InvalidSwapper(
170:                                 swapData.target
171:                             );
172:                         }
173: 
174:                         SwapperLib.swap(centralRegistry, swapData);
175:                     }
176:                 }
177:             }
178: 
179:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
180:             
181:             if (totalAmountA == 0) {
182:                 revert AerodromeVolatileCToken__SlippageError();
183:             }
184: 
185:             
186:             address _asset = asset();
187:             
188:             
189:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
190:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
191:                 ? r0
192:                 : r1;
193: 
194:             
195:             
196:             
197:             uint256 swapAmount = VelodromeLib._optimalDeposit(
198:                 address(sd.pairFactory),
199:                 _asset,
200:                 totalAmountA,
201:                 reserveA,
202:                 0,
203:                 0,
204:                 0,
205:                 false
206:             );
207:             
208:             VelodromeLib._swapExactTokensForTokens(
209:                 address(sd.router),
210:                 _asset,
211:                 sd.token0,
212:                 sd.token1,
213:                 swapAmount,
214:                 false
215:             );
216:             totalAmountA -= swapAmount;
217: 
218:             
219:             yield = VelodromeLib._addLiquidity(
220:                 address(sd.router),
221:                 sd.token0,
222:                 sd.token1,
223:                 false,
224:                 totalAmountA,
225:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
226:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
227:             );
228: 
229:             
230:             
231:             _afterDeposit(yield, 0);
232: 
233:             
234:             _setNewVaultData(yield, vestPeriod);
235: 
236:             emit Harvest(yield); // <= FOUND
237:         }
238:         
239:     }

['130']

130:     function harvest(
131:         bytes calldata data
132:     ) external override returns (uint256 yield) {
133:         
134:         _canCompound();
135: 
136:         
137:         _vestIfNeeded();
138: 
139:         
140:         if (_checkVestStatus(_vaultData)) {
141:             _updateVestingPeriodIfNeeded();
142: 
143:             
144:             StrategyData memory sd = strategyData;
145: 
146:             
147:             sd.gauge.getReward(address(this));
148: 
149:             {
150:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount, 
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert VelodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert VelodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0() // <= FOUND
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield); // <= FOUND
243:         }
244:     }

['117']

117:     function harvest(
118:         bytes calldata data
119:     ) external override returns (uint256 yield) {
120:         
121:         _canCompound();
122: 
123:         
124:         _vestIfNeeded();
125: 
126:         
127:         if (_checkVestStatus(_vaultData)) {
128:             _updateVestingPeriodIfNeeded();
129: 
130:             
131:             StrategyData memory sd = strategyData;
132: 
133:             
134:             sd.gauge.getReward(address(this));
135: 
136:             {
137:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
138:                 
139:                 if (rewardAmount > 0) {
140:                     
141:                     
142:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
143:                         rewardAmount, 
144:                         centralRegistry.protocolHarvestFee(),
145:                         1e18
146:                     );
147:                     rewardAmount -= protocolFee;
148:                     SafeTransferLib.safeTransfer(
149:                         address(rewardToken),
150:                         centralRegistry.feeAccumulator(),
151:                         protocolFee
152:                     );
153: 
154:                     
155:                     if (!rewardTokenIsUnderlying) {
156:                         SwapperLib.Swap memory swapData = abi.decode(
157:                             data,
158:                             (SwapperLib.Swap)
159:                         );
160: 
161:                         if (!centralRegistry.isSwapper(swapData.target)) {
162:                             revert VelodromeVolatileCToken__InvalidSwapper(
163:                                 swapData.target
164:                             );
165:                         }
166: 
167:                         SwapperLib.swap(centralRegistry, swapData);
168:                     }
169:                 }
170:             }
171: 
172:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this)); // <= FOUND
173: 
174:             
175:             if (totalAmountA == 0) {
176:                 revert VelodromeVolatileCToken__SlippageError();
177:             }
178: 
179:             
180:             address _asset = asset();
181:             
182:             
183:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves(); // <= FOUND
184:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0() // <= FOUND
185:                 ? r0
186:                 : r1;
187: 
188:             
189:             
190:             
191:             uint256 swapAmount = VelodromeLib._optimalDeposit(
192:                 address(sd.pairFactory),
193:                 _asset,
194:                 totalAmountA,
195:                 reserveA,
196:                 0,
197:                 0,
198:                 0,
199:                 false
200:             );
201:             
202:             VelodromeLib._swapExactTokensForTokens(
203:                 address(sd.router),
204:                 _asset,
205:                 sd.token0,
206:                 sd.token1,
207:                 swapAmount,
208:                 false
209:             );
210:             totalAmountA -= swapAmount;
211: 
212:             
213:             yield = VelodromeLib._addLiquidity(
214:                 address(sd.router),
215:                 sd.token0,
216:                 sd.token1,
217:                 false,
218:                 totalAmountA,
219:                 IERC20(sd.token1).balanceOf(address(this)),  // <= FOUND
220:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
221:             );
222: 
223:             
224:             
225:             _afterDeposit(yield, 0);
226: 
227:             
228:             _setNewVaultData(yield, vestPeriod);
229: 
230:             emit Harvest(yield); // <= FOUND
231:         }
232:     }

['108']

108:     function addAsset(
109:         address asset,
110:         string memory ticker, 
111:         address proxyFeed, 
112:         uint256 heartbeat, 
113:         bool inUSD
114:     ) external {
115:         _checkElevatedPermissions();
116: 
117:         if (heartbeat != 0) {
118:             if (heartbeat > DEFAULT_HEART_BEAT) {
119:                 revert Api3Adaptor__InvalidHeartbeat();
120:             }
121:         }
122: 
123:         bytes32 dapiName = Bytes32Helper.stringToBytes32(ticker);
124:         bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
125: 
126:         
127:         
128:         if (dapiNameHash != IProxy(proxyFeed).dapiNameHash()) { // <= FOUND
129:             revert Api3Adaptor__DAPINameHashError();
130:         }
131: 
132:         AdaptorData storage data;
133: 
134:         if (inUSD) {
135:             data = adaptorDataUSD[asset];
136:         } else {
137:             data = adaptorDataNonUSD[asset];
138:         }
139: 
140:         data.heartbeat = heartbeat != 0
141:             ? heartbeat
142:             : DEFAULT_HEART_BEAT;
143: 
144:         
145: 
146:         
147:         
148:         
149:         
150:         data.max = (uint256(int256(type(int224).max)) * 9) / 10; // <= FOUND
151:         data.dapiNameHash = dapiNameHash;
152:         data.proxyFeed = IProxy(proxyFeed);
153:         data.isConfigured = true;
154: 
155:         
156:         bool isUpdate;
157:         if (isSupportedAsset[asset]) {
158:             isUpdate = true;
159:         }
160: 
161:         isSupportedAsset[asset] = true;
162:         emit Api3AssetAdded(asset, data, isUpdate); // <= FOUND
163:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (!ICamelotPair(asset).stableSwap()) { // <= FOUND
42:             revert CamelotStableLPAdaptor__AssetIsNotStableLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotStableLPAssetAdded(asset, data, isUpdate); // <= FOUND
53:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (ICamelotPair(asset).stableSwap()) { // <= FOUND
42:             revert CamelotVolatileLPAdaptor__AssetIsNotVolatileLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotVolatileLPAssetAdded(asset, data, isUpdate); // <= FOUND
53:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (!IVeloPool(asset).stable()) { // <= FOUND
42:             revert VelodromeStableLPAdaptor__AssetIsNotStableLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50:         
51:         AdaptorData memory data = _addAsset(asset);
52:         emit VelodromeStableLPAssetAdded(asset, data, isUpdate); // <= FOUND
53:     }

['38']

38:     function addAsset(
39:         address asset
40:     ) external override {
41:         _checkElevatedPermissions();
42: 
43:         if (IVeloPool(asset).stable()) { // <= FOUND
44:             revert VelodromeVolatileLPAdaptor__AssetIsNotVolatileLP();
45:         }
46: 
47:         
48:         bool isUpdate;
49:         if (isSupportedAsset[asset]) {
50:             isUpdate = true;
51:         }
52: 
53:         AdaptorData memory data = _addAsset(asset);
54:         emit VelodromeVolatileLPAssetAdded(asset, data, isUpdate); // <= FOUND
55:     }

['113']

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() // <= FOUND
130:         );
131: 
132:         
133:         uint256 maxFromChainlink = uint256(
134:             uint192(feedAggregator.maxAnswer())
135:         );
136:         uint256 minFromChainklink = uint256(
137:             uint192(feedAggregator.minAnswer())
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) { // <= FOUND
151:             bufferedMaxPrice = type(uint240).max; // <= FOUND
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); // <= FOUND
184:     }

['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;
129:         paymentTokenDecimals = IERC20(paymentTokenAddress).decimals(); // <= FOUND
130: 
131:         emit LBPStarted(startTimestamp); // <= FOUND
132:     }

['878']

878:     function listToken(address mToken) external { // <= FOUND
879:         _checkElevatedPermissions();
880: 
881:         if (tokenData[mToken].isListed) {
882:             _revert(_INVALID_PARAMETER_SELECTOR);
883:         }
884: 
885:         
886:         IMToken(mToken).isCToken(); // <= FOUND
887: 
888:         
889:         
890:         if (!IMToken(mToken).startMarket(msg.sender)) { // <= FOUND
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; ) {
901:             unchecked {
902:                 if (tokensListed[i++] == mToken) {
903:                     _revert(_INVALID_PARAMETER_SELECTOR);
904:                 }
905:             }
906:         }
907: 
908:         tokensListed.push(mToken);
909:         emit TokenListed(mToken); // <= FOUND
910:     }

[NonCritical-5] It is standard for all external and public functions to be override from an interface

Resolution

This is to ensure the whole API is extracted in a interface

Num of instances: 397

Findings

Click to show findings

['108']

108:     function addAsset(
109:         address asset,
110:         string memory ticker, 
111:         address proxyFeed, 
112:         uint256 heartbeat, 
113:         bool inUSD
114:     ) external 

['134']

134:     function reQueryRewardTokens() external 

['163']

163:     function reQueryUnderlyingTokens() external 

['157']

157:     function rewardTokens() external view returns (address[] memory) 

['162']

162:     function underlyingTokens() external view returns (address[] memory) 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['93']

93:     function addAsset(
94:         address asset, 
95:         bool inUSD,
96:         uint8 decimals
97:     ) external 

['70']

70:     function addAsset(address asset) external virtual 

['70']

70:     function addAsset(address asset) external virtual 

['17']

17:     function aggregator() external view returns (address) 

['22']

22:     function maxAnswer() external view returns (int192) 

['44']

44:     function minAnswer() external view returns (int192) 

['67']

67:     function decimals() external view returns (uint8) 

['81']

81:     function latestRoundData()
82:         external
83:         view
84:         returns (
85:             uint80 roundId,
86:             int256 answer,
87:             uint256 startedAt,
88:             uint256 updatedAt,
89:             uint80 answeredInRound
90:         )
91:     

['70']

70:     function withdrawNativeYield(address[] calldata nonMTokens) external 

['119']

119:     function claimYieldForGauge(
120:         address marketManager,
121:         bool claimWETHYield,
122:         bool claimUSDBYield
123:     ) external nonReentrant returns (
124:         uint256 WETHYield,
125:         uint256 USDBYield
126:     ) 

['307']

307:     function claimYieldForAutoCompounding(
308:         address marketManager,
309:         bool claimWETHYield,
310:         bool claimUSDBYield
311:     ) external nonReentrant returns (
312:         uint256 WETHYield,
313:         uint256 USDBYield
314:     ) 

['372']

372:     function setCTokenToDTokenYieldDonation(
373:         address cToken,
374:         address dToken
375:     ) external 

['398']

398:     function claimPendingNativeYield(address[] calldata nonMTokens) external 

['427']

427:     function notifyRewards(
428:         address marketManager,
429:         bool isWETH,
430:         uint256 amount
431:     ) external 

['456']

456:     function notifyIsMarketManager(
457:         address notifiedMarketManager,
458:         bool isSupported
459:     ) external 

['469']

469:     function getClaimableNativeYield(
470:         address delegatedAddress
471:     ) external view returns (uint256) 

['37']

37:     function borrowAndBridge(
38:         address dToken,
39:         uint256 borrowAmount,
40:         SwapperLib.Swap memory swapData,
41:         uint256 dstChainId
42:     ) external payable nonReentrant 

['144']

144:     function depositAsCollateral(
145:         uint256 assets,
146:         address receiver
147:     ) external nonReentrant returns (uint256 shares) 

['170']

170:     function depositAsCollateralFor(
171:         uint256 assets,
172:         address receiver
173:     ) external nonReentrant returns (uint256 shares) 

['187']

187:     function withdrawCollateral(
188:         uint256 assets,
189:         address receiver,
190:         address owner
191:     ) external nonReentrant returns (uint256 shares) 

['201']

201:     function redeemCollateral(
202:         uint256 shares,
203:         address receiver,
204:         address owner
205:     ) external nonReentrant returns (uint256 assets) 

['216']

216:     function redeemCollateralFor(
217:         uint256 shares,
218:         address receiver,
219:         address owner
220:     ) external nonReentrant returns (uint256 assets) 

['227']

227:     function balanceOfUnderlyingSafe(
228:         address account
229:     ) external view returns (uint256) 

['236']

236:     function balanceOfUnderlying(
237:         address account
238:     ) external view returns (uint256) 

['244']

244:     function exchangeRateSafe() external view returns (uint256) 

['250']

250:     function exchangeRateCached() external view returns (uint256) 

['778']

778:     function getSnapshot(
779:         address account
780:     ) external view returns (uint256, uint256, uint256) 

['795']

795:     function getSnapshotPacked(
796:         address
797:     ) external view returns (AccountSnapshot memory) 

['288']

288:     function startMarket(address by) external virtual returns (bool) 

['312']

312:     function underlying() external view returns (address) 

['484']

484:     function seize(
485:         address liquidator,
486:         address account,
487:         uint256 liquidatedTokens,
488:         uint256 protocolTokens
489:     ) external nonReentrant 

['530']

530:     function seizeAccountLiquidation(
531:         address liquidator,
532:         address account,
533:         uint256 shares
534:     ) external nonReentrant 

['41']

41:     function withdrawByPositionFolding(
42:         address owner,
43:         uint256 assets,
44:         bytes calldata params
45:     ) external nonReentrant 

['164']

164:     function getVaultYieldStatus() external view returns (VaultData memory) 

['169']

169:     function vaultCompoundFee() external view returns (uint256) 

['174']

174:     function vaultYieldFee() external view returns (uint256) 

['179']

179:     function vaultHarvestFee() external view returns (uint256) 

['205']

205:     function setVestingPeriod(uint256 newVestingPeriod) external 

['219']

219:     function setCompoundingPaused(bool state) external 

['52']

52:     function setExitFee(uint256 newExitFee) external 

['41']

41:     function withdrawByPositionFolding(
42:         address owner,
43:         uint256 assets,
44:         bytes calldata params
45:     ) external nonReentrant 

['102']

102:     function mintGaugeEmissions(address gaugePool, uint256 amount) external 

['113']

113:     function mintLockBoost(uint256 amount) external 

['126']

126:     function mintVeCVELock(uint256 amount) external 

['139']

139:     function burnVeCVELock(uint256 amount) external 

['151']

151:     function mintTreasury(uint256 amount) external 

['167']

167:     function mintCommunityAllocation(uint256 amount) external 

['185']

185:     function mintBuilder() external 

['212']

212:     function setBuilderAddress(address newAddress) external 

['229']

229:     function bridge(
230:         uint256 dstChainId,
231:         address recipient,
232:         uint256 amount
233:     ) external payable returns (uint64) 

['248']

248:     function bridgeFee() external view returns (uint256) 

['89']

89:     function claim(
90:         uint256 amount,
91:         bool locked,
92:         bytes32[] calldata proof
93:     ) external nonReentrant 

['162']

162:     function canClaim(
163:         address user,
164:         uint256 amount,
165:         bytes32[] calldata proof
166:     ) external view returns (bool) 

['711']

711:     function rescueToken(address token, uint256 amount) external 

['215']

215:     function withdrawRemainingTokens() external 

['234']

234:     function setMerkleRoot(bytes32 newRoot) external 

['252']

252:     function setPauseState(bool paused) external 

['132']

132:     function recordEpochRewards(uint256 rewardsPerCVE) external 

['150']

150:     function startLocker() external 

['711']

711:     function rescueToken(address token, uint256 amount) external 

['190']

190:     function notifyLockerShutdown() external 

['204']

204:     function currentEpoch(uint256 time) external view returns (uint256) 

['219']

219:     function hasRewardsToClaim(address user) external view returns (bool) 

['236']

236:     function hypotheticalRewardsClaim(
237:         address user
238:     ) external view returns (uint256) 

['278']

278:     function updateUserClaimIndex(address user, uint256 index) external 

['287']

287:     function resetUserClaimIndex(address user) external 

['298']

298:     function claimRewards(
299:         RewardsData calldata rewardsData,
300:         bytes calldata params,
301:         uint256 aux
302:     ) external nonReentrant 

['331']

331:     function claimRewardsFor(
332:         address user,
333:         uint256 epochs,
334:         RewardsData calldata rewardsData,
335:         bytes calldata params,
336:         uint256 aux
337:     ) external nonReentrant 

['355']

355:     function manageRewardsFor(
356:         address user,
357:         uint256 epochs
358:     ) external nonReentrant returns (uint256) 

['303']

303:     function withdrawReservesMulti(address[] calldata dTokens) external 

['329']

329:     function setCVE(address newCVE) external 

['340']

340:     function setVeCVE(address newVeCVE) external 

['351']

351:     function setCVELocker(address newCVELocker) external 

['362']

362:     function setProtocolMessagingHub(
363:         address newProtocolMessagingHub
364:     ) external 

['385']

385:     function setOracleRouter(address newOracleRouter) external 

['396']

396:     function setFeeAccumulator(address newFeeAccumulator) external 

['407']

407:     function setWormholeCore(address newWormholeCore) external 

['418']

418:     function setWormholeRelayer(address newWormholeRelayer) external 

['429']

429:     function setCircleTokenMessenger(
430:         address newCircleTokenMessenger
431:     ) external 

['442']

442:     function setTokenBridge(address newTokenBridge) external 

['454']

454:     function registerWormholeChainIDs(
455:         uint256[] calldata chainIds,
456:         uint16[] calldata wormholeChainIds
457:     ) external 

['472']

472:     function registerCCTPDomains(
473:         uint256[] calldata chainIds,
474:         uint32[] calldata cctpDomains
475:     ) external 

['490']

490:     function setGelatoSponsor(address newGelatoSponsor) external 

['504']

504:     function setProtocolCompoundFee(uint256 value) external 

['529']

529:     function setProtocolYieldFee(uint256 value) external 

['554']

554:     function setProtocolLeverageFee(uint256 value) external 

['577']

577:     function setProtocolInterestRateFee(
578:         address market,
579:         uint256 value
580:     ) external 

['608']

608:     function setEarlyUnlockPenaltyMultiplier(uint256 value) external 

['634']

634:     function setVoteBoostMultiplier(uint256 value) external 

['656']

656:     function setLockBoostMultiplier(uint256 value) external 

['676']

676:     function incrementApprovalIndex() external 

['687']

687:     function disableDelegable(bool delegable) external 

['699']

699:     function transferDaoOwnership(address newDaoAddress) external 

['729']

729:     function migrateTimelockConfiguration(address newTimelock) external 

['751']

751:     function transferEmergencyCouncil(address newEmergencyCouncil) external 

['786']

786:     function addChainSupport(
787:         address newOmnichainOperator,
788:         address messagingHub,
789:         address cveAddress,
790:         uint256 chainId,
791:         uint256 sourceAux,
792:         uint256 destinationAux,
793:         uint16 messagingChainId
794:     ) external 

['836']

836:     function removeChainSupport(
837:         address currentOmnichainOperator,
838:         uint256 chainId
839:     ) external 

['883']

883:     function setExternalCallDataChecker(
884:         address target,
885:         address callDataChecker
886:     ) external 

['898']

898:     function addZapper(address newZapper) external 

['917']

917:     function removeZapper(address currentZapper) external 

['936']

936:     function addSwapper(address newSwapper) external 

['955']

955:     function removeSwapper(address currentSwapper) external 

['974']

974:     function addVeCVELocker(address newVeCVELocker) external 

['993']

993:     function removeVeCVELocker(address currentVeCVELocker) external 

['1012']

1012:     function addGaugeController(address newGaugeController) external 

['1031']

1031:     function removeGaugeController(address currentGaugeController) external 

['1053']

1053:     function addHarvester(address newHarvester) external 

['1072']

1072:     function removeHarvester(address currentHarvester) external 

['1091']

1091:     function addEndpoint(address newEndpoint) external 

['1110']

1110:     function removeEndpoint(address currentEndpoint) external 

['1123']

1123:     function getOmnichainOperators(
1124:         address _address,
1125:         uint256 chainID
1126:     ) external view returns (OmnichainData memory) 

['113']

113:     function addAsset(
114:         address asset, 
115:         address aggregator, 
116:         uint256 heartbeat, 
117:         bool inUSD
118:     ) external 

['102']

102:     function mintGaugeEmissions(address gaugePool, uint256 amount) external 

['113']

113:     function mintLockBoost(uint256 amount) external 

['126']

126:     function mintVeCVELock(uint256 amount) external 

['139']

139:     function burnVeCVELock(uint256 amount) external 

['229']

229:     function bridge(
230:         uint256 dstChainId,
231:         address recipient,
232:         uint256 amount
233:     ) external payable returns (uint64) 

['126']

126:     function enterCurve(
127:         address cToken,
128:         ZapperData calldata zapData,
129:         SwapperLib.Swap[] calldata tokenSwaps,
130:         address lpMinter,
131:         address[] calldata tokens,
132:         address recipient
133:     ) external payable nonReentrant returns (uint256 outAmount) 

['175']

175:     function exitCurve(
176:         address lpMinter,
177:         ZapperData calldata zapData,
178:         address[] calldata tokens,
179:         uint256 singleAssetWithdraw,
180:         uint256 singleAssetIndex,
181:         SwapperLib.Swap[] calldata tokenSwaps,
182:         address recipient
183:     ) external nonReentrant returns (uint256 outAmount) 

['227']

227:     function redeemAndExitCurve(
228:         RedemptionData calldata redemptionData,
229:         address lpMinter,
230:         ZapperData calldata zapData,
231:         address[] calldata tokens,
232:         uint256 singleAssetWithdraw,
233:         uint256 singleAssetIndex,
234:         SwapperLib.Swap[] calldata tokenSwaps,
235:         address recipient
236:     ) external nonReentrant returns (uint256 outAmount) 

['268']

268:     function enterBalancer(
269:         address cToken,
270:         ZapperData calldata zapData,
271:         SwapperLib.Swap[] calldata tokenSwaps,
272:         address balancerVault,
273:         bytes32 balancerPoolId,
274:         address[] calldata tokens,
275:         address recipient
276:     ) external payable nonReentrant returns (uint256 outAmount) 

['321']

321:     function exitBalancer(
322:         BPTRedemption calldata balancerData,
323:         ZapperData calldata zapData,
324:         address[] calldata tokens,
325:         SwapperLib.Swap[] calldata tokenSwaps,
326:         address recipient
327:     ) external nonReentrant returns (uint256 outAmount) 

['374']

374:     function redeemAndExitBalancer(
375:         RedemptionData calldata redemptionData,
376:         BPTRedemption calldata balancerData,
377:         ZapperData calldata zapData,
378:         address[] calldata tokens,
379:         SwapperLib.Swap[] calldata tokenSwaps,
380:         address recipient
381:     ) external nonReentrant returns (uint256 outAmount) 

['413']

413:     function enterVelodrome(
414:         address cToken,
415:         ZapperData calldata zapData,
416:         SwapperLib.Swap[] calldata tokenSwaps,
417:         address router,
418:         address factory,
419:         address recipient
420:     ) external payable nonReentrant returns (uint256 outAmount) 

['455']

455:     function exitVelodrome(
456:         address router,
457:         ZapperData calldata zapData,
458:         SwapperLib.Swap[] calldata tokenSwaps,
459:         address recipient
460:     ) external nonReentrant returns (uint256 outAmount) 

['492']

492:     function redeemAndExitVelodrome(
493:         RedemptionData calldata redemptionData,
494:         address router,
495:         ZapperData calldata zapData,
496:         SwapperLib.Swap[] calldata tokenSwaps,
497:         address recipient
498:     ) external nonReentrant returns (uint256 outAmount) 

['97']

97:     function start(
98:         uint256 startTimestamp,
99:         uint256 softPriceInUSD,
100:         uint256 cveAmountInLBP,
101:         address paymentTokenAddress
102:     ) external 

['138']

138:     function commit(uint256 amount) external 

['160']

160:     function commitFor(uint256 amount, address recipient) external 

['178']

178:     function claim() external returns (uint256 amount) 

['207']

207:     function withdrawFunds() external 

['57']

57:     function updateDaoAddress() external 

['88']

88:     function setReentrancyConfig(
89:         uint256 coinsLength,
90:         uint256 gasLimit
91:     ) external 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['343']

343:     function raiseBounds(
344:         address asset,
345:         uint256 newLowerBound,
346:         uint256 newUpperBound
347:     ) external 

['88']

88:     function setReentrancyConfig(
89:         uint256 coinsLength,
90:         uint256 gasLimit
91:     ) external 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['343']

343:     function raiseBounds(
344:         address asset,
345:         uint256 newLowerBound,
346:         uint256 newUpperBound
347:     ) external 

['222']

222:     function startMarket(address by) external nonReentrant returns (bool) 

['250']

250:     function transfer(
251:         address to,
252:         uint256 tokens
253:     ) external nonReentrant returns (bool) 

['263']

263:     function transferFrom(
264:         address from,
265:         address to,
266:         uint256 tokens
267:     ) external nonReentrant returns (bool) 

['276']

276:     function borrow(uint256 amount) external nonReentrant 

['298']

298:     function borrowFor(
299:         address account,
300:         address recipient,
301:         uint256 amount
302:     ) external nonReentrant 

['326']

326:     function borrowForPositionFolding(
327:         address account,
328:         uint256 amount,
329:         bytes calldata params
330:     ) external nonReentrant 

['361']

361:     function repay(uint256 amount) external nonReentrant 

['373']

373:     function repayFor(address account, uint256 amount) external nonReentrant 

['392']

392:     function repayWithBadDebt(
393:         address liquidator,
394:         address account,
395:         uint256 repayRatio
396:     ) external nonReentrant 

['442']

442:     function liquidateExact(
443:         address account,
444:         uint256 amount,
445:         IMToken collateralToken
446:     ) external nonReentrant 

['456']

456:     function liquidate(
457:         address account,
458:         IMToken collateralToken
459:     ) external nonReentrant 

['472']

472:     function redeem(uint256 tokens) external nonReentrant 

['497']

497:     function redeemFor(
498:         address account,
499:         address recipient,
500:         uint256 tokens
501:     ) external nonReentrant 

['532']

532:     function redeemUnderlyingForPositionFolding(
533:         address account,
534:         uint256 amount,
535:         bytes calldata params
536:     ) external nonReentrant 

['569']

569:     function mint(uint256 amount) external nonReentrant returns (bool) 

['581']

581:     function mintFor(
582:         uint256 amount,
583:         address recipient
584:     ) external nonReentrant returns (bool) 

['594']

594:     function depositReserves(uint256 amount) external nonReentrant 

['627']

627:     function withdrawReserves(uint256 amount) external nonReentrant 

['659']

659:     function processWithdrawReserves() external 

['696']

696:     function approve(address spender, uint256 tokens) external returns (bool) 

['711']

711:     function rescueToken(address token, uint256 amount) external 

['738']

738:     function setInterestRateModel(address newInterestRateModel) external 

['751']

751:     function setInterestFactor(uint256 newInterestFactor) external 

['764']

764:     function balanceOfUnderlyingSafe(
765:         address account
766:     ) external returns (uint256) 

['778']

778:     function getSnapshot(
779:         address account
780:     ) external view returns (uint256, uint256, uint256) 

['795']

795:     function getSnapshotPacked(
796:         address account
797:     ) external view returns (AccountSnapshot memory) 

['812']

812:     function utilizationRate() external view returns (uint256) 

['824']

824:     function borrowRatePerYear() external view returns (uint256) 

['837']

837:     function predictedBorrowRatePerYear() external view returns (uint256) 

['849']

849:     function supplyRatePerYear() external view returns (uint256) 

['863']

863:     function totalBorrowsWithUpdateSafe()
864:         external
865:         nonReentrant
866:         returns (uint256)
867:     

['879']

879:     function debtBalanceWithUpdateSafe(
880:         address account
881:     ) external nonReentrant returns (uint256) 

['109']

109:     function setDelegateApproval(
110:         address delegate,
111:         bool isApproved
112:     ) external 

['258']

258:     function updateDynamicInterestRateModel(
259:         uint256 baseRatePerYear,
260:         uint256 vertexRatePerYear,
261:         uint256 vertexUtilStart,
262:         uint256 adjustmentRate,
263:         uint256 adjustmentVelocity,
264:         uint256 vertexMultiplierMax,
265:         uint256 decayRate,
266:         bool vertexReset
267:     ) external 

['290']

290:     function getBorrowRateWithUpdate(
291:         uint256 cash,
292:         uint256 borrows,
293:         uint256 reserves
294:     ) external returns (uint256 borrowRate) 

['347']

347:     function getPredictedBorrowRatePerYear(
348:         uint256 cash,
349:         uint256 borrows,
350:         uint256 reserves
351:     ) external view returns (uint256) 

['363']

363:     function getBorrowRatePerYear(
364:         uint256 cash,
365:         uint256 borrows,
366:         uint256 reserves
367:     ) external view returns (uint256) 

['380']

380:     function getSupplyRatePerYear(
381:         uint256 cash,
382:         uint256 borrows,
383:         uint256 reserves,
384:         uint256 interestFee
385:     ) external view returns (uint256) 

['393']

393:     function compoundRate() external pure returns (uint256) 

['401']

401:     function currentRatesData() external view returns (uint256, uint256) 

['181']

181:     function multiSwap(
182:         bytes calldata data,
183:         address[] calldata tokens
184:     ) external nonReentrant 

['260']

260:     function executeOTC(
261:         address tokenToOTC,
262:         uint256 amountToOTC
263:     ) external nonReentrant 

['317']

317:     function sendWormholeMessages(
318:         uint256 dstChainId,
319:         address toAddress
320:     ) external 

['371']

371:     function receiveExecutableLockData(uint256 amount) external 

['392']

392:     function receiveCrossChainLockData(
393:         EpochRolloverData memory data
394:     ) external 

['420']

420:     function executeEpochFeeRouter(uint256 chainId) external 

['491']

491:     function migrateFeeAccumulator() external 

['534']

534:     function setEarmarked(address token, bool state) external 

['542']

542:     function notifyUpdatedMessagingHub() external 

['568']

568:     function addRewardTokens(address[] calldata newTokens) external 

['592']

592:     function removeRewardToken(address rewardTokenToRemove) external 

['639']

639:     function getRewardTokenBalances()
640:         external
641:         view
642:         returns (uint256[] memory)
643:     

['62']

62:     function quoteWormholeFee(
63:         uint256 dstChainId,
64:         bool transferToken
65:     ) external view returns (uint256) 

['169']

169:     function addAsset(address asset, address alteredToken) external 

['267']

267:     function setGMXReader(address newReader) external 

['275']

275:     function setGMXDataStore(address newDataStore) external 

['206']

206:     function afterDepositExecution(
207:         bytes32 key,
208:         IGMXDeposit.Props memory,
209:         IGMXEventUtils.EventLogData memory eventData
210:     ) external 

['230']

230:     function setGMXDepositVault(address newDepositVault) external 

['238']

238:     function setGMXExchangeRouter(address newExchangeRouter) external 

['246']

246:     function setGMXRouter(address newRouter) external 

['275']

275:     function setGMXDataStore(address newDataStore) external 

['262']

262:     function setGMXDepositHandler(address newDepositHandler) external 

['62']

62:     function gaugeWeight(
63:         uint256 epoch,
64:         address token
65:     ) external view returns (uint256, uint256) 

['126']

126:     function massUpdatePools(address[] calldata tokens) external 

['126']

126:     function start(address marketManager_) external 

['145']

145:     function addExtraReward(address newReward) external 

['168']

168:     function removeExtraReward(uint256 index, address newReward) external 

['193']

193:     function getRewardTokensLength() external view returns (uint256) 

['204']

204:     function setRewardPerSec(
205:         address token,
206:         uint256 epoch,
207:         address rewardToken,
208:         uint256 newRewardPerSec
209:     ) external 

['318']

318:     function pendingRewards(
319:         address token,
320:         address user
321:     ) external view returns (uint256[] memory results) 

['334']

334:     function deposit(
335:         address token,
336:         address user,
337:         uint256 amount
338:     ) external nonReentrant 

['401']

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

['436']

436:     function claim(address token) external nonReentrant 

['482']

482:     function claimAndExtendLock(
483:         address token,
484:         uint256 lockIndex,
485:         bool continuousLock,
486:         RewardsData memory rewardsData,
487:         bytes memory params,
488:         uint256 aux
489:     ) external nonReentrant 

['547']

547:     function claimAndLock(
548:         address token,
549:         bool continuousLock,
550:         RewardsData memory rewardsData,
551:         bytes memory params,
552:         uint256 aux
553:     ) external nonReentrant 

['203']

203:     function isListed(address mToken) external view returns (bool) 

['212']

212:     function assetsOf(
213:         address account
214:     ) external view returns (IMToken[] memory) 

['221']

221:     function tokenDataOf(
222:         address account,
223:         address mToken
224:     ) external view returns (
225:         bool hasPosition, 
226:         uint256 balanceOf, 
227:         uint256 collateralPostedOf
228:     ) 

['241']

241:     function statusOf(
242:         address account
243:     ) external view returns (uint256, uint256, uint256) 

['252']

252:     function solvencyOf(
253:         address account
254:     ) external view returns (uint256, uint256) 

['264']

264:     function flaggedForLiquidation(
265:         address account
266:     ) external view returns (bool) 

['285']

285:     function hypotheticalLiquidityOf(
286:         address account,
287:         address mTokenModified,
288:         uint256 redeemTokens, 
289:         uint256 borrowAmount 
290:     ) external view returns (uint256, uint256) 

['315']

315:     function postCollateral(
316:         address account,
317:         address cToken,
318:         uint256 tokens
319:     ) external 

['357']

357:     function removeCollateral(
358:         address cToken,
359:         uint256 tokens
360:     ) external 

['402']

402:     function reduceCollateralIfNecessary(
403:         address account,
404:         address cToken,
405:         uint256 balance,
406:         uint256 tokens
407:     ) external 

['415']

415:     function canMint(address mToken) external view 

['431']

431:     function canRedeem(
432:         address mToken,
433:         address account,
434:         uint256 amount
435:     ) external view 

['445']

445:     function canRedeemWithPrune(
446:         address mToken,
447:         address account,
448:         uint256 amount
449:     ) external 

['473']

473:     function canRedeemWithCollateralRemoval(
474:         address mToken,
475:         address account,
476:         uint256 balance,
477:         uint256 amount,
478:         bool forceRedeemCollateral
479:     ) external 

['506']

506:     function canBorrow(
507:         address dToken,
508:         address account,
509:         uint256 amount
510:     ) external 

['520']

520:     function canBorrowWithPrune(
521:         address dToken,
522:         address account,
523:         uint256 amount
524:     ) external 

['543']

543:     function canBorrowWithNotify(
544:         address dToken,
545:         address account,
546:         uint256 amount
547:     ) external 

['565']

565:     function notifyBorrow(address mToken, address account) external 

['577']

577:     function canRepay(address mToken, address account) external view 

['609']

609:     function canLiquidate(
610:         address dToken,
611:         address cToken,
612:         address account,
613:         uint256 amount,
614:         bool liquidateExact
615:     ) external view returns (uint256, uint256, uint256) 

['635']

635:     function canLiquidateWithExecution(
636:         address dToken,
637:         address cToken,
638:         address account,
639:         uint256 amount,
640:         bool liquidateExact
641:     ) external returns (uint256, uint256, uint256) 

['668']

668:     function canSeize(
669:         address collateralToken,
670:         address debtToken
671:     ) external view 

['697']

697:     function canTransfer(
698:         address mToken,
699:         address from,
700:         uint256 amount
701:     ) external view 

['714']

714:     function canTransferWithPrune(
715:         address mToken,
716:         address from,
717:         uint256 amount
718:     ) external 

['743']

743:     function liquidateAccount(address account) external 

['878']

878:     function listToken(address mToken) external 

['927']

927:     function updateCollateralToken(
928:         IMToken mToken,
929:         uint256 collRatio,
930:         uint256 collReqSoft,
931:         uint256 collReqHard,
932:         uint256 liqIncSoft,
933:         uint256 liqIncHard,
934:         uint256 liqFee,
935:         uint256 baseCFactor
936:     ) external 

['1086']

1086:     function setCTokenCollateralCaps(
1087:         address[] calldata mTokens,
1088:         uint256[] calldata newCollateralCaps
1089:     ) external 

['1131']

1131:     function setMintPaused(address mToken, bool state) external 

['1147']

1147:     function setBorrowPaused(address mToken, bool state) external 

['1162']

1162:     function setRedeemPaused(bool state) external 

['1173']

1173:     function setTransferPaused(bool state) external 

['1184']

1184:     function setSeizePaused(bool state) external 

['1196']

1196:     function setPositionFolding(address newPositionFolding) external 

['711']

711:     function rescueToken(address token, uint256 amount) external 

['116']

116:     function withdrawRemainingAirdropTokens() external 

['136']

136:     function setOptionsTerms(
137:         uint256 timestampStart,
138:         uint256 strikePrice
139:     ) external 

['70']

70:     function depositOneBalanceFee() external nonReentrant 

['94']

94:     function receiveWormholeMessages(
95:         bytes memory payload,
96:         bytes[] memory ,
97:         bytes32 ,
98:         uint16 ,
99:         bytes32 deliveryHash
100:     ) external payable 

['133']

133:     function setOneBalanceAddress(address newGelatoOneBalance) external 

['152']

152:     function addAssetPriceFeed(address asset, address feed) external 

['164']

164:     function replaceAssetPriceFeed(
165:         address asset,
166:         address feedToRemove,
167:         address feedToAdd
168:     ) external 

['185']

185:     function removeAssetPriceFeed(address asset, address feed) external 

['194']

194:     function notifyFeedRemoval(address asset) external 

['205']

205:     function addMTokenSupport(address newMToken) external 

['222']

222:     function removeMTokenSupport(address mTokenToRemove) external 

['235']

235:     function addApprovedAdaptor(address adaptorToAdd) external 

['251']

251:     function replaceApprovedAdaptor(
252:         address adaptorToRemove,
253:         address adaptorToAdd
254:     ) external 

['280']

280:     function removeApprovedAdaptor(address adaptorToRemove) external 

['299']

299:     function setDivergenceFlags(
300:         uint256 maxCautionDivergence,
301:         uint256 maxBadSourceDivergence
302:     ) external 

['328']

328:     function setChainlinkDelay(uint256 delay) external 

['350']

350:     function getPricesForAsset(
351:         address asset,
352:         bool inUSD
353:     ) external view returns (FeedData[] memory) 

['403']

403:     function isSupportedAsset(address asset) external view returns (bool) 

['413']

413:     function isSequencerValid() external view returns (bool) 

['483']

483:     function getPrices(
484:         address[] calldata assets,
485:         bool[] calldata inUSD,
486:         bool[] calldata getLower
487:     ) external view returns (uint256[] memory, uint256[] memory) 

['52']

52:     function getPricesForMarket(
53:         address account,
54:         IMToken[] calldata assets,
55:         uint256 errorCodeBreakpoint
56:     )
57:         external
58:         view
59:         returns (AccountSnapshot[] memory, uint256[] memory, uint256)
60:     

['134']

134:     function reQueryRewardTokens() external 

['163']

163:     function reQueryUnderlyingTokens() external 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['174']

174:     function leverage(
175:         LeverageStruct calldata leverageData,
176:         uint256 slippage
177:     ) external checkSlippage(msg.sender, slippage) nonReentrant 

['194']

194:     function leverageFor(
195:         LeverageStruct calldata leverageData,
196:         address account,
197:         uint256 slippage
198:     ) external checkSlippage(account, slippage) nonReentrant 

['217']

217:     function deleverage(
218:         DeleverageStruct calldata deleverageData,
219:         uint256 slippage
220:     ) external checkSlippage(msg.sender, slippage) nonReentrant 

['234']

234:     function deleverageFor(
235:         DeleverageStruct calldata deleverageData,
236:         address account,
237:         uint256 slippage
238:     ) external checkSlippage(account, slippage) nonReentrant 

['97']

97:     function receiveWormholeMessages(
98:         bytes memory payload,
99:         bytes[] memory ,
100:         bytes32 srcAddress,
101:         uint16 srcChainId,
102:         bytes32 deliveryHash
103:     ) external payable 

['289']

289:     function sendFees(
290:         uint256 dstChainId,
291:         address to,
292:         uint256 amount
293:     ) external 

['349']

349:     function bridgeCVE(
350:         uint256 dstChainId,
351:         address recipient,
352:         uint256 amount
353:     ) external payable returns (uint64) 

['375']

375:     function bridgeVeCVELock(
376:         uint256 dstChainId,
377:         address recipient,
378:         uint256 amount,
379:         bool continuousLock
380:     ) external payable returns (uint64) 

['408']

408:     function sendWormholeMessages(
409:         uint256 dstChainId,
410:         address toAddress,
411:         bytes calldata payload
412:     ) external payable returns (uint64) 

['431']

431:     function cveBridgeFee(
432:         uint256 dstChainId
433:     ) external view returns (uint256) 

['441']

441:     function flipMessagingHubStatus() external 

['466']

466:     function returnReimbursedFees() external 

['481']

481:     function withdrawNative(uint256 amount) external 

['93']

93:     function claimAndSwap(
94:         SwapperLib.Swap memory swapperData,
95:         address recipient
96:     ) external nonReentrant returns (uint256 outAmount) 

['150']

150:     function claimZapAndDeposit(
151:         SwapperLib.ZapperCall memory zapperCall,
152:         address marketManager,
153:         address cToken,
154:         address recipient
155:     ) external nonReentrant returns (uint256) 

['214']

214:     function claimSwapAndRepay(
215:         SwapperLib.Swap memory swapperData,
216:         address marketManager,
217:         address dToken,
218:         uint256 repayAmount,
219:         address recipient
220:     ) external nonReentrant returns (uint256) 

['281']

281:     function addAuthorizedMarketManager(address newMarketManager) external 

['302']

302:     function removeAuthorizedMarketManager(
303:         address currentMarketManager
304:     ) external 

['319']

319:     function addAuthorizedOutputToken(address outputToken) external 

['336']

336:     function removeAuthorizedOutputToken(address outputToken) external 

['88']

88:     function zapAndDeposit(
89:         SwapperLib.ZapperCall memory zapperCall,
90:         address cToken,
91:         address recipient
92:     ) external payable nonReentrant returns (uint256) 

['133']

133:     function swapAndRepay(
134:         SwapperLib.Swap memory swapperData,
135:         address dToken,
136:         uint256 repayAmount,
137:         address recipient
138:     ) external payable nonReentrant returns (uint256) 

['183']

183:     function redeemAndSwap(
184:         RedemptionData calldata redemptionData,
185:         SwapperLib.Swap memory swapperData,
186:         address recipient
187:     ) external nonReentrant returns (uint256) 

['140']

140:     function setRewardRouter(address newRouter) external 

['214']

214:     function addAsset(address asset, AdaptorData memory data) external 

['220']

220:     function queryUserLocks(
221:         address user
222:     ) external view returns (uint256[] memory, uint256[] memory) 

['711']

711:     function rescueToken(address token, uint256 amount) external 

['269']

269:     function shutdown() external 

['285']

285:     function createLock(
286:         uint256 amount,
287:         bool continuousLock,
288:         RewardsData calldata rewardsData,
289:         bytes calldata params,
290:         uint256 aux
291:     ) external nonReentrant 

['317']

317:     function createLockFor(
318:         address recipient,
319:         uint256 amount,
320:         bool continuousLock,
321:         RewardsData calldata rewardsData,
322:         bytes calldata params,
323:         uint256 aux
324:     ) external nonReentrant 

['356']

356:     function extendLock(
357:         uint256 lockIndex,
358:         bool continuousLock,
359:         RewardsData calldata rewardsData,
360:         bytes calldata params,
361:         uint256 aux
362:     ) external nonReentrant 

['422']

422:     function increaseAmountAndExtendLock(
423:         uint256 amount,
424:         uint256 lockIndex,
425:         bool continuousLock,
426:         RewardsData calldata rewardsData,
427:         bytes calldata params,
428:         uint256 aux
429:     ) external nonReentrant 

['460']

460:     function increaseAmountAndExtendLockFor(
461:         address recipient,
462:         uint256 amount,
463:         uint256 lockIndex,
464:         bool continuousLock,
465:         RewardsData calldata rewardsData,
466:         bytes calldata params,
467:         uint256 aux
468:     ) external nonReentrant 

['502']

502:     function disableContinuousLock(
503:         uint256 lockIndex,
504:         RewardsData calldata rewardsData,
505:         bytes calldata params,
506:         uint256 aux
507:     ) external nonReentrant 

['540']

540:     function combineAllLocks(
541:         bool continuousLock,
542:         RewardsData calldata rewardsData,
543:         bytes calldata params,
544:         uint256 aux
545:     ) external nonReentrant 

['666']

666:     function processExpiredLock(
667:         uint256 lockIndex,
668:         bool relock,
669:         bool continuousLock,
670:         RewardsData calldata rewardsData,
671:         bytes calldata params,
672:         uint256 aux
673:     ) external nonReentrant 

['759']

759:     function bridgeVeCVELock(
760:         uint256 lockIndex,
761:         uint256 dstChainId,
762:         bool continuousLock,
763:         RewardsData calldata rewardsData,
764:         bytes calldata params,
765:         uint256 aux
766:     ) external payable nonReentrant returns (uint64 sequence) 

['821']

821:     function earlyExpireLock(
822:         uint256 lockIndex,
823:         RewardsData calldata rewardsData,
824:         bytes calldata params,
825:         uint256 aux
826:     ) external nonReentrant 

['901']

901:     function updateUserPoints(address user, uint256 epoch) external 

['920']

920:     function getVotes(address user) external view returns (uint256) 

['950']

950:     function getVotesForEpoch(
951:         address user,
952:         uint256 epoch
953:     ) external view returns (uint256) 

['105']

105:     function underlyingAssetAggregator()
106:         public
107:         view
108:         virtual
109:         returns (address)
110:     

['115']

115:     function getWrappedAssetWeight() public view virtual returns (uint256) 

['18']

18:     function stringToBytes32(
19:         string memory stringData
20:     ) public pure returns (bytes32 result) 

['417']

417:     function redeemFor(
418:         uint256 shares,
419:         address receiver,
420:         address owner
421:     ) public nonReentrant returns (uint256 assets) 

['932']

932:     function isCToken() public pure returns (bool) 

['563']

563:     function supportsInterface(
564:         bytes4 interfaceId
565:     ) public pure virtual returns (bool) 

['574']

574:     function totalAssetsSafe()
575:         public
576:         view
577:         virtual
578:         nonReadReentrant
579:         returns (uint256)
580:     

['597']

597:     function convertToSharesSafe(
598:         uint256 assets
599:     ) public view nonReadReentrant returns (uint256) 

['621']

621:     function convertToAssetsSafe(
622:         uint256 shares
623:     ) public view nonReadReentrant returns (uint256) 

['248']

248:     function rewardRate() public view returns (uint256) 

['255']

255:     function vestingPeriodEnd() public view returns (uint256) 

['263']

263:     function lastVestClaim() public view returns (uint256) 

['376']

376:     function epochsToClaim(address user) public view returns (uint256) 

['1141']

1141:     function addMarketManager(
1142:         address newMarketManager,
1143:         uint256 marketInterestFactor
1144:     ) public virtual 

['1186']

1186:     function removeMarketManager(
1187:         address currentMarketManager
1188:     ) public virtual 

['140']

140:     function reQueryRewardTokens() public 

['140']

140:     function reQueryRewardTokens() public 

['232']

232:     function softCap() public view returns (uint256) 

['237']

237:     function priceAt(
238:         uint256 amount
239:     ) public view returns (uint256 price) 

['254']

254:     function currentPrice() public view returns (uint256) 

['259']

259:     function currentStatus() public view returns (SaleStatus) 

['52']

52:     function isLocked(
53:         address curvePool,
54:         uint256 coinsLength
55:     ) public view returns (bool) 

['894']

894:     function debtBalanceCached(address account) public view returns (uint256) 

['917']

917:     function decimals() public view returns (uint8) 

['925']

925:     function marketUnderlyingHeld() public view returns (uint256) 

['932']

932:     function isCToken() public pure returns (bool) 

['939']

939:     function exchangeRateWithUpdateSafe()
940:         public
941:         nonReentrant
942:         returns (uint256)
943:     

['952']

952:     function exchangeRateCached() public view returns (uint256) 

['977']

977:     function accrueInterest() public 

['67']

67:     function isDelegate(
68:         address user,
69:         address delegate
70:     ) public view returns (bool) 

['82']

82:     function getUserApprovalIndex(
83:         address user
84:     ) public view returns (uint256) 

['93']

93:     function hasDelegatingDisabled(
94:         address user
95:     ) public view returns (bool) 

['137']

137:     function _checkIsDelegate(
138:         address user,
139:         address delegate
140:     ) public view returns (bool) 

['417']

417:     function utilizationRate(
418:         uint256 cash,
419:         uint256 borrows,
420:         uint256 reserves
421:     ) public pure returns (uint256) 

['436']

436:     function getPredictedBorrowRate(
437:         uint256 cash,
438:         uint256 borrows,
439:         uint256 reserves
440:     ) public view returns (uint256) 

['467']

467:     function getBorrowRate(
468:         uint256 cash,
469:         uint256 borrows,
470:         uint256 reserves
471:     ) public view returns (uint256) 

['496']

496:     function getSupplyRate(
497:         uint256 cash,
498:         uint256 borrows,
499:         uint256 reserves,
500:         uint256 interestFee
501:     ) public view returns (uint256) 

['512']

512:     function vertexMultiplier() public view returns (uint256) 

['518']

518:     function updateTimestamp() public view returns (uint256) 

['152']

152:     function totalAssets() public view virtual returns (uint256 assets) 

['167']

167:     function convertToShares(uint256 assets) public view virtual returns (uint256 shares) 

['192']

192:     function convertToAssets(uint256 shares) public view virtual returns (uint256 assets) 

['220']

220:     function previewDeposit(uint256 assets) public view virtual returns (uint256 shares) 

['238']

238:     function previewMint(uint256 shares) public view virtual returns (uint256 assets) 

['266']

266:     function previewWithdraw(uint256 assets) public view virtual returns (uint256 shares) 

['294']

294:     function previewRedeem(uint256 shares) public view virtual returns (uint256 assets) 

['326']

326:     function maxDeposit(address to) public view virtual returns (uint256 maxAssets) 

['337']

337:     function maxMint(address to) public view virtual returns (uint256 maxShares) 

['347']

347:     function maxWithdraw(address owner) public view virtual returns (uint256 maxAssets) 

['357']

357:     function maxRedeem(address owner) public view virtual returns (uint256 maxShares) 

['376']

376:     function deposit(uint256 assets, address to) public virtual returns (uint256 shares) 

['393']

393:     function mint(uint256 shares, address to) public virtual returns (uint256 assets) 

['409']

409:     function withdraw(uint256 assets, address to, address owner)
410:         public
411:         virtual
412:         returns (uint256 shares)
413:     

['429']

429:     function redeem(uint256 shares, address to, address owner)
430:         public
431:         virtual
432:         returns (uint256 assets)
433:     

['665']

665:     function getOracleRouter() public view returns (IOracleRouter) 

['672']

672:     function vaultCompoundFee() public view returns (uint256) 

['678']

678:     function vaultYieldFee() public view returns (uint256) 

['139']

139:     function currentEpoch() public view returns (uint256) 

['145']

145:     function epochOfTimestamp(
146:         uint256 timestamp
147:     ) public view returns (uint256) 

['154']

154:     function epochStartTime(uint256 epoch) public view returns (uint256) 

['161']

161:     function epochEndTime(uint256 epoch) public view returns (uint256) 

['169']

169:     function isGaugeEnabled(
170:         uint256 epoch,
171:         address token
172:     ) public view returns (bool) 

['196']

196:     function updatePool(address token) public virtual 

['247']

247:     function rewardAllocation(
248:         address token,
249:         uint256 epoch,
250:         address rewardToken
251:     ) public view returns (uint256) 

['263']

263:     function pendingRewards(
264:         address token,
265:         address user,
266:         address rewardToken
267:     ) public view returns (uint256) 

['453']

453:     function LiquidationStatusOf(
454:         address account,
455:         address debtToken,
456:         address collateralToken
457:     )
458:         public view
459:         returns (
460:             uint256 lfactor,
461:             uint256 debtTokenPrice,
462:             uint256 collateralTokenPrice
463:         )
464:     

['202']

202:     function optionsExercisable() public view returns (bool) 

['210']

210:     function exerciseOption(uint256 amount) public payable 

['435']

435:     function getPrice(
436:         address asset,
437:         bool inUSD,
438:         bool getLower
439:     ) public view returns (uint256 price, uint256 errorCode) 

['160']

160:     function getProtocolLeverageFee() public view returns (uint256) 

['539']

539:     function queryAmountToBorrowForLeverageMax(
540:         address account,
541:         address borrowToken
542:     ) public view returns (uint256) 

['994']

994:     function currentEpoch(uint256 time) public view returns (uint256) 

['1005']

1005:     function nextEpochStartTime() public view returns (uint256) 

['1014']

1014:     function freshLockEpoch() public view returns (uint256) 

['1021']

1021:     function freshLockTimestamp() public view returns (uint40) 

['1040']

1040:     function getVotesForSingleLockForTime(
1041:         address user,
1042:         uint256 lockIndex,
1043:         uint256 time,
1044:         uint256 currentLockBoost
1045:     ) public view returns (uint256) 

['1072']

1072:     function getUnlockPenalty(
1073:         address user,
1074:         uint256 lockIndex
1075:     ) public view returns (uint256) 

[NonCritical-6] Using abi.encodePacked can result in hash collision when used in hashing functions

Resolution

Consider using abi.encode as this pads data to 32 byte segments

Num of instances: 1

Findings

Click to show findings

['179']

174:                 
175:                 return
176:                     verify(
177:                         proof,
178:                         merkleRoot,
179:                         keccak256(abi.encodePacked(user, amount)) // <= FOUND
180:                     );

[NonCritical-7] Overly complicated arithmetic

Resolution

To maintain readability in code, particularly in Solidity which can involve complex mathematical operations, it is often recommended to limit the number of arithmetic operations to a maximum of 2-3 per line. Too many operations in a single line can make the code difficult to read and understand, increase the likelihood of mistakes, and complicate the process of debugging and reviewing the code. Consider splitting such operations over more than one line, take special care when dealing with division however. Try to limit the number of arithmetic operations to a maximum of 3 per line.

Num of instances: 14

Findings

Click to show findings

['237']

236:         return
237:             ((((1e18) * sqrtReserve) / sqrtPrice) * price0 * 2) / totalSupply; // <= FOUND

['472']

472:                 rewards += _calculateRewardsForEpoch(user, startEpoch + i++); // <= FOUND

['157']

156: 
157:         uint256 price = (out * WAD * (10 ** data.quoteTokenDecimals)) / // <= FOUND
158:             sample /
159:             (10 ** data.baseTokenDecimals); 

['962']

957:         
958:         
959:         
960:         
961:         return
962:             ((marketUnderlyingHeld() + totalBorrows - totalReserves) * WAD) / // <= FOUND
963:             totalSupply;

['428']

427: 
428:         return (borrows * WAD) / (cash + borrows - reserves); // <= FOUND

['837']

833:         
834:         
835:         
836:         
837:         uint256 cFactor = ((current - start) * WAD) / (end - start); // <= FOUND

['881']

878:         
879:         
880:         
881:         uint256 cFactor = ((start - current) * WAD) / (start - end); // <= FOUND

['833']

824: 
825:         
826:         
827:         
828:         
829:         
830:         
831:         
832:         feeTokenBalanceForChain =
833:         (((feeTokenBalance * WAD) / totalLockedTokens) * lockedTokens) / // <= FOUND
834:             WAD;

['1064']

1060: 
1061:         
1062:         
1063:         return
1064:             (lock.amount * ((lock.unlockTime - time) / EPOCH_DURATION)) / // <= FOUND
1065:             LOCK_DURATION_EPOCHS;

['1421']

1416:         
1417:         
1418:         
1419:         return
1420:             (amount *
1421:                 ((penalty * (LOCK_DURATION - (unlockTime - block.timestamp))) / // <= FOUND
1422:                     LOCK_DURATION)) / WAD;

['240']

240:             a = (((amount0 * 10000) / (10000 - swapFee)) * 1e18) / decimals0; // <= FOUND

['246']

246:             uint256 p = (y * (((x2 * 3 + y2) * 1e18) / (y2 * 3 + x2))) / x; // <= FOUND

['249']

249:             uint256 den = ((a + x) * p) / 1e18 + y; // <= FOUND

['258']

258:         uint256 b = amount0 * 10000 * reserve0 * 4 * swapFeeFactor; // <= FOUND

[NonCritical-8] It's not standard to end and begin a code object on the same line

Resolution

Placing the closing and opening brackets of distinct code blocks on the same line can impair code readability and clarity, leading to potential misinterpretations. To improve maintainability and reduce the chances of introducing errors, it is advisable to start each code block on a new line, making the boundaries between different code sections clearer and easier to discern unless it is an else statement.

Num of instances: 2

Findings

Click to show findings

['285']

285: 
286:                 } 1 { // <= FOUND

['287']

287: 
288:                 } { // <= FOUND

[NonCritical-9] Using constants directly, rather than caching the value, saves gas

Resolution

In smart contract development, especially within Ethereum, gas optimization is crucial. Utilizing constants directly, instead of assigning them to a variable (caching) before use, can save gas as reading from a constant is cheaper than reading from storage or a variable. Constants in Solidity are replaced by their actual value in the EVM bytecode, eliminating the need for a SLOAD operation, which costs more gas. Hence, if a value will not change throughout the contract's life, defining it as a constant and using it directly in expressions/functions can be a gas-efficient practice.

Num of instances: 1

Findings

Click to show findings

['227']

227: 
228:         uint256 amount = _BASE_UNDERLYING_RESERVE; // <= FOUND

[NonCritical-10] State variables which are not modified within functions should be set as constants or immutable for values set at deployment

Resolution

Set state variables listed below as constant or immutable for values set at deployment. Ensure it is safe to do so

Num of instances: 18

Findings

Click to show findings

['47']

47: uint256 public paymentTokenPrice; // <= FOUND

['49']

49: uint256 public saleDecimalAdjustment;  // <= FOUND

['79']

79: string public name; // <= FOUND

['81']

81: string public symbol; // <= FOUND

['104']

104: internal _epochRewardPerSec; // <= FOUND

['24']

24: address public polygonOneBalanceFeeManager; // <= FOUND

['11']

11: address public sDai; // <= FOUND

['12']

12: address public dai; // <= FOUND

['13']

13: address public daiAggregator; // <= FOUND

['11']

11: address public sFrax; // <= FOUND

['12']

12: address public frax; // <= FOUND

['13']

13: address public fraxAggregator; // <= FOUND

['11']

11: address public wstETH; // <= FOUND

['12']

12: address public stETH; // <= FOUND

['13']

13: address public stETHAggregator; // <= FOUND

['84']

84: string internal _name; // <= FOUND

['86']

86: string internal _symbol; // <= FOUND

['26']

26: internal _isDelegate; // <= FOUND

[NonCritical-11] External call recipient may consume all transaction gas

Resolution

When making external calls, the called contract can intentionally or unintentionally consume all provided gas, leading to unintended transaction reversion. To mitigate this risk, it's crucial to specify a gas limit when making the call. By using addr.call{gas: <amount>}(""), you allocate a specific amount of gas to the external call, ensuring the parent transaction has gas left for post-call operations. This approach safeguards against malevolent contracts aiming to exhaust gas and provides greater control over transaction execution.

Num of instances: 1

Findings

Click to show findings

['86']

86:             value: value // <= FOUND
87:         }(zapperCall.call); // <= FOUND

[NonCritical-12] ERC4626 maxWithdraw/maxRedeem function not to spec

Resolution

In instances where withdrawals/redeems are paused, the maxWithdraw/maxRedeem functions should return 0 as stated in the spec.

Num of instances: 2

Findings

Click to show findings

['347']

347:     function maxWithdraw(address owner) public view virtual returns (uint256 maxAssets) { // <= FOUND
348:         maxAssets = convertToAssets(balanceOf(owner));
349:     }

['357']

357:     function maxRedeem(address owner) public view virtual returns (uint256 maxShares) { // <= FOUND
358:         maxShares = balanceOf(owner);
359:     }

[NonCritical-13] Function call in event emit

Resolution

Emits are designed to make users aware of state variable changes. As such the event declaration should be clear on what it will output, by passing in function calls this can affect the readability of a emit declaration. As such it is advisable to make function calls outside of the event emit and pass the return value into the emit instead.

Num of instances: 133

Findings

Click to show findings

['129']

129:     function harvest(
130:         bytes calldata data
131:     ) external override returns (uint256 yield) {
132:         
133:         _canCompound();
134: 
135:         
136:         _vestIfNeeded();
137: 
138:         
139:         if (_checkVestStatus(_vaultData)) {
140:             _updateVestingPeriodIfNeeded();
141: 
142:             
143:             StrategyData memory sd = strategyData;
144: 
145:             
146:             sd.gauge.getReward(address(this));
147: 
148:             {
149:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
150: 
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount,
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert AerodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert AerodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['124']

124:     function harvest(
125:         bytes calldata data
126:     ) external override returns (uint256 yield) {
127:         
128:         _canCompound();
129: 
130:         
131:         _vestIfNeeded();
132: 
133:         
134:         if (_checkVestStatus(_vaultData)) {
135:             _updateVestingPeriodIfNeeded();
136: 
137:             
138:             StrategyData memory sd = strategyData;
139: 
140:             
141:             sd.gauge.getReward(address(this));
142: 
143:             {
144:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
145:                 
146:                 if (rewardAmount > 0) {
147:                     
148:                     
149:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
150:                         rewardAmount,
151:                         centralRegistry.protocolHarvestFee(),
152:                         1e18
153:                     );
154:                     rewardAmount -= protocolFee;
155:                     SafeTransferLib.safeTransfer(
156:                         address(rewardToken),
157:                         centralRegistry.feeAccumulator(),
158:                         protocolFee
159:                     );
160: 
161:                     
162:                     if (!rewardTokenIsUnderlying) {
163:                         SwapperLib.Swap memory swapData = abi.decode(
164:                             data,
165:                             (SwapperLib.Swap)
166:                         );
167: 
168:                         if (!centralRegistry.isSwapper(swapData.target)) {
169:                             revert AerodromeVolatileCToken__InvalidSwapper(
170:                                 swapData.target
171:                             );
172:                         }
173: 
174:                         SwapperLib.swap(centralRegistry, swapData);
175:                     }
176:                 }
177:             }
178: 
179:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
180:             
181:             if (totalAmountA == 0) {
182:                 revert AerodromeVolatileCToken__SlippageError();
183:             }
184: 
185:             
186:             address _asset = asset();
187:             
188:             
189:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
190:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0()
191:                 ? r0
192:                 : r1;
193: 
194:             
195:             
196:             
197:             uint256 swapAmount = VelodromeLib._optimalDeposit(
198:                 address(sd.pairFactory),
199:                 _asset,
200:                 totalAmountA,
201:                 reserveA,
202:                 0,
203:                 0,
204:                 0,
205:                 false
206:             );
207:             
208:             VelodromeLib._swapExactTokensForTokens(
209:                 address(sd.router),
210:                 _asset,
211:                 sd.token0,
212:                 sd.token1,
213:                 swapAmount,
214:                 false
215:             );
216:             totalAmountA -= swapAmount;
217: 
218:             
219:             yield = VelodromeLib._addLiquidity(
220:                 address(sd.router),
221:                 sd.token0,
222:                 sd.token1,
223:                 false,
224:                 totalAmountA,
225:                 IERC20(sd.token1).balanceOf(address(this)), 
226:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
227:             );
228: 
229:             
230:             
231:             _afterDeposit(yield, 0);
232: 
233:             
234:             _setNewVaultData(yield, vestPeriod);
235: 
236:             emit Harvest(yield);
237:         }
238:         
239:     }

['212']

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) {
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) {
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:     }

['177']

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) {
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) {
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:     }

['177']

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) {
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) {
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:     }

['147']

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) {
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) {
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,
281:                     approx,
282:                     limit
283:                 );
284:             }
285: 
286:             
287:             _setNewVaultData(yield, vestPeriod);
288: 
289:             emit Harvest(yield);
290:         }
291:     }

['70']

70:     function harvest(
71:         bytes calldata data
72:     ) external override returns (uint256 yield) {
73:         
74:         _canCompound();
75: 
76:         
77:         _vestIfNeeded();
78: 
79:         
80:         if (_checkVestStatus(_vaultData)) {
81:             _updateVestingPeriodIfNeeded();
82: 
83:             
84:             rewardRouter.handleRewards(
85:                 true,
86:                 true,
87:                 true,
88:                 true,
89:                 true,
90:                 true,
91:                 false
92:             );
93:             uint256 rewardAmount = WETH.balanceOf(address(this));
94: 
95:             
96:             if (rewardAmount > 0) {
97:                 
98:                     
99:                 uint256 protocolFee = FixedPointMathLib.mulDiv(
100:                     rewardAmount,
101:                     centralRegistry.protocolHarvestFee(),
102:                     1e18
103:                 );
104:                 rewardAmount -= protocolFee;
105:                 SafeTransferLib.safeTransfer(
106:                     address(WETH),
107:                     centralRegistry.feeAccumulator(),
108:                     protocolFee
109:                 );
110: 
111:                 SwapperLib.Swap memory swapData = abi.decode(
112:                     data,
113:                     (SwapperLib.Swap)
114:                 );
115: 
116:                 if (!centralRegistry.isSwapper(swapData.target)) {
117:                     revert StakedGMXCToken__InvalidSwapper(swapData.target);
118:                 }
119: 
120:                 yield = SwapperLib.swap(centralRegistry, swapData);
121: 
122:                 
123:                 if (yield == 0) {
124:                     revert StakedGMXCToken__SlippageError();
125:                 }
126:             }
127: 
128:             
129:             
130:             _afterDeposit(yield, 0);
131: 
132:             
133:             _setNewVaultData(yield, vestPeriod);
134: 
135:             emit Harvest(yield);
136:         }
137:     }

['130']

130:     function harvest(
131:         bytes calldata data
132:     ) external override returns (uint256 yield) {
133:         
134:         _canCompound();
135: 
136:         
137:         _vestIfNeeded();
138: 
139:         
140:         if (_checkVestStatus(_vaultData)) {
141:             _updateVestingPeriodIfNeeded();
142: 
143:             
144:             StrategyData memory sd = strategyData;
145: 
146:             
147:             sd.gauge.getReward(address(this));
148: 
149:             {
150:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
151:                 
152:                 if (rewardAmount > 0) {
153:                     
154:                     
155:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
156:                         rewardAmount, 
157:                         centralRegistry.protocolHarvestFee(),
158:                         1e18
159:                     );
160:                     rewardAmount -= protocolFee;
161:                     SafeTransferLib.safeTransfer(
162:                         address(rewardToken),
163:                         centralRegistry.feeAccumulator(),
164:                         protocolFee
165:                     );
166: 
167:                     
168:                     if (!rewardTokenIsUnderlying) {
169:                         SwapperLib.Swap memory swapData = abi.decode(
170:                             data,
171:                             (SwapperLib.Swap)
172:                         );
173: 
174:                         if (!centralRegistry.isSwapper(swapData.target)) {
175:                             revert VelodromeStableCToken__InvalidSwapper(
176:                                 swapData.target
177:                             );
178:                         }
179: 
180:                         SwapperLib.swap(centralRegistry, swapData);
181:                     }
182:                 }
183:             }
184: 
185:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
186: 
187:             
188:             if (totalAmountA == 0) {
189:                 revert VelodromeStableCToken__SlippageError();
190:             }
191: 
192:             
193:             address _asset = asset();
194:             
195:             
196:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
197:             (uint256 reserveA, uint256 reserveB) = sd.token0 ==
198:                 IVeloPair(_asset).token0()
199:                 ? (r0, r1)
200:                 : (r1, r0);
201:             
202:             
203:             uint256 swapAmount = VelodromeLib._optimalDeposit(
204:                 address(sd.pairFactory),
205:                 _asset,
206:                 totalAmountA,
207:                 reserveA,
208:                 reserveB,
209:                 sd.decimalsA,
210:                 sd.decimalsB,
211:                 true
212:             );
213:             
214:             VelodromeLib._swapExactTokensForTokens(
215:                 address(sd.router),
216:                 _asset,
217:                 sd.token0,
218:                 sd.token1,
219:                 swapAmount,
220:                 true
221:             );
222:             totalAmountA -= swapAmount;
223: 
224:             
225:             yield = VelodromeLib._addLiquidity(
226:                 address(sd.router),
227:                 sd.token0,
228:                 sd.token1,
229:                 true,
230:                 totalAmountA,
231:                 IERC20(sd.token1).balanceOf(address(this)), 
232:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
233:             );
234: 
235:             
236:             
237:             _afterDeposit(yield, 0);
238: 
239:             
240:             _setNewVaultData(yield, vestPeriod);
241: 
242:             emit Harvest(yield);
243:         }
244:     }

['117']

117:     function harvest(
118:         bytes calldata data
119:     ) external override returns (uint256 yield) {
120:         
121:         _canCompound();
122: 
123:         
124:         _vestIfNeeded();
125: 
126:         
127:         if (_checkVestStatus(_vaultData)) {
128:             _updateVestingPeriodIfNeeded();
129: 
130:             
131:             StrategyData memory sd = strategyData;
132: 
133:             
134:             sd.gauge.getReward(address(this));
135: 
136:             {
137:                 uint256 rewardAmount = rewardToken.balanceOf(address(this));
138:                 
139:                 if (rewardAmount > 0) {
140:                     
141:                     
142:                     uint256 protocolFee = FixedPointMathLib.mulDiv(
143:                         rewardAmount, 
144:                         centralRegistry.protocolHarvestFee(),
145:                         1e18
146:                     );
147:                     rewardAmount -= protocolFee;
148:                     SafeTransferLib.safeTransfer(
149:                         address(rewardToken),
150:                         centralRegistry.feeAccumulator(),
151:                         protocolFee
152:                     );
153: 
154:                     
155:                     if (!rewardTokenIsUnderlying) {
156:                         SwapperLib.Swap memory swapData = abi.decode(
157:                             data,
158:                             (SwapperLib.Swap)
159:                         );
160: 
161:                         if (!centralRegistry.isSwapper(swapData.target)) {
162:                             revert VelodromeVolatileCToken__InvalidSwapper(
163:                                 swapData.target
164:                             );
165:                         }
166: 
167:                         SwapperLib.swap(centralRegistry, swapData);
168:                     }
169:                 }
170:             }
171: 
172:             uint256 totalAmountA = IERC20(sd.token0).balanceOf(address(this));
173: 
174:             
175:             if (totalAmountA == 0) {
176:                 revert VelodromeVolatileCToken__SlippageError();
177:             }
178: 
179:             
180:             address _asset = asset();
181:             
182:             
183:             (uint256 r0, uint256 r1, ) = IVeloPair(_asset).getReserves();
184:             uint256 reserveA = sd.token0 == IVeloPair(_asset).token0()
185:                 ? r0
186:                 : r1;
187: 
188:             
189:             
190:             
191:             uint256 swapAmount = VelodromeLib._optimalDeposit(
192:                 address(sd.pairFactory),
193:                 _asset,
194:                 totalAmountA,
195:                 reserveA,
196:                 0,
197:                 0,
198:                 0,
199:                 false
200:             );
201:             
202:             VelodromeLib._swapExactTokensForTokens(
203:                 address(sd.router),
204:                 _asset,
205:                 sd.token0,
206:                 sd.token1,
207:                 swapAmount,
208:                 false
209:             );
210:             totalAmountA -= swapAmount;
211: 
212:             
213:             yield = VelodromeLib._addLiquidity(
214:                 address(sd.router),
215:                 sd.token0,
216:                 sd.token1,
217:                 false,
218:                 totalAmountA,
219:                 IERC20(sd.token1).balanceOf(address(this)), 
220:                 VelodromeLib.VELODROME_ADD_LIQUIDITY_SLIPPAGE
221:             );
222: 
223:             
224:             
225:             _afterDeposit(yield, 0);
226: 
227:             
228:             _setNewVaultData(yield, vestPeriod);
229: 
230:             emit Harvest(yield);
231:         }
232:     }

['108']

108:     function addAsset(
109:         address asset,
110:         string memory ticker, 
111:         address proxyFeed, 
112:         uint256 heartbeat, 
113:         bool inUSD
114:     ) external {
115:         _checkElevatedPermissions();
116: 
117:         if (heartbeat != 0) {
118:             if (heartbeat > DEFAULT_HEART_BEAT) {
119:                 revert Api3Adaptor__InvalidHeartbeat();
120:             }
121:         }
122: 
123:         bytes32 dapiName = Bytes32Helper.stringToBytes32(ticker);
124:         bytes32 dapiNameHash = keccak256(abi.encodePacked(dapiName));
125: 
126:         
127:         
128:         if (dapiNameHash != IProxy(proxyFeed).dapiNameHash()) {
129:             revert Api3Adaptor__DAPINameHashError();
130:         }
131: 
132:         AdaptorData storage data;
133: 
134:         if (inUSD) {
135:             data = adaptorDataUSD[asset];
136:         } else {
137:             data = adaptorDataNonUSD[asset];
138:         }
139: 
140:         data.heartbeat = heartbeat != 0
141:             ? heartbeat
142:             : DEFAULT_HEART_BEAT;
143: 
144:         
145: 
146:         
147:         
148:         
149:         
150:         data.max = (uint256(int256(type(int224).max)) * 9) / 10;
151:         data.dapiNameHash = dapiNameHash;
152:         data.proxyFeed = IProxy(proxyFeed);
153:         data.isConfigured = true;
154: 
155:         
156:         bool isUpdate;
157:         if (isSupportedAsset[asset]) {
158:             isUpdate = true;
159:         }
160: 
161:         isSupportedAsset[asset] = true;
162:         emit Api3AssetAdded(asset, data, isUpdate); // <= FOUND
163:     }

['170']

170:     function removeAsset(address asset) external override { // <= FOUND
171:         _checkElevatedPermissions();
172: 
173:         
174:         if (!isSupportedAsset[asset]) {
175:             revert Api3Adaptor__AssetIsNotSupported();
176:         }
177: 
178:         
179:         delete isSupportedAsset[asset];
180: 
181:         
182:         delete adaptorDataUSD[asset];
183:         delete adaptorDataNonUSD[asset];
184: 
185:         
186:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
187:         
188:         emit Api3AssetRemoved(asset);
189:     }

['148']

148:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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) {
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); // <= FOUND
200:     }

['207']

207:     function removeAsset(address asset) external override { // <= FOUND
208:         _checkElevatedPermissions();
209: 
210:         
211:         if (!isSupportedAsset[asset]) {
212:             revert BalancerStablePoolAdaptor__AssetIsNotSupported();
213:         }
214: 
215:         
216:         
217:         delete isSupportedAsset[asset];
218:         delete adaptorData[asset];
219: 
220:         
221:         
222:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
223:         emit BalancerStablePoolAssetRemoved(asset); // <= FOUND
224:     }

['93']

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);
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); // <= FOUND
147:     }

['154']

154:     function removeAsset(address asset) external override { // <= FOUND
155:         _checkElevatedPermissions();
156: 
157:         
158:         if (!isSupportedAsset[asset]) {
159:             revert BaseRedstoneCoreAdaptor__AssetIsNotSupported();
160:         }
161: 
162:         
163:         
164:         delete isSupportedAsset[asset];
165:         delete adaptorDataUSD[asset];
166:         delete adaptorDataNonUSD[asset];
167: 
168:         
169:         
170:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
171:         
172:         emit RedstoneCoreAssetRemoved(asset);
173:     }

['219']

219:     function setCompoundingPaused(bool state) external { // <= FOUND
220:         
221:         
222:         if (lastVestClaim() == 0) {
223:             _revert(_UNAUTHORIZED_SELECTOR);
224:         }
225: 
226:         if (state) {
227:             _checkDaoPermissions();
228:         } else {
229:             _checkElevatedPermissions();
230:         }
231: 
232:         
233:         compoundingPaused = state ? 2 : 1;
234:         emit CompoundingPaused(state); // <= FOUND
235:     }

['103']

103:     function _setExitFee(uint256 newExitFee) internal { // <= FOUND
104:         
105:         newExitFee = _bpToWad(newExitFee);
106: 
107:         
108:         if (newExitFee > MAXIMUM_EXIT_FEE) {
109:             revert CTokenCompoundingWithExitFee__InvalidExitFee();
110:         }
111: 
112:         
113:         uint256 oldExitFee = exitFee;
114: 
115:         
116:         exitFee = newExitFee;
117:         emit ExitFeeSet(oldExitFee, newExitFee); // <= FOUND
118:     }

['89']

89:     function claim(
90:         uint256 amount,
91:         bool locked,
92:         bytes32[] calldata proof
93:     ) external nonReentrant {
94:         if (isPaused == 2) {
95:             revert CVEInitialDistribution__Paused();
96:         }
97: 
98:         
99:         if (amount > maximumClaimAmount) {
100:             revert CVEInitialDistribution__ParametersAreInvalid();
101:         }
102: 
103:         
104:         if (merkleRoot == bytes32(0)) {
105:             revert CVEInitialDistribution__Unauthorized();
106:         }
107: 
108:         
109:         if (block.timestamp >= endClaimTimestamp) {
110:             revert CVEInitialDistribution__NotEligible();
111:         }
112: 
113:         
114:         if (distributionClaimed[msg.sender]) {
115:             revert CVEInitialDistribution__NotEligible();
116:         }
117: 
118:         
119:         
120:         if (
121:             !verify(
122:                 proof,
123:                 merkleRoot,
124:                 keccak256(abi.encodePacked(msg.sender, amount))
125:             )
126:         ) {
127:             revert CVEInitialDistribution__NotEligible();
128:         }
129: 
130:         
131:         distributionClaimed[msg.sender] = true;
132: 
133:         
134:         
135:         if (locked) {
136:             RewardsData memory emptyData;
137:             uint256 boostedAmount = amount * lockedClaimMultiplier;
138:             SafeTransferLib.safeApprove(cve, address(veCVE), boostedAmount);
139: 
140:             
141:             veCVE.createLockFor(
142:                 msg.sender,
143:                 boostedAmount,
144:                 true,
145:                 emptyData,
146:                 "",
147:                 0
148:             );
149:         } else {
150:             
151:             SafeTransferLib.safeTransfer(cve, msg.sender, amount);
152:         }
153: 
154:         
155:         emit DistributionClaimed(msg.sender, amount);
156:     }

['215']

215:     function withdrawRemainingTokens() external { // <= FOUND
216:         _checkDaoPermissions();
217: 
218:         if (block.timestamp < endClaimTimestamp) {
219:             revert CVEInitialDistribution__TransferError();
220:         }
221: 
222:         uint256 amount = IERC20(cve).balanceOf(address(this));
223:         SafeTransferLib.safeTransfer(
224:             cve,
225:             centralRegistry.daoAddress(),
226:             amount
227:         );
228: 
229:         emit RemainingTokensWithdrawn(amount);
230:     }

['400']

400:     function _claimRewards(
401:         address user,
402:         address recipient,
403:         uint256 epochs,
404:         RewardsData calldata rewardsData,
405:         bytes calldata params,
406:         uint256 aux
407:     ) internal {
408:         uint256 rewards = _calculateRewards(user, epochs);
409: 
410:         
411:         
412:         uint256 rewardAmount = _processRewards(
413:             recipient,
414:             rewards,
415:             rewardsData,
416:             params,
417:             aux
418:         );
419: 
420:         
421:         
422:         if (rewardAmount > 0) {
423:             emit RewardPaid(
424:                 user,
425:                 rewardsData.asCVE ? cve : rewardToken,
426:                 rewardAmount
427:             );
428:         }
429:     }

['437']

437:     function _claimRewardsDirect(
438:         address user,
439:         uint256 epochs
440:     ) internal returns (uint256 rewards) {
441:         rewards = _calculateRewards(user, epochs);
442: 
443:         
444:         
445:         if (rewards > 0) {
446:             
447:             
448:             SafeTransferLib.safeTransfer(rewardToken, msg.sender, rewards);
449: 
450:             emit RewardPaid(user, rewardToken, rewards);
451:         }
452:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (!ICamelotPair(asset).stableSwap()) {
42:             revert CamelotStableLPAdaptor__AssetIsNotStableLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotStableLPAssetAdded(asset, data, isUpdate); // <= FOUND
53:     }

['60']

60:     function removeAsset(address asset) external override { // <= FOUND
61:         _checkElevatedPermissions();
62: 
63:         _removeAsset(asset);
64:         emit CamelotStableLPAssetRemoved(asset); // <= FOUND
65:     }

['36']

36:     function addAsset(
37:         address asset
38:     ) external override {
39:         _checkElevatedPermissions();
40: 
41:         if (ICamelotPair(asset).stableSwap()) {
42:             revert CamelotVolatileLPAdaptor__AssetIsNotVolatileLP();
43:         }
44: 
45:         
46:         bool isUpdate;
47:         if (isSupportedAsset[asset]) {
48:             isUpdate = true;
49:         }
50: 
51:         AdaptorData memory data = _addAsset(asset);
52:         emit CamelotVolatileLPAssetAdded(asset, data, isUpdate); // <= FOUND
53:     }

['60']

60:     function removeAsset(address asset) external virtual override { // <= FOUND
61:         _checkElevatedPermissions();
62: 
63:         _removeAsset(asset);
64:         emit CamelotVolatileLPAssetRemoved(asset); // <= FOUND
65:     }

['329']

329:     function setCVE(address newCVE) external { // <= FOUND
330:         _checkElevatedPermissions();
331: 
332:         cve = newCVE;
333:         emit CoreContractSet("CVE", newCVE); // <= FOUND
334:     }

['340']

340:     function setVeCVE(address newVeCVE) external { // <= FOUND
341:         _checkElevatedPermissions();
342: 
343:         veCVE = newVeCVE;
344:         emit CoreContractSet("VeCVE", newVeCVE); // <= FOUND
345:     }

['351']

351:     function setCVELocker(address newCVELocker) external { // <= FOUND
352:         _checkElevatedPermissions();
353: 
354:         cveLocker = newCVELocker;
355:         emit CoreContractSet("CVE Locker", newCVELocker); // <= FOUND
356:     }

['362']

362:     function setProtocolMessagingHub(
363:         address newProtocolMessagingHub
364:     ) external {
365:         _checkElevatedPermissions();
366: 
367:         protocolMessagingHub = newProtocolMessagingHub;
368: 
369:         
370:         
371:         if (feeAccumulator != address(0)) {
372:             IFeeAccumulator(feeAccumulator).notifyUpdatedMessagingHub();
373:         }
374: 
375:         emit CoreContractSet(
376:             "Protocol Messaging Hub",
377:             newProtocolMessagingHub
378:         );
379:     }

['385']

385:     function setOracleRouter(address newOracleRouter) external { // <= FOUND
386:         _checkElevatedPermissions();
387: 
388:         oracleRouter = newOracleRouter;
389:         emit CoreContractSet("Oracle Router", newOracleRouter); // <= FOUND
390:     }

['396']

396:     function setFeeAccumulator(address newFeeAccumulator) external { // <= FOUND
397:         _checkElevatedPermissions();
398: 
399:         feeAccumulator = newFeeAccumulator;
400:         emit CoreContractSet("Fee Accumulator", newFeeAccumulator); // <= FOUND
401:     }

['407']

407:     function setWormholeCore(address newWormholeCore) external { // <= FOUND
408:         _checkElevatedPermissions();
409: 
410:         wormholeCore = IWormhole(newWormholeCore);
411:         emit WormholeCoreSet(newWormholeCore); // <= FOUND
412:     }

['418']

418:     function setWormholeRelayer(address newWormholeRelayer) external { // <= FOUND
419:         _checkElevatedPermissions();
420: 
421:         wormholeRelayer = IWormholeRelayer(newWormholeRelayer);
422:         emit WormholeRelayerSet(newWormholeRelayer); // <= FOUND
423:     }

['429']

429:     function setCircleTokenMessenger(
430:         address newCircleTokenMessenger
431:     ) external {
432:         _checkElevatedPermissions();
433: 
434:         circleTokenMessenger = ITokenMessenger(newCircleTokenMessenger);
435:         emit CircleTokenMessengerSet(newCircleTokenMessenger); // <= FOUND
436:     }

['442']

442:     function setTokenBridge(address newTokenBridge) external { // <= FOUND
443:         _checkElevatedPermissions();
444: 
445:         tokenBridge = ITokenBridge(newTokenBridge);
446:         emit TokenBridgeSet(newTokenBridge); // <= FOUND
447:     }

['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) {
462:             wormholeChainId[chainIds[i]] = wormholeChainIds[i];
463:         }
464:         emit WormholeChainIDsSet(chainIds, wormholeChainIds); // <= FOUND
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) {
481:             cctpDomain[chainIds[i]] = cctpDomains[i];
482:         }
483:         emit CCTPDomainsSet(chainIds, cctpDomains); // <= FOUND
484:     }

['490']

490:     function setGelatoSponsor(address newGelatoSponsor) external { // <= FOUND
491:         _checkElevatedPermissions();
492: 
493:         gelatoSponsor = newGelatoSponsor;
494:         emit GelatoSponsorSet(newGelatoSponsor); // <= FOUND
495:     }

['504']

504:     function setProtocolCompoundFee(uint256 value) external { // <= FOUND
505:         _checkElevatedPermissions();
506: 
507:         
508:         if (value > 500) {
509:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
510:         }
511:         
512:         
513:         
514:         protocolCompoundFee = _bpToWad(value);
515: 
516:         
517:         protocolHarvestFee = protocolYieldFee + _bpToWad(value);
518: 
519:         emit FeeSet("Compound", value);
520:     }

['529']

529:     function setProtocolYieldFee(uint256 value) external { // <= FOUND
530:         _checkElevatedPermissions();
531: 
532:         
533:         if (value > 5000) {
534:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
535:         }
536:         
537:         
538:         
539:         protocolYieldFee = _bpToWad(value);
540: 
541:         
542:         protocolHarvestFee = _bpToWad(value) + protocolCompoundFee;
543: 
544:         emit FeeSet("Yield", value);
545:     }

['554']

554:     function setProtocolLeverageFee(uint256 value) external { // <= FOUND
555:         _checkElevatedPermissions();
556: 
557:         
558:         if (value > 200) {
559:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
560:         }
561:         
562:         
563:         
564:         protocolLeverageFee = _bpToWad(value);
565: 
566:         emit FeeSet("Leverage", value);
567:     }

['577']

577:     function setProtocolInterestRateFee(
578:         address market,
579:         uint256 value
580:     ) external {
581:         _checkElevatedPermissions();
582: 
583:         
584:         if (value > 5000) {
585:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
586:         }
587: 
588:         
589:         if (!isMarketManager[market]) {
590:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
591:         }
592: 
593:         
594:         
595:         
596:         protocolInterestFactor[market] = _bpToWad(value);
597: 
598:         emit InterestFeeSet(market, value);
599:     }

['608']

608:     function setEarlyUnlockPenaltyMultiplier(uint256 value) external { // <= FOUND
609:         _checkElevatedPermissions();
610: 
611:         
612:         if (value > 9000) {
613:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
614:         }
615: 
616:         
617:         
618:         if (value < 3000 && value != 0) {
619:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
620:         }
621: 
622:         earlyUnlockPenaltyMultiplier = value;
623: 
624:         emit MultiplierSet("Early Unlock Penalty", value);
625:     }

['634']

634:     function setVoteBoostMultiplier(uint256 value) external { // <= FOUND
635:         _checkElevatedPermissions();
636: 
637:         
638:         
639:         if (value < DENOMINATOR && value != 0) {
640:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
641:         }
642: 
643:         voteBoostMultiplier = value;
644: 
645:         emit MultiplierSet("Vote Boost", value);
646:     }

['656']

656:     function setLockBoostMultiplier(uint256 value) external { // <= FOUND
657:         _checkElevatedPermissions();
658: 
659:         
660:         if (value < DENOMINATOR && value != 0) {
661:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
662:         }
663: 
664:         lockBoostMultiplier = value;
665: 
666:         emit MultiplierSet("Lock Boost", value);
667:     }

['676']

676:     function incrementApprovalIndex() external { // <= FOUND
677:         uint256 newIndex = userApprovalIndex[msg.sender] + 1;
678:         userApprovalIndex[msg.sender] = newIndex;
679: 
680:         emit ApprovalIndexIncremented(msg.sender, newIndex);
681:     }

['687']

687:     function disableDelegable(bool delegable) external { // <= FOUND
688:         delegatingDisabled[msg.sender] = delegable;
689: 
690:         emit DelegableStatusSet(msg.sender, delegable);
691:     }

['699']

699:     function transferDaoOwnership(address newDaoAddress) external { // <= FOUND
700:         _checkElevatedPermissions();
701: 
702:         
703:         address previousDaoAddress = daoAddress;
704:         daoAddress = newDaoAddress;
705: 
706:         
707:         delete hasDaoPermissions[previousDaoAddress];
708:         
709:         hasDaoPermissions[newDaoAddress] = true;
710:         emit OwnershipTransferred(previousDaoAddress, newDaoAddress); // <= FOUND
711: 
712:         
713:         if (timelock != address(0)) {
714:             if (
715:                 ERC165Checker.supportsInterface(
716:                     timelock,
717:                     type(ITimelock).interfaceId
718:                 )
719:             ) {
720:                 ITimelock(timelock).updateDaoAddress();
721:             }
722:         }
723:     }

['729']

729:     function migrateTimelockConfiguration(address newTimelock) external { // <= FOUND
730:         _checkEmergencyCouncilPermissions();
731: 
732:         
733:         address previousTimelock = timelock;
734:         timelock = newTimelock;
735: 
736:         
737:         delete hasDaoPermissions[previousTimelock];
738:         delete hasElevatedPermissions[previousTimelock];
739: 
740:         
741:         hasDaoPermissions[newTimelock] = true;
742:         hasElevatedPermissions[newTimelock] = true;
743: 
744:         emit NewTimelockConfiguration(previousTimelock, newTimelock);
745:     }

['751']

751:     function transferEmergencyCouncil(address newEmergencyCouncil) external { // <= FOUND
752:         _checkEmergencyCouncilPermissions();
753: 
754:         
755:         address previousEmergencyCouncil = emergencyCouncil;
756:         emergencyCouncil = newEmergencyCouncil;
757: 
758:         
759:         delete hasDaoPermissions[previousEmergencyCouncil];
760:         delete hasElevatedPermissions[previousEmergencyCouncil];
761: 
762:         
763:         hasDaoPermissions[newEmergencyCouncil] = true;
764:         hasElevatedPermissions[newEmergencyCouncil] = true;
765: 
766:         emit EmergencyCouncilTransferred(
767:             previousEmergencyCouncil,
768:             newEmergencyCouncil
769:         );
770:     }

['786']

786:     function addChainSupport(
787:         address newOmnichainOperator,
788:         address messagingHub,
789:         address cveAddress,
790:         uint256 chainId,
791:         uint256 sourceAux,
792:         uint256 destinationAux,
793:         uint16 messagingChainId
794:     ) external {
795:         _checkElevatedPermissions();
796: 
797:         
798:         if (
799:             omnichainOperators[newOmnichainOperator][chainId].isAuthorized == 2
800:         ) {
801:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
802:         }
803: 
804:         
805:         if (supportedChainData[chainId].isSupported == 2) {
806:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
807:         }
808: 
809:         supportedChainData[chainId] = ChainData({
810:             isSupported: 2,
811:             messagingHub: messagingHub,
812:             asSourceAux: sourceAux,
813:             asDestinationAux: destinationAux,
814:             cveAddress: cveAddress
815:         });
816:         messagingToGETHChainId[messagingChainId] = chainId;
817:         GETHToMessagingChainId[chainId] = messagingChainId;
818:         supportedChains++;
819:         omnichainOperators[newOmnichainOperator][chainId] = OmnichainData({
820:             isAuthorized: 2,
821:             messagingChainId: messagingChainId,
822:             cveAddress: cveAddress
823:         });
824: 
825:         emit NewChainAdded(chainId, newOmnichainOperator);
826:     }

['836']

836:     function removeChainSupport(
837:         address currentOmnichainOperator,
838:         uint256 chainId
839:     ) external {
840:         
841:         
842:         _checkDaoPermissions();
843: 
844:         OmnichainData storage operatorToRemove = omnichainOperators[
845:             currentOmnichainOperator
846:         ][chainId];
847:         
848:         if (
849:             omnichainOperators[currentOmnichainOperator][chainId]
850:                 .isAuthorized < 2
851:         ) {
852:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
853:         }
854: 
855:         
856:         if (supportedChainData[chainId].isSupported < 2) {
857:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
858:         }
859: 
860:         
861:         supportedChainData[chainId].isSupported = 1;
862:         
863:         operatorToRemove.isAuthorized = 1;
864:         
865:         supportedChains--;
866:         
867:         delete GETHToMessagingChainId[
868:             messagingToGETHChainId[operatorToRemove.messagingChainId]
869:         ];
870:         delete messagingToGETHChainId[operatorToRemove.messagingChainId];
871: 
872:         emit RemovedChain(chainId, currentOmnichainOperator);
873:     }

['898']

898:     function addZapper(address newZapper) external { // <= FOUND
899:         _checkElevatedPermissions();
900: 
901:         
902:         if (isZapper[newZapper]) {
903:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
904:         }
905: 
906:         isZapper[newZapper] = true;
907: 
908:         emit NewCurvanceContract("Zapper", newZapper);
909:     }

['917']

917:     function removeZapper(address currentZapper) external { // <= FOUND
918:         _checkElevatedPermissions();
919: 
920:         
921:         if (!isZapper[currentZapper]) {
922:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
923:         }
924: 
925:         delete isZapper[currentZapper];
926: 
927:         emit RemovedCurvanceContract("Zapper", currentZapper);
928:     }

['936']

936:     function addSwapper(address newSwapper) external { // <= FOUND
937:         _checkElevatedPermissions();
938: 
939:         
940:         if (isSwapper[newSwapper]) {
941:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
942:         }
943: 
944:         isSwapper[newSwapper] = true;
945: 
946:         emit NewCurvanceContract("Swapper", newSwapper);
947:     }

['955']

955:     function removeSwapper(address currentSwapper) external { // <= FOUND
956:         _checkElevatedPermissions();
957: 
958:         
959:         if (!isSwapper[currentSwapper]) {
960:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
961:         }
962: 
963:         delete isSwapper[currentSwapper];
964: 
965:         emit RemovedCurvanceContract("Swapper", currentSwapper);
966:     }

['974']

974:     function addVeCVELocker(address newVeCVELocker) external { // <= FOUND
975:         _checkElevatedPermissions();
976: 
977:         
978:         if (isVeCVELocker[newVeCVELocker]) {
979:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
980:         }
981: 
982:         isVeCVELocker[newVeCVELocker] = true;
983: 
984:         emit NewCurvanceContract("VeCVELocker", newVeCVELocker);
985:     }

['993']

993:     function removeVeCVELocker(address currentVeCVELocker) external { // <= FOUND
994:         _checkElevatedPermissions();
995: 
996:         
997:         if (!isVeCVELocker[currentVeCVELocker]) {
998:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
999:         }
1000: 
1001:         delete isVeCVELocker[currentVeCVELocker];
1002: 
1003:         emit RemovedCurvanceContract("VeCVELocker", currentVeCVELocker);
1004:     }

['1012']

1012:     function addGaugeController(address newGaugeController) external { // <= FOUND
1013:         _checkElevatedPermissions();
1014: 
1015:         
1016:         if (isGaugeController[newGaugeController]) {
1017:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1018:         }
1019: 
1020:         isGaugeController[newGaugeController] = true;
1021: 
1022:         emit NewCurvanceContract("Gauge Controller", newGaugeController);
1023:     }

['1031']

1031:     function removeGaugeController(address currentGaugeController) external { // <= FOUND
1032:         _checkElevatedPermissions();
1033: 
1034:         
1035:         if (!isGaugeController[currentGaugeController]) {
1036:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1037:         }
1038: 
1039:         delete isGaugeController[currentGaugeController];
1040: 
1041:         emit RemovedCurvanceContract(
1042:             "Gauge Controller",
1043:             currentGaugeController
1044:         );
1045:     }

['1053']

1053:     function addHarvester(address newHarvester) external { // <= FOUND
1054:         _checkElevatedPermissions();
1055: 
1056:         
1057:         if (isHarvester[newHarvester]) {
1058:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1059:         }
1060: 
1061:         isHarvester[newHarvester] = true;
1062: 
1063:         emit NewCurvanceContract("Harvestor", newHarvester);
1064:     }

['1072']

1072:     function removeHarvester(address currentHarvester) external { // <= FOUND
1073:         _checkElevatedPermissions();
1074: 
1075:         
1076:         if (!isHarvester[currentHarvester]) {
1077:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1078:         }
1079: 
1080:         delete isHarvester[currentHarvester];
1081: 
1082:         emit RemovedCurvanceContract("Harvestor", currentHarvester);
1083:     }

['1091']

1091:     function addEndpoint(address newEndpoint) external { // <= FOUND
1092:         _checkElevatedPermissions();
1093: 
1094:         
1095:         if (isEndpoint[newEndpoint]) {
1096:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1097:         }
1098: 
1099:         isEndpoint[newEndpoint] = true;
1100: 
1101:         emit NewCurvanceContract("Endpoint", newEndpoint);
1102:     }

['1110']

1110:     function removeEndpoint(address currentEndpoint) external { // <= FOUND
1111:         _checkElevatedPermissions();
1112: 
1113:         
1114:         if (!isEndpoint[currentEndpoint]) {
1115:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1116:         }
1117: 
1118:         delete isEndpoint[currentEndpoint];
1119: 
1120:         emit RemovedCurvanceContract("Endpoint", currentEndpoint);
1121:     }

['1141']

1141:     function addMarketManager(
1142:         address newMarketManager,
1143:         uint256 marketInterestFactor
1144:     ) public virtual {
1145:         _checkElevatedPermissions();
1146: 
1147:         
1148:         if (isMarketManager[newMarketManager]) {
1149:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1150:         }
1151: 
1152:         
1153:         if (
1154:             !ERC165Checker.supportsInterface(
1155:                 newMarketManager,
1156:                 type(IMarketManager).interfaceId
1157:             )
1158:         ) {
1159:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1160:         }
1161: 
1162:         
1163:         if (marketInterestFactor > 5000) {
1164:             _revert(_PARAMETERS_MISCONFIGURED_SELECTOR);
1165:         }
1166: 
1167:         isMarketManager[newMarketManager] = true;
1168:         
1169:         marketManagers.push(newMarketManager);
1170:         
1171:         
1172:         protocolInterestFactor[newMarketManager] = _bpToWad(
1173:             marketInterestFactor
1174:         );
1175: 
1176:         emit NewCurvanceContract("Market Manager", newMarketManager);
1177:         emit InterestFeeSet(newMarketManager, marketInterestFactor); // <= FOUND
1178:     }

['1186']

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) {
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:     }

['113']

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())
135:         );
136:         uint256 minFromChainklink = uint256(
137:             uint192(feedAggregator.minAnswer())
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); // <= FOUND
184:     }

['191']

191:     function removeAsset(address asset) external override { // <= FOUND
192:         _checkElevatedPermissions();
193: 
194:         
195:         if (!isSupportedAsset[asset]) {
196:             revert ChainlinkAdaptor__AssetIsNotSupported();
197:         }
198: 
199:         
200:         delete isSupportedAsset[asset];
201: 
202:         
203:         delete adaptorDataUSD[asset];
204:         delete adaptorDataNonUSD[asset];
205: 
206:         
207:         
208:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
209:         emit ChainlinkAssetRemoved(asset); // <= FOUND
210:     }

['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;
129:         paymentTokenDecimals = IERC20(paymentTokenAddress).decimals();
130: 
131:         emit LBPStarted(startTimestamp);
132:     }

['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;
197: 
198:         SafeTransferLib.safeTransfer(cve, msg.sender, amount);
199: 
200:         emit Claimed(msg.sender, amount);
201:     }

['289']

289:     function _commit(uint256 amount, address recipient) internal { // <= FOUND
290:         userCommitted[recipient] += amount;
291:         saleCommitted += amount;
292: 
293:         emit Committed(recipient, amount);
294:     }

['177']

177:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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) {
207:             revert Curve2PoolAssetAdaptor__InvalidAssetIndex();
208:         }
209:         if (
210:             pool.coins(uint256(uint128(data.baseTokenIndex))) != data.baseToken
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); // <= FOUND
252:     }

['196']

196:     function addAsset(address asset, AdaptorData memory data) external { // <= FOUND
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; ) {
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:         
299:         bool isUpdate;
300:         if (isSupportedAsset[asset]) {
301:             isUpdate = true;
302:         }
303: 
304:         isSupportedAsset[asset] = true;
305:         emit CurvePoolAssetAdded(asset, data, isUpdate); // <= FOUND
306:     }

['259']

259:     function removeAsset(address asset) external override { // <= FOUND
260:         _checkElevatedPermissions();
261: 
262:         
263:         if (!isSupportedAsset[asset]) {
264:             revert Curve2PoolAssetAdaptor__AssetIsNotSupported();
265:         }
266: 
267:         
268:         
269:         delete isSupportedAsset[asset];
270:         delete adaptorData[asset];
271: 
272:         
273:         
274:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
275:         emit CurvePoolAssetRemoved(asset); // <= FOUND
276:     }

['313']

313:     function removeAsset(address asset) external override { // <= FOUND
314:         _checkElevatedPermissions();
315: 
316:         
317:         if (!isSupportedAsset[asset]) {
318:             revert Curve2PoolLPAdaptor__AssetIsNotSupported();
319:         }
320: 
321:         
322:         
323:         delete isSupportedAsset[asset];
324:         delete adaptorData[asset];
325: 
326:         
327:         
328:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
329:         emit CurvePoolAssetRemoved(asset); // <= FOUND
330:     }

['101']

101:     function _setReentrancyConfig(
102:         uint256 coinsLength,
103:         uint256 gasLimit
104:     ) internal {
105:         
106:         if (gasLimit < MIN_GAS_LIMIT) {
107:             revert CurveBaseAdaptor__InvalidConfiguration();
108:         }
109: 
110:         
111:         
112:         if (coinsLength < 2 || coinsLength > 4) {
113:             revert CurveBaseAdaptor__InvalidConfiguration();
114:         }
115: 
116:         reentrancyConfig[coinsLength] = gasLimit;
117:         emit UpdatedReentrancyConfiguration(coinsLength, gasLimit); // <= FOUND
118:     }

['222']

222:     function startMarket(address by) external nonReentrant returns (bool) { // <= FOUND
223:         if (msg.sender != address(marketManager)) {
224:             _revert(_UNAUTHORIZED_SELECTOR);
225:         }
226: 
227:         uint256 amount = _BASE_UNDERLYING_RESERVE;
228:         SafeTransferLib.safeTransferFrom(
229:             underlying,
230:             by,
231:             address(this),
232:             amount
233:         );
234: 
235:         
236:         
237:         
238:         
239:         totalSupply = totalSupply + amount;
240:         balanceOf[address(this)] = balanceOf[address(this)] + amount;
241: 
242:         emit Transfer(address(0), address(this), amount);
243:         return true;
244:     }

['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;
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); // <= FOUND
434:     }

['696']

696:     function approve(address spender, uint256 tokens) external returns (bool) { // <= FOUND
697:         allowance[msg.sender][spender] = tokens;
698: 
699:         emit Approval(msg.sender, spender, tokens);
700:         return true;
701:     }

['977']

977:     function accrueInterest() public { // <= FOUND
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(
1016:             cachedData.lastTimestampUpdated +
1017:                 (interestCompounds * cachedData.compoundRate)
1018:         );
1019:         marketData.exchangeRate = uint216(exchangeRateNew);
1020:         totalBorrows = totalBorrowsNew;
1021: 
1022:         
1023:         
1024:         uint256 newReserves = ((interestFactor * debtAccumulated) / WAD);
1025:         if (newReserves > 0) {
1026:             totalReserves = newReserves + reservesPrior;

['1049']

1049:     function _setInterestRateModel(
1050:         IInterestRateModel newInterestRateModel
1051:     ) internal {
1052:         
1053:         if (
1054:             !ERC165Checker.supportsInterface(
1055:                 address(newInterestRateModel),
1056:                 type(IInterestRateModel).interfaceId
1057:             )
1058:         ) {
1059:             revert DToken__ValidationFailed();
1060:         }
1061: 
1062:         
1063:         address oldInterestRateModel = address(interestRateModel);
1064: 
1065:         
1066:         interestRateModel = newInterestRateModel;
1067:         marketData.compoundRate = newInterestRateModel.compoundRate();
1068: 
1069:         emit NewMarketInterestRateModel(
1070:             oldInterestRateModel,
1071:             address(newInterestRateModel),
1072:             marketData.compoundRate
1073:         );
1074:     }

['1080']

1080:     function _setInterestFactor(uint256 newInterestFactor) internal { // <= FOUND
1081:         
1082:         if (newInterestFactor > 5000) {
1083:             revert DToken__ExcessiveValue();
1084:         }
1085: 
1086:         
1087:         uint256 oldInterestFactor = interestFactor;
1088: 
1089:         
1090:         
1091:         
1092:         interestFactor = newInterestFactor * 1e14;
1093: 
1094:         emit NewInterestFactor(oldInterestFactor, newInterestFactor);
1095:     }

['1104']

1104:     function _transfer(
1105:         address spender,
1106:         address from,
1107:         address to,
1108:         uint256 tokens
1109:     ) internal {
1110:         
1111:         if (from == to) {
1112:             revert DToken__TransferError();
1113:         }
1114: 
1115:         
1116:         marketManager.canTransfer(address(this), from, tokens);
1117: 
1118:         
1119:         if (spender != from) {
1120:             
1121:             
1122:             allowance[from][spender] = allowance[from][spender] - tokens;
1123:         }
1124: 
1125:         
1126:         balanceOf[from] = balanceOf[from] - tokens;
1127:         
1128:         
1129:         unchecked {
1130:             balanceOf[to] = balanceOf[to] + tokens;
1131:         }
1132: 
1133:         
1134:         GaugePool gaugePool = _gaugePool();
1135:         gaugePool.withdraw(address(this), from, tokens);
1136:         gaugePool.deposit(address(this), to, tokens);
1137: 
1138:         
1139:         emit Transfer(from, to, tokens);
1140:     }

['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:     }

['1194']

1194:     function _redeem(
1195:         address account,
1196:         address recipient,
1197:         uint256 tokens,
1198:         uint256 amount
1199:     ) internal {
1200:         
1201:         
1202:         
1203:         
1204:         if (marketUnderlyingHeld() < amount) {
1205:             revert DToken__InsufficientUnderlyingHeld();
1206:         }
1207: 
1208:         
1209:         balanceOf[account] = balanceOf[account] - tokens;
1210:         
1211:         
1212:         unchecked {
1213:             totalSupply = totalSupply - tokens;
1214:         }
1215: 
1216:         
1217:         
1218:         _gaugePool().withdraw(address(this), account, tokens);
1219: 
1220:         
1221:         SafeTransferLib.safeTransfer(underlying, recipient, amount);
1222: 
1223:         emit Transfer(account, address(0), tokens);
1224:     }

['1231']

1231:     function _borrow(
1232:         address account,
1233:         uint256 amount,
1234:         address recipient
1235:     ) internal {
1236:         
1237:         
1238:         
1239:         
1240:         
1241:         
1242:         
1243:         
1244:         if (
1245:             marketUnderlyingHeld() - totalReserves <
1246:             amount + _BASE_UNDERLYING_RESERVE
1247:         ) {
1248:             revert DToken__InsufficientUnderlyingHeld();
1249:         }
1250: 
1251:         
1252:         
1253:         _debtOf[account].principal = debtBalanceCached(account) + amount;
1254:         _debtOf[account].accountExchangeRate = marketData.exchangeRate;
1255:         totalBorrows = totalBorrows + amount;
1256: 
1257:         
1258:         SafeTransferLib.safeTransfer(underlying, recipient, amount);
1259: 
1260:         emit Borrow(account, amount);
1261:     }

['1271']

1271:     function _repay(
1272:         address payer,
1273:         address account,
1274:         uint256 amount
1275:     ) internal returns (uint256) {
1276:         
1277:         marketManager.canRepay(address(this), account);
1278: 
1279:         
1280:         uint256 accountDebt = debtBalanceCached(account);
1281: 
1282:         
1283:         if (amount > accountDebt) {
1284:             revert DToken__ExcessiveValue();
1285:         }
1286: 
1287:         
1288:         amount = amount == 0 ? accountDebt : amount;
1289: 
1290:         SafeTransferLib.safeTransferFrom(
1291:             underlying,
1292:             payer,
1293:             address(this),
1294:             amount
1295:         );
1296: 
1297:         
1298:         
1299:         
1300:         unchecked {
1301:             _debtOf[account].principal = accountDebt - amount;
1302:         }
1303:         _debtOf[account].accountExchangeRate = marketData.exchangeRate;
1304:         totalBorrows -= amount;
1305: 
1306:         emit Repay(payer, account, amount);
1307:         return amount;
1308:     }

['1321']

1321:     function _liquidate(
1322:         address liquidator,
1323:         address account,
1324:         uint256 amount,
1325:         IMToken collateralToken,
1326:         bool exactAmount
1327:     ) internal {
1328:         
1329:         accrueInterest();
1330: 
1331:         
1332:         assembly {
1333:             if eq(account, caller()) {
1334:                 
1335:                 mstore(0x00, 0xefeae624)
1336:                 revert(0x1c, 0x04)
1337:             }
1338:         }
1339: 
1340:         
1341:         if (!collateralToken.isCToken()) {
1342:             revert DToken__ValidationFailed();
1343:         }
1344: 
1345:         uint256 liquidatedTokens;
1346:         uint256 protocolTokens;
1347: 
1348:         
1349:         
1350:         (amount, liquidatedTokens, protocolTokens) = marketManager
1351:             .canLiquidateWithExecution(
1352:                 address(this),
1353:                 address(collateralToken),
1354:                 account,
1355:                 amount,
1356:                 exactAmount
1357:             );
1358: 
1359:         
1360:         if (!marketManager.isListed(address(this))) {
1361:             revert DToken__ValidationFailed();
1362:         }
1363: 
1364:         
1365:         uint256 accountDebt = debtBalanceCached(account);
1366: 
1367:         
1368:         SafeTransferLib.safeTransferFrom(
1369:             underlying,
1370:             liquidator,
1371:             address(this),
1372:             amount
1373:         );
1374: 
1375:         
1376:         
1377:         _debtOf[account].principal = accountDebt - amount;
1378:         _debtOf[account].accountExchangeRate = marketData.exchangeRate;
1379:         totalBorrows -= amount;
1380: 
1381:         emit Repay(liquidator, account, amount);
1382: 
1383:         
1384:         
1385:         
1386:         collateralToken.seize(
1387:             liquidator,
1388:             account,
1389:             liquidatedTokens,
1390:             protocolTokens
1391:         );
1392: 
1393:         emit Liquidated(
1394:             liquidator,
1395:             account,
1396:             amount,
1397:             address(collateralToken),
1398:             liquidatedTokens
1399:         );
1400:     }

['109']

109:     function setDelegateApproval(
110:         address delegate,
111:         bool isApproved
112:     ) external {
113:         if (hasDelegatingDisabled(msg.sender)) {
114:             revert Delegable__DelegatingDisabled();
115:         }
116: 
117:         uint256 approvalIndex = getUserApprovalIndex(msg.sender);
118:         _isDelegate[msg.sender][approvalIndex][delegate] = isApproved;
119: 
120:         emit DelegateApproval(
121:             msg.sender, 
122:             delegate, 
123:             approvalIndex, 
124:             isApproved
125:         );
126:     }

['578']

578:     function _updateDynamicInterestRateModel(
579:         uint256 baseRatePerYear,
580:         uint256 vertexRatePerYear,
581:         uint256 vertexUtilStart,
582:         uint256 adjustmentRate,
583:         uint256 adjustmentVelocity,
584:         uint256 vertexMultiplierMax,
585:         uint256 decayRate,
586:         bool vertexReset
587:     ) internal {
588:         
589:         
590:         
591:         baseRatePerYear = _bpToWad(baseRatePerYear);
592:         vertexRatePerYear = _bpToWad(vertexRatePerYear);
593:         vertexUtilStart = _bpToWad(vertexUtilStart);
594:         adjustmentVelocity = _bpToWad(adjustmentVelocity);
595:         vertexMultiplierMax = _bpToWad(vertexMultiplierMax);
596:         decayRate = _bpToWad(decayRate);
597: 
598:         
599:         if (
600:             adjustmentVelocity > MAX_VERTEX_ADJUSTMENT_VELOCITY ||
601:             adjustmentVelocity < MIN_VERTEX_ADJUSTMENT_VELOCITY
602:         ) {
603:             revert DynamicInterestRateModel__InvalidAdjustmentVelocity();
604:         }
605: 
606:         
607:         if (
608:             adjustmentRate > MAX_VERTEX_ADJUSTMENT_RATE ||
609:             adjustmentRate < MIN_VERTEX_ADJUSTMENT_RATE
610:         ) {
611:             revert DynamicInterestRateModel__InvalidAdjustmentRate();
612:         }
613: 
614:         
615:         if (decayRate > MAX_VERTEX_DECAY_RATE) {
616:             revert DynamicInterestRateModel__InvalidDecayRate();
617:         }
618: 
619:         
620:         
621:         if (vertexMultiplierMax * vertexRatePerYear > type(uint192).max) {
622:             revert DynamicInterestRateModel__InvalidMultiplierMax();
623:         }
624: 
625:         RatesConfiguration storage config = ratesConfig;
626: 
627:         config.baseInterestRate =
628:             (INTEREST_COMPOUND_RATE * baseRatePerYear * WAD) /
629:             (_SECONDS_PER_YEAR * vertexUtilStart);
630:         config.vertexInterestRate =
631:             (INTEREST_COMPOUND_RATE * vertexRatePerYear) /
632:             _SECONDS_PER_YEAR;
633: 
634:         config.vertexStartingPoint = vertexUtilStart;
635:         config.adjustmentRate = adjustmentRate;
636:         config.adjustmentVelocity = adjustmentVelocity;
637: 
638:         {
639:             
640:             uint256 newMultiplier = vertexReset ? WAD : vertexMultiplier();
641:             _currentRates = _packRatesData(
642:                 newMultiplier,
643:                 uint64(block.timestamp + config.adjustmentRate)
644:             );
645:         }
646: 
647:         config.decayRate = decayRate;
648: 
649:         
650:         
651:         uint256 thresholdLength = (WAD - vertexUtilStart) / 2;
652:         config.increaseThreshold = vertexUtilStart + thresholdLength;
653:         config.increaseThresholdMax = WAD;
654: 
655:         
656:         
657:         config.decreaseThreshold = vertexUtilStart;
658:         config.decreaseThresholdMax = vertexUtilStart - thresholdLength;
659: 
660:         RatesConfiguration memory cachedConfig = config;
661: 
662:         emit NewDynamicInterestRateModel(
663:             cachedConfig.baseInterestRate, 
664:             cachedConfig.vertexInterestRate, 
665:             vertexUtilStart, 
666:             adjustmentRate, 
667:             adjustmentVelocity, 
668:             vertexMultiplierMax, 
669:             decayRate, 
670:             cachedConfig.increaseThreshold, 
671:             WAD, 
672:             cachedConfig.decreaseThreshold, 
673:             cachedConfig.decreaseThresholdMax, 
674:             vertexReset 
675:         );
676:     }

['179']

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);
194: 
195:         uint64 sequence = tokenBridge.transferTokensWithPayload{
196:             value: messageFee
197:         }(
198:             token,
199:             amount,
200:             wormholeChainId,
201:             bytes32(uint256(uint160(to))),
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)))),
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:     }

['169']

169:     function addAsset(address asset, address alteredToken) external { // <= FOUND
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) {
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();

['246']

246:     function removeAsset(address asset) external override { // <= FOUND
247:         _checkElevatedPermissions();
248: 
249:         
250:         if (!isSupportedAsset[asset]) {
251:             revert GMAdaptor__AssetIsNotSupported();
252:         }
253: 
254:         
255:         
256:         delete isSupportedAsset[asset];
257:         delete marketData[asset];
258: 
259:         
260:         
261:         IOracleRouter(centralRegistry.oracleRouter()).notifyFeedRemoval(asset);
262:         emit GMXGMAssetRemoved(asset); // <= FOUND
263:     }

['206']

206:     function afterDepositExecution(
207:         bytes32 key,
208:         IGMXDeposit.Props memory,
209:         IGMXEventUtils.EventLogData memory eventData
210:     ) external {
211:         if (msg.sender != gmxDepositHandler) {
212:             revert GMCToken__CallerIsNotGMXDepositHandler();
213:         }
214:         if (!_isDepositKey[key]) {
215:             revert GMCToken__InvalidDepositKey();
216:         }
217: 
218:         uint256 yield = eventData.uintItems.items[0].value;
219: 
220:         
221:         _setNewVaultData(yield, vestPeriod);
222: 
223:         delete _isDepositKey[key];
224: 
225:         emit Harvest(yield);
226:     }

['145']

145:     function addExtraReward(address newReward) external { // <= FOUND
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; ) {
154:             
155:             if (rewardTokens[i++] == newReward) {
156:                 revert GaugeErrors.InvalidAddress();
157:             }
158:         }
159: 
160:         rewardTokens.push(newReward);
161: 
162:         emit AddExtraReward(newReward);
163:     }

['168']

168:     function removeExtraReward(uint256 index, address newReward) external { // <= FOUND
169:         _checkDaoPermissions();
170: 
171:         
172:         if (newReward == cve) {
173:             revert GaugeErrors.Unauthorized();
174:         }
175: 
176:         if (newReward != address(rewardTokens[index])) {
177:             revert GaugeErrors.InvalidAddress();
178:         }
179: 
180:         
181:         
182:         uint256 rewardTokensLength = rewardTokens.length;
183:         if (index != (rewardTokensLength - 1)) {
184:             rewardTokens[index] = rewardTokens[rewardTokensLength - 1];
185:         }
186:         rewardTokens.pop();
187: 
188:         emit RemoveExtraReward(newReward);
189:     }

['334']

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; ) {
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:     }

['401']

401:     function withdraw(
402:         address token,
403:         address user,
404:         uint256 amount
405:     ) external nonReentrant {
406:         if (amount == 0) {
407:             revert GaugeErrors.InvalidAmount();
408:         }
409: 
410:         
411:         
412:         if (
413:             msg.sender != token ||
414:             !IMarketManager(marketManager).isListed(token)
415:         ) {
416:             revert GaugeErrors.InvalidToken();
417:         }
418: 
419:         if (balanceOf[token][user] < amount) {
420:             revert GaugeErrors.InvalidAmount();
421:         }
422: 
423:         updatePool(token);
424:         _calcPending(user, token);
425: 
426:         balanceOf[token][user] -= amount;
427:         totalSupply[token] -= amount;
428: 
429:         _calcDebt(user, token);
430: 
431:         emit Withdraw(user, token, amount);
432:     }

['436']

436:     function claim(address token) external nonReentrant { // <= FOUND
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; ) {
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:     }

['482']

482:     function claimAndExtendLock(
483:         address token,
484:         uint256 lockIndex,
485:         bool continuousLock,
486:         RewardsData memory rewardsData,
487:         bytes memory params,
488:         uint256 aux
489:     ) external nonReentrant {
490:         
491:         
492:         if (block.timestamp < startTime) {
493:             revert GaugeErrors.NotStarted();
494:         }
495: 
496:         updatePool(token);
497:         _calcPending(msg.sender, token);
498: 
499:         
500:         uint256 rewards = userDebtInfo[token][msg.sender][cve].rewardPending;
501:         if (rewards == 0) {
502:             revert GaugeErrors.NoReward();
503:         }
504: 
505:         
506:         userDebt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment