xanoutas/solidity-security-findings huggingface dataset:
No protection for the protocol swap on _liquidateUser which could cause a possible fund loss
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Impact
No protection for the protocol swap when _liquidateUser() is called, and that could cause loss for the protocol (For example, because of a sandwich attack occurrence ..etc).

## Proof of Concept

In `liquidate()` method, `_closedLiquidation()` is called which will call `_liquidateUser()` to handle the liquidations of insolvent users.
As you see below there is a swap done after building `swapData` 
```solidity
        uint256 minAssetMount = 0;
        if (_dexData.length > 0) {
            minAssetMount = abi.decode(_dexData, (uint256));
        }


        uint256 balanceBefore = yieldBox.balanceOf(address(this), assetId);


        ISwapper.SwapData memory swapData = swapper.buildSwapData(
            collateralId,
            assetId,
            0,
            collateralShare,
            true,
            true
        );
        swapper.swap(swapData, minAssetMount, address(this), "");
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L603-L618)

This is to swap the collateral to asset in order to pay the debt. However, swap here is relying on `swapData` and `minAssetMount`, and if  `_dexData` is empty then `minAssetMount` will be zero 
```solidity
uint256 minAssetMount = 0;
        if (_dexData.length > 0) {
            minAssetMount = abi.decode(_dexData, (uint256));
        }
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L603-L606)

if minAssetMount happened to be zero then the result of swap might has loss  unexpectedly since there is no protection for how minimum amount the protocol should receive from swapping.
```solidity
swapper.swap(swapData, minAssetMount, address(this), "");
```

Note: Attacker could be even the liquidator him/her self

## Tools Used
Manual Review


## Recommended Mitigation Steps
Check `minAssetMount` if it is zero after _dexData condition, then set a dynamic default value for it (based on the exchange rate) before the swap. 
For example, 70% at least of the value as a minimum. Otherwise, it will be a loss for the protocol. 
Please consider making the percentage configurable in case you need to update it in future.





## Assessed type

Other
Vesting.sol : `data.revoked` is not implemented. Owner can not revoke for registered user.
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/Vesting.sol#L139


# Vulnerability details

## Impact
Owner can not stop an user from claiming the vested funds. Owner wanted to do that because that user turns into malicious or the user's account compromised.

## Proof of Concept

