xanoutas/solidity-security-findings huggingface dataset:
Users can prevent themselves from getting liquidated indefinitely
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L140-L188
https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L104-L111


# Vulnerability details

## Impact

The logic for liquidating a user involves calling `_decreaseUserShare` for the user being liquidated, as their entire WBTC-WETH collateral is being taken. However, in this call, the parameter `useCooldown`=true, which means that you must wait until `cooldownExpiration` is reached for a user. A malicious user who is getting liquidated can then abuse this by front-running all calls to liquidate themselves by adding a very small amount of LP such that `cooldownExpiration` is incremented, making the liquidation call revert. This action can be done indefinitely to prevent liquidations forever at very low cost.

## Proof of Concept

When a user is getting liquidated, the liquidator will call [`CollateralAndLiquidity:liquidateUser`](https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L140-L188), which is defined as follows:
```solidity
function liquidateUser( address wallet ) external nonReentrant
    {
    require( wallet != msg.sender, "Cannot liquidate self" );

    // First, make sure that the user's collateral ratio is below the required level
    require( canUserBeLiquidated(wallet), "User cannot be liquidated" );

    uint256 userCollateralAmount = userShareForPool( wallet, collateralPoolID );

    // Withdraw the liquidated collateral from the liquidity pool.
    // The liquidity is owned by this contract so when it is withdrawn it will be reclaimed by this contract.
    (uint256 reclaimedWBTC, uint256 reclaimedWETH) = pools.removeLiquidity(wbtc, weth, userCollateralAmount, 0, 0, totalShares[collateralPoolID] );

    // Decrease the user's share of collateral as it has been liquidated and they no longer have it.
@>    _decreaseUserShare( wallet, collateralPoolID, userCollateralAmount, true );
    
    ...
    }
```

As can be seen, there is a call to `_decreaseShare` with the parameter `useCooldown` set to `true`. Let's see how this is used in the [`StakingRewards:_decreaseUserShare`](https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L104-L111) function call:
```solidity
if ( useCooldown )
if ( msg.sender != address(exchangeConfig.dao()) ) // DAO doesn't use the cooldown
    {
@>    require( block.timestamp >= user.cooldownExpiration, "Must wait for the cooldown to expire" );

    // Update the cooldown expiration for future transactions
    user.cooldownExpiration = block.timestamp + stakingConfig.modificationCooldown();
    }
```

As can be seen, `(block.timestamp >= user.cooldownExpiration)` is required, else this call will fail. Although this is already an issue in itself, a malicious user can abuse this to continuously increase `user.cooldownExpiration` every time there is an attempt to liquidate them by simply calling
User can prevent liquidators from closing their unhealthy positions
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L154


# Vulnerability details

# Bug Description

In the Salty protocol, the USDS stablecoin is collateralized by WETH/WBTC. Users deposit collateral and borrow USDS based on their collateral's value. A liquidation mechanism activates if the collateral value falls below a set threshold (110% by default).

Unfortunately this measurement can be DOSd, forcing the protocol to incur bad debt. To liquidate a user the `liquidateUser()` function must be called. This function then calls to the  `_decreaseUserShare()` function.  The `_decreaseUserShare()` function gets called with the `useCooldown` parameter set to true. 

```solidity
if ( useCooldown )
if ( msg.sender != address(exchangeConfig.dao()) ) // DAO doesn't use the cooldown
	{
	require( block.timestamp >= user.cooldownExpiration, "Must wait for the cooldown to expire" );

	// Update the cooldown expiration for future transactions
	user.cooldownExpiration = block.timestamp + stakingConfig.modificationCooldown();
	}
```

If this parameter is set, all calls to `_decreaseUserShare()` will revert while the cooldown has not passed. The cooldown will be restarted everytime a call to `_increaseUserShare()` happens. So if a user detects that his position will be liquidated, he can frontrun the liquidation and quickly deposit a small amount of collateral restarting his cooldown. The cooldown duration is DAO controlled and can be in the range of 15min-6h. Subsequently the liquidation attempt will revert and the users position will be save from liquidation for up to 6 hours. When the cooldown runs out the user can keep monitoring the mempool and frontrun any new liquidation attempts using the same technique.

