xanoutas/solidity-security-findings huggingface dataset:
Distributor.distribute sends less amount that it should because of rounding
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
Distributor.distribute sends less amount that it should because of rounding. It first divides and then multiplies.

## Proof of Concept
When some amount of token is distributed using `Distributor.distribute` then it should be equally distributed among receivers.
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/Distributor.sol#L87-L137
```solidity
    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);
        }
    }
```

After we know `totalShares`, which depends on if token is RToken or RSR, the function calculates `tokensPerShare`.
`tokensPerShare = amount / totalShares`.
In this moment we already lost precision because of rounding.

Later we use  `tokensPerShare` to know total amount of erc20 token that is needed to be send to recipient `uint256 transferAmt = tokensPerShare * numberOfShares`, but this value is already incorrect. And the amount that is sent to recipient is less than needed.

## Tools Used
VsCode
## Recommended Mitigation Steps
Do not calculate `tokensPerShare` before. 
You can st
BackingManager.handoutExcessAssets divides before multiplication
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L221-L235


# Vulnerability details

## Impact
BackingManager.handoutExcessAssets divides before multiplication. Because of that less amount of rewards is sent to rTokenTrader and rsrTrader.

## Proof of Concept
While calculating amount that should be sent to `rTokenTrader` and `rsrTrader` inside `handoutExcessAssets` function, BackingManager divides before multiplication and sends less amount of rewards because of rounding.

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L221-L235
```solidity
        for (uint256 i = 0; i < length; ++i) {
            IAsset asset = assetRegistry.toAsset(erc20s[i]);


            uint192 req = needed.mul(basketHandler.quantity(erc20s[i]), CEIL);
            if (asset.bal(address(this)).gt(req)) {
                // delta: {qTok}, the excess quantity of this asset that we hold
                uint256 delta = asset.bal(address(this)).minus(req).shiftl_toUint(
                    int8(IERC20Metadata(address(erc20s[i])).decimals())
                );
                // no div-by-0: Distributor guarantees (totals.rTokenTotal + totals.rsrTotal) > 0
                // initial division is intentional here! We'd rather save the dust than be unfair
                toRSR[i] = (delta / (totals.rTokenTotal + totals.rsrTotal)) * totals.rsrTotal;
                toRToken[i] = (delta / (totals.rTokenTotal + totals.rsrTotal)) * totals.rTokenTotal;
            }
        }
```

For example if totals.rTokenTotal == totals.rsrTotal = 100 and delta = 500 then both toRSR[i] and toRToken[i] should be 250, but they are 200.
As result less amount than needed is sent to contracts.

## Tools Used
VsCode
## Recommended Mitigation Steps
Multiply before division.
RToken.redeem doesn't have slippage protection
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L475-L488


# Vulnerability details

## Impact
RToken.redeem doesn't have slippage protection. In case if it's not enough of erc20 token at the moment then less amount than should will be sent to user.

## Proof of Concept
When user redeems his rTokens, then first amount of baskets that will be exhanged is calculated and amount that should be paid to user according to basket price.

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L461-L488
```soldiity
        uint256 supply = totalSupply();
        uint192 baskets = uint192(mulDiv256(basketsNeeded_, amount, supply));
        emit Redemption(redeemer, amount, baskets);


        (address[] memory erc20s, uint256[] memory amounts) = basketHandler.quote(baskets, FLOOR);


        // ==== Prorate redemption ====
        // i.e, set amounts = min(amounts, balances * amount / totalSupply)
        //   where balances[i] = erc20s[i].balanceOf(this)


        uint256 erc20length = erc20s.length;


        // D18{1} = D18 * {qRTok} / {qRTok}
        // downcast is safe: amount <= balanceOf(redeemer) <= totalSupply(), so prorate < 1e18
        uint192 prorate = uint192((FIX_ONE_256 * amount) / supply);


        // Bound each withdrawal by the prorata share, in case we're currently under-collateralized
        for (uint256 i = 0; i < erc20length; ++i) {
            // {qTok}
            uint256 bal = IERC20Upgradeable(erc20s[i]).balanceOf(address(backingManager));


            // gas-optimization: only do the full mulDiv256 if prorate is 0
            uint256 prorata = (prorate > 0)
                ? (prorate * bal) / FIX_ONE // {qTok} = D18{1} * {qTok} / D18
                : mulDiv256(bal, amount, supply); // {qTok} = {qTok} * {qRTok} / {qRTok}


            if (prorata < amounts[i]) amounts[i] = prorata;
        }
```

