xanoutas/solidity-security-findings huggingface dataset:
Missing Fee Parameter in pairFor Function Leads to Incorrect Pair Address Calculation
Audit: 2024-10-ronin-findings
Severity: MEDIUM

# Lines of code

https://github.com/ronin-chain/katana-v3-contracts/blob/03c80179e04f40d96f06c451ea494bb18f2a58fc/src/periphery/libraries/KatanaV2Library.sol#L18-L32


# Vulnerability details

## Summary
The pairFor function in the `KatanaV2Library` lacks a fee parameter, which is essential for calculating pair addresses in exchanges with varying fee tiers.

## Vulnerability Details
Does not account for fee tiers, potentially leading to incorrect pair address calculations.
```javascript
     17   // calculates the CREATE2 address for a pair without making any external calls
    //@audit lack of fee param
     18   function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
     19     (address token0, address token1) = sortTokens(tokenA, tokenB);
     20     pair = address(
     21       uint256(
     22         keccak256(
     23           abi.encodePacked(
     24             hex"ff",
     25             factory,
     26             keccak256(abi.encodePacked(token0, token1)),
     27             hex"e85772d2fe4ad93037659afaee57751696456eb5dd99987e43f3cf11c6e255a2" // init code hash
     28           )
     29         )
     30       )
     31     );
     32   }
```

## Impact
Without a fee parameter, the function may generate incorrect addresses for pairs with different fee structures.

## Tools Used
Manual Review

## Recommendations
Modify the pairFor function to include a fee parameter.


## Assessed type

Other
katanaV3SwapCallback and _swap() do not deal with the recurisve base case of empty path well, leading to non-function for v3SwapExactOutput.
Audit: 2024-10-ronin-findings
Severity: MEDIUM

# Lines of code

V3SwapRouter.sol#L50-L78
V3SwapRouter.sol#L191-L202


# Vulnerability details

## Proof of Concept

The katanaV3SwapCallback() function is designed to handle two types of swaps in a multi-hop transaction: exact input swaps and exact output swaps. This function is called by each pool involved in the swap and processes each hop differently depending on whether the swap is an exact input or exact output swap.

```javascript
  function katanaV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external { /
    if (amount0Delta <= 0 && amount1Delta <= 0) revert V3InvalidSwap(); // swaps entirely within 0-liquidity regions are not supported
    (, address payer) = abi.decode(data, (bytes, address));
    bytes calldata path = data.toBytes(0); // first argument, path, second argument: payer

    // because exact output swaps are executed in reverse order, in this case tokenOut is actually tokenIn
    (address tokenIn, uint24 fee, address tokenOut) = path.decodeFirstPool();

    if (computePoolAddress(tokenIn, tokenOut, fee) != msg.sender) revert V3InvalidCaller();  // possiblel collision attack

    (bool isExactInput, uint256 amountToPay) =
      amount0Delta > 0 ? (tokenIn < tokenOut, uint256(amount0Delta)) : (tokenOut < tokenIn, uint256(amount1Delta));

    if (isExactInput) {
      // Pay the pool (msg.sender)
      payOrPermit2Transfer(tokenIn, payer, msg.sender, amountToPay);
    } else {
      // either initiate the next swap or pay
      if (path.hasMultiplePools()) {
        // this is an intermediate step so the payer is actually this contract
        path = path.skipToken();
        _swap(-amountToPay.toInt256(), msg.sender, path, payer, false);  // isExactIn = false
      } else {
        if (amountToPay > maxAmountInCached) revert V3TooMuchRequested();
        // note that because exact output swaps are executed in reverse order, tokenOut is actually tokenIn
        payOrPermit2Transfer(tokenOut, payer, msg.sender, amountToPay);
      }
    }
  }
```

In an exact output swap case, the katanaV3SwapCallback() function is designed to work backward through each pool in the swap path to ensure that the user receives the exact amount of the final output token specified, while keeping the input token spent within a specified maximum for the first pool. This approach allows each pool in the swap sequence to provide just enough input for the exact output amount needed for the next pool in the chain.


