Farseen0/scar-pairs huggingface dataset:
MEDIUM severity: TRST-User deposits can fail despite using the correct method for calculation of deposit amounts. **Description:**
Users can use the `getAmtsNeededForDeposit()` function to get the amount of tokens that 
maintain the desired proportion for vault deposits. It will perform a calculation very similar to 
the one in `ratiosMatch()`, which will verify the deposit.

However, a difference between the.....

Vulnerable code:
for (uint256 i = 0; i < balances.length; i++) {
               if (i == indexOfReferenceToken) {
                amtsNeeded[i] = amtIn;
         } else {
         // amtsNeeded[i] = (amtIn * balances[i]) / 
                  balances[indexOfReferenceToken];
                     amtsNeeded[i] = Arithmetic.overflowResistantFraction(amtIn, 
                  balances[i], balances[indexOfReferenceToken]);
               }
            }
MEDIUM severity: TRST-Deposits of fee-on-transfer tokens will favor later depositors, making earlier investors lose funds. **Description:**
When deposits are processed, the percentage of **Denominator** minted to the depositor is 
linear to the contribution, compared to the current balance. 

The calculation will lead to incorrect results when using fee-on-transfer (tax) tokens. The 
"before-tax" amount of the...

Vulnerable code:
uint256 T = vlt.virtualTotalBalance(); //will be at least 1
         uint256 D = vlt.D();
         if (functions.willOverflowWhenMultiplied(amt, D)) {
            require(T > amt || T > D, "overflow");
         }
         deltaN = Arithmetic.overflowResistantFraction(amt, D, T);
             vlt.setN(msg.sender, vlt.N(msg.sender) + deltaN);
                  vlt.setD(D + deltaN); //D always kept = sum of all Ns, plus 
                    vlt.initD()
         for (uint256 i = 0; i < tkns.length; i++) {
            if (amts[i] > 0) {
         IERC20(tkns[i]).safeTransferFrom(msg.sender, vaultAddress, amts[i]);
            }
         }
HIGH severity: TRST-Several popular ERtokens are incompatible with the vault due to MAX approve. **Description:**
There are several instances where the vault approves use of funds to the manager or a trade 
router. It will set approval to MAX_UINT256. 

