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 |
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 |
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 |
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 |
Ensure such accounts are protected and consider implementing multi sig to prevent a single point of failure
Num of instances: 5
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: }
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
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: }
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
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: }
24
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 are labeled with ' <= FOUND'
Click to show findings
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: }
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
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: }
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: }
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: }
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: }
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: }
Implement a zero address check for found instances
Num of instances: 7
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: }
Perform multiplication operations first
Num of instances: 3
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: }
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
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: }
Num of instances: 1
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: }
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
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: }
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
Click to show findings
['86']
86: unchecked {
87: gasSpent = gasStart - gasleft(); // <= FOUND
88: }
['1212']
1212: unchecked {
1213: totalSupply = totalSupply - tokens; // <= FOUND
1214: }
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
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
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
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: }
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
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: }
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
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: }
Num of instances: 2
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
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
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: }
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
Click to show findings
['592']
592:
593: IERC20(cve).approve(address(veCVE), lockAmount); // <= FOUND
['267']
267: cve.approve(veCVE, amount); // <= FOUND
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
Click to show findings
['71']
71: receive() external payable {}
['71']
71: receive() external payable {}
['71']
71: receive() external payable {}
['71']
71: receive() external payable {}
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
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: );
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
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: }
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
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: }
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
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: }
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
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: }
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
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
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
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: }
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
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
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
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: }
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
Click to show findings
['52']
52: function setExitFee(uint256 newExitFee) external {
53: _checkElevatedPermissions();
54: _setExitFee(newExitFee);
55: }
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
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: }
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
Click to show findings
['69']
67: constructor(
68: ICentralRegistry centralRegistry_,
69: IERC20 asset_, // <= FOUND
70: address marketManager_, // <= FOUND
71: IVeloGauge gauge,
72: IVeloPairFactory pairFactory,
73: IVeloRouter router
74: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
75: if (block.chainid != 8453) {
76: revert AerodromeStableCToken__ChainIsNotSupported();
77: }
78:
79:
80: address _asset = asset(); // <= FOUND
81:
82:
83: if (gauge.stakingToken() != _asset) {
84: revert AerodromeStableCToken__StakingTokenIsNotAsset(
85: gauge.stakingToken()
86: );
87: }
88:
89:
90: if (!IVeloPool(_asset).stable()) {
91: revert AerodromeStableCToken__AssetIsNotStable();
92: }
93:
94:
95: strategyData.token0 = IVeloPool(_asset).token0();
96: strategyData.token1 = IVeloPool(_asset).token1();
97:
98:
99: if (strategyData.token1 == address(rewardToken)) {
100: strategyData.token1 = strategyData.token0;
101: strategyData.token0 = address(rewardToken);
102: }
103: strategyData.decimalsA = 10 ** IERC20(strategyData.token0).decimals();
104: strategyData.decimalsB = 10 ** IERC20(strategyData.token1).decimals();
105:
106: strategyData.gauge = gauge;
107: strategyData.router = router;
108: strategyData.pairFactory = pairFactory;
109:
110: isUnderlyingToken[strategyData.token0] = true;
111: isUnderlyingToken[strategyData.token1] = true;
112:
113: rewardTokenIsUnderlying = (address(rewardToken) ==
114: strategyData.token0 ||
115: address(rewardToken) == strategyData.token1);
116: }
['67']
65: constructor(
66: ICentralRegistry centralRegistry_,
67: IERC20 asset_, // <= FOUND
68: address marketManager_, // <= FOUND
69: IVeloGauge gauge,
70: IVeloPairFactory pairFactory,
71: IVeloRouter router
72: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73: if (block.chainid != 8453) {
74: revert AerodromeVolatileCToken__ChainIsNotSupported();
75: }
76:
77:
78: address _asset = asset(); // <= FOUND
79:
80:
81: if (gauge.stakingToken() != _asset) {
82: revert AerodromeVolatileCToken__StakingTokenIsNotAsset(
83: gauge.stakingToken()
84: );
85: }
86:
87:
88: if (IVeloPool(_asset).stable()) {
89: revert AerodromeVolatileCToken__AssetIsNotStable();
90: }
91:
92:
93: strategyData.token0 = IVeloPool(_asset).token0();
94: strategyData.token1 = IVeloPool(_asset).token1();
95:
96:
97: if (strategyData.token1 == address(rewardToken)) {
98: strategyData.token1 = strategyData.token0;
99: strategyData.token0 = address(rewardToken);
100: }
101: strategyData.gauge = gauge;
102: strategyData.router = router;
103: strategyData.pairFactory = pairFactory;
104:
105: isUnderlyingToken[strategyData.token0] = true;
106: isUnderlyingToken[strategyData.token1] = true;
107:
108: rewardTokenIsUnderlying = (address(rewardToken) ==
109: strategyData.token0 ||
110: address(rewardToken) == strategyData.token1);
111: }
['65']
63: constructor(
64: ICentralRegistry centralRegistry_,
65: IERC20 asset_, // <= FOUND
66: address marketManager_, // <= FOUND
67: uint256 pid_,
68: address rewarder_, // <= FOUND
69: address booster_ // <= FOUND
70: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
71: strategyData.pid = pid_;
72: strategyData.booster = IBooster(booster_);
73:
74:
75: (address pidToken, , , address balRewards, , bool shutdown) = IBooster( // <= FOUND
76: booster_
77: ).poolInfo(strategyData.pid);
78:
79:
80:
81: if (pidToken != asset() || shutdown || balRewards != rewarder_) {
82: revert AuraCToken__InvalidVaultConfig();
83: }
84:
85: strategyData.rewarder = IBaseRewardPool(rewarder_);
86: strategyData.balancerVault = IBalancerVault(
87: IBalancerPool(pidToken).getVault()
88: );
89: strategyData.balancerPoolId = IBalancerPool(pidToken).getPoolId();
90:
91:
92:
93: strategyData.rewardTokens.push() = _BAL;
94:
95:
96: strategyData.rewardTokens.push() = _AURA;
97:
98: uint256 extraRewardsLength = IBaseRewardPool(rewarder_)
99: .extraRewardsLength();
100: for (uint256 i; i < extraRewardsLength; ) {
101: unchecked {
102: address rewardToken = IStashWrapper( // <= FOUND
103: IRewards(IBaseRewardPool(rewarder_).extraRewards(i++))
104: .rewardToken()
105: ).baseToken();
106:
107: if (rewardToken != _AURA && rewardToken != _BAL) {
108: strategyData.rewardTokens.push() = rewardToken;
109: }
110: }
111: }
112:
113:
114: (address[] memory queriedTokens, , ) = strategyData
115: .balancerVault
116: .getPoolTokens(strategyData.balancerPoolId);
117: strategyData.underlyingTokens = queriedTokens;
118:
119: uint256 numUnderlyingTokens = strategyData.underlyingTokens.length;
120: for (uint256 i; i < numUnderlyingTokens; ) {
121: unchecked {
122: isUnderlyingToken[strategyData.underlyingTokens[i++]] = true;
123: }
124: }
125: }
['30']
28: constructor(
29: ICentralRegistry centralRegistry_,
30: IERC20 asset_, // <= FOUND
31: address marketManager_ // <= FOUND
32: ) CTokenCompounding(
33: centralRegistry_,
34: asset_,
35: marketManager_
36: ) {
37: nativeYieldManager = IBlastCentralRegistry(address(centralRegistry_)).nativeYieldManager();
38:
39:
40:
41: CHAIN_YIELD_MANAGER.configureClaimableGas();
42: }
['101']
99: constructor(
100: ICentralRegistry centralRegistry_,
101: IERC20 asset_, // <= FOUND
102: address MarketManager_ // <= FOUND
103: ) Delegable(centralRegistry_) {
104: _asset = asset_;
105: _name = string.concat("Curvance collateralized ", asset_.name());
106: _symbol = string.concat("c", asset_.symbol());
107: _decimals = asset_.decimals();
108:
109: if (
110: !ERC165Checker.supportsInterface(
111: address(centralRegistry_),
112: type(ICentralRegistry).interfaceId
113: )
114: ) {
115: revert CTokenBase__InvalidCentralRegistry();
116: }
117:
118:
119: if (!centralRegistry.isMarketManager(MarketManager_)) {
120: revert CTokenBase__InvalidMarketManager();
121: }
122:
123:
124: marketManager = IMarketManager(MarketManager_);
125:
126:
127:
128: if (asset_.totalSupply() >= type(uint232).max) {
129: revert CTokenBase__UnderlyingAssetTotalSupplyExceedsMaximum();
130: }
131: }
['37']
35: constructor(
36: ICentralRegistry centralRegistry_,
37: IERC20 asset_, // <= FOUND
38: address marketManager_, // <= FOUND
39: uint256 exitFee_
40: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
41: _setExitFee(exitFee_);
42: }
['21']
21: constructor(address _target) { // <= FOUND
22: target = _target;
23: }
['91']
89: constructor(
90: ICentralRegistry centralRegistry_,
91: address marketManager_, // <= FOUND
92: address WETH_ // <= FOUND
93: ) {
94: if (
95: !ERC165Checker.supportsInterface(
96: address(centralRegistry_),
97: type(ICentralRegistry).interfaceId
98: )
99: ) {
100: revert ComplexZapper__InvalidCentralRegistry();
101: }
102:
103: centralRegistry = centralRegistry_;
104:
105:
106:
107: if (!centralRegistry.isMarketManager(marketManager_)) {
108: revert ComplexZapper__InvalidMarketManager();
109: }
110:
111: marketManager = IMarketManager(marketManager_);
112: WETH = WETH_;
113: }
['67']
65: constructor(
66: ICentralRegistry centralRegistry_,
67: IERC20 asset_, // <= FOUND
68: address marketManager_, // <= FOUND
69: uint256 pid_,
70: address rewarder_, // <= FOUND
71: address booster_ // <= FOUND
72: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73:
74:
75: if (pid_ <= 176) {
76: revert Convex2PoolCToken__UnsafePool();
77: }
78:
79: strategyData.pid = pid_;
80: strategyData.booster = IBooster(booster_);
81:
82:
83: (address pidToken, , , address crvRewards, , bool shutdown) = IBooster( // <= FOUND
84: booster_
85: ).poolInfo(strategyData.pid);
86:
87:
88:
89: if (
90: pidToken != address(asset_) || shutdown || crvRewards != rewarder_
91: ) {
92: revert Convex2PoolCToken__InvalidVaultConfig();
93: }
94:
95: strategyData.curvePool = ICurveFi(pidToken); // <= FOUND
96:
97: uint256 coinsLength;
98: address token; // <= FOUND
99:
100:
101: while (true) {
102: try ICurveFi(pidToken).coins(coinsLength) {
103: ++coinsLength;
104: } catch {
105: break;
106: }
107: }
108:
109:
110: if (coinsLength != 2) {
111: revert Convex2PoolCToken__InvalidCoinLength();
112: }
113:
114: strategyData.rewarder = IBaseRewardPool(rewarder_);
115:
116:
117:
118: reQueryRewardTokens();
119:
120:
121: strategyData.underlyingTokens = new address[](coinsLength);
122: for (uint256 i; i < coinsLength; ) {
123: token = ICurveFi(pidToken).coins(i);
124: strategyData.underlyingTokens[i] = token;
125: isUnderlyingToken[token] = true;
126:
127: unchecked {
128: ++i;
129: }
130: }
131: }
['67']
65: constructor(
66: ICentralRegistry centralRegistry_,
67: IERC20 asset_, // <= FOUND
68: address marketManager_, // <= FOUND
69: uint256 pid_,
70: address rewarder_, // <= FOUND
71: address booster_ // <= FOUND
72: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
73:
74:
75: if (pid_ <= 176) {
76: revert Convex3PoolCToken__UnsafePool();
77: }
78:
79: strategyData.pid = pid_;
80: strategyData.booster = IBooster(booster_);
81:
82:
83: (address pidToken, , , address crvRewards, , bool shutdown) = IBooster( // <= FOUND
84: booster_
85: ).poolInfo(strategyData.pid);
86:
87:
88:
89: if (
90: pidToken != address(asset_) || shutdown || crvRewards != rewarder_
91: ) {
92: revert Convex3PoolCToken__InvalidVaultConfig();
93: }
94:
95: strategyData.curvePool = ICurveFi(pidToken); // <= FOUND
96:
97: uint256 coinsLength;
98: address token; // <= FOUND
99:
100:
101: while (true) {
102: try ICurveFi(pidToken).coins(coinsLength) {
103: ++coinsLength;
104: } catch {
105: break;
106: }
107: }
108:
109:
110: if (coinsLength != 3) {
111: revert Convex3PoolCToken__InvalidCoinLength();
112: }
113:
114: strategyData.rewarder = IBaseRewardPool(rewarder_);
115:
116:
117:
118: reQueryRewardTokens();
119:
120:
121: strategyData.underlyingTokens = new address[](coinsLength);
122: for (uint256 i; i < coinsLength; ) {
123: token = ICurveFi(pidToken).coins(i);
124: strategyData.underlyingTokens[i] = token;
125: isUnderlyingToken[token] = true;
126:
127: unchecked {
128: ++i;
129: }
130: }
131: }
['162']
160: constructor(
161: ICentralRegistry centralRegistry_,
162: address underlying_, // <= FOUND
163: address marketManager_, // <= FOUND
164: address interestRateModel_ // <= FOUND
165: ) Delegable(centralRegistry_) {
166: if (
167: !ERC165Checker.supportsInterface(
168: address(centralRegistry_),
169: type(ICentralRegistry).interfaceId
170: )
171: ) {
172: revert DToken__InvalidCentralRegistry();
173: }
174:
175:
176:
177: if (!centralRegistry.isMarketManager(marketManager_)) {
178: revert DToken__MarketManagerIsNotLendingMarket();
179: }
180:
181:
182: marketManager = IMarketManager(marketManager_);
183:
184:
185: marketData.lastTimestampUpdated = uint40(block.timestamp);
186: marketData.exchangeRate = uint216(WAD);
187:
188: _setInterestRateModel(IInterestRateModel(interestRateModel_));
189:
190:
191:
192: uint256 newInterestFactor = centralRegistry.protocolInterestFactor(
193: marketManager_
194: );
195: interestFactor = newInterestFactor;
196:
197: emit NewInterestFactor(0, newInterestFactor);
198:
199: underlying = underlying_;
200: name = string.concat(
201: "Curvance interest bearing ",
202: IERC20(underlying_).name()
203: );
204: symbol = string.concat("c", IERC20(underlying_).symbol());
205:
206:
207:
208: if (IERC20(underlying).totalSupply() >= type(uint232).max) {
209: revert DToken__UnderlyingAssetTotalSupplyExceedsMaximum();
210: }
211: }
['78']
76: constructor(
77: ICentralRegistry centralRegistry_,
78: address gmxReader_, // <= FOUND
79: address gmxDataStore_ // <= FOUND
80: ) BaseOracleAdaptor(centralRegistry_) {
81: if (block.chainid != 42161) {
82: revert GMAdaptor__ChainIsNotSupported();
83: }
84:
85: _setGMXReader(gmxReader_);
86: _setGMXDataStore(gmxDataStore_);
87: }
['185']
183: constructor(
184: ICentralRegistry centralRegistry_,
185: address gaugePool_ // <= FOUND
186: ) LiquidityManager(centralRegistry_) {
187: if (
188: !ERC165Checker.supportsInterface(
189: address(gaugePool_),
190: type(IGaugePool).interfaceId
191: )
192: ) {
193: _revert(_INVALID_PARAMETER_SELECTOR);
194: }
195:
196: gaugePool = IGaugePool(gaugePool_); // <= FOUND
197: }
['61']
59: constructor(
60: ICentralRegistry centralRegistry_,
61: IERC20 asset_, // <= FOUND
62: address marketManager_, // <= FOUND
63: IPendleRouter router_
64: ) CTokenCompounding(centralRegistry_, asset_, marketManager_) {
65: strategyData.router = router_;
66: strategyData.lp = IPMarket(address(asset_));
67:
68: (strategyData.sy, strategyData.pt, strategyData.yt) = strategyData
69: .lp
70: .readTokens();
71:
72: strategyData.rewardTokens = strategyData.lp.getRewardTokens();
73:
74:
75:
76: strategyData.underlyingTokens = strategyData.sy.getTokensIn();
77: uint256 numUnderlyingTokens = strategyData.underlyingTokens.length;
78: for (uint256 i; i < numUnderlyingTokens; ) {
79: unchecked {
80: isUnderlyingToken[strategyData.underlying