In the exact output swap process, the case where the path is empty marks the final stage of the recursive backward calculations. The problem lies in the following line when path is empty, note that path.hasMultiplePools is true for the last path but path becomes empty after skipToken() call:

```javascript
 if (path.hasMultiplePools()) { // 
        // this is an intermediate step so the payer is actually this contract
        path = path.skipToken();
@        _swap(-amountToPay.toInt256(), msg.sender, path, pay
Precision Loss in `getAmountOut` Function Leads to Incorrect Price Calculations
Audit: 2024-10-ronin-findings
Severity: MEDIUM

# Lines of code

https://github.com/ronin-chain/katana-v3-contracts/blob/03c80179e04f40d96f06c451ea494bb18f2a58fc/src/periphery/libraries/KatanaV2Library.sol#L46


# Vulnerability details

## Summary
The `getAmountOut` function in `KatanaV2Library.sol` can return zero due to precision loss when there's a large disparity between `reserveIn` and `reserveOut`. While this doesn't directly cause loss of funds, it can lead to incorrect price calculations that affect other protocol functions.

## PoC
```javascript
function testPrecisionLoss() public {
    uint256 amountIn = 100;
    uint256 reserveIn = 20000;
    uint256 reserveOut = 100;
    
    
    uint256 amountInWithFee = 100 * 997;                    // = 99,700
    uint256 numerator = 99,700 * 100;                       // = 9,970,000
    uint256 denominator = (20000 * 1000) + 99,700;         // = 20,099,700
    uint256 amountOut = 9,970,000 / 20,099,700;            // = 0 (0.496 rounded down)
    
    // User loses all expected output
    assert(amountOut == 0);  // True, demonstrating the issue
}
```


## Assessed type

Other
Insufficient input validation may lead to loss of funds due to invalid or malicious input data.
Audit: 2024-10-ronin-findings
Severity: MEDIUM

# Lines of code

https://github.com/ronin-chain/katana-operation-contracts/blob/27f9d28e00958bf3494fa405a8a5acdcd5ecdc5d/src/aggregate-router/base/Dispatcher.sol#L50


# Vulnerability details

## Proof of Concept
Insufficient Input Validation: The contract relies on user-supplied data but does not adequately validate these inputs, such as recipient or amount. Malicious or erroneous inputs could result in unintended transactions or loss of funds.

Proof of Concept: If a user provides a 0x0 recipient address, funds could be sent to an invalid address, effectively burning them. Another example would be overly high values for slippage in swap functions (amountOutMin).
User calls dispatch with recipient set to 0x0.
Funds are transferred to a zero address, making them unrecoverable.

Impact: Loss of funds due to invalid or malicious input data.

## Recommended Mitigation Steps
1. Validate recipient addresses and ensure they are not set to 0x0.
2. Set upper/lower bounds for amounts, slippage tolerances, and other sensitive parameters.
3. Implement sanity checks for other inputs to ensure they are within acceptable ranges.

Example:In Solidity, failing to validate addresses has led to several incidents of funds being sent to 0x0.





## Assessed type

Invalid Validation
Missing access control override in BoringFactory
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/Penrose.sol#L414-L421


# Vulnerability details

## Impact
```
    function addBigBang(
        address mc,
        address _contract
    ) external onlyOwner registeredBigBangMasterContract(mc) {
        isMarketRegistered[_contract] = true;
        clonesOf[mc].push(_contract);
        emit RegisterBigBang(_contract, mc);
    }