The issue is that there are several popular tokens(https://github.com/d-xo/weird-erc20#revert-on-large-approvals--transfers) (UNI, COMP and...

Vulnerable code:
for (uint i = 0; i < tokens.length; i++) {
         //allow vault manager to withdraw tokens
                   IERC20(tokens[i]).safeIncreaseAllowance(ownerIn, 
         type(uint256).max); 
         }
LOW severity: TRST-Autotrader can steal gas. **Description:**
The autotrader receives gas compensation for the invocation of a trade action.

Note that **tx.gasprice** is controlled by the sender, by passing an arbitrary priority fee. This way, 
the fee calculation may grant them a large profit. The key point is that **gasStationParam** is...

Vulnerable code:
if (useGasStation && (msg.sender == _autoTrader) && (_autoTrader != 
          vlt.operator())) { //operator pays gas to _autoTrader for auto trades
             uint256 gasPrice = tx.gasprice;
               if (gasPrice == 0){
            gasPrice = 1;
         }
         uint256 fee = gasPrice * (gasStart - gasleft() + 
      gasStationParam);
      GS.removeGas(fee, payable(_autoTrader), vlt.operator());
      }
LOW severity: TRST-Owner can steal all funds. **Description:**
The owner can set a new operator using the function below:

They can also allow an arbitrary, malicious router with the code below:

During trading, routers receive MAX approval.

The combination of these permissions allows an owner to drain all deployed vaults, by adding 
a...

Vulnerable code:
function setOperator(address operatorIn) external nonReentrant {
         require(msg.sender == owner() || msg.sender == operator, "only ownop");
      operator = operatorIn;
      }

function allowRouter(address routerAddress, string calldata nameIn, uint256 routerType) external      onlyOwner 
             returns (address routerInfoContractAddress){
                require(!allowedRoutersMap[routerAddress].allowed, "router allowed");
                   require(routerType == 0 || routerType == 1, "must be 0 or 1");
                   allowedRoutersList.push(routerAddress);
            RouterInfo ri = new RouterInfo(owner(), nameIn, routerAddress, 
         routerType);
            allowedRoutersMap[routerAddress] = routerInfo(true, 
            allowedRoutersList.length 
         - 1, 
            address(ri));
         
            return address(ri);
         
         }

//make sure router can spend vault's spend token
         //alternate idea: transfer tokens to this contract, trade, transfer back
            uint256 currentAllowance =  IERC20(params.spendToken).allowance(vaultAddress, 
                params.routerAddress);
             if (currentAllowance < params.spendAmt)
         vlt.increaseAllowance(params.spendToken, params.routerAddress, 
            type(uint256).max - currentAllowance);
LOW severity: TRST-When owner is also the operator, they can drain all funds in a vault. **Description:** 
The code changes implemented to fix slippage changes introduced a significant centralization 
risk. The owner can change price feeds at any time.

This can be used to skew the minimum to receive value queried in `trade()`:

Therefore, owner can perform the sandwich attack...

Vulnerable code:
function addPriceFeed(address token, address priceFeed) external  onlyOwner {
         // require(aggregatorAddresses[token] == address(0), "price feed exists");
               aggregatorAddresses[token] = priceFeed;
                  emit PriceFeedAdded(token, priceFeed, true);
         }
         function removePriceFeed(address token) external onlyOwner {
             aggregatorAddresses[token] = address(0);
                emit PriceFeedAdded(token, address(0), false);
         }

//check slippage with chainlink oracle
      uint256 maxSlippage = AI.getPairMaxSlippage(params.spendToken, params.receiveToken);
         require(CLI.getMinReceived(params.spendToken, params.receiveToken, 
            params.spendAmt, maxSlippage) <= params.receiveAmtMin, "rec min too low");
HIGH severity: TRST-Incorrect implementation of getProfitSharingE18() greatly reduces Lender's yield. **Description:**
`ProfitSharingModel.getProfitSharingE18()` calculates the share of profit that Lender gets 
based on the APR of the position. According to the formula, the higher the APR, the lower 
the share of profit the Lender gets, but due to the wrong implementation of the...

Vulnerable code:
- if (_annualizedYieldE18 < MAX_ANNUALIZED_YEILD) {
            + if (_annualizedYieldE18 >= MAX_ANNUALIZED_YEILD) { 
            return 0.25e18;
            }
HIGH severity: TRST-On liquidation, if netPnLE36 <= 0, the premium paid by the liquidator is locked in the contract.. **Description:**
When liquidating a position, the liquidator is required to pay premium to Lender, which is 
accumulated in **sharingProfitTokenAmts** together with Lender's profit and paid to Lender in 
`_shareProfitsAndRepayAllDebts()`. 

However, if **netPnLE36 <= 0**,...

Vulnerable code:
(
        netPnLE36,
             lenderProfitUSDValueE36,
                 borrowTotalUSDValueE36,
                     positionOpenUSDValueE36,
                      sharingProfitTokenAmts ) = calcProfitInfo(_positionManager, _user, _posId);
        // 2. add liquidation premium to the shared profit amounts
                            uint lenderLiquidatationPremiumBPS = IConfig(config).lenderLiquidatePremiumBPS();
                         for (uint i; i < sharingProfitTokenAmts.length; ) {
                    sharingProfitTokenAmts[i] +=
                (pos.openTokenInfos[i].borrowAmt * lenderLiquidatationPremiumBPS) / BPS;
        unchecked {
        ++i;
        }
        }

function _shareProfitsAndRepayAllDebts( address _positionManager, address _posOwner, uint _posId,
                    int _netPnLE36, uint[] memory _shareProfitAmts, address[] memory _tokens,
                         OpenTokenInfo[] memory _openTokenInfos
                              ) internal {
                    // 0. load states
            address _lendingProxy = lendingProxy;
                    // 1. if net pnl is positive, share profits to lending proxy
                 if (_netPnLE36 > 0) {
            for (uint i; i < _shareProfitAmts.length; ) {
                if (_shareProfitAmts[i] > 0) {
                    ILendingProxy(_lendingProxy).shareProfit(_tokens[i], _shareProfitAmts[i]);
                 }
                     unchecked {
                         ++i;
                      }
                  }
            emit ProfitShared(_posOwner, _posId, _tokens, _shareProfitAmts);
            }

underlyingAmts = new uint[](underlyingTokens.length);
                    for (uint i; i < underlyingTokens.length; ) {
                        underlyingAmts[i] = IERC20(underlyingTokens[i]).balanceOf(address(this));
                             if (underlyingAmts[i] < _params.minUnderlyingAmts[i]) {
                                 revert TokenAmountLessThanExpected(
                         underlyingTokens[i],
                      underlyingAmts[i],
                 _params.minUnderlyingAmts[i]
              );
            }
            _doRefund(underlyingTokens[i], underlyingAmts[i]);
                 unchecked {
                      ++i;
                  }
HIGH severity: TRST-The liquidated person can make the liquidator lose premium by adding collateral in advance. **Description:**
When the position with **debtRatioE18 >= 1e18** or **startLiqTimestamp ! = 0**, the position can 
be liquidated. On liquidation, the liquidator needs to pay premium, but the profit is related 
to the position's health factor and **deltaTime**, and when **discount == 0**, the...

Vulnerable code:
uint deltaTime;
            // 1.1 check the amount of time since position is marked
            if (pos.startLiqTimestamp > 0) {
                 deltaTime = Math.max(deltaTime, block.timestamp - pos.startLiqTimestamp);
            }
            // 1.2 check the amount of time since position is past the deadline
             if (block.timestamp > pos.positionDeadline) {
                    deltaTime = Math.max(deltaTime, block.timestamp - pos.positionDeadline);
            }
            // 1.3 cap time-based discount, as configured
              uint timeDiscountMultiplierE18 = Math.max(
                IConfig(config).minLiquidateTimeDiscountMultiplierE18(),
                     ONE_E18 - deltaTime * IConfig(config).liquidateTimeDiscountGrowthRateE18()
            );
            // 2. calculate health-based discount factor
            uint curHealthFactorE18 = (ONE_E18 * ONE_E18) /
             getPositionDebtRatioE18(_positionManager, _user, _posId);
                 uint minDesiredHealthFactorE18 = IConfig(config).minDesiredHealthFactorE18s(strategy);
            // 2.1 interpolate linear health discount factor (according to the diagram in documentation)
            uint healthDiscountMultiplierE18 = ONE_E18;
             if (curHealthFactorE18 < ONE_E18) {
                 healthDiscountMultiplierE18 = curHealthFactorE18 > minDesiredHealthFactorE18
                     ? ((curHealthFactorE18 - minDesiredHealthFactorE18) * ONE_E18) /
            (ONE_E18 - minDesiredHealthFactorE18)
            : 0;
            }
            // 3. final liquidation discount = apply the two discount methods together
            liquidationDiscountMultiplierE18 =
            (timeDiscountMultiplierE18 * healthDiscountMultiplierE18) /
            ONE_E18;
HIGH severity: TRST-First depositor can steal asset tokens of others. **Description:**
The first depositor can be front run by an attacker and as a result will lose a considerable 
part of the assets provided.
When the pool has no share supply, in `_mintInternal()`, the amount of shares to be minted is 
equal to the assets provided. An attacker can abuse of this...

Vulnerable code:
function _mintInternal(address _receiver, uint _balanceIncreased, uint _totalAsset
             ) internal returns (uint mintShares) {
                unfreezeTime[_receiver] = block.timestamp + mintFreezeInterval;
        if (freezeBuckets.interval > 0) {
             FreezeBuckets.addToFreezeBuckets(freezeBuckets, _balanceIncreased.toUint96());
        }
                 uint _totalSupply = totalSupply();
                    if (_totalAsset == 0 || _totalSupply == 0) {
                     mintShares = _balanceIncreased + _totalAsset;
                 } else {
             mintShares = (_balanceIncreased * _totalSupply) / _totalAsset;
             }
            if (mintShares == 0) {
        revert ZeroAmount();
        }
        _mint(_receiver, mintShares);
        }
HIGH severity: TRST-The attacker can use larger dust when opening a position to perform griefing attacks. **Description:**
When opening a position, unused assets are sent to **dustVault** as dust, but since these dust 
are not subtracted from **inputAmt**, they are included in the calculation of 
**positionOpenUSDValueE36**, resulting in a small **netPnLE36**, which can be used by an 
attacker to...

Vulnerable code:
uint inputTotalUSDValueE36;
                for (uint i; i < openTokenInfos.length; ) {
                 inputTotalUSDValueE36 += openTokenInfos[i].inputAmt * tokenPriceE36s[i];
                      borrowTotalUSDValueE36 += openTokenInfos[i].borrowAmt * tokenPriceE36s[i];
                 unchecked {
            ++i;
                }
            }
                // 1.3 calculate net pnl (including strategy users & borrow profit)
            positionOpenUSDValueE36 = inputTotalUSDValueE36 + borrowTotalUSDValueE36;
            netPnLE36 = positionCurUSDValueE36.toInt256() - positionOpenUSDValueE36.toInt256();

minLpUSDValueE36 = ((inputUSDValueE36 + borrowUSDValueE36) *
               IConfig(_config).posMinLpSlippageMultiplierE18s(strategy)) / ONE_E18;
            // 4. get min & max borrow value cap
            (minBorrowUSDValueE18, maxBorrowUSDValueE18) = 
                 IConfig(_config).getMinMaxCapBorrowUSDValueE18( strategy);
            }
                    return
                        lpUSDValueE36 >= minLpUSDValueE36 &&
HIGH severity: TRST-An attacker can increase liquidity to the position's UniswapNFT to prevent the position from being closed. **Description:**
UniswapV3NPM allows the user to increase liquidity to any NFT.

When closing a position, in `_redeemPosition()`, only the initial liquidity of the NFT will be 
decreased, and then the NFT will be burned.

 If the liquidity of the NFT is not 0, burning will fail.

    
 This allows.....

Vulnerable code:
function increaseLiquidity(IncreaseLiquidityParams calldata params)
                 external payable override checkDeadline(params.deadline)
                    returns (
                     uint128 liquidity, uint256 amount0, uint256 amount1)
            {
            Position storage position = _positions[params.tokenId];
                PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
                    IUniswapV3Pool pool;
                        (liquidity, amount0, amount1, pool) = addLiquidity(

function _redeemPosition(
                    address _user, uint _posId
                     ) internal override returns (address[] memory rewardTokens, uint[] memory rewardAmts) {
                        address _positionManager = positionManager;
                    uint128 collAmt = IUniswapV3PositionManager(_positionManager).getPositionCollAmt(_user, 
                    _posId);
                    // 1. take lp & extra coll tokens from lending proxy
                    _takeAllCollTokens(_positionManager, _user, _posId, address(this));
                         UniV3ExtraPosInfo memory extraPosInfo = IUniswapV3PositionManager(_positionManager)
                             .getDecodedExtraPosInfo(_user, _posId);
                        address _uniswapV3NPM = uniswapV3NPM; // gas saving
                    // 2. remove underlying tokens from lp (internal remove in NPM)
                    IUniswapV3NPM(_uniswapV3NPM).decreaseLiquidity(
                        IUniswapV3NPM.DecreaseLiquidityParams({
                            tokenId: extraPosInfo.uniV3PositionId,liquidity: collAmt, amount0Min: 0,
                    amount1Min: 0,
                         deadline: block.timestamp
                    })
                    );
                    ...
                    // 4. burn LP position
                          IUniswapV3NPM(_uniswapV3NPM).burn(extraPosInfo.uniV3PositionId);
                      }

function burn(uint256 tokenId) external payable override isAuthorizedForToken(tokenId) {
            Position storage position = _positions[tokenId];
                require(position.liquidity == 0 && position.tokensOwed0 == 0 && position.tokensOwed1 == 0,'Not cleared');
             delete _positions[tokenId];
        _burn(tokenId);
        }
MEDIUM severity: TRST-markLiquidationStatus() may cause the liquidator to lose premium. **Description:**
When the **debtRatioE18** of a position is greater than 1 and less than 1.03 (unmark), the 
liquidator can call `markLiquidationStatus()` to accumulate **timeDiscountMultiplierE18** by 
making **pos.startLiqTimestamp == block.timestamp**. The liquidated person can also call...

Vulnerable code:
- if (debtRatioE18 >= ONE_E18 && startLiqTimestamp == 0) {
            + if (debtRatioE18 >= IBasePositionViewer(_positionViewer).unmarkLiqDebtRatioE18() && 
              startLiqTimestamp == 0) {
        // mark liquidatable if the position is unhealthy and is not marked yet
        pos.startLiqTimestamp = block.timestamp.toUint32();
        } else if (
          startLiqTimestamp != 0 &&
              debtRatioE18 < IBasePositionViewer(_positionViewer).unmarkLiqDebtRatioE18()
        ) {
        // unmark liquidatable if the position is already marked and debt ratio falls below "unmark debt ratio"
        pos.startLiqTimestamp = 0;
        } else {
          // revert otherwise
              revert MarkLiquidationStatusFailed();
          }
MEDIUM severity: TRST-SwapHelper.getCalldata should check whitelistedRouters[_router]. **Description:**
`SwapHelper.getCalldata()` returns data for swap based on the input, and uses 
**whitelistedRouters** to limit the **_router** param. The issue here is that when 
`setWhitelistedRouters()` sets the **_routers** state to **false**, it does not reset the data in 
**routerTypes** and.....

Vulnerable code:
for (uint i; i < _statuses.length; ) {
             whitelistedRouters[_routers[i]] = _statuses[i];
                 if (_statuses[i]) {
                 routerTypes[_routers[i]] = _types[i];
        emit SetRouterType(_routers[i], _types[i]);
        }
              emit SetWhitelistedRouter(_routers[i], _statuses[i]);
           unchecked {
        ++i;
        }
        }
MEDIUM severity: TRST-The swap when closing a position does not consider shareProfitAmts. **Description:**
When closing a position, token swap is performed to ensure that the closer can repay the 
debt, for example, when **operation == EXACT_IN**, tokens of **borrowAmt** are required to be 
excluded from the swap, and when **operation == EXACT_OUT**, tokens of **borrowAmt** are...

Vulnerable code:
for (uint i; i < swapParams.length; ) {
        // find excess amount after repay
        uint swapAmt = swapParams[i].operation == SwapOperation.EXACT_IN
          ? IERC20(swapParams[i].tokenIn).balanceOf(address(this)) - openTokenInfos[i].borrowAmt
             : openTokenInfos[i].borrowAmt - IERC20(swapParams[i].tokenOut).balanceOf(address(this));
                 swapAmt = (swapAmt * swapParams[i].percentSwapE18) / ONE_E18
                 if (swapAmt == 0) {
              revert SwapZeroAmount();
             }
MEDIUM severity: TRST-The freeze mechanism reduces the borrowableAmount, which reduces Lender's yield. **Description:**
The contract has two freeze intervals, **mintFreezeInterval** and **freezeBuckets.interval**, the 
former to prevent users from making flash accesses and the latter to prevent borrowers 
from running out of funds. 
Both freeze intervals are applied when a user deposits, and due to.....

Vulnerable code:
function _mintInternal(address _receiver,uint _balanceIncreased, uint _totalAsset
          ) internal returns (uint mintShares) {
             unfreezeTime[_receiver] = block.timestamp + mintFreezeInterval;
          if (freezeBuckets.interval > 0) {
         FreezeBuckets.addToFreezeBuckets(freezeBuckets, _balanceIncreased.toUint96());
        }
LOW severity: TRST-In _takeToken(), when the token address is WETH, the user can only use ETH, not WETH. **Description:**
In _takeToken(), when the token address is WETH, it checks **msg.value == amount**, which 
means that the user can only use ETH and not WETH.

**Recommended Mitigation:**
Change to

**Team response:**
Intended behavior

**Mitigation Review:**
The team says this makes end users not.....

Vulnerable code:
function _takeToken(
        address _gateway,
            address _token,
                address _from,
                     address _to,
        uint128 _amount
             ) internal returns (uint128 amount) {
                - if (_from == _to || (_token == WETH && msg.value != _amount)) {
                     + if (_from == _to || (_token == WETH && (msg.value != _amount || msg.value != 0)) {
                        revert InvalidTakeToken();
        }
        if (_amount > 0) {
            - if (_token == WETH) {
                + if (_token == WETH && msg.value == _amount) {
                    IWETH9(WETH).deposit{value: msg.value}();
            if (_to != address(this)) {
        IERC20(WETH).safeTransfer(_to, msg.value);
        }
                    amount = msg.value.toUint128();
        } else {
                uint balanceBefore = IERC20(_token).balanceOf(address(this));
            IStrategyGateway(_gateway).transmitToken(_token, _from, _to, _amount);
        amount = (IERC20(_token).balanceOf(address(this)) - balanceBefore).toUint128();
            }  
        }
        }
LOW severity: TRST-transferToTreasury() does not check whether the LendingPool has been delisted. **Description:**
`transferToTreasury()` is used to transfer rewards from a delisted LendingPool to Treasury, but 
it does not check if the LendingPool has been delisted, which allows it to transfer rewards 
from any LendingPool to Treasury.

Vulnerable code:
/// @dev transfer fund to treasury (incase: delist lending pool)
        /// @param _token token address to transfer.
        function transferToTreasury(address _token) external override onlyAuthorized(keccak256('exec')) 
             {
        uint totalReward = IERC20(_token).balanceOf(address(this));
        // get treasury address.
            address treasury = ILendingProxy(lendingProxy).treasury();
        // transfer to treasury address.
        IERC20(_token).safeTransfer(treasury, totalReward);
            emit TransferToTreasury(treasury, totalReward);
        }
LOW severity: TRST-feeBPS and lenderLiquidatePremiumBPS need to be limited to a maximum value. **Description:**
feeBPS is used to determine the profit received by the Lending Pool, and 
lenderLiquidatePremiumBPS is used to determine the premium paid by the liquidator, when 
both values are large, the user will suffer a loss, so a reasonable limit should be set for both 
values.

Vulnerable code:
function shareProfit(address _token, uint _profit) external {
          uint toTreasury = (_profit * feeBPS[_token]) / BPS;
             uint toRewardVault = _profit - toTreasury;
        // send to treasury
        IERC20(_token).safeTransferFrom(msg.sender, treasury, toTreasury);
        // send to reward vault
             IERC20(_token).safeTransferFrom(msg.sender, rewardVault, toRewardVault);
        emit ProfitShare(msg.sender, _token, toRewardVault, toTreasury, block.timestamp);
        }
        ...
                uint lenderLiquidatationPremiumBPS = IConfig(config).lenderLiquidatePremiumBPS();
        for (uint i; i < sharingProfitTokenAmts.length; ) {
                 sharingProfitTokenAmts[i] +=
             (pos.openTokenInfos[i].borrowAmt * lenderLiquidatationPremiumBPS) /
        BPS;
        unchecked {
        ++i;
            }
        }