# Impact

This vulnerability allows users to indefinitely delay liquidation, potentially leading the protocol to incur bad debt. If exploited on a large scale, it could result in the USDS stablecoin becoming undercollateralized, risking a depeg.

# Proof of Concept

The exploit scenario is as follows:

- Alice deposits collateral and borrows USDS.
- Bob attempts to liquidate Alice's position when it becomes vulnerable.
- Alice sees Bob's transaction in the mempool and front-runs it by depositing a small amount of collateral.
- Bob's liquidation attempt fails due to the reset cooldown.

```solidity
function testLiquidateImpossible() public {

	//------------------------- SETUP --------------------------------------------------

	// Total needs to be worth at least $2500
	uint256 depositedWBTC = ( 1000 ether *10**8) / priceAggregator.getPriceBTC();
	uint256 depositedWETH = ( 1000 ether *10**18) / priceAggregator.getPriceETH();

	(uint256 reserveWBTC, uint256 reserveWETH) = pools.getPoolReserves(wbtc, weth);

	//------------------------ TEST---------------------------------------------------------

	// Alice will deposit collateral
	vm.startPrank(alice);
	collateralAndLiquidity.depositCollateralAn
Excess burning of USDC
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/53516c2cdfdfacb662cdea6417c52f23c94d5b5b/src/stable/CollateralAndLiquidity.sol#L125-L128


# Vulnerability details

## Impact
Users have the liberty to borrow USDS by providing overcollaterized asset(WBTC/WETH), this asset can with returned back when the user repays the loan, when a user defaults, the user asset are then liquidated and converted to USDS to be burnt. 

In the case of liquidation, after another user helps to liquidate an underwater loan for a commission, the asset are sent to Liquidizer contract, when ```collateralAndLiquidity.liquidizer().performUpkeep();``` is called, the liquidizer contract sells the required collaterals that have been sent to convert them to USDS, then calls ```_possiblyBurnUSDS();``` to burn all ```usdsThatShouldBeBurned```
``` solidity
	// This corresponds to USDS that was borrowed by users who had their collateral liquidated.
	// Because liquidated collateral no longer exists, the borrowed USDS needs to be burned in order to "undo" the collateralized position
	// and return state back to where it was before the liquidated user deposited collateral and borrowed USDS.
	uint256 public usdsThatShouldBeBurned;
```
as stated by the comment above ```usdsThatShouldBeBurned``` corresponds to usds that was borrowed by users that had their collateral liquidated.

The problem here is that users that did not have their collateral liquidated also increases the value. This should not be so because in ```repayUSDS()``` borrowed USDS have been transferred to USDS contract to be burnt internally 
``` solidity
// Have the user send the USDS to the USDS contract so that it can later be burned (on USDS.performUpkeep)
usds.safeTransferFrom(msg.sender, address(usds), amountRepaid);
// Have USDS remember that the USDS should be burned
liquidizer.incrementBurnableUSDS( amountRepaid );
```
The implication will be that, the liquidizer will burn excess USDS than it should

## Proof of Concept
Let us consider a case of two borrowers; Alice and Bob
1. Alice and Bob takes a loan of 1_000_000 USDS each with the right amount of shares to keep the loan overcollaterized.
2. Alice calls repayUSDS() to pay back all the debt, 1_000_000 USDS was sent to USDS contract and ```usdsThatShouldBeBurned``` was still incremented by same amount. 
So USDS balance in USDS contract is 1,000,000 and ```usdsThatShouldBeBurned``` is 1,000,000
``` solidity
// Have the user send the USDS to the USDS contract so that it can later be burned (on USDS.performUpkeep)
usds.safeTransferFrom(msg.sender, address(usds), amountRepaid);
// Have USDS remember that the USDS should be burned
liquidizer.incrementBurnableUSDS( amountRepaid );
```
3. Bob's loan on the other hand became undercollateralized and due for liquidation.
4. Greg then called the ```liquidateUser()``` to liquidate Bob, Bob's collaterals are pulled out and sent to the Liquidizer contract, and ```usdsThatShouldBeBurned``` is increased by anothe
No slippage protection, so attackers can build bots to front-run trades
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolUtils.sol#L54
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolUtils.sol#L67
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L382-L391
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L387
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L350-L353
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L235
 https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L257-L268


# Vulnerability details

## Impact
No slippage protection, so attackers can build bots to front-run trades

## Proof of Concept
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolUtils.sol#L54

When `_placeInternalSwap` is executed, the parameters are only pools, token address, amountIn, maximumInternalSwapPercentTimes1000. The problem occurs here:
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/PoolUtils.sol#L67

```solidity
        swapAmountOut = pools.depositSwapWithdraw(
            tokenIn,
            tokenOut,
            amountIn,
            0,
            block.timestamp
        );