It's possible that after switching the basket or for another reason it will be nor enough of some erc20 token inside BackingManager. In such case user will receive less amount of token than he should. `if (prorata < amounts[i]) amounts[i] = prorata;`

Currently user doesn't have ability to provide minimum amount of tokens that he wants to receive from redeem. So as result it's possible that user will be underpaid after tx.

## Tools Used
VsCode
## Recommended Mitigation Steps
Consider a way for user to control minimum amount of each erc20 token of basket that he expects to receive.
the user can exchange the exact amount of token of the pervious basket to the new prime basket  
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L315


# Vulnerability details

## Impact
if the user have some queue and the a ** _switchBasket ** occurs and a new primary basket with a new erc20 collateral is selected and the new collateral is some more expensive than the previous one the user will just Have to call **issue**within  a small amount  and the previous deposited amount will be added together within the new one  so the user will  call  **cancel **  since
      
  **queue.tokens = erc20s;**  the internal function  **refundSpan** will withdraw the new token 
``` 
for (uint256 i = 0; i < queue.tokens.length; ++i) {
            IERC20Upgradeable(queue.tokens[i]).safeTransfer(account, amt[i]);
        }
    }

``` 
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used
Manual

## Recommended Mitigation Steps
if  the user have some amount on the queue dont add the new deposited amount on the previous one
a user can redeem within the previous basket 
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L302-#L304


# Vulnerability details

## Impact
when a **_switchBasket ** occurs and a new primary basket with a new erc20 collateral is selected and if the user have deposited on the previous  basket and vested and and if he he calls **issue** again  with small amount the previous **amtRToken**  and the new **amtRToken** will be added together so the user can mint the previous and the new amtRToken amount 
## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

## Tools Used
Manual 
## Recommended Mitigation Steps
IF THE USER HAS SOME AMOUNT ON THE QUEUE  and the primary basket have been changed remove remove the previous amtRToken  and withdraw the previous collateral to the user
user can lose there collateral if the new collateral token is smaller than the nonce -1 collateral  
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RToken.sol#L315


# Vulnerability details

## Impact
 when a _switchBasket occurs and a new primary basket with a new erc20 collateral is selected and if a users have deposited on the previous basket and didn't vest it and again if he deposited on the new prime basket he can't withdraw the previous one because 	        queue.tokens = erc20s; will be updated and when canceling  the new erc20 will be withdrawn 
 

## Proof of Concept

## Tools Used
Manual 
## Recommended Mitigation Steps
add a function to withdraw the previous deposited token
The stake() function fails to update the totalStakes
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

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


# Vulnerability details

