YieldFlow

CertiKFiles named in the report

CertiK reviewed YieldFlow on 12 Sep 2023: 18 findings, 0 critical; 15 marked fixed, 3 open.

No open critical findings
Open the reportOpen YieldFlow’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings18

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High100
Medium000
Low2015
LowComparison to Boolean ConstantFixed

Boolean constants can be used directly and do not need to be compared to true or false.

LowIncompatibility with Deflationary TokensFixed

When transferring deflationary ERC20 tokens, the input amount may not be equal to the received amount due to the charged transaction fee. For example, if a user sends 100 deflationary tokens (with a 10% transaction fee), only 90 tokens actually arrived to the contract. However, a failure to discount such fees may allow the same user to withdraw 100 tokens from the contract, which causes the contract to lose 10 tokens in such a transaction. Reference: https://thoreum-finance.medium.com/what-exploit-happened-today-for-gocerberus-and-garuda-also-for-lokum-ybear-piggy-caramelswap-3943ee23a39f The contracts in scope are based on the `UniswapV3` mechanics which strictly do not support deflationary tokens.

LowIncorrect Input Variable CheckFixed

The `require` statement below ```solidity=129 require(owner != address(0), "Owner cant be zero address"); .. .. owner = _owner; ``` is meant to check that the input variable `_owner` is not the zero address. However, the condition check is executed at line 129 while the `owner` variable is initialized at line 132. As a consequence the `require` will always trigger an error since it will check an uninitialized variable.

LowIncorrect Token Addresses ValidationFixed

The following `require` statement ```solidity require(_token1 != address(0) || _token0 != address(0), "Zero address for tokens") ``` is meant to validate that the input addresses for `_token0` and `_token1` are both not zero addresses. However, due to the use of the logical OR `||` operator, this condition will pass even if only one of the tokens is the zero address. This means that the function will proceed even if one of the token addresses is not properly initialized, which may lead to unexpected behavior or potential issues later in the function execution.

LowMissing Reasonable Limit for `fee` and `earnCooldFixed

The function `setEarnCooldownPeriod` allows setting the `earnCooldownPeriod` variable. When the users call the function `increaseLiquidity` the following line is executed: ```solidity=535 // users will only be considered for fees after earnCooldownPeriod userElement.earnTimestamp = block.timestamp + earnCooldownPeriod; ``` As a consequence, users will not receive fees until this period is over. Without enforcing a condition checking for a reasonable limit on this variable, the account with modification access can decide to set a really high limit preventing users from earning fees for their participation in the protocol. Additionally, there are no upper boundaries for the `fee` parameter in the function `changePositionParameter` which is used to change position parameters. Thus, it is possible to set a fee rate up to any arbitrary amount.

LowMissing Zero Address ValidationFixed

Addresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. ```solidity=132 owner = _owner; ``` ```solidity=133 feeReceiver = _owner; ``` - `_owner` is not zero-checked before being used. --- ```solidity=147 yieldManager = _yieldManager; ``` - `_yieldManager` is not zero-checked before being used. --- ```solidity=916 owner = _newOwner; ``` - `_newOwner` is not zero-checked before being used. --- ```solidity=925 feeReceiver = _newFeeReceiver; ``` - `_newFeeReceiver` is not zero-checked before being used. --- ```solidity=939 token0 = _newToken0; ``` - `_newToken0` is not zero-checked before being used. --- ```solidity=940 token1 = _newToken1; ``` - `_newToken1` is not zero-checked before being used. --- ```solidity=950 yieldManager = _newYieldManager; ``` - `_newYieldManager` is not zero-checked before being used.

LowPotential Divide by ZeroFixed