```
In the `pools.depositSwapWithdraw` function, the fourth parameter is `minAmountOut`. Setting it to 0 here will cause attackers to monitor the memory pool and exploiting slippage to steal funds.

### Next, I prove that this vulnerability can be exploited by an attacker:
The value of swapAmountOut depends on the pools.depositSwapWithdraw function. In the depositSwapWithdraw function, we can see that the fourth parameter is set to 0.
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L382-L391
```solidity
    function depositSwapWithdraw(
        IERC20 swapTokenIn,
        IERC20 swapTokenOut,
        uint256 swapAmountIn,
        uint256 minAmountOut,
        uint256 deadline
    )
        external
        nonReentrant
        ensureNotExpired(deadline)
        returns (uint256 swapAmountOut)
```
The value of `swapAmountOut` depends on the `_adjustReservesForSwapAndAttemptArbitrage` function,minAmountOut==0
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L387
```solidity
        swapAmountOut = _adjustReservesForSwapAndAttemptArbitrage(
            swapTokenIn,
            swapTokenOut,
            swapAmountIn,
            minAmountOut
        );
```
The value of swapAmountOut depends on the `_adjustReservesForSwapAndAttemptArbitrage` function, `minAmountOut`==0 will allow hackers to manipulate the value of `swapAmountOut`, so that `swapAmountOut` is just >= `minAmountOut`, resulting in a very small number of tokens output and a large amount of profits.
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L350-L353
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#L235
https://github.com/code-423n4/2024-01-salty/blob/main/src/pools/Pools.sol#
Saltys Oracle can be manipulated to crash WETH or WTBC value and liquidate users if other pricefeed fails
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/price_feed/PriceAggregator.sol#L108-L146


# Vulnerability details

# Bug Description

In the Salty protocol, a combination of bugs across various components can be exploited by a malicious actor to liquidate healthy positions in the market. This complex exploit involves manipulating price feed and exploiting low liquidity in Salty's AMM pools.

## 1. Stablecoin

The protocol's USDS stablecoin, pegged to WBTC and WETH, allows users to deposit collateral and mint USDS. If the collateral value falls below 110%, the positions are subject to liquidation.
 
## 2. Pricefeeds

Salty protocol's valuation of collateral relies on three price feeds: Salty's own USDS/WBTC pool, Uniswap's USDC/WBTC pool, and Chainlink's USD/BTC price feed. The system requires at least two of these feeds to provide non-zero values for a valid price assessment. However, a critical flaw arises when either Chainlink's feed returns an incorrectly low value or the Uniswap TWAP returns an incorrectly low value. This could happen due to a flash crash. The protocol's price aggregator, in this case would be picking the 2 closest prices to calculate the average price.

```solidity
function _aggregatePrices( uint256 price1, uint256 price2, uint256 price3 ) internal view returns (uint256)
	{
	uint256 numNonZero;

	if (price1 > 0)
		numNonZero++;

	if (price2 > 0)
		numNonZero++;

	if (price3 > 0)
		numNonZero++;

	// If less than two price sources then return zero to indicate failure
	if ( numNonZero < 2 )
		return 0;

	uint256 diff12 = _absoluteDifference(price1, price2);
	uint256 diff13 = _absoluteDifference(price1, price3);
	uint256 diff23 = _absoluteDifference(price2, price3);

	uint256 priceA;
	uint256 priceB;

	if ( ( diff12 <= diff13 ) && ( diff12 <= diff23 ) )
		(priceA, priceB) = (price1, price2);
	else if ( ( diff13 <= diff12 ) && ( diff13 <= diff23 ) )
		(priceA, priceB) = (price1, price3);
	else if ( ( diff23 <= diff12 ) && ( diff23 <= diff13 ) )
		(priceA, priceB) = (price2, price3);

	uint256 averagePrice = ( priceA + priceB ) / 2;

	// If price sources are too far apart then return zero to indicate failure
	if (  (_absoluteDifference(priceA, priceB) * 100000) / averagePrice > maximumPriceFeedPercentDifferenceTimes1000 )
		return 0;

	return averagePrice;
	}