```
allows only onlyOwner to call this function, while `deploy()` is parent function is not gated and can be called by anyone and it will push new deployment to `masterContractOf` and `clonesOf` mapping directly.

This impacts `_getMasterContractLength` including those directly deployed clones too

## Proof of Concept
N/A

## Tools Used
Manual Review

## Recommended Mitigation Steps
Override with Access Control


## Assessed type

Access Control
TOFT in (m)TapiocaOft contracts can be stolen by calling removeCollateral() with a malicious removeParams.market
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/BaseTOFT.sol#L190
https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/BaseTOFT.sol#L516
https://github.com/Tapioca-DAO/tapiocaz-audit/blob/bcf61f79464cfdc0484aa272f9f6e28d5de36a8f/contracts/tOFT/modules/BaseTOFTMarketModule.sol#L230-L231


# Vulnerability details

## Impact
The `TOFT` available in the `TapiocaOFT` contract can be stolen when calling `removeCollateral()` with a malicious market.

## Proof of Concept
`(m)TapiocaOFT` inherit `BaseTOFT`, which has a function `removeCollateral()` that accepts a market address as an argument. This function calls `_lzSend()` internally on the source chain, which then is forwarded to the destination chain by the relayer and calls `lzReceive()`. 

`lzReceive()` reaches `_nonBlockingLzReceive()` in `BaseTOFT` and delegate calls to the `BaseTOFTMarketModule` on function `remove()`. This function approves `TOFT` to the `removeParams.market`  and then calls function `removeCollateral()` of the provided market. There is no validation whatsoever in this address, such that a malicious market can be provided that steals all funds, as can be seen below:
```solidity
function remove(bytes memory _payload) public {
    ...
    approve(removeParams.market, removeParams.share); // no validation prior to this 2 calls
    IMarket(removeParams.market).removeCollateral(
        to,
        to,
        removeParams.share
    );
    ...
}
```

The following POC in Foundry demonstrates this vulnerability, the attacker is able to steal all `TOFT` in `mTapiocaOFT`:
```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.18;

import {Test, console} from "forge-std/Test.sol";

import {TapiocaOFT} from "contracts/tOFT/TapiocaOFT.sol";
import {BaseTOFTMarketModule} from "contracts/tOFT/modules/BaseTOFTMarketModule.sol";

import {IYieldBoxBase} from "tapioca-periph/contracts/interfaces/IYieldBoxBase.sol";
import {ISendFrom} from "tapioca-periph/contracts/interfaces/ISendFrom.sol";
import {ICommonData} from "tapioca-periph/contracts/interfaces/ICommonData.sol";
import {ITapiocaOFT} from "tapioca-periph/contracts/interfaces/ITapiocaOFT.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract MaliciousMarket {
    address public immutable attacker;
    address public immutable tapiocaOft;

    constructor(address attacker_, address tapiocaOft_) {
        attacker = attacker_;
        tapiocaOft = tapiocaOft_;
    } 

    function removeCollateral(address, address, uint256 share) external {
        IERC20(tapiocaOft).transferFrom(msg.sender, attacker, share);
    }
}