Performing division by zero would raise an error and revert the transaction. ```solidity=423 userElement.liquidity = userElement.liquidity * liquidity / totalLiquidity; ``` The expression `userElement.liquidity * liquidity / totalLiquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=426 userElement.token0Balance += amount0Diff * userElement.liquidity / liquidity; ``` The expression `amount0Diff * userElement.liquidity / liquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=429 userElement.token1Balance += amount1Diff * userElement.liquidity / liquidity; ``` The expression `amount1Diff * userElement.liquidity / liquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=800 uint share0 = amount0 * userElement.liquidity / totalLiquidity; ``` The expression `amount0 * userElement.liquidity / totalLiquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=801 uint share1 = amount1 * userElement.liquidity / totalLiquidity; ``` The expression `amount1 * userElement.liquidity / totalLiquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=807 uint share0 = amount0 * userElement.liquidity / totalLiquidity; ``` The expression `amount0 * userElement.liquidity / totalLiquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455]. --- ```solidity=808 uint share1 = amount1 * userElement.liquidity / totalLiquidity; ``` The expression `amount1 * userElement.liquidity / totalLiquidity` may divide by zero. Its divisor has has estimated interval [0, 340282366920938463463374607431768211455].

LowPotential Out-of-Gas ExceptionFixed

When a loop allows an arbitrary number of iterations or accesses state variables in its body, the function may run out of gas and revert the transaction. The highlighted locations contain a loop including the following line of code: ```solidity for (uint i = 0; i < userList.length; i++) { ``` Thus the loop body accesses the storage variable `userList`, whose length is not known at deployment time.

LowPotential ReentrancyFixed

A reentrancy attack can occur when the contract creates a function that makes an external call to another untrusted contract before resolving any effects. If the attacker can control the untrusted contract, they can make a recursive call back to the original function, repeating interactions that would have otherwise not run after the external call resolved the effects. The `RangePositionManager` contract includes several external calls made in different functions like `collect`, `decreaseLiquidity`, `swap`, and `mint` that can potentially be manipulated by an attacker for a reentrancy attack in the case untrusted token contracts are used for the token pairs. Additionally, beyond untrusted contracts, in the case tokens with upgradeable contracts are used, they may be updated to an implementation allowing for reentrancy.

LowPull-Over-Push PatternFixed

The highlighted functions allow setting new accounts, that are in charge of managing critical functionalities for the correct functioning of the protocol, without guaranteeing the designated accounts are able to actuate transactions on-chain.

Show 5 more
LowUnnecessary Storage Read Access in For LoopFixed

The for loop contains repeated storage read access in the condition check. Given that the ending condition does not change in the for loop, the repeated storage read is unnecessary, and its associated high gas cost can be eliminated. ```solidity=436 for (uint i = 0; i < userList.length; i++) { ``` Loop condition `i < userList.length` accesses the `length` field of a storage array. --- ```solidity=680 for (uint i = 0; i < userList.length; i++) { ``` Loop condition `i < userList.length` accesses the `length` field of a storage array. --- ```solidity=816 for (uint i = 0; i < userList.length; i++) { ``` Loop condition `i < userList.length` accesses the `length` field of a storage array.

LowUnused Return ValueFixed

The smart contract does not check or store the return value of calls to `transfer` in a local or state variable, which may introduce vulnerabilities due to the unhandled outcome. Some ERC-20 tokens' transfer functions return no values, while others return a bool value, they should be handled with care. If a function returns `false` instead of reverting upon failure, an unchecked failed transfer could be mistakenly considered successful in the contract. ```solidity=720 IERC20(token0).transfer(sponsor, sponsorFee0); ``` --- ```solidity=727 IERC20(token0).transfer(sponsor, sponsorFee1); ``` --- ```solidity=875 IERC20(token0).transfer(sponsor, sPerfFee0); ``` --- ```solidity=883 IERC20(token1).transfer(sponsor, sPerfFee1); ```

LowUnused Storage VariableFixed

The `tickMovement` storage variable is set in the contract constructor and can be modified by the contract owner with a call to the `setTickMovement` method. However, such variable is never read by any other contract logic.

LowUsage of `transfer`/`send` for Sending Native TokFixed

It is not recommended to use Solidity's `transfer()` and `send()` functions for transferring native tokens, since some contracts may not be able to receive the funds. Those functions forward only a fixed amount of gas (2300 specifically) and the receiving contracts may run out of gas before finishing the transfer. Also, EVM instructions' gas costs may increase in the future. Thus, some contracts that can receive now may stop working in the future due to the gas limitation.

LowVariables That Could Be Declared as ImmutableFixed

The linked variables assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (3)

  • contracts/libraries/LiquidityAmounts.sol
  • contracts/RangePositionManager.sol
  • contracts/libraries/PoolAddress.sol

Part of YieldFlow’s security record

YieldFlow
Audits2 audits by 1 firmLast audit Apr 2024, by CertiK.
Critical findingsNo open critical findingsAcross the 2 reports, 3 critical findings were reported. Each is marked fixed by the firm that reported it.
Open YieldFlow’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us