```

So that means that if an attacker is able to use the moment of the flash crash in chainlink or uniswap and reduce the salty price very low, he will put the aggregator into a state where it returns a average price which is very close to zero (ignoring the 3rd normal price). Using this low price the attacker can now liquidate before healthy position.

## 3. Manipulating Salty

As salty is a very new AMM, liquidity in pools will be very low at the start. This would allow an attacker to get a flashloan and use it to destabilize the USDS/WBTC pool (which we will consider our attack vector for this case) until it has a very low price. If this is do
Potential USDS Value Loss Due to Shortfall in Protocol
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/Liquidizer.sol#L115-L126


# Vulnerability details

## Impact
There's a risk that `USDS` may lose its 1:1 parity, leading to many undercollateralized positions that become impossible to liquidate due to inadequate incentives. 

## Proof of Concept
The protocol includes a safeguard for shortfalls, such as those that might occur in undercollateralized liquidations, by withdrawing a small percentage of Protocol Owned Liquidity (POL) and converting it to USDS for burning.

When `Liquidizer::performUpkeep` is called, it triggers `_possiblyBurnUSDS`. However, if the percentage of the protocol's assets is insufficient to maintain stability, and `usdsBalance < usdsThatShouldBeBurned`, the protocol's response is:

```solidity
dao.withdrawPOL(salt, usds, PERCENT_POL_TO_WITHDRAW);
dao.withdrawPOL(dai, usds, PERCENT_POL_TO_WITHDRAW);
```
This withdrawal does not immediately convert to USDS for burning and relies on the next call of `Liquidizer::performUpkeep`. It doesn't verify if the 1% withdrawn is sufficient. In extreme circumstances, this approach could lead to a breakdown in the protocol, potentially causing a loss in USDS value due to not burning the necessary amount.


## Tools Used
Manual code review 

## Recommended Mitigation Steps

Ensure the protocol has adequate solvency to handle such situations. Implement mechanisms that can promptly respond and mitigate risks, guaranteeing enough assets are converted to USDS for burning when necessary


## Assessed type

Math
A malicious user can receive more liquidity rewards than usual.
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L243


# Vulnerability details

## Impact
A malicious user can deposit recursively by using borrowed USDS so he can receive more reward than usual.
And this can decrease value of USDS.


## Proof of Concept
A user can borrow USDS in proportion of deposited collateral.
But a malicious user can swap USDS to collateral and deposit recursively.
This means that he can deposit at most about 150% of collateral than initially owned.
But liquidity reward is calculated only in proportion of collateral regardless of borrowed amount.
So he can receive more reward than usual.

`StakingRewards.sol#userRewardForPool` function which calculates liquidity reward for a user is as follows.
```solidity
	function userRewardForPool( address wallet, bytes32 poolID ) public view returns (uint256)
		{
		// If there are no shares for the pool, the user can't have any shares either and there can't be any rewards
		if ( totalShares[poolID] == 0 )
			return 0;

		UserShareInfo memory user = _userShareInfo[wallet][poolID];
		if ( user.userShare == 0 )
			return 0;

		// Determine the share of the rewards for the user based on their deposited share
243		uint256 rewardsShare = ( totalRewards[poolID] * user.userShare ) / totalShares[poolID];

		// Reduce by the virtualRewards - as they were only added to keep the share / rewards ratio the same when the used added their share

		// In the event that virtualRewards exceeds rewardsShare due to precision loss - just return zero
		if ( user.virtualRewards > rewardsShare )
			return 0;

		return rewardsShare - user.virtualRewards;
		}
```
As we can see above, the amount of reward is related only to the value of collateral.

