xanoutas/solidity-security-findings huggingface dataset:
Interest Does Not Accrue to yUSDY Holder
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L449-L456
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L434-L440


# Vulnerability details

## Impact

According to the contest description "the price of a single rUSDY token is fixed at a price of 1 Dollar, with yield being accrued in the form of additional rUSDY tokens.". In order for the interest to accrue to USDY holders who wrap the token to `yUSDY`, when users convert `yUSDY` back to `USDY`, the conversion should happen on a 1-to-1 basis. However, the `unwrap()` function in the code base calculates the `USDY` amount incorrectly, such that all the accrued interest is lost, and the user does not receive any interest accrual. 

## Proof of Concept

Please refer to the following Forge test. Note that while Alice has more `yUSDY` than what she started with due to interest accrual, the amount Alice receives in `USDY` remains the same. 

pragma solidity 0.8.16;

import "contracts/rwaOracles/RWADynamicOracle.sol";
import "lib/forge-std/src/console.sol";
import "forge-tests/USDY_BasicDeployment.sol";

contract Test_rUSDY_1 is USDY_BasicDeployment {
  uint256 periodStart = 1690833600; // JUL 31 8pm UTC
  uint256 periodEnd = 1693512000; // AUG 31 8pm UTC
  uint256 dailyIR = 1.00013368 * 1e27; // (1 + Daily IR, scaled to 1e27),
  uint256 rangeStartPrice = 1e18; // Range Initial price

  function setUp() public override {
    super.setUp();
    oracleUSDY.overrideRange(
      0,
      periodStart,
      periodEnd,
      dailyIR,
      rangeStartPrice
    );
    vm.warp(periodStart + 1);
  }

  function test_rUSDY_interest_accrual() public {
    _mintRWAToUser(alice, 100e18);
    vm.startPrank(alice);
    usdy.approve(address(rUSDYToken), 100e18);
    rUSDYToken.wrap(100e18);

    vm.warp(block.timestamp + 1 days);
    assertEq(100.013368 * 1e18, rUSDYToken.balanceOf(alice));
    assertEq(10_000 * 100e18, rUSDYToken.sharesOf(alice));

    rUSDYToken.unwrap(rUSDYToken.balanceOf(alice));
    assertEq(100e18, usdy.balanceOf(alice));

  }

}


## Tools Used

Manual Review. Foundry. 

## Recommended Mitigation Steps

The conversion from `yUSDY` to `USDY `should be on a 1-to-1 basis. 


## Assessed type

Math
Vulnerability in the setRange() function where invalid ranges could be set that overlap or go backwards in time
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L151-L171


# Vulnerability details

## Impact
It will lead to incorrect price derivations. It can lead to the wrong range parameters being used for price derivation.

## Proof of Concept
This only checks that the new endTimestamp is greater than the previous range's end time. It does not check that the start time of the new range (which is the previous range's end time) is less than the endTimestamp.

For example, If one  could do:
// Set initial valid range
setRange(1000, 5%) 

// Set invalid overlapping range 
setRange(900, 10%)

Now there are two ranges that overlap:

Range 1: start 0, end 1000, rate 5%
Range 2: start 900, end 1000, rate 10%

This is invalid and can cause issues in deriving the price. For example calling getPrice() at timestamp 950 could give two different results depending on which range is checked first.

## Tools Used
Manual
## Recommended Mitigation Steps
Add a check in setRange() to ensure the new start time is after the previous end time


## Assessed type

Other
The roundsUpTo8 function can allow for some manipulation of return values around the 0.5e10 cutoff.
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L283-L285


# Vulnerability details

## Impact
This could allow manipulators to benefit by being able to buy/sell around the rounded prices.

## Proof of Concept
This means any value with a remainder greater than or equal to 5e9 will be rounded up.

A manipulator could exploit this by:
- Calling getPrice() close to the end of a range.
- Checking the remainder of the returned price modulo 1e10.
- If the remainder is less than 0.5e10, wait until slightly past the start of the next range and call again.
- The new price will now likely have a remainder >= 0.5e10 and be rounded up.

For example:
- Range 1 ends at timestamp 10000, price is 199.9999999999e9
- Remainder is 0.99999999999e9, so price rounds down to 199e9
- Range 2 starts at timestamp 10001, new price is 200.5e9
- Remainder is now 0.5e9, so price rounds up to 201e9

By waiting for the next range, the caller increased the returned price by 1e9.

## Tools Used
Manual
## Recommended Mitigation Steps 
The roundsUpTo8 function could be changed to round to the nearest even number instead of always rounding up


## Assessed type

Other
Frontrunning Attack for setting range price
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L151
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L186


# Vulnerability details

## Impact

