xanoutas/solidity-security-findings huggingface dataset:
Excessive sold asset is not handled properly in SGLLeverage#sellCollateral
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/Singularity.sol#L322
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/SGLLeverage.sol#L117


# Vulnerability details

## Impact

Excessive sold asset is not handled properly in SGLLeverage#sellCollateral

## Proof of Concept

In Singularity, user can [sellCollateral](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/Singularity.sol#L322)

```solidity
    /// @notice Lever down: Sell collateral to repay debt; excess goes to YB
    /// @param from The user who sells
    /// @param share Collateral YieldBox-shares to sell
    /// @param minAmountOut Mininal proceeds required for the sale
    /// @param swapper Swapper to execute the sale
    /// @param dexData Additional data to pass to the swapper
    /// @return amountOut Actual asset amount received in the sale
    function sellCollateral(
        address from,
        uint256 share,
        uint256 minAmountOut,
        ISwapper swapper,
        bytes calldata dexData
    ) external returns (uint256 amountOut) {
        bytes memory result = _executeModule(
            Module.Leverage,
            abi.encodeWithSelector(
                SGLLeverage.sellCollateral.selector,
                from,
                share,
                minAmountOut,
                swapper,
                dexData
            )
        );
        amountOut = abi.decode(result, (uint256));
    }
```

as the comment suggest, the Sell collateral to repay debt; excess goes to yield box

but when we are calling [sell Collateral](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/SGLLeverage.sol#L117) in SGLLeverage module

```solidity
   uint256 shareOut;
        (amountOut, shareOut) = swapper.swap(
            swapData,
            minAmountOut,
            from,
            dexData
        );
        // As long as the ratio is correct, we trust `amountOut` resp.
        // `shareOut`, because all money received by the swapper gets used up
        // one way or another, or the transaction will revert.
        require(amountOut >= minAmountOut, "SGL: not enough");
        uint256 partOwed = userBorrowPart[from];
        uint256 amountOwed = totalBorrow.toElastic(partOwed, true);
        uint256 shareOwed = yieldBox.toShare(assetId, amountOwed, true);
        if (shareOwed <= shareOut) {
            _repay(from, from, false, partOwed);
        } else {
            //repay as much as we can
            uint256 partOut = totalBorrow.toBase(amountOut, false);
            _repay(from, from, false, partOut);
        }
```

as we can see, the swapped amount is amountOut

if the case of shareOwed <= shareOut

```solidity
if (shareOwed <= shareOut) {
	_repay(from, fro
Reentrancy vulnerability in Penrose.executeMarketFn
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/Penrose.sol#L424-L450


# Vulnerability details

## Impact
This vulnerability could allow an attacker to withdraw funds from the Penrose contract. This could result in a loss of funds for the user.

## Proof of Concept
The Penrose.executeMarketFn function has external calls inside a loop. This could potentially lead to reentrancy attacks, as an attacker could call the function, then call back to the contract before the loop has finished executing. This could allow the attacker to withdraw funds from the contract without actually executing the function.

The attacker deposits funds into the Penrose contract.
The attacker calls the Penrose.executeMarketFn function, passing in an array of addresses and an array of data.
The attacker then calls back to the Penrose contract before the loop has finished executing.
The attacker withdraws the funds from the Penrose contract.
The Penrose contract does not execute the function, and the attacker keeps the funds.

## Tools Used
VScode

## Recommended Mitigation Steps
The functions Penrose.executeMarketFn This could potentially lead to reentrancy attacks, as an attacker could call the function, then call back to the contract before the loop has finished executing. This could allow the attacker to withdraw funds from the contract without actually executing the function.

The following are some ways to mitigate the risk of reentrancy attacks in these functions:

The functions could be modified to not allow callbacks to the contract.
The contracts could implement a reentrancy guard. A reentrancy guard is a variable that is set to true when the contract is executing a function that is vulnerable to reentrancy attacks. The function will then check the reentrancy guard before it calls another contract. If the reentrancy guard is true, the function will not call the other contract.
The contracts could use a timelock. A timelock is a mechanism that prevents a contract from executing a function for a specified period of time. This would prevent the attacker from calling back to the contract before the reentrancy guard has had a chance to reset.
It is important to note that there is no foolproof way to prevent reentrancy attacks. However, by following the best practices outlined above, you can significantly reduce the risk of your contracts being vulnerable to these attacks.




## Assessed type

Reentrancy
Liquidation using `_orderBookLiquidation()` may still result in unhealthy accounts
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/markets/singularity/SGLLiquidation.sol#L97


# Vulnerability details

### Impact
Users who are liquidated through the `_orderBookLiquidation()` function call in the `liquidate()` function in `SGLLiquidation.sol` may still have insolvent accounts. Hence, they need to be liquidated again through the `liquidate()` function. This results in potential fund loss for users who buy the collateral. 

### Proof of Concept
An account is not solvent when the debt to collateral ratio exceeds 75% (as per spec).

During the `_orderBookLiquidation()` function call, the insolvent account's debt is liquidated to 25% of the collateral, the difference being calculated from the `_computeAssetAmountToSolvency()` function call on line 110. This amount is removed from the accounts debt on line 123. Then, the equivalent value in collateral (+ some fees) is removed from the account's collateral on line 133. The resulting debt to collateral ratio is hence (D-V)/(C-(V+F)), where V is the amount removed, D is the initial debt, F is the fee, and C is the initial collateral. This amount is not guaranteed to be healthy; the final ratio may still exceed 75%.


Users who call `liquidate()` are entitled to liquidate enough collateral until the account becomes healthy. A correct design should allow users to reach this condition in one call. With the current design, users may have to __call multiple times__, missing the best market values of collaterals, let alone wasting gas.

We demonstrate this bug in the following POC, which can be deployed and run.

#### Preconditions:
In order to run the POC, two preconditions need to be satisfied. The core reason for having these preconditions is that a `liquidationQueue` is necessary to invoke `_orderBookLiquidation`. However, to save space in the POC code which is already quite substantial, we apply these preconditions so that the code can run.
```solidity
... 
if (true) { //@audit: Change to true
    uint256 needed = 0;
    for (uint256 i = 0; i < maxBorrowParts.length; i++) {
        needed += maxBorrowParts[i];
    }
    if (true) {  //@audit: Change to true
        _orderBookLiquidation(
...
```
1. Change the conditions on line [43](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/SGLLiquidation.sol#L43) and line [45](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/SGLLiquidation.sol#L48) to true. These conditions essentially guard the `_orderBookLiquidation()` function call and do not have any impact on the vulnerability. For simplicity and space reasons, we remove them to save space for the actual POC code.

```solidity
...
uint256 returnedShare = yieldBox.balanceOf(address(this), assetId) -
            uint256(totalAsset.elastic);
uint256 extraShare = 0; //@audit: Change to 0
...
```
2. Additional
Reentrancy vulnerability in BaseUSDO._executeOnDestination
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/usd0/BaseUSDO.sol#L375-L397


# Vulnerability details

## Impact
The BaseUSDO._executeOnDestination function is vulnerable to reentrancy attacks. Reentrancy 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 tricked into performing unintended actions.
In the case of BaseUSDO._executeOnDestination, the function calls the _executeModule function. The _executeModule function can call back to the BaseUSDO contract if the module being executed is a callback module. This could allow an attacker to withdraw funds from the BaseUSDO contract without actually executing the module.

## Proof of Concept
This vulnerability could allow an attacker to withdraw funds from the BaseUSDO contract. This could result in a loss of funds for the user.
The following is a proof of concept for the vulnerability:
The attacker deposits funds into the BaseUSDO contract.
The attacker calls the BaseUSDO._executeOnDestination function, passing in the address of a callback module.
The callback module calls back to the BaseUSDO contract.
The attacker withdraws the funds from the BaseUSDO contract.
The BaseUSDO contract does not execute the module, and the attacker keeps the funds.

## Tools Used
VScode
## Recommended Mitigation Steps
The _executeModule function could be modified to not allow callbacks to the BaseUSDO contract.
The BaseUSDO contract could implement a reentrancy guard.
The BaseUSDO contract could use a timelock.


## Assessed type

Reentrancy
Unsafe price query for GLP
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/023751a4e987cf7c203ab25d3abba58f7344f213/contracts/oracle/implementations/GLPOracle.sol#L19


# Vulnerability details

## Impact
GLPOracle uses:
```
 glpManager.getPrice({maximise: false});
```
so it always gives minimum price of GLP

USDC has >40% weightage in GLP pool, so if USDC were to depeg to "x" (x < 1), GLP min price would use "x" while GLP max price will be basis USDC price of 1$ due to [strict stable pricing](https://github.com/gmx-io/gmx-contracts/blob/0b5a5371a77bf584298d46958543a1405dd1c403/contracts/core/VaultPriceFeed.sol#L48-L53) and hence `glpManager.getPrice(false)` is not awalys correct.

If GLP transfer is from user to protocol => it should be valued using min price
If GLP transfer is from protocol to user => it should be valued using max price

## Proof of Concept
- https://hackmd.io/@Xbg00EiJQ-a4a46Yn6SZJA/H1xJW1qhi
- It has happened in recent USDC depeg

## Tools Used
Manual Review

## Recommended Mitigation Steps
add boolean in data field to specify min or max (https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/023751a4e987cf7c203ab25d3abba58f7344f213/contracts/oracle/implementations/GLPOracle.sol#L25)


## Assessed type

Oracle
A revert will occur in `removeAsset` method when a user has a lending allowance from a lender in some cases
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/Singularity.sol#L226


# Vulnerability details

## Impact
A revert will occur in `removeAsset` method when a user has a lending allowance from a lender. Here is an example to demonstrate the case:

1. Bob has _yieldBoxShares[from][ASSET_SIG] = 1000e18
2. Bob gives 1000e18 lending allowance to Alice (or an operator) to remove the asset.
3. Later, Alice tries to remove the asset but the method revert. 

Thus, the protocol is not working as intended. Users can not delegate removing assets unless they give a big allowance to work around the issue. Check the PoC to understand the Issue. 


## Proof of Concept

In `removeAsset()`, share is is returned by `_removeAsset`. Then, _allowedLend checks for the allowance 
 ```solidity
function removeAsset(
        address from,
        address to,
        uint256 fraction
    ) public notPaused returns (uint256 share) {
        _accrue();
        share = _removeAsset(from, to, fraction, true);
        _allowedLend(from, share);
    }
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/markets/singularity/Singularity.sol#L226) 

The issue starts from `_removeAsset()` method. which could return a share that is bigger than  `_yieldBoxShares[from][ASSET_SIG]` .

Let's take a look at `_removeAsset()` method :
```solidity
    function _removeAsset(
        address from,
        address to,
        uint256 fraction,
        bool updateYieldBoxShares
    ) internal returns (uint256 share) {
        if (totalAsset.base == 0) {
            return 0;
        }
        Rebase memory _totalAsset = totalAsset;
        uint256 allShare = _totalAsset.elastic +
            yieldBox.toShare(assetId, totalBorrow.elastic, true);
        share = (fraction * allShare) / _totalAsset.base;
        balanceOf[from] -= fraction;
        emit Transfer(from, address(0), fraction);
        _totalAsset.elastic -= uint128(share);
        _totalAsset.base -= uint128(fraction);
        require(_totalAsset.base >= 1000, "SGL: min limit");
        totalAsset = _totalAsset;
        emit LogRemoveAsset(from, to, share, fraction);
        yieldBox.transfer(address(this), to, assetId, share);
        if (updateYieldBoxShares) {
            if (share > _yieldBoxShares[from][ASSET_SIG]) {
                _yieldBoxShares[from][ASSET_SIG] = 0; //some assets accrue in time
            } else {
                _yieldBoxShares[from][ASSET_SIG] -= share;
            }
        }
    }


    /// @dev Return the equivalent of collateral borrow part in asset amount.
    function _getAmountForBorrowPart(
        uint256 borrowPart
    ) internal view returns (uint256) {
        return totalBorrow.toElastic(borrowPart, false);
    }
}
```
[Code link](https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/2286f80f928f41c8bc189d0657d74ba83286c668/contracts/ma
ERC1155 tokenOut doesn't have approve and it will cause a revert in swap at UniswappV3Swapper
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/UniswapV3Swapper.sol#L195-L204


# Vulnerability details

## Impact
YieldBox's `approve` method doesn't exist as well as for any ERC1155. Therefore, In case the tokenOut is ERC1155, it will cause a revert in `swap` method of UniswappV3Swapper. This is because an `approve` call is made before depositing into YieldBox 
```
	if (swapData.yieldBoxData.depositToYb) {
			_safeApprove(tokenOut, address(yieldBox), amountOut);
			(, shareOut) = yieldBox.depositAsset(
				swapData.tokensData.tokenOutId,
				address(this),
				to,
				amountOut,
				0
			);
		}
```
[Code link](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/UniswapV3Swapper.sol#L195-L204)


## Proof of Concept

There is a call done in `swap` method to safe approve
```solidity
	_safeApprove(tokenOut, address(yieldBox), amountOut);
```
[Code link](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/UniswapV3Swapper.sol#L197): 

_safeApprove basically calls `approve()` 
 ```solidity
	abi.encodeWithSelector(0x095ea7b3, to, value)
```
[Code link](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/BaseSwapper.sol#L100) :

But `approve(to,amount)` method is not implemented in YieldBox.sol contract since it is ERC1155. 

`swap` function would fail because `_safeApprove()` require the call to succeed. Thus. it will revert causing a revert in `swap` method of UniswappV3Swapper:
```solidity
require(
            success && (data.length == 0 || abi.decode(data, (bool))),
            "BaseSwapper::safeApprove: approve failed"
        );
```
 [Code link](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/BaseSwapper.sol#L102-L105)

This happen in case tokenOut is ERC1155 token
```solidity
	// Compute outputs
	amountOut = swapRouter.exactInputSingle(params);
	if (swapData.yieldBoxData.depositToYb) {
		_safeApprove(tokenOut, address(yieldBox), amountOut);
		(, shareOut) = yieldBox.depositAsset(
			swapData.tokensData.tokenOutId,
			address(this),
			to,
			amountOut,
			0
		);
	}
```
[Code link](https://github.com/Tapioca-DAO/tapioca-periph-audit/blob/main/contracts/Swapper/UniswapV3Swapper.sol#L195-L204)

Please note this is applicable on other type of swappers (i.e. UniswappV2Swapper and CurveSwapper)


## Tools Used
Manual Review

## Recommended Mitigation Steps

Avoid calling `_safeApprove()` if the token is ERC1155


## Assessed type

Other
State variable used in cross-function reentrancies
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/markets/singularity/SGLLendingCommon.sol#L58-L80
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/markets/Market.sol#L51
https://github.com/Tapioca-DAO/tapioca-bar-audit/blob/master/contracts/markets/Market.sol#L57


# Vulnerability details

## Impact
The SGLStorage.totalAsset and Market.totalBorrow state variables are 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 SGLBorrow.borrow() function.

## Proof of Concept

If exploited, this vulnerability could allow an attacker to modify the SGLStorage.totalAsset and Market.totalBorrow state variables. This could allow the attacker to borrow more asset than they are entitled to, or to prevent other users from borrowing asset.

## Tools Used
VScode

## Recommended Mitigation Steps
The following mitigation steps can be taken to address this vulnerability:

The SGLStorage.totalAsset and Market.totalBorrow state variables should be made immutable.
Recommendation:

I recommend that the following mitigation step be taken to address this vulnerability:

The SGLStorage.totalAsset and Market.totalBorrow state variables should be made immutable. This can be done by adding the onlyOwner modifier to the SGLStorage.totalAsset and Market.totalBorrow state variables.



## Assessed type

Reentrancy
twTAP not getting burn after  TAP withdrawal 
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/governance/twTAP.sol#L522
https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/governance/twTAP.sol#L373
https://github.com/Tapioca-DAO/tap-token-audit/blob/59749be5bc2286f0bdbf59d7ddc258ddafd49a9f/contracts/governance/twTAP.sol#L252


# Vulnerability details

## Impact
User who wants to un-stake their Token Should burn their twTAP NFT. So they don't have access to the fees generate by TAPIOCA DAO anymore. 

## Proof of Concept
- Alice is Staking some TAP tokens thought the "Participate" function.
- After at least One Week (if she set that min duration), she can un-stake her TAP tokens thought 
  the "releaseTap" function.
- She still has the twTAP NFT token as it hasn't been burned.

## Tools Used
Manual Review

## Recommended Mitigation Steps
Don't forget to burn the twTAP Token before sending back the stake TAP. 


## Assessed type

ERC721
Missing update in 'borrow' Function of SGLborrow.sol Leads to Inaccurate Exchange Rates
Audit: 2023-07-tapioca-findings
Severity: MEDIUM

# Lines of code

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


# Vulnerability details

## Description
The 'SGLborrow.sol' contract contains a critical vulnerability in the 'borrow' function, which omits calling the 'updateExchangeRate' method before initiating the borrowing process. This oversight can result in inaccurate exchange rates being used during the borrow transaction, potentially leading to unfair loan terms and financial losses for users.

The 'updateExchangeRate' method is essential for keeping the exchange rates up-to-date, ensuring accurate and fair conversions of assets. By not calling this method before executing the borrow process, the contract might use outdated exchange rates, leading to incorrect calculations for loan amounts and repayment terms.

This vulnerability can be exploited by malicious users to manipulate the borrow process in their favor, taking advantage of the inaccurate exchange rates. It can lead to discrepancies in loan repayments, affecting the platform's financial stability and credibility.

## Mitigation
To address this vulnerability, it is crucial to include a call to the 'updateExchangeRate' method at the beginning of the 'borrow' function. This ensures that the exchange rates are current before any borrowing calculations take place. By incorporating this update, the contract can maintain accuracy and fairness in its financial operations.


## Assessed type

Other