On the other hand, there is not interest on loan and no swap fee so a malicious user to deposit recursively doesn't have any problem.

This can decrease usds by rate of [wbtc/usds], [weth/usds] pool.

## Tools Used
Manual Review

## Recommended Mitigation Steps
There can be two ways to mitigate this problem.
1. We can distribute rewards corresponding to `[deposited - borrowed]`.
2. We can apply interest on loan.



## Assessed type

Other
Users can use cooldown to avoid liquidation
Audit: 2024-01-salty-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-01-salty/blob/main/src/stable/CollateralAndLiquidity.sol#L153-L154
https://github.com/code-423n4/2024-01-salty/blob/main/src/staking/StakingRewards.sol#L64-L71


# Vulnerability details

## Issue Description

When a user's collateral value falls below the required threshold to maintain their debt (borrowed USDS), their position becomes liquidatable (`canUserBeLiquidated(wallet) == true`), and any user can call the `liquidateUser` function to liquidate it.

In the `liquidateUser` function, after removing the provided liquidity from the pool, a call to `_decreaseUserShare` is made to reduce the user's (borrower's) shares associated with their deposited collateral:

```solidity
// Withdraw the liquidated collateral from the liquidity pool.
// The liquidity is owned by this contract so when it is withdrawn, it will be reclaimed by this contract.
(uint256 reclaimedWBTC, uint256 reclaimedWETH) = pools.removeLiquidity(
    wbtc,
    weth,
    userCollateralAmount,
    0,
    0,
    totalShares[collateralPoolID]
);

// Decrease the user's share of collateral as it has been liquidated and they no longer have it.
//@audit can the user use cooldown to prevent liquidation
_decreaseUserShare(
    wallet,
    collateralPoolID,
    userCollateralAmount,
    true
);
```

The `_decreaseUserShare` function is invoked with `useCooldown == true`, which means that the following code will be run in the call:

```solidity
if (useCooldown)
    if (
        msg.sender != address(exchangeConfig.dao())
    ) // DAO doesn't use the cooldown
    {
        require(
            block.timestamp >= user.cooldownExpiration,
            "Must wait for the cooldown to expire"
        );

        // Update the cooldown expiration for future transactions
        user.cooldownExpiration =
            block.timestamp +
            stakingConfig.modificationCooldown();
    }
```

This is used to set a delay between each increase/decrease shares transaction.

A borrower can exploit this cooldown feature to avoid getting liquidated. When the borrower sees that their position has become liquidatable, they can front-run the liquidation call and make a call to the `depositCollateralAndIncreaseShare` function to increase their shares only by providing a very small amount of collateral (an amount greater or equal to the `DUST` amount to avoid a revert in `Pools.addLiquidity`) and also triggering the cooldown period by doing so.

When the liquidator's call goes through, it will revert because the cooldown hasn't passed yet, and the borrower can keep their unhealthy position and avoid liquidation. This process can be repeated as many times as the borrower wants.

This issue can result in a deppeg of the USDS token and the accrual of bad debt in the protocol, as a user can practically avoid the liquidation of their unhealthy position forever at no cost (depositing each time the DUST amount of collateral).

## Impact

Users can avoid liquidation