xanoutas/solidity-security-findings huggingface dataset:
absence of Validation of Fee Receiver Address Leading to Loss of Funds
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L650-L656
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L315-L318
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L450-L452


# Vulnerability details

## Description 
the vulnerability here is  arises from the lack of validation checks on the feeReceiver address when it is set or modified.
The contract does not ensure that the feeReceiver is capable of managing or forwarding the received USDC. This oversight allows the feeReceiver to be set to the contract address itself or another contract address that does not expect to receive USDC, leading to potential fund lock-up.
here the mint() and redeem() functions, where fees are transferred to the feeReceiver. The setting of the feeReceiver occurs without checks in the setFeeReceiver() function
```solidity
function setFeeReceiver(address _feeReceiver) external override onlyRole(DEFAULT_ADMIN_ROLE) {
    require(_feeReceiver != address(0), "FeeReceiver cannot be 0x0");
    feeReceiver = _feeReceiver;
}
```
and here
```solidity
// Inside _mint() function
usdc.transferFrom(msg.sender, feeReceiver, usdcfees);
```
and here 
```solidity
// Inside _redeem() function
usdc.transfer(feeReceiver, usdcFees);
```
## Impact

 if the feeReceiver is incorrectly set to a non-custodial address or the contract itself, the collected fees become irretrievable within the contract's logic, effectively locking the funds. This can result in a significant loss over time, especially as the contract continues to collect fees from minting and redeeming operations.