Vesting contract has a variable `data.revoked` which is set false while registering an user by the owner by calling the [registerUser](https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/Vesting.sol#L130) function.
once the user is vested by certain amount of funds, the user can claim after the vesting period is passed using the [claim()](https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/Vesting.sol#L110C14-L110C21). 
When we look at the `claim()`, 'revoke' is not used.

    function claim() external nonReentrant {
        if (start == 0 || seeded == 0) revert NotStarted();
        uint256 _claimable = claimable(msg.sender);
        if (_claimable == 0) revert NothingToClaim();


        _totalClaimed += _claimable;
        users[msg.sender].claimed += _claimable;
        users[msg.sender].latestClaimTimestamp = block.timestamp;


        token.safeTransfer(msg.sender, _claimable);
        emit Claimed(msg.sender, _claimable);
    }


## Tools Used

## Recommended Mitigation Steps

We suggest add a new function to revoke from claim for an user.
Verify this before allowing claim for that user.


## Assessed type

Error
Reentrancy vulnerability in SGLBorrow.borrow()
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/markets/singularity/SGLBorrow.sol#L21-L36


# Vulnerability details

## Impact
The warning you provided is about a potential reentrancy vulnerability in the SGLBorrow.borrow() function. A reentrancy vulnerability occurs when a contract calls another contract, and the second contract calls back to the first contract before the first contract has finished executing. This can allow the first contract to be called multiple times, which could lead to unintended consequences.

In the case of the SGLBorrow.borrow() function, the warning is about the _borrow() function. This function borrows an amount of asset from the contract and transfers it to the user. The _borrow() function calls the yieldBox.transfer() function to transfer the asset to the user. The yieldBox.transfer() function could call back to the _borrow() function if it is interrupted, such as by a low gas limit. If the yieldBox.transfer() function calls back to the _borrow() function, the _borrow() function could be executed multiple times, which could lead to the user borrowing more asset than they are entitled to.

The warning also mentions that the Market.totalBorrow and Market.userBorrowPart state variables can be used in cross-function reentrancies. This means that these state variables could be modified by another contract, which could then be used to exploit the reentrancy vulnerability in the borrow() function.

## Proof of Concept

The proof of concept for this vulnerability is provided in the following GitHub links:

    function borrow(
        address from,
        address to,
        uint256 amount
    ) public notPaused solvent(from) returns (uint256 part, uint256 share) {
        if (amount == 0) return (0, 0);
        uint256 allowanceShare = _computeAllowanceAmountInAsset(
            from,
            exchangeRate,
            amount,
            asset.safeDecimals()
        );
        _allowedBorrow(from, allowanceShare);


        (part, share) = _borrow(from, to, amount);
    }

The proof of concept can be executed by creating a new contract that calls the borrow() function with a large amount of asset. The yieldBox.transfer() function will then be called to transfer the asset to the contract. If the yieldBox.transfer() function is interrupted, it will call back to the _borrow() function. The _borrow() function will then be executed again, and the contract can borrow more asset. This process can be repeated until the user's collateral is depleted.

The following code shows how the vulnerability could be exploited:

contract Attacker {
    function exploit() {
        // Create a contract with the SGLBorrow contract
        address borrowContract = address(0x...);

        // Call the borrow function with a negative amount
        borrowContract.borrow(msg.sender, msg.sender, -1000000);
    }
}

## Tools Used
Solidity language analyzer
## Recommended Mitigation Steps
To address these v
Front-run grief in `participate` preventing people from gaining voting power
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/options/TapiocaOptionBroker.sol#L240


# Vulnerability details

### Impact
The call `participate` checks for `hasVotingPower` which is computed in this way:
https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/options/TapiocaOptionBroker.sol#L240

```solidity
        bool hasVotingPower = _amount >=
            computeMinWeight(pool.totalDeposited, MIN_WEIGHT_FACTOR);
```

This is based on [`pool.totalDeposited`](https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/options/TapiocaOptionBroker.sol#L261) which is not the total supply of `tOLP` but rather the totalSupply of `tOLP` that is voting

This creates a scenario of non-idempotency for the different combination of:
- Whale Deposits first vs Fish Deposits Second
- Fish Deposits First vs Whale Deposit Second

A small deposit may pass the `hasVotingPower` at the beginning

However, a Whale, could front-run such a call (or multiple calls), as a way to get more relative voting power

### POC

If a Whale Deposits First, only they can vote

-> Big whale locks more and gains voting power
-> Small fish was expecting to be able to vote it but can't

### Mitigation Step

Consider either using totalSupply to determine a fixed cap on voting or try using a "totalSupply by Epoch" mechanism that removes race conditions




## Assessed type

Invalid Validation
computeClosingFactor will revert if collateralizationRate is greater than 89285 (approx 89%)
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L790-L796
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/Market.sol#L290-L293


# Vulnerability details

## Impact
`computeClosingFactor` method returns the maximum liquidatable amount for user. In BigBang, this method is called by `_updateBorrowAndCollateralShare` which is basically called by other methods on liquidation as follows:
 `liquidate` => `_closedLiquidation` => `_liquidateUser` => `_updateBorrowAndCollateralShare` => `computeClosingFactor`
 
Here is code of `_updateBorrowAndCollateralShare` method where it calls `computeClosingFactor`
```solidity
	function _updateBorrowAndCollateralShare(
		address user,
		uint256 maxBorrowPart,
		uint256 _exchangeRate
	)
	.
	.
	uint256 availableBorrowPart = computeClosingFactor(
		userBorrowPart[user],
		collateralPartInAsset,
		borrowAssetDecimals,
		collateralDecimals,
		FEE_PRECISION_DECIMALS
	);
	.
	.
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/bigBang/BigBang.sol#L790-L796)
 
 
The issue is `computeClosingFactor` **will revert if collateralizationRate is greater than 89285**. The revert is due to Arithmetic over/underflow or Division by 0 (depending on how higher the collateralizationRate is). This causes callers (methods of liquidations) to fail as well. Eventually, blocking liquidation functionality completely which is obviously harmful to the protocol and any party that benefit from it (e.g. liquidators).

Here is an explanation how this is caused:
The denominator is calculated as follows
```solidity
        uint256 denominator = ((10 ** ratesPrecision) -
            (collateralizationRate *
                ((10 ** ratesPrecision) + liquidationMultiplier)) /
            (10 ** ratesPrecision)) * (10 ** (18 - ratesPrecision));
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/Market.sol#L290-L293)


For simplicity, assume 
B = (collateralizationRate * ((10 ** ratesPrecision) + liquidationMultiplier))
A = (10 ** ratesPrecision)

Now, B is deducted from A. 
if B > A, then the method will revert.   
if A == B,  denominator will be zero. therefore, a division by zero occurs later. 



Please note that this is also applicable to other markets (e.g. Singularity) since they inherit from Market contract. Check `SGLLiquidation._updateBorrowAndCollateralShare` Line: 225


## Proof of Concept
- When you run the PoC below, you will get the following results:
```sh
[PASS] test_colRate_greater_than_89286_will_revert() (gas: 34473)
Logs:
  collateralizationRate 89286: revert with Division or modulo by 0
  collateralizationRate 9000: revert with Arithmetic over/underflow

[PASS] test_colRate_lower_than_89286_works() (gas: 31540)
Logs:
The catch block of the try...catch of `GlpStrategy.sol#_vestByGlp()` should revert.
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-yieldbox-strategies-audit/blob/05ba7108a83c66dada98bc5bc75cf18004f2a49b/contracts/glp/GlpStrategy.sol#L273


# Vulnerability details

## Impact
Unwanted state update after an error that is supposed to revert execution.

## Proof of Concept
The try...catch block calls the `gmxVester.withdraw()` function and it is supposed to revert on error according to the comment above the try...catch statement. The try block will be prevent the revert and allow the develop call the `revert()` function in the catch block. However the catch block is empty and not reverting which will lead to unwanted state update.

```
function _vestByGlp() private {
        uint256 freeEsGmx = esGmx.balanceOf(address(this));
        uint256 stakedEsGmx = stakedGmxTracker.depositBalances(
            address(this),
            address(esGmx)
        );
        uint256 available = freeEsGmx + stakedEsGmx;
        uint256 vestable = _getVestableAmount(
            glpVester,
            available,
            stakedGlpTracker.balanceOf(address(this))
        );
        if (vestable == 0) {
            return;
        }

        if (vestable > freeEsGmx) {
            // Reverts if the total of (tokens vesting) + (tokens claimable) is
            // zero, which is possible if the reward distributor ran out of
            // esGMX. Rather than calculating this twice, let the call fail:
            try gmxVester.withdraw() {} catch {}//@audit should revert.
            gmxRewardRouter.unstakeEsGmx(vestable - freeEsGmx);
        }

        glpVester.deposit(vestable);
    }
```

## Tools Used
Manual Review

## Recommended Mitigation Steps
Call the revert function in the catch block so that the function reverts when call to `gmxVester.withdraw()` reverts.
```
try gmxVester.withdraw() {} catch {revert() }
```


## Assessed type

Error
`deadline` CHECK IS MISSING IN THE `swap` TRANSACTIONS OF THE `CurveSwapper` CONTRACT
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/CurveSwapper.sol#L94-L142
https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/CurveSwapper.sol#L146-L167


# Vulnerability details

## Impact

The `CurveSwapper.swap` function uses the `Curve pool` for the `swap` transactions. But the problem here is that there is no `deadline` parameter used for these `swap` operations when using the `CurveSwapper` contract unlike in the case of `UniswapV2Swapper` contract and `UniswapV3Swapper` contract.

Since the `CurveSwapper.swap` function does not implement the `deadline` check, the `swap` transactions which uses the `Curve pool` could get delayed in the `mempool` due to low transaction gas provided or due to network congestion and could be executed at a later point in time which could be disadvantageous to the `user` who initiated the `swap` operation.

## Proof of Concept

```solidity
    function swap(
        SwapData calldata swapData,
        uint256 amountOutMin,
        address to,
        bytes memory data
    ) external override returns (uint256 amountOut, uint256 shareOut) {
        // Get Curve tokens' indexes & addresses
        uint256[] memory tokenIndexes = abi.decode(data, (uint256[]));
        address tokenIn = curvePool.coins(tokenIndexes[0]);
        address tokenOut = curvePool.coins(tokenIndexes[1]);

        // Get tokens' amounts
        (uint256 amountIn, ) = _getAmounts(
            swapData.amountData,
            swapData.tokensData.tokenInId,
            swapData.tokensData.tokenOutId,
            yieldBox
        );

        // Retrieve tokens from sender or from YieldBox
        amountIn = _extractTokens(
            swapData.yieldBoxData,
            yieldBox,
            tokenIn,
            swapData.tokensData.tokenInId,
            amountIn,
            swapData.amountData.shareIn
        );

        // Swap & compute output
        amountOut = _swapTokensForTokens(
            int128(int256(tokenIndexes[0])),
            int128(int256(tokenIndexes[1])),
            amountIn,
            amountOutMin
        );
        if (swapData.yieldBoxData.depositToYb) {
            _safeApprove(tokenOut, address(yieldBox), amountOut);
            (, shareOut) = yieldBox.depositAsset(
                swapData.tokensData.tokenOutId,
                address(this),
                to,
                amountOut,
                0
            );
        } else {
            IERC20(tokenOut).safeTransfer(to, amountOut);
        }
    }
```

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/CurveSwapper.sol#L94-L142

```solidity
    function _swapTokensForTokens(
        int128 i,
        int128 j,
        uint256 amountIn,
        uint256 amountOutMin
    ) private returns (uint256) {
        address tokenIn = curvePool.coins(uint256(uint128(i)));
        address tokenOut = curvePool.coins(uint256(uint128(j)));

        uint256 outputAmo
Missing sequencer online check for chainlink L2 feeds on arbitrum
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/023751a4e987cf7c203ab25d3abba58f7344f213/contracts/oracle/utils/ChainlinkUtils.sol#L33


# Vulnerability details

## Impact

If the Arbitrum sequencer were to go offline the Chainlink oracle may return an invalid/stale
price. It should always be checked before consuming any data from Chainlink.

The Chainlink docs(https://docs.chain.link/data-feeds/l2-sequencer-feeds) on L2 Sequencer Uptime Feeds specify more details.

## Proof of Concept
If there is non-trivial downtime, oracle implementation is susceptible to stale price which can be maliciously used to extract value from protocol.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Check sequencer uptime before consuming any price data.


## Assessed type

Oracle
slippage sandwich attack
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-yieldbox-strategies-audit/blob/05ba7108a83c66dada98bc5bc75cf18004f2a49b/contracts/lido/LidoEthStrategy.sol#L151


# Vulnerability details

## Impact
The impact is related to a hard-coded slippage of 2.5% in the protocol. This slippage allows MEV bots to always execute a sandwich attack on the withdrawer. A sandwich attack is a type of front-running where the attacker (MEV bot) inserts their transaction between the user's transaction, manipulating the pool price to their advantage and causing the depositor to experience slippage.

## Proof of Concept
1. A user initiates a transaction to withdraw funds from the protocol as usual.
2. The MEV bot identifies the user's transaction and quickly inserts its own transaction to manipulate the pool price.
3. The user unknowingly withdraws funds at the manipulated price, incurring slippage.
4. The MEV bot then manipulates the price back to normal, profiting from the slippage experienced by the depositor.

## Recommended Mitigation Steps
- user should set slippage and strategy check the slippage (<2.5%)


## Assessed type

MEV
no withdraw functionality (contract locking ether)
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-yieldbox-strategies-audit/blob/05ba7108a83c66dada98bc5bc75cf18004f2a49b/contracts/balancer/BalancerStrategy.sol#L301


# Vulnerability details

## Impact
Sending money by calling a function that is payable but does not have a withdraw function wall cause the funds to be stuck forever.

## Proof of Concept
https://github.com/Tapioca-DAO/tapioca-yieldbox-strategies-audit/blob/05ba7108a83c66dada98bc5bc75cf18004f2a49b/contracts/balancer/BalancerStrategy.sol#L301
```
    receive() external payable{}
```
the receive function above lacks the functionality to withdraw funds and hence permanently causes the funds sent by calling the function to get lost/stuck forever, If a user loses their funds in this manner, the confidence of the users may decrease.

## Recommended Mitigation Steps
adding a withdraw function to the receive function


## Assessed type

Payable
Oracle Data is insufficiently Validated
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/a3b45512580f8a76be45c19f635689f48c0128c3/contracts/oracle/implementations/SGOracle.sol#L51


# Vulnerability details

## Impact

Stale Data from the oracle.

## Proof of Concept

Chainlink oracles updates the value when one of the 2 Trigger happens:

1. If `Deviation Threshold` exceeds. Deviation Threshold is the specific amount of deviation in prices which happens after which a new aggregation round will start and the price will be updated.
2. If `Heartbeat Threshold` exceeds. Heartbeat Threshold is a specific amount of time from the last update after which a new aggregation round will start and the price will be updated.
Link: https://docs.chain.link/architecture-overview/architecture-decentralized-model/

But in `_get()` function, Heartbeat Threshold is not implemented to check whether the last observed price was stale or not.

```solidity
File: SGOracle.sol

    function _get() internal view returns (uint256 _maxPrice) {
        uint256 lpPrice = (SG_POOL.totalLiquidity() *
            uint256(UNDERLYING.latestAnswer())) / SG_POOL.totalSupply();

        return lpPrice;
    }

```
[Link to code](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/a3b45512580f8a76be45c19f635689f48c0128c3/contracts/oracle/implementations/SGOracle.sol#L51)

Chainlink in there docs have clearly written: "Your application should track the latestTimestamp variable or use the updatedAt value from the latestRoundData() function to make sure that the latest answer is recent enough for your application to use it. If your application detects that the reported answer is not updated within the heartbeat or within time limits that you determine are acceptable for your application, pause operation or switch to an alternate operation mode while identifying the cause of the delay."

So by adding the require check as recommended, it can stop the protocol from using the stale price which is even recommended by chainlink.

Link: https://docs.chain.link/data-feeds/#check-the-timestamp-of-the-latest-answer

## Tools Used

VS Code

## Recommended Mitigation Steps

As Chainlink recommends:

> Your application should track the latestTimestamp variable or use the updatedAt value from the latestRoundData() function to make sure that the latest answer is recent enough for your application to use it. If your application detects that the reported answer is not updated within the heartbeat or within time limits that you determine are acceptable for your application, pause operation or switch to an alternate operation mode while identifying the cause of the delay.

Recommended Mitigation:

Add the Following 3 more require Statements in the `_get()` method to mitigate the issue after setting the HEARTBEAT_PERIOD as set by chainlink.

```diff
File: SGOracle.sol

    function _get() internal view returns (uint256 _maxPrice) {

+       (uint80 roundID, int256 price, , uint256 updatedAt, uint80 answeredInRound) = UNDE