contract TapiocaOFTPOC is Test {
    address public constant LZ_ENDPOINT = 0x66A71Dcef29A0fFBDBE3c6a460a3B5BC225Cd675;
    uint16 internal constant PT_MARKET_REMOVE_COLLATERAL = 772;

    function test_POC_StealAllAssetsInTapiocaOFT_RemoveCollateral_Ma
Miusing empty string insteaf of oracleData in updateExchangeRate
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/Market.sol#L341


# Vulnerability details

## Impact
contract has this storage:
```
    /// @notice oracleData
    bytes public oracleData;
```

```
        (updated, rate) = oracle.get("");
```

Instead of `oracle.get("")`, it should use proper oracle data which was set otherwise it can lead to undefined behavior where oracle expects data.


## Proof of Concept
N/A (covered in above section)

## Tools Used
Manual Review

## Recommended Mitigation Steps
use `oracleData` instead of empty string


## Assessed type

Other
The USDOOptionsModule contract's exercise function allows for dangerous call delegation
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L138-L204


# Vulnerability details

## Impact

The [USDOOptionsModule contract](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L16) is a module that is used by the [BaseUSDO contract](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/BaseUSDO.sol#L46) to facilitate functionality for oTap actions. The module functionality is invoked through the invocation of a [delegatecall](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/BaseUSDO.sol#L369) within the BaseUSDO contract's _executeModule function. Additionally, the BaseUSDO contract facilitates the functionality to execute an oTap 'exercise' action on the USDOOptionsModule contract of a [destination block chain](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/BaseUSDO.sol#L464-L478). The function [encodes](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/BaseUSDO.sol#L467) the selector for the USDOOptionsModule contract's [exercise function](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L138) as part of the call data so that the function is invoked once the message has been received by the BaseUSDO contract implementation on the destination block chain. However, the exercise function's [delegatecall](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L174) is problematic because the function invokes a delegatecall on any address that is defined as the [module](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L139) parameter without restriction. Because this is also a public function, a malicious user can pass the address for a contract they have deployed that has functionality with-in it to manipulate the contract's state. For example, this module contract inherits from the [LzApp contract](https://github.com/Tapioca-DAO/tapioca-sdk/blob/f1ec0fdc944f2f6360c43cb8c95a08cb337b0b43/src/contracts/lzApp/LzApp.sol#L14) which inherits Open Zeppelin's Ownable contract. This means that a contract can be deployed that includes a function with the correct [signature](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/modules/USDOOptionsModule.sol#L175-L184) with functionality that will update the owner to another address which will then be invoked when the respective contract's address is defined a
Incorrect parameter for getCallerReward might return 0 reward despite insolvency
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L577


# Vulnerability details

## Impact

The calculation of the caller reward uses an incorrect value. If the exchangeRate remains the same but a lot of interest accrues, then there will be no liquidation reward. Without a liquidation reward borrowing positions will not get liquidated and incur bad debt for the protocol.

## Proof of Concept
The function `Market._getCallerReward` is called in `BigBang._liquidateUser` like this:

```js
//BigBang
uint256 callerReward = _getCallerReward(
    userBorrowPart[user],
    startTVLInAsset,
    maxTVLInAsset
);

//Market
    function _getCallerReward(
        uint256 borrowed,
        uint256 startTVLInAsset,
        uint256 maxTVLInAsset
    ) internal view returns (uint256) {
        if (borrowed == 0) return 0;
        if (startTVLInAsset == 0) return 0;

        if (borrowed < startTVLInAsset) return 0;
```

The issue is that `userBorrowPart` does not describe the actual borrowed value/debt, but just his share of the total debt. The part does not increase over time through accruals (opposed to the elastic value which the part describes). 

A simple example:
- user provides 1 ETH collateral at a current price of 2000$
- with a collateralization rate of 75% the user can borrow up to 1500$
- he borrows 1499$. assuming no interest has accrued yet, his `userBorrowPart` will also be 1499 (ignoring 1e18 factor for simplicity).
- now interest accrues and users actual debt will increase. He is insolvent at 1500, but in the code above `if (borrowed < startTVLInAsset) return 0` will always be true, because  `borrowed` will always be 1499 (`startTVLInAsset` is point of insolvency, here 1500).

## Tools Used

Manual review

## Recommended Mitigation Steps

Use the elastic value (users debt consisting of borrowed amount + interest)


## Assessed type

Other
borrowInternal() of BaseTOFTMarketModule.sol has phantom permit functions
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapiocaz-audit/blob/master/contracts/tOFT/modules/BaseTOFTMarketModule.sol#L293-#L303


# Vulnerability details

## Impact

A malicious actor could steal funds from a User who has already done his first deposit. 

## Proof of Concept

Consider the case where attacker uses a token with phantom permit function as collateral, the most famous ones being WETH, BNB, HEX etc. Let’s consider WETH in this example.  
Attack Scenario: 
Attacker calls borrowInternal() of BaseTOFTMarketModule.sol, with carefully inputted `approvals` parameters to skip the first 2 “if” conditions of `_callApproval` function. 
Subsequently, performing IERC20Permit(approvals[i].target).permit() functionality with Attacker inputted variables where attacker inputs approvals[i].owner to an account who has already deposited some tokens through the functionality in order 
BaseTOFTMarketModule.sol:borrowInternal()->MagnetarMarketModule.sol: _depositAddCollateralAndBorrowFromMarket -> MagnetarMarketModule.sol: _extractTokens  
 
Thereby confirming Users approval of the token for the MagnetarMarketModule.sol 
 
3) As Weth doesn’t have a permit function , permit() call will get directed to it’ fallback  
function. Weth has a fallback functionality as below:  
```solidity  
 
function() public payable { 
 
deposit(); 
 
} 
 
 
function deposit() public payable { 
 
balanceOf[msg.sender] += msg.value; 
 
Deposit(msg.sender, msg.value); 
 
} 

 
```  
This function is called when receiving ETH (and just deposits it, to mint wrapped ETH with it) but, crucially, is also called when an undefined function is invoked on the WETH contract. All WETH of all such users can be stolen and deposited onBehalf of the Attacker. 
 
Another Vulnerability of the Tokens with Phantom Permits: 
Unexpected reverts are caused by IERC20Permit(approvals[i].target).permit() by tokens like stETH which does not have a permit function. 


## Tools Used

Manual Review

## Reference 

https://media.dedaub.com/phantom-functions-and-the-billion-dollar-no-op-c56f062ae49f 


## Recommended Mitigation Steps

Setting a precedent of explicitly reverting when calling unsupported permit functions can help ensure Protocol and it’s Trusted Users do not suffer from calling phantom permits. 



## Assessed type

Other
Re-entrancy in flash minting USDO can bypass max checks
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/usd0/USDO.sol#L81-L94


# Vulnerability details

## Impact
```
    function flashLoan(
        IERC3156FlashBorrower receiver,
        address token,
        uint256 amount,
        bytes calldata data
    ) external override notPaused returns (bool) {
        // @audit re-enter and mint
        require(token == address(this), "USDO: token not valid");
        require(maxFlashLoan(token) >= amount, "USDO: amount too big");
        require(amount > 0, "USDO: amount not valid");
        uint256 fee = flashFee(token, amount);
        _mint(address(receiver), amount);

        require(
            receiver.onFlashLoan(msg.sender, token, amount, fee, data) ==
                FLASH_MINT_CALLBACK_SUCCESS,
            "USDO: failed"
        );
        ...
```
`maxFlashLoan` prevents flash minting more than specificed amount but attacker can re-enter flash-mint from `onFlashLoan` to mint more and bypass checks

Due to this breach, high enough flash mint could off balance the on chain liquidity.

## Proof of Concept
N/A (covered in above section)

## Tools Used
Manual Review

## Recommended Mitigation Steps
Add Re-entrancy guard


## Assessed type

Reentrancy
Setting debtStartPoint > 0 breaks many BigBang actions
Audit: 2023-07-tapioca-findings
Severity: HIGH

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L192


# Vulnerability details

## Impact
If `BigBang.debtStartPoint` is set to a value > 0, many core features will break, e.g. deposits of collateral will be possible, but removal not, which would effectively lock collateral inside the contract.

## Proof of Concept
`BigBang.getDebtRate` uses the variable `debtStartPoint` to offset the starting point where interest begins to rise:

```js
uint256 debtPercentage = ((_currentDebt - debtStartPoint) *
    DEBT_PRECISION) / (_maxDebtPoint - debtStartPoint); 
```

`debtStartPoint`, which is only set once during `init`, would be greater than `_currentDebt` after initialization and cause an underflow. All actions that depend on `getDebtRate` would be unusable, These actions are: 
borrow, repay, buyCollateral, sellCollateral, liquidate, and removeCollateral. Adding collateral is however still possible, making it possible for collateral to get locked.

This has not been discovered during testing as all tests currently set `debtStartPoint` to 0:

```js
bigBangRegData = await registerBigBangMarket(
    mediumRiskBigBangMC.address,
    yieldBox,
    bar,
    wbtc,
    wbtcAssetId,
    usd0WethOracle,
    ethers.utils.parseEther('1'),
    ethers.utils.parseEther('0.5'),
    ethers.utils.parseEther('0.005'),
    ethers.utils.parseEther('0.035'),
    0, // -> debtStartPoint
    staging,
);
```
## Tools Used

Manual Review

## Recommended Mitigation Steps

The following check is missing: `if (_currentDebt < debtStartPoint) return minDebtRate;`





## Assessed type

Other