## Proof of Concept
Let's assume that the contract has collected 10,000 USDC in fees over various minting and redeeming operations, directed to what is believed to be a valid fee management address.
- The contract admin inadvertently sets the feeReceiver to the contract address itself, perhaps misunderstanding its role or due to a clerical error.
Configuration:
    feeReceiver Address: 0xContractAddress (the contract's own address)
    Collected Fees: 10,000 USDC
- The 10,000 USDC in fees are sent to the contract's address. Without a mechanism in place for the contract to withdraw or distribute these USDC tokens, they become locked within the contract.
- Attempts to correct the feeReceiver address after fees have been transferred are futile regarding the already transferred funds, as there's no function within the contract to "rescue" or redirect these tokens.
## Tools Used
manual review 
## Recommended Mitigation Steps
it's need to ensure the new feeReceiver address is not the contract itself or another contract incapable of handling USDC


## Assessed type

Other
The contract assumes that the price of USDC is always $1.00 which will be a problem if the price depegs
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L685
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L699


# Vulnerability details

The `ousgInstantManager` is used to mint or redeem OUSG/rOUSG using USDC on a 1:1 ratio. To calculate the amount of OUSG we will mint or redeem we use the `_getMintAmount()` and `_getRedemptionAmount()` functions. 


```solidity
function _getMintAmount(
    uint256 usdcAmountIn,
    uint256 price
  ) internal view returns (uint256 ousgAmountOut) {
    uint256 amountE36 = _scaleUp(usdcAmountIn) * 1e18;
    ousgAmountOut = amountE36 / price;
  }

function _getRedemptionAmount(
    uint256 ousgAmountBurned,
    uint256 price
  ) internal view returns (uint256 usdcOwed) {
    uint256 amountE36 = ousgAmountBurned * price;
    usdcOwed = _scaleDown(amountE36 / 1e18);
  }
```
So for example if we want to mint and use 105 USDC worth $1 and the price of OUSG is 105 the calculation will be:

(105e6 * 1e12) * 1e18 / 105e18 = 1e18

However the problem here is that the USDC is always priced at 1$ because we are only using the amount of USDC being sent without the current price. This will create problems when the price of USDC depegs which happened for example last year. 


Users will be able to use USDC to mint OUSG as if the price was `1$` even though the price can be lower allowing the users to profit from this. Or if the price goes above `1$`, users will receive the amount of USDC when redeeming as if it was 1$ even though it is worth more. 

## Impact
If the price of USDC depegs the users will be able to mint OUSG using a discounted price or they will be able to receive USDC at a higher price when redeeming allowing them to profit from events like this. 


## Proof of Concept
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L685

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L699

```solidity
function _getMintAmount(
    uint256 usdcAmountIn,
    uint256 price
  ) internal view returns (uint256 ousgAmountOut) {
    uint256 amountE36 = _scaleUp(usdcAmountIn) * 1e18;
    ousgAmountOut = amountE36 / price;
  }

function _getRedemptionAmount(
    uint256 ousgAmountBurned,
    uint256 price
  ) internal view returns (uint256 usdcOwed) {
    uint256 amountE36 = ousgAmountBurned * price;
    usdcOwed = _scaleDown(amountE36 / 1e18);
  }
```

As you can see here the amount of OUSG that the user will mint/redeem uses the amount of USDC sent which is always priced at 1$ and it doesnt check the current price. 

## Tools Used

Manual Review

## Recommended Mitigation Steps
The easiest way to fix this is to get the USDC price from an oracle and revert if it is for example `< 0.99
The Protocol will doesn't work as whole, because `USDC` and `BUIDL` token implementations doesn't inherit `IERC20Metadata` interface.
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L278-L324
https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L388-L456


# Vulnerability details

## Impact

The Protocol will doesn't work as whole, because `USDC` and `BUIDL` token implementations doesn't inherit `IERC20Metadata` interface.

In the context of the Ondo Finance ecosystem, particularly within the `OUSGInstantManager` contract operations involving `USDC` and `BUIDL` tokens, there arises a critical compatibility issue. The IERC20Metadata interface extension of the ERC-20 standard provides additional functionalities, including access to token metadata such as name, symbol, and decimals. This information is crucial for interacting contracts to properly handle token amounts and display characteristics.

## Proof of Concept

During minting and redemption, there is a require check that ensure the decimals of `USDC` and `BUIDL` tokens are equal to 6.

```solidity
function _mint(
  uint256 usdcAmountIn,
  address to
) internal returns (uint256 ousgAmountOut) {
  require(
    IERC20Metadata(address(usdc)).decimals() == 6,
    "OUSGInstantManager::_mint: USDC decimals must be 6"
  ); //@note
  require(
    usdcAmountIn >= minimumDepositAmount,
    "OUSGInstantManager::_mint: Deposit amount too small"
  );
  _checkAndUpdateInstantMintLimit(usdcAmountIn);
  if (address(investorBasedRateLimiter) != address(0)) {
    investorBasedRateLimiter.checkAndUpdateMintLimit(msg.sender, usdcAmountIn);
  }

  require(
    usdc.allowance(msg.sender, address(this)) >= usdcAmountIn,
    "OUSGInstantManager::_mint: Allowance must be given to OUSGInstantManager"
  );

  uint256 usdcfees = _getInstantMintFees(usdcAmountIn);
  uint256 usdcAmountAfterFee = usdcAmountIn - usdcfees;

  // Calculate the mint amount based on mint fees and usdc quantity
  uint256 ousgPrice = getOUSGPrice();
  ousgAmountOut = _getMintAmount(usdcAmountAfterFee, ousgPrice);

  require(
    ousgAmountOut > 0,
    "OUSGInstantManager::_mint: net mint amount can't be zero"
  );

  // Transfer USDC
  if (usdcfees > 0) {
    usdc.transferFrom(msg.sender, feeReceiver, usdcfees);
  }
  usdc.transferFrom(msg.sender, usdcReceiver, usdcAmountAfterFee);

  emit MintFeesDeducted(msg.sender, feeReceiver, usdcfees, usdcAmountIn);

  ousg.mint(to, ousgAmountOut);
}

```

```solidity
function _redeem(
  uint256 ousgAmountIn
) internal returns (uint256 usdcAmountOut) {
  require(
    IERC20Metadata(address(usdc)).decimals() == 6,
    "OUSGInstantManager::_redeem: USDC decimals must be 6"
  ); //@note
  require(
    IERC20Metadata(address(buidl)).decimals() == 6,
    "OUSGInstantManager::_redeem: BUIDL decimals must be 6"
  ); //@note
  uint256 ousgPrice = getOUSGPrice();
  uint256 usdcAmountToRedeem = _getRedemptionAmount(ousgAmountIn, ousgPrice);

  require(
    usdcAmountToRedeem >= minimumRedemptionAmount,
    "OUSGInstantManager::_redeem: R
The oracle price fetch in `ROUSG::getOUSGPrice()` lacks sanity / validity checks
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/rOUSG.sol#L378-L380


# Vulnerability details

## Impact
The function `ROUSG::getOUSGPrice()` simply accepts the price fetched from `oracle.getPriceData()` without checking the validity whether the it is too low or too high as shown in the code below. 

[getOUSGPrice()](https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/rOUSG.sol#L378-L380)

```
function getOUSGPrice() public view returns (uint256 price) {
  (price, ) = oracle.getPriceData();
}
```

If the price went too low, the users will lose on redemption. On the flip side, if the price went too high, the protocol will lose on redemption hence threatening the solvency.

## Proof of Concept
## Tools Used
Manual Review

## Recommended Mitigation Steps
Record the previous price and compare it to the current fetched price. Should the disparity between the two price is too large in percentage, then revert the transaction. As to how much price disparity is "too large", it's up to the protocol team to decide.


## Assessed type

Oracle
Unlimited Wrapping of OUSG
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/rOUSG.sol#L415-L422


# Vulnerability details

## Impact
In ousgInstantManager, the function mintRebasingOUSG allows minting of rOUSG.
The function handles the approval of OUSG to the rOUSG contract for the 'ousgAmountOut'.
It then calls the rOSUG.wrap() function.
This function calculates the appropriate amount of shares, mint the shares to the msg.sender, and then transfers the OUSG from the user to the rOUSG contract.

The issue lies in the fact that the rOUSG.wrap() can be called externally and doesn't validate the approval of _OUSGAmount nor is the result of ousg.transferFrom checked to be true. As a result any amount can be given directly to the rOUSG.wrap() function which results in the shares being minted and the transferFrom silently failing.

## Proof of Concept
Reference Links to affected code

## Tools Used
Manual Review

## Recommended Mitigation Steps
Implement a check on allowance of ousg.
Reference: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L298-L301


## Assessed type

Invalid Validation
OUSGInstantManager.sol incorrectly assumes value of USDC is always $1  f
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L278
https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L388
https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L479


# Vulnerability details

## Impact
OUSGInstantManager.sol operates under the assumption that the value of USDC is always precisely $1. This creates an opportunity for attackers to exploit fluctuations in the value of USDC, leading to potential instant profits or immediate losses for users.

## Proof of Concept
Users have the ability to `mint()` OUSG by exchanging USDC, as well as to `redeem()` OUSG for USDC. Both functions rely on `getOUSGPrice()` to fetch the price feed for the [SHV ETF](https://data.chain.link/feeds/ethereum/mainnet/shv-usd), which is used to calculate the exchange rate between USDC and OUSG. It's important to note that the price feed returns the SHV/USD rate.

```solidity
  function getOUSGPrice() public view returns (uint256 price) {
    (price, ) = oracle.getPriceData();
    require(
      price > MINIMUM_OUSG_PRICE,
      "OUSGInstantManager::getOUSGPrice: Price unexpectedly low"
    );
  }
```

OUSGInstantManager.sol incorrectly assumes that the value of USDC remains constant at 1 dollar. Despite USDC being a stablecoin, its value rarely matches exactly 1 dollar, with minor fluctuations being common. Throughout the lifespan of [USDC](https://coinmarketcap.com/currencies/usd-coin/), its value has fluctuated by as much as 1.0341 and 0.9701, representing an approximate deviation of ±3%.

Attackers can exploit fluctuations in the value of USDC when it falls below $1:

1. Assume the value of USDC is $0.97.
2. SHV/USD price feed indicates $110.
3. The attacker `mint()` OUSG by exchanging 250,000 USDC.
4. Based on the current implementation, the attacker would receive 250,000/110 = 2272.72 OUSG.
5. However, the actual amount should have been (250,000 * 0.97) / 110 = 2204.54.
6. The attacker receives an additional 68.17 OUSG.

Conversely, users may incur immediate losses in `mint()` when the value of USDC exceeds $1:

1. Assume the value of USDC is $1.03.
2. SHV/USD price feed indicates $110.
3. The user `mint()` OUSG by exchanging 250,000 USDC.
4. Based on the current implementation, the user would receive 250,000/110 = 2272.72 OUSG.
5. However, the actual amount should have been (250,000 * 1.03) / 110 = 2204.54 = 2340.90.
6. The user receives 68.18 less OUSG.

Similar vulnerabilities apply to the `redeem()` function. Attackers can easily exploit this assumption by flashloaning a large amount of USDC to `mint()` and then exchanging OUSG in an external liquidity pool for immediate profit.

While profits from this attack may be minimal when the value of USDC remains close to $1, in a black swan event where the value deviates by 2-3%, the potential for profit and loss is significantly magnified.

## Tools Us
`OUSGInstantManager` will allow Excessive OUSG Token Minting During USDC Depeg Event
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/main/contracts/ousg/ousgInstantManager.sol#L307-L308


# Vulnerability details

## Proof of Concept

Any User can use `mint` function in `ousgInstantManager` contract to mint `OUSG` tokens by providing `USDC` token.

It calls internal function `_mint` where the main logic resides.

```solidity

  function _mint(uint256 usdcAmountIn, address to) internal returns (uint256 ousgAmountOut) {
    
    // SNIP: Validation

    uint256 usdcfees = _getInstantMintFees(usdcAmountIn);
    uint256 usdcAmountAfterFee = usdcAmountIn - usdcfees;

    // Calculate the mint amount based on mint fees and usdc quantity
    uint256 ousgPrice = getOUSGPrice();
    ousgAmountOut = _getMintAmount(usdcAmountAfterFee, ousgPrice);

    require(ousgAmountOut > 0, "OUSGInstantManager::_mint: net mint amount can't be zero");

    // SNIP: Transfering USDC

    ousg.mint(to, ousgAmountOut);
  }

```

**2 important Points to understand here:**

#### 1. OUSG Price Stability:

The contract depends on the OUSG price obtained from an oracle, which is heavily constrained (As per Readme) to ensure stability.

> OUSG Price - The OUSG price tracks an off chain portfolio of cash equivalents and treasury bills, price changes are heavily constrained in the OUSG Oracle, which uses the change in the price of SHV to set the allowable OUSG price in between updates. We are aware that the SHV price could differ from the OUSG portfolio, so any findings related to this price discrepancy is out of scope. Also, scenarios where the OUSG price increases by many orders of magnitudes are not realistic and consequently not considered valid.

As per `RWAOracleRateCheck` Oracle, Constraints includes:

1. OUSG price updates restricted to once every `23 hours`.
2. Price deviations limited to a maximum of `1%`.

```solidity

      function setPrice(int256 newPrice) external onlyRole(SETTER_ROLE) {
        if (newPrice <= 0) {
          revert InvalidPrice();
        }
 @->    if (block.timestamp - priceTimestamp < MIN_PRICE_UPDATE_WINDOW) {
          revert PriceUpdateWindowViolation();
        }
 @->    if (_getPriceChangeBps(rwaPrice, newPrice) > MAX_CHANGE_DIFF_BPS) {
          revert DeltaDifferenceConstraintViolation();
        }

        // Set new price
        int256 oldPrice = rwaPrice;
        rwaPrice = newPrice;
        priceTimestamp = block.timestamp;

        emit RWAPriceSet(oldPrice, newPrice, block.timestamp);
      }

```

These constraints ensure relative stability of the OUSG price.

#### 2. Calculation Assumptions:

The calculation of the amount of OUSG tokens to mint assumes a fixed conversion rate of `1 USDC = 1 USD`. 

Key point:

* The `_getMintAmount` function calculates the OUSG amount based on the provided USDC amount and the OUSG price obtained from the oracle (By just upscaling and dividing).

```solidity

  function _getMintAmount(
    uint256 usdcAmountIn,
    uint256 price
  ) internal view returns (uin
Malicious investors can use redemptions to convert mostly all of Ondo's BUIDL tokens to USDC sitting in the `OUSGInstantManager` contract.
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L426-L429


# Vulnerability details

It is mentioned in the [README, under `Attack ideas` section](https://github.com/code-423n4/2024-03-ondo-finance?tab=readme-ov-file#attack-ideas-where-to-focus-for-bugs):
> Any ways for malicious investors to use redemptions to convert all of Ondo's BUIDL tokens to USDC sitting in the OUSGInstantManager contract.

Investors call [redeem](https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L335-L351) to withdraw their USDC back by burning OUSG tokens. The internal function `_redeem` has [3 strategies](https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L426-L448) to pay back USDC to investors, based on the amount of USDC to be withdrawn:
```js
function _redeem(
    uint256 ousgAmountIn
  ) internal returns (uint256 usdcAmountOut) {
    // more code
    usdcAmountOut = usdcAmountToRedeem - usdcFees;

    // more code

    uint256 usdcBalance = usdc.balanceOf(address(this));

    if (usdcAmountToRedeem >= minBUIDLRedeemAmount) {
      // amount of USDC needed is over minBUIDLRedeemAmount, do a BUIDL redemption
      // to cover the full amount
      _redeemBUIDL(usdcAmountToRedeem);
    } else if (usdcAmountToRedeem > usdcBalance) {
      // There isn't enough USDC held by this contract to cover the redemption,
      // so we perform a BUIDL redemption of BUIDL's minimum required amount.
      // The remaining amount of USDC will be held in the contract for future redemptions.
      _redeemBUIDL(minBUIDLRedeemAmount);
      emit MinimumBUIDLRedemption(
        msg.sender,
        minBUIDLRedeemAmount,
        usdcBalance + minBUIDLRedeemAmount - usdcAmountToRedeem
      );
    } else {
      // We have enough USDC sitting in this contract already, so use it
      // to cover the redemption and fees without redeeming more BUIDL.
      emit BUIDLRedemptionSkipped(
        msg.sender,
        usdcAmountToRedeem,
        usdcBalance - usdcAmountToRedeem
      );
    }

    if (usdcFees > 0) {
      usdc.transfer(feeReceiver, usdcFees);
    }
    emit RedeemFeesDeducted(msg.sender, feeReceiver, usdcFees, usdcAmountOut);

    usdc.transfer(msg.sender, usdcAmountOut);
}
```
We can see that in the first strategy (`if (usdcAmountToRedeem >= minBUIDLRedeemAmount)`), the contract will do a BUIDL redemption to cover the full USDC amount, and the redemption value is equal to the USDC amount to be withdrawn by the investor. The issue is that malicious investors can perform a sequence of transactions to convert all BUIDL tokens to USDC by depositing and withdrawing USDC amount greater than `minBUIDLRedeemAmount`. `minBUIDLRedeemAmount` is initially set to `$250k`, so it is likely possible and easy to perform such at
An upgrade in the USDC contract renders the whole ousgInstantManager contract unusable
Audit: 2024-03-ondo-finance-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L282-L285
https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/contracts/ousg/ousgInstantManager.sol#L282-L285


# Vulnerability details

## Impact
`USDC` is an upgradeable contract. An upgrade in the decimals of the `USDC` token renders the whole `ousgInstantManager` completely unusable. Users won't be able to mint nor redeem which are the 2 core functionalities of the `ousgInstantManager` contract. Furthermore, funds of users will be locked in the contract since they will not be able to redeem. While there is a `retrieveTokens()` function, transferring tokens to all victims is a huge burden and this event would significantly lower the trust people have in the protocol.

## Proof of Concept
`ousgInstantManager::_mint()` and `ousgInstantManager::_redeem()` have the following 2 require statements:
```solidity
require(
      IERC20Metadata(address(usdc)).decimals() == 6,
      "OUSGInstantManager::_mint: USDC decimals must be 6"
    );
```
```solidity
require(
      IERC20Metadata(address(usdc)).decimals() == 6, // @audit Wont work if USDC upgrades to different amount of decimals
      "OUSGInstantManager::_redeem: USDC decimals must be 6"
    );
```
They require that the decimals of the `USDC` token are equal to 6. However, since `USDC` is an upgradeable contract, that can change at any moment rendering the whole contract absolutely useless as these are the only 2 core functionalities of the contract.

## Tools Used
Manual Review

## Recommended Mitigation Steps
A solution would be to make the contract more flexible. For example, having a variable named `usdcDecimals` and having a setter function for it in the event of `USDC` upgrading would solve this issue.



## Assessed type

Upgradable