Oracle decentralization can be bypassed, allowing setRange function to be frontrunned by potential oracle feeder (or person in control of oracle feeder), allowing the frontrunner to yield higher interest and drain all protocol funds.

## Proof of Concept
The only way to update the price range for USDY  which is called using function setRange.

Current code forces the admin to call function setRange in order to update the price, but thecurrent implementation, the function did not check if it's not paused.

What would happen if the admin setRange was frontrunned by a feeder? The feeder who make the call will be allowed to set any price timestamp and interest rates without any restriction. This would allow the malicious hacker to yield higher interest and drain all protocol funds. The following concepts can also be applied to overrideRange function.

Exploit Scenario:
1) Alice programs bots to monitor pauseOracle() and setRange() 

2) Alice's bots detect the new change given price range for USDY and the corresponding unpausing, then proceeds to:

    Firstly, deposit more USDY
    Call setPrice through the feeder he control, and set the interest rate to the maximum value 
    Generate Yield
    Withdraw funds

```
  function setRange(
    uint256 endTimestamp,
    uint256 dailyInterestRate
  ) external onlyRole(SETTER_ROLE) {
    Range memory lastRange = ranges[ranges.length - 1];


    // Check that the endTimestamp is greater than the last range's end time
    if (lastRange.end >= endTimestamp) revert InvalidRange();


    uint256 prevClosePrice = derivePrice(lastRange, lastRange.end - 1);
    ranges.push(
      Range(lastRange.end, endTimestamp, dailyInterestRate, prevClosePrice)
    );
    emit RangeSet(
      ranges.length - 1,
      lastRange.end,
      endTimestamp,
      dailyInterestRate,
      prevClosePrice
    );
  }
```

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L151C1-L171C4
```
  function overrideRange(
    uint256 indexToModify,
    uint256 newStart,
    uint256 newEnd,
    uint256 newDailyIR,
    uint256 newPrevRangeClosePrice
  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
    // Check that the ranges start and end time are less than each other
    if (newStart >= newEnd) revert InvalidRange();

    uint256 rangeLength = ranges.length;
    // Case 1: The range being modified is the first range
    if (indexToModify == 0) {
      // If the length of ranges is greater than 1,
      // Ensure that the newEnd time is not greater than the start time of the next range
      if (rangeLength > 1 && newEnd > ranges[indexToModify + 1].st
The burn function in the rUSDY contract only emits a TokensBurnt event and does not emit a Transfer event.
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L672-L683


# Vulnerability details

## Impact
• Burn transactions are not properly recorded in the main token transfer log. This makes tracking burns and reconciling balances more difficult.
• Tools and services relying on the standard Transfer event for burns will not work correctly.
• Violates the ERC-20 standard which requires a Transfer event on burns


## Proof of Concept
When tokens are burned, a Transfer event should be emitted with the burnt tokens being transferred from the account being burnt to address(0). This allows burn transactions to be tracked in the transfer log and matches the behavior of a normal token transfer.

## Tools Used
Manual
## Recommended Mitigation Steps
A Transfer event should also be emitted from _account to address(0) for the _amount burnt:


## Assessed type

Other
blocked/sanctioned accounts can still approve other accounts to spend their tokens even though direct transfers are prevented
Audit: 2023-09-ondo-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L626-L655
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L276-L279
https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/usdy/rUSDY.sol#L301-L312


# Vulnerability details

## Impact
This allows a blocked/sanctioned account to approve another account, which can then drain their tokens via transferFrom even though direct transfers are blocked

## Proof of Concept

- The _beforeTokenTransfer hook checks the from/to addresses on direct transfers, but does not check the owner when approve() is called.
- A blocked/sanctioned account can call approve() to allow another address to spend their tokens.
- That other address can then call transferFrom() to transfer the tokens, even though direct transfers from the blocked account would fail.
This allows blocked accounts to bypass the transfer restrictions like:

// Blocked account
address blocked = 0x123; 

// Other account
address other = 0x456;

// Blocked account approves other to spend tokens
approve(other, 1000);

// Other can now transferFrom even though direct transfers would fail
other.transferFrom(blocked, other, 1000);


## Tools Used
Manual
## Recommended Mitigation Steps
the _beforeTokenTransfer hook should also check msg.sender when transferFrom is called, and revert if they are blocked/sanctioned.


## Assessed type

Other
Upgraded Q -> 2 from #506 [1695291399781]
Audit: 2023-09-ondo-findings
Severity: MEDIUM

Judge has assessed an item in Issue #506 as 2 risk. The relevant finding follows:

 [L‑01] The admin wont be able to burn rUSDY if the address is blacklisted/sanctioned and not on the allowlist
The burn() function in rUSDY.sol allows the admin to seize rUSDY if the user is not legally allowed to own it. It will first burn the shares and then it will transfer the USDY. The problem is that the user can be blacklisted/sanctioned which will make the tx revert because of _beforeTokenTransfer(). The user also needs to be on the allowlist.

It is very likely that the user will first be blacklisted to prevent him from transferring his assets before they are seized so the admin wont be able to seize the assets because when burning _beforeTokenTransfer() checks if the address is blacklisted/sanctioned or no and reverts.

Impact
The admin wont be able to seize assets from the user and he will have to unblacklist him and maybe put him on the allowlist to do this which can be a problem because the user can quickly transfer his assets when he is not blacklisted.
lack of address(0) check for oracle address
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/3362e1252f3a54943e2517460e5a7988388bc821/contracts/usdy/rUSDY.sol#L663


# Vulnerability details

## Impact
no oracle no updated prices. Not sure what happens if try to call getPrice() in this scenario, but IF it returns price of zero, then number of tokens from getRUSDYByShares() will be zero for any amount of USDY shares...

## Proof of Concept

## Tools Used

## Recommended Mitigation Steps

```solidity
  function setOracle(address _oracle) external onlyRole(USDY_MANAGER_ROLE) {
  ++ require(_oracle != address(0))
    oracle = IRWADynamicOracle(_oracle);  
  }