## Impact
Detailed description of the impact of this finding.
The stake function fails to update the new stakeRSR, as a result, stakeRate will be become smaller (invariant takeRate' = stakeRate for stake() is violated) and future functionality of stake() and unstake()) will all be affected. 

## Proof of Concept
Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.
The stake() function fails to update ``totalStakes``. Although the ``newTotalStakes`` has been calculated, the function does not use it to update ``totalStakes``. 



## Tools Used
Remix

## Recommended Mitigation Steps
WE update ``totalStakes`` using ``newTotalStakes`` below:
```

function stake(uint256 rsrAmount) external {
        require(rsrAmount > 0, "Cannot stake zero");

        if (!main.pausedOrFrozen()) _payoutRewards();

        // Compute stake amount
        // This is not an overflow risk according to our expected ranges:
        //   rsrAmount <= 1e29, totalStaked <= 1e38, 1e29 * 1e38 < 2^256.
        // stakeAmount: how many stRSR the user shall receive.
        // pick stakeAmount as big as we can such that (newTotalStakes <= newStakeRSR * stakeRate)
        uint256 newStakeRSR = stakeRSR + rsrAmount;
        // newTotalStakes: {qStRSR} = D18{qStRSR/qRSR} * {qRSR} / D18
        uint256 newTotalStakes = (stakeRate * newStakeRSR) / FIX_ONE;
        uint256 stakeAmount = newTotalStakes - totalStakes;

        // Update staked
        address account = _msgSender();
        stakeRSR = newStakeRSR; // @audit: save gas
        totalStakes = newTotalStakes; // @audit: need to modify totalStakes

        // == Interactions ==
        IERC20Upgradeable(address(rsr)).safeTransferFrom(account, address(this), rsrAmount); // audit: we transfer first


        _mint(account, stakeAmount);

        // Transfer RSR from account to this contract
        emit Staked(era, account, rsrAmount, stakeAmount);

    }

```
The ERC20 token might be locked up forever.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BackingManager.sol#L72-#L77


# Vulnerability details

## Impact
The ERC20 token might be locked up forever .

## More Details and Proof of Concept
The `grantRTokenAllowance` function calls the `safeApprove` function on the ERC20 token with an allowance of UINT_MAX which is the maximum value of a uint256 variable.

It means that the ERC20 token is giving the maximum value to the `rToken` contract.

When the ERC20 token is approved to transfer, the allowance will set to the specified value. 
The approved address (rToken contract) can on behalf of the owner to transfer many tokens .
The allowance can be revoked by the owner, when they are calling the approve function with zero value or they call the decreaseAllowance (a standard function of the ERC20 token and can be found in IERC20) function with a value less than the current allowance.

But in this case, by setting the allowance to UINT_MAX, the owner will not be able to decrease the allowance as the maximum value of a uint256 variable is 2^256-1. This means that the tokens would be locked up forever and will not be able to be transferred. 

## Tools Used
VS Code

## Recommended Mitigation Steps
Set a time-limited allowance for the rToken contract (e.g use `timeDuration`)
Implement a mechanism to limit the allowance given to the rToken contract before it is revoked.

---
Additionally, if the `rToken` is controlled by the attacker (which means compromised), the attacker could use the allowance to mint unlimited amount of tokens. It may cause the value of the `rTokens` to drop and may cause the backing assets to be locked indefinitely.
`prepareTradeSell` function does not check whether the sell price of the token (sellPrice) is greater than zero.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RevenueTrader.sol#L69-#L81


# Vulnerability details

## Impact
The function (`prepareTradeSell`) does not check whether the sell price of the token (sellPrice) is greater than zero.

## Proof of Concept
The problem of `prepareTradeSell` function in the manageToken function is:
The function does not check whether the sell price of the token (sellPrice) is greater than zero. It may cause the trade to fail and it may result in the loss of assets or other unintended consequences.

Also, the `prepareTradeSell` function does not check whether the buy price of the token (buyPrice) is less than the maximum value (FIX_MAX line #72) this can cause the contract to try to trade an asset with an unknown buy price.

For lines 74 to 81 
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RevenueTrader.sol#L74-#L81
The sellAmount returned by sell.bal(address(this)) will always be greater than zero, but it should check if the returned value of sellAmount is greater than zero before executing the trade.

## Tools Used
VS Code

## Recommended Mitigation Steps
It is recommended to check the sell price, buy price, and sell amount are greater than zero before executing the trade to mitigate these risks such as using `require`:
`require(sellPrice > 0, "Sell price must be greater than zero");`
The `require` can be placed before the TradeInfo memory variable is assigned.

Or 

Use safe math library to ensure the values will not trigger overflow or underflow.
manageToken function does not check the amount of tokens being transferred
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/RevenueTrader.sol#L53-#L65


# Vulnerability details

## Impact
It seems that `manageToken` function does not check the amount of tokens when the tokes are transferred

## Proof of Concept
The `manageToken` function calls the `balanceOf` funciton (`uint256 bal = erc20.balanceOf(address(this));`) . 
This code also assign the value to the variable `bal`.

After that, there is no checking to ensure that the `bal` variable is not larger than the actual balance of the contract.
And then, the `manageToken` function calls the `distribute` function or tryTrade function from TradeLib.sol library 
```
IERC20Upgradeable(address(erc20)).safeApprove(address(distributor), 0);
IERC20Upgradeable(address(erc20)).safeApprove(address(distributor), bal);
distributor.distribute(erc20, address(this), bal);
```
with the bal variable as the amount to transfer or trade.

The attackers may allowed to call the `manageToken` function with a large amount of tokens by increasing the balance of the contract and transfer more tokens than the actual balance in the contract.

## Tools Used
VS Code

## Recommended Mitigation Steps
Try to use the `require` function such as `require(bal <= erc20.balanceOf(address(this)), "Insufficient balance to transfer tokens");`
Or 
Use a safe math library such as OpenZeppelin's SafeMath library to perform mathematical operations that automatically check for overflow and underflow.
backup erc20  is not checked against the basket erc20
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L252


# Vulnerability details

## Impact
the function **setBackupConfig** checks if there is a duplicated erc20s token in the backup but  it is missing a check against the config.erc20s because if there is  a same erc20 in the backup and cofig.erc20 there will be some issue the first one is if one of the basket token default and it is the same token as in backup the backup token is also defaulted so the basket will default the other one is MAX_TARGET_AMT can be bypassed because if Goodweight   is  less than targetweight the backup erc20 is used and if there is a same erc20 token in the basket and backup the missing weight can be added to the duplicated erc20 token so that the basket token may have more than MAX_TARGET_AMT and this may give some wrong calculation 
## Proof of Concept
https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/p1/BasketHandler.sol#L252
## Tools Used
Manual analysis

## Recommended Mitigation Steps
check the backup token with the basket token
Unchecked token redemption bug in "Libraries/RedemptionBattery.sol" smart contract allowing arbitrary large amount of tokens to be drained.
Audit: 2023-01-reserve-findings
Severity: HIGH

# Lines of code

https://github.com/reserve-protocol/protocol/blob/df7ecadc2bae74244ace5e8b39e94bc992903158/contracts/libraries/RedemptionBattery.sol#L44


# Vulnerability details

## Impact
``` require(amount <= charge, "redemption battery insufficient"); ```

This code only checks if the `amount` being redeemed is less than or equal to the current charge of the battery but does not check if it is greater than the total supply of the token.

This bug is severe. As the `discharge` function does not check if the "amount" being redeemed is greater than the total supply of the token, it allows an attacker to redeem an `arbitrary` large amount of tokens and drain the entire supply. This can result in a complete loss of the token for all other users.

An attacker could exploit this vulnerability by repeatedly calling the "discharge" function with large amounts of tokens until the entire supply is depleted. This could effectively render the token worthless and cause significant financial loss for any users or investors who hold the token.

The attacker can also use the same vulnerability to manipulate the token's value. For instance, if the token's value is determined by the total supply, the attacker can use this bug to artificially decrease the total supply and make the token's value increase, then sell the token at a higher price.

## Proof of Concept

This would involve creating a contract that utilizes the `RedemptionBatteryLib` library and calls the "discharge" function with a number of tokens that exceeds the total supply.

```
pragma solidity 0.8.9;

import "./RedemptionBatteryLib.sol";

contract TokenDrain {
    using RedemptionBatteryLib for RedemptionBatteryLib.Battery;
    RedemptionBatteryLib.Battery public battery;
    uint256 public totalSupply;

    constructor(uint256 _totalSupply) public {
        totalSupply = _totalSupply;
    }

    function drain() public {
        require(msg.sender == address(this), "only owner can drain the token");
        // Attacker calls the discharge function with a large amount of tokens
        // that exceeds the total supply, draining the entire supply
        battery.discharge(totalSupply, totalSupply + 1);
    }
}
```

the "drain" function can be called by the contract's owner to drain the entire supply of tokens, regardless of the current charge of the battery. The function calls the "discharge" function in the `RedemptionBatteryLib` library with a number of tokens that exceeds the total supply, allowing the attacker to drain the entire supply.

## Tools Used

Manual audit, Vs Code

## Recommended Mitigation Steps

The `discharge` function ensures that the amount being redeemed is not greater than the total supply of the token. This can be done by adding an additionally `require` statement that compares the amount being redeemed to the total supply.

```
function discharge(
        Battery storage battery,
        uint256 supply,
        uint256 amount
    ) internal {
        if (battery.redemption