```


## Assessed type

Invalid Validation
Potential Out-of-Bounds Error When Modifying Ranges
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L194


# Vulnerability details

**The method allows for the modification of a range based on an index. However, there's no explicit check to ensure that the provided indexToModify is within the bounds of the ranges array. If an out-of-bounds index is provided, the method will throw a generic revert error without informing the user about the specific nature of the mistake.**

## Impact
User Confusion: Users or administrators trying to modify a range might encounter an unexpected revert without understanding the root cause.
System Interruption: Automated systems or scripts might halt due to unexpected reverts, leading to potential interruptions in the platform's operations.
Gas Wastage: Transactions that trigger this revert will waste gas fees, leading to monetary losses for users.

## Proof of Concept
Alice, an administrator, tries to modify the Range at index 10, but the ranges array only has 8 entries.
Instead of receiving a clear error message indicating the out-of-bounds mistake, Alice receives a generic revert error.
Due to the lack of clear error messaging, Alice spends additional time troubleshooting the issue and retrying the transaction multiple times, incurring additional gas costs and experiencing frustration.
Automated systems that interact with this function might also face unplanned halts, leading to broader system interruptions.

## Tools Used

## Recommended Mitigation Steps
Implement an explicit check for the indexToModify against the length of the ranges array and provide a clear error message if the index is out of bounds.
Introduce unit tests to cover edge cases around the boundaries of the ranges array to ensure smooth operation.
Consider introducing a more structured error reporting mechanism to help users and developers diagnose and rectify issues more efficiently.


## Assessed type

Invalid Validation
No zero address check on constructor parameters in contracts
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/bridge/DestinationBridge.sol#L55


# Vulnerability details

## Impact
Detailed description of the impact of this finding
There is no address(0) check in the constructor() of the following contracts;
1. Sourcesbridge.sol
2. rSUDY.sol
3. Destination ridge.sol
4. rSUDYFactory.sol
Lack of addr(0) check can lead to loss of important roles in the protocol dealing varying degree of damages.
## 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 review 
## Recommended Mitigation Steps
Check if the address passed in the input is the zero address and revert accordingly.
```solidity
   if(address =address(0) revert"");
```


## Assessed type

Invalid Validation
_execute can be reorder a batch of commands while executing
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/bridge/DestinationBridge.sol#L85-L89


# Vulnerability details

## Impact

Lets see what is written in by Axelar:

AxelarGatewayMultisig.execute() takes a signed batch of commands. Each command has a corresponding commandID. This is guaranteed to be unique from the Axelar network. execute intentionally allows retrying a commandID if the command failed to be processed; this is because commands are state dependent, and someone might submit command 2 before command 1 causing it to fail.

This means that an attacker can successfully rearrange execution order of commands within a batch. Because they allow reentrancy in the `execute()` function, a malicious player can gain reentrancy over execute and execute later commands in a batch before earlier commands are fully executed.

## Proof of Concept

This internal function is not safe from reentrancy and can be easily manipulated:

    function _execute(
      string calldata srcChain,
      string calldata srcAddr,
      bytes calldata payload
    ) internal override whenNotPaused {

Thus if an attacker manages to reenter the `_execute` function with the same batch of commands, previously successfully executed and ongoing commands will be skipped due to premature marking of the success flag.

## Tools Used

Manual review

## Recommended Mitigation Steps

Add a `nonReentrant` modifier in the `_execute()` internal function


## Assessed type

Reentrancy
Absence of Start-End Time Validation in Range Creation
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/47d34d6d4a5303af5f46e907ac2292e6a7745f6c/contracts/rwaOracles/RWADynamicOracle.sol#L111


# Vulnerability details

**The code allows the creation of a Range structure without ensuring that startTime is strictly less than endTime. The absence of this validation might lead to unintended behavior or logical errors in functionalities that rely on the order of these timestamps.**

## Impact
Inverse Range: Creating a range where endTime is earlier than startTime could lead to functions that depend on the time range to malfunction or return unintended results.

Zero-Length Range: If both startTime and endTime are the same, the range would effectively represent zero duration, which might cause miscalculations in time-based functionalities.

Any external contracts or operations relying on the correct ordering of startTime and endTime might fail or produce unexpected outcomes.

Potential Denial of Service or logic loops if functions assume endTime is always greater than startTime.

## Proof of Concept
Alice attempts to create a new Range with startTime set to a timestamp of 2023-12-15 and endTime set to 2023-12-01.

The contract allows the creation without any validation checks.

Bob, relying on the Range for a time-based calculation, fetches the range. However, due to the inverse order, his calculations fail or produce an incorrect result.

Carol, another user, tries to calculate a duration based on the same range and mistakenly assumes a duration of 14 days, when in fact the range is logically invalid.

## Tools Used

## Recommended Mitigation Steps
Introduce a validation step during the creation or modification of a Range to ensure that startTime is strictly less than endTime.

Implement appropriate error messages to inform the user or caller of the specific validation failure.

Expand test coverage to include scenarios where startTime and endTime are inverse or equal to ensure the contract behaves as expected.


## Assessed type

Invalid Validation
Minted Shares would be Inflate Shares by 10000 due to Math Error in the wrap function
Audit: 2023-09-ondo-findings
Severity: MEDIUM

# Lines of code

https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L436


# Vulnerability details

## Impact
BPS_DENOMINATOR in the rUSDY.sol contract is an important variable used to scale up usdy amount -> shares as noted in it comment [description](https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L90) but it is only used to scale up when minting shares but not scaled down before subtracting it from total shares and burning it, this would cause fund math calculation related errors and possible inflation of shares
## Proof of Concept
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L436
```solidity
 function wrap(uint256 _USDYAmount) external whenNotPaused {
    require(_USDYAmount > 0, "rUSDY: can't wrap zero USDY tokens");
--->    _mintShares(msg.sender, _USDYAmount * BPS_DENOMINATOR);
    usdy.transferFrom(msg.sender, address(this), _USDYAmount);
    emit Transfer(address(0), msg.sender, getRUSDYByShares(_USDYAmount));
    emit TransferShares(address(0), msg.sender, _USDYAmount);
  }
```
The function above shows how the _USDy amouth is scaled up before minting shares however no scale down was done before burning shares as would be shown below
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L449-L453
```solidity
 function unwrap(uint256 _rUSDYAmount) external whenNotPaused {
    require(_rUSDYAmount > 0, "rUSDY: can't unwrap zero rUSDY tokens");
    uint256 usdyAmount = getSharesByRUSDY(_rUSDYAmount);
    if (usdyAmount < BPS_DENOMINATOR) revert UnwrapTooSmall();
----> _burnShares(msg.sender, usdyAmount);
    usdy.transfer(msg.sender, usdyAmount / BPS_DENOMINATOR);
    emit TokensBurnt(msg.sender, _rUSDYAmount);
  }
```
https://github.com/code-423n4/2023-09-ondo/blob/main/contracts/usdy/rUSDY.sol#L588
```solidity
  function _burnShares(
    address _account,
    uint256 _sharesAmount
  ) internal whenNotPaused returns (uint256) {
    require(_account != address(0), "BURN_FROM_THE_ZERO_ADDRESS");

    _beforeTokenTransfer(_account, address(0), _sharesAmount);

    uint256 accountShares = shares[_account];
    require(_sharesAmount <= accountShares, "BURN_AMOUNT_EXCEEDS_BALANCE");

    uint256 preRebaseTokenAmount = getRUSDYByShares(_sharesAmount);

 ---->totalShares -= _sharesAmount;
...
```
As seen from the two functions above, at no point in time in the unwrap(...) or _burnShares(...) functions was a scale down down with BPS_DENOMINATOR before deducting it from total shares
## Tools Used
Manual Review
## Recommended Mitigation Steps
Mitigation depends on Sponsor preference, one way could be to ensure a scale down of the shares with BPS_DENOMINATOR before burning the shares in unwrap function call, another way could be to mint the shares without up scaling it in the wrap function call as provided below.
multiplication of _USDYAmount with BPS_DENOMINATOR should simply be replaced with just _USDYAmount
```solidity
 function wrap(ui