Ondo Finance - pfapostol's results

Institutional-Grade Finance, Now Onchain.

General Information

Platform: Code4rena

Start Date: 29/03/2024

Pot Size: $36,500 USDC

Total HM: 5

Participants: 72

Period: 5 days

Judge: 3docSec

Total Solo HM: 1

Id: 357

League: ETH

Ondo Finance

Findings Distribution

Researcher Performance

Rank: 49/72

Findings: 1

Award: $8.28

🌟 Selected for report: 0

🚀 Solo Findings: 0

The content section shows only 10 examples, subsequent cases are listed as links.

Summary

Low Risk Issues

IssueInstances
[L-01]increase/decrease allowance should be used instead of approve4
[L-02]Must approve or increase allowance first4
[L-03]Emitting storage values instead of the memory one.13
[L-04]Missing contract-existence checks before low-level calls2
[L-05]Functions calling contracts/addresses with transfer hooks are missing reentrancy guards4
[L-06]Approve zero first2
[L-07]Code does not follow the best practice of check-effects-interaction15
[L-08]Consider bounding input array length2
[L-09]approve will always revert as the IERC20 interface mismatch2
[L-10]Return values of approve not checked2
[L-11]Large transfers may not work with some ERC20 tokens9
[L-12]Large approvals may not work with some ERC20 tokens2
[L-13]Use increaseAllowance()/decreaseAllowance() instead of approve()/safeApprove()2
[L-14]Consider the case where totalSupply is zero1

NonCritical Risk Issues

IssueInstances
[N-01]Keccak state variables should be immutable not constant5
[N-02]Private and internal state variables should have a preceding _ in their name unless they are constants4
[N-03]Emits without msg.sender parameter1
[N-04]NatSpec @param is missing30
[N-05]Use multiple require() and if statements instead of &&1
[N-06]Import only specific files23
[N-07]Constants in comparisons should appear on the left side33
[N-08]Consider using delete rather than assigning zero/false to clear values2
[N-09]Unused contract variables1
[N-10]Remaining eth may not be refunded to users2
[N-11]External calls in an un-bounded for-loop may result in a DOS2
[N-12]constructor missing zero address check2
[N-13]Large multiples of ten should use scientific notation3
[N-14]Complex math should be split into multiple steps1
[N-15]Duplicated require/if statements should be refactored19
[N-16]Variable names don't follow the Solidity naming convention1
[N-17]Contract functions should use an interface22
[N-18]State variables should include comments34
[N-19]Missing NatSpec from contract declarations1
[N-20]Inconsistent usage of require/error17
[N-21]Use of override is unnecessary20
[N-22]Modifier declarations should have NatSpec descriptions1
[N-23]Function declarations should have NatSpec descriptions13
[N-24]Function names should use lowerCamelCase13
[N-25]Setters should prevent re-setting of the same value11
[N-26]Consider disallowing transfers to address(this)4
[N-27]Named imports of parent contracts are missing3
[N-28]Events may be emitted out of order due to reentrancy9
[N-29]Missing zero address check in initializer1
[N-30]Missing NatSpec @author from contract declaration1
[N-31]Missing NatSpec @dev from contract declaration2
[N-32]Missing NatSpec @notice from contract declaration1
[N-33]Missing NatSpec from error declaration1
[N-34]Missing NatSpec @dev from error declaration1
[N-35]Missing NatSpec @notice from error declaration1
[N-36]Missing NatSpec @param from error declaration1
[N-37]Missing NatSpec @dev from event declaration1
[N-38]Missing NatSpec @notice from event declaration1
[N-39]Missing NatSpec @param from event declaration1
[N-40]Missing NatSpec from modifiers definitions1
[N-41]Missing NatSpec @dev from modifier declaration3
[N-42]Missing NatSpec @notice from modifier declaration1
[N-43]Missing NatSpec @param from modifier declaration3
[N-44]Missing NatSpec @dev from function declaration48
[N-45]Missing NatSpec @notice from function declaration26
[N-46]Simplify complex require statements1
[N-47]Events should be emitted before external calls14
[N-48]Use @inheritdoc for overridden functions20
[N-49]Missing NatSpec from variable declarations34
[N-50]Contract name does not follow the Solidity Style Guide3
[N-51]Variable names for immutables should use UPPER_CASE_WITH_UNDERSCORES8
[N-52]Use a single file for system wide constants10
[N-53]Contracts should have full test coverage1
[N-54]Large or complicated code bases should implement invariant tests1
[N-55]Codebase should implement formal verification testing1
[N-56]State variables not capped at reasonable values1
[N-57]Consider adding a block/deny-list3
[N-58]Expressions for constant values should use immutable rather than constant6
[N-59]Lines are too long15
[N-60]Typos1

Low Risk Issues

[L-01]<a name="l-01"></a> increase/decrease allowance should be used instead of approve

In order to prevent front running, increase/decrease allowance should be used in place of approve where possible

There are 4 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
252	    _approve(msg.sender, _spender, _amount);
...
285	    _approve(_sender, msg.sender, currentAllowance - _amount);
...
306	    _approve(
307	      msg.sender,
308	      _spender,
309	      allowances[msg.sender][_spender] + _addedValue
310	    );
...
337	    _approve(msg.sender, _spender, currentAllowance - _subtractedValue);

GitHub : 252, 285, 306..310, 337

[L-02]<a name="l-02"></a> Must approve or increase allowance first

In the context of ERC20 tokens, a token holder needs to "approve" another account to transfer a certain amount of their tokens on their behalf. This is accomplished by calling the approve() function, which takes the address of the spender and the amount they're allowed to spend as parameters.

This approval step is a crucial part of the ERC20 standard because it allows for secure delegated transfers. A common use case for this feature is in decentralized exchanges or DeFi protocols, where a user can approve a smart contract to transfer tokens on their behalf.

After the approval step, the approved account (or contract) can then transfer tokens up to the approved amount from the token holder's account by calling the transferFrom() function. This function takes three parameters: the address of the token holder, the recipient's address, and the amount to transfer.

If an account tries to call transferFrom() before the token holder has called approve(), the transaction will fail because the ERC20 contract checks whether the transferFrom() caller has an adequate allowance.

In summary, if a contract or user needs to move ERC20 tokens on behalf of another account, it's necessary to ensure that the token holder has first called approve() to set a sufficient allowance. This is a key aspect of ERC20 token security and functionality.

There are 4 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
 // @audit function call transfer from, but not approve
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
317	      usdc.transferFrom(msg.sender, feeReceiver, usdcfees);
...
319	    usdc.transferFrom(msg.sender, usdcReceiver, usdcAmountAfterFee);
...
 // @audit function call transfer from, but not approve
335	  function redeem(
336	    uint256 ousgAmountIn
337	  )
338	    external
339	    override
340	    nonReentrant
341	    whenRedeemNotPaused
342	    returns (uint256 usdcAmountOut)
343	  {
...
348	    ousg.transferFrom(msg.sender, address(this), ousgAmountIn);

GitHub : 317, 319, 348


- contracts/ousg/rOUSG.sol
 // @audit function call transfer from, but not approve
415	  function wrap(uint256 _OUSGAmount) external whenNotPaused {
...
419	    ousg.transferFrom(msg.sender, address(this), _OUSGAmount);

GitHub : 419

[L-03]<a name="l-03"></a> Emitting storage values instead of the memory one.

Emitted values should not be read from storage again. Instead, the existing values from memory should be used.

There are 13 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
321	    emit MintFeesDeducted(msg.sender, feeReceiver, usdcfees, usdcAmountIn);
...
435	      emit MinimumBUIDLRedemption(
436	        msg.sender,
437	        minBUIDLRedeemAmount,
438	        usdcBalance + minBUIDLRedeemAmount - usdcAmountToRedeem
439	      );
...
453	    emit RedeemFeesDeducted(msg.sender, feeReceiver, usdcFees, usdcAmountOut);
...
558	    emit MintFeeSet(mintFee, _mintFee);
...
571	    emit RedeemFeeSet(redeemFee, _redeemFee);
...
589	    emit MinimumDepositAmountSet(minimumDepositAmount, _minimumDepositAmount);
...
606	    emit MinimumRedemptionAmountSet(
607	      minimumRedemptionAmount,
608	      _minimumRedemptionAmount
609	    );
...
625	    emit MinimumBUIDLRedemptionAmountSet(
626	      minBUIDLRedeemAmount,
627	      _minimumBUIDLRedemptionAmount
628	    );
...
641	    emit OracleSet(address(oracle), _oracle);
...
654	    emit FeeReceiverSet(feeReceiver, _feeReceiver);

GitHub : 321, 435..439, 453, 558, 571, 589, 606..609, 625..628, 641, 654, 666..669


- contracts/ousg/rOUSG.sol

GitHub : 614


- contracts/ousg/rOUSGFactory.sol

GitHub : 96..102

[L-04]<a name="l-04"></a> Missing contract-existence checks before low-level calls

Low-level calls return success if there is no code present at the specified address. In addition to the zero-address checks, add a check to verify that <address>.code.length > 0

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit contract existance not checked before this call
805	      (bool success, bytes memory ret) = address(exCallData[i].target).call{

GitHub : 805


- contracts/ousg/rOUSGFactory.sol
// @audit contract existance not checked before this call
126	      (bool success, bytes memory ret) = address(exCallData[i].target).call{

GitHub : 126

[L-05]<a name="l-05"></a> Functions calling contracts/addresses with transfer hooks are missing reentrancy guards

Even if the function follows the best practice of check-effects-interaction, not using a reentrancy guard when there may be transfer hooks will open the users of this protocol up to read-only reentrancies with no way to protect against it, except by block-listing the whole protocol.`

There are 4 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
819	  function retrieveTokens(
820	    address token,
821	    address to,
822	    uint256 amount
823	  ) external onlyRole(DEFAULT_ADMIN_ROLE) {
...
// @audit Function `retrieveTokens` doesn't  have the `nonReentrant` modifier
824	    IERC20(token).transfer(to, amount);

GitHub : 824


- contracts/ousg/rOUSG.sol
415	  function wrap(uint256 _OUSGAmount) external whenNotPaused {
...
// @audit Function `wrap` doesn't  have the `nonReentrant` modifier
419	    ousg.transferFrom(msg.sender, address(this), _OUSGAmount);
...
431	  function unwrap(uint256 _rOUSGAmount) external whenNotPaused {
...
// @audit Function `unwrap` doesn't  have the `nonReentrant` modifier
437	    ousg.transfer(
...
624	  function burn(
625	    address _account,
626	    uint256 _amount
627	  ) external onlyRole(BURNER_ROLE) {
...
// @audit Function `burn` doesn't  have the `nonReentrant` modifier
634	    ousg.transfer(

GitHub : 419, 437, 634

[L-06]<a name="l-06"></a> Approve zero first

Some ERC20 tokens (like USDT) do not work when changing the allowance from an existing non-zero allowance value. For example Tether (USDT)'s approve() function will revert if the current approval is not zero, to protect against front-running changes of approvals.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
254	  function mintRebasingOUSG(
255	    uint256 usdcAmountIn
256	  )
257	    external
258	    override
259	    nonReentrant
260	    whenMintNotPaused
261	    returns (uint256 rousgAmountOut)
262	  {
263	    uint256 ousgAmountOut = _mint(usdcAmountIn, address(this));
264	    ousg.approve(address(rousg), ousgAmountOut);
265	    rousg.wrap(ousgAmountOut);
266	    rousgAmountOut = rousg.getROUSGByShares(
267	      ousgAmountOut * OUSG_TO_ROUSG_SHARES_MULTIPLIER
268	    );
269	    rousg.transfer(msg.sender, rousgAmountOut);
270	    emit InstantMintRebasingOUSG(
271	      msg.sender,
272	      usdcAmountIn,
273	      ousgAmountOut,
274	      rousgAmountOut
275	    );
276	  }
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
459	    require(
460	      buidl.balanceOf(address(this)) >= minBUIDLRedeemAmount,
461	      "OUSGInstantManager::_redeemBUIDL: Insufficient BUIDL balance"
462	    );
463	    uint256 usdcBalanceBefore = usdc.balanceOf(address(this));
464	    buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);
465	    buidlRedeemer.redeem(buidlAmountToRedeem);
466	    require(
467	      usdc.balanceOf(address(this)) == usdcBalanceBefore + buidlAmountToRedeem,
468	      "OUSGInstantManager::_redeemBUIDL: BUIDL:USDC not 1:1"
469	    );
470	  }

GitHub : 254..276, 458..470

[L-07]<a name="l-07"></a> Code does not follow the best practice of check-effects-interaction

Code should follow the best-practice of check-effects-interaction, where state variables are updated before any external calls are made. Doing so prevents a large class of reentrancy bugs.

There are 15 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Some statements does not follow CEI
335	  function redeem(
336	    uint256 ousgAmountIn
337	  )
338	    external
339	    override
340	    nonReentrant
341	    whenRedeemNotPaused
342	    returns (uint256 usdcAmountOut)
343	  {
...
// @audit Statement out of CEI order
349	    usdcAmountOut = _redeem(ousgAmountIn);
...
// @audit Some statements does not follow CEI
362	  function redeemRebasingOUSG(
363	    uint256 rousgAmountIn
364	  )
365	    external
366	    override
367	    nonReentrant
368	    whenRedeemNotPaused
369	    returns (uint256 usdcAmountOut)
370	  {
...
// @audit Statement out of CEI order
379	    usdcAmountOut = _redeem(ousgAmountIn);
...
// @audit Some statements does not follow CEI
554	  function setMintFee(
555	    uint256 _mintFee
556	  ) external override onlyRole(CONFIGURER_ROLE) {
...
// @audit Statement out of CEI order
559	    mintFee = _mintFee;
...
// @audit Some statements does not follow CEI
567	  function setRedeemFee(
568	    uint256 _redeemFee
569	  ) external override onlyRole(CONFIGURER_ROLE) {
...
// @audit Statement out of CEI order
572	    redeemFee = _redeemFee;
...
// @audit Some statements does not follow CEI
581	  function setMinimumDepositAmount(
582	    uint256 _minimumDepositAmount
583	  ) external override onlyRole(CONFIGURER_ROLE) {
...
// @audit Statement out of CEI order
590	    minimumDepositAmount = _minimumDepositAmount;

GitHub : 349, 379, 559, 572, 590, 610, 655, 809


- contracts/ousg/rOUSG.sol

GitHub : 403, 480, 518, 539, 567


- contracts/ousg/rOUSGFactory.sol

GitHub : 95, 130

[L-08]<a name="l-08"></a> Consider bounding input array length

The functions below take in an unbounded array, and make function calls for entries in the array. While the function will revert if it eventually runs out of gas, it may be a nicer user experience to require() that the length of the array is below some reasonable maximum, so that the user doesn't have to use up a full transaction's gas only to see that the transaction reverts.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit array parameter `exCallData` is used as loop condition and length is not cheched in function
795	    ExCallData[] calldata exCallData
...
// @audit loop uses unbounded parameter
804	    for (uint256 i = 0; i < exCallData.length; ++i) {

GitHub : 804


- contracts/ousg/rOUSGFactory.sol
// @audit array parameter `exCallData` is used as loop condition and length is not cheched in function
122	    ExCallData[] calldata exCallData
...
// @audit loop uses unbounded parameter
125	    for (uint256 i = 0; i < exCallData.length; ++i) {

GitHub : 125

[L-09]<a name="l-09"></a> approve will always revert as the IERC20 interface mismatch

Some tokens, such as USDT, have a different implementation for the approve function: when the address is cast to a compliant IERC20 interface and the approve function is used, it will always revert due to the interface mismatch.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
264	    ousg.approve(address(rousg), ousgAmountOut);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
464	    buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);

GitHub : 264, 464

[L-10]<a name="l-10"></a> Return values of approve not checked

Not all IERC20 implementations (e.g. USDT, KNC) revert when there's a failure in approve. The function signature has a boolean return value and they indicate errors that way instead.

By not checking the return value, operations that should have marked as failed, may potentially go through without actually approving anything.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
264	    ousg.approve(address(rousg), ousgAmountOut);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
464	    buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);

GitHub : 264, 464

[L-11]<a name="l-11"></a> Large transfers may not work with some ERC20 tokens

Some IERC20 implementations (e.g UNI, COMP) may fail if the valued transferred is larger than uint96. Source.

There are 9 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
317	      usdc.transferFrom(msg.sender, feeReceiver, usdcfees);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
319	    usdc.transferFrom(msg.sender, usdcReceiver, usdcAmountAfterFee);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
348	    ousg.transferFrom(msg.sender, address(this), ousgAmountIn);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
451	      usdc.transfer(feeReceiver, usdcFees);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
455	    usdc.transfer(msg.sender, usdcAmountOut);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
824	    IERC20(token).transfer(to, amount);

GitHub : 317, 319, 348, 451, 455, 824


- contracts/ousg/rOUSG.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
419	    ousg.transferFrom(msg.sender, address(this), _OUSGAmount);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
437	    ousg.transfer(
438	      msg.sender,
439	      ousgSharesAmount / OUSG_TO_ROUSG_SHARES_MULTIPLIER
440	    );
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
634	    ousg.transfer(
635	      msg.sender,
636	      ousgSharesAmount / OUSG_TO_ROUSG_SHARES_MULTIPLIER
637	    );

GitHub : 419, 437..440, 634..637

[L-12]<a name="l-12"></a> Large approvals may not work with some ERC20 tokens

Not all IERC20 implementations are totally compliant, and some (e.g UNI, COMP) may fail if the valued passed to approve is larger than uint96. If the approval amount is type(uint256).max, which may cause issues with systems that expect the value passed to approve to be reflected in the allowances mapping.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
264	    ousg.approve(address(rousg), ousgAmountOut);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
464	    buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);

GitHub : 264, 464

[L-13]<a name="l-13"></a> Use increaseAllowance()/decreaseAllowance() instead of approve()/safeApprove()

Changing an allowance with approve() brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. Refer to ERC20 API: An Attack Vector on the Approve/TransferFrom Methods. It is recommended to use the increaseAllowance()/decreaseAllowance() to avoid ths problem.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
264	    ousg.approve(address(rousg), ousgAmountOut);
...
// @audit Contracts that implement `ERC20`: `ERC20`, `IERC20`, `IERC20Metadata`, `IOMMF`, `IRWALike`, `IUSDY`, `IWOMMF`, `MockUSDC`, `SafeTransferLib`, `SafeERC20`
464	    buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);

GitHub : 264, 464

[L-14]<a name="l-14"></a> Consider the case where totalSupply is zero

The following functions should handle the edge case where the totalSupply is zero, for example to avoid division by zero errors, as such errors may negatively impact the logic of these functions.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
188	  function totalSupply() public view returns (uint256) {
189	    return
190	      (totalShares * getOUSGPrice()) / (1e18 * OUSG_TO_ROUSG_SHARES_MULTIPLIER);
191	  }

GitHub : 188..191

NonCritical Risk Issues

[N-01]<a name="n-01"></a> Keccak state variables should be immutable not constant

Constant keccak variables should be replaced with immutable variables in Solidity contracts to optimize gas usage and enhance efficiency. While constant variables are evaluated and computed at runtime, immutable variables are assigned during contract deployment and stored directly in the contract bytecode. By using immutable for keccak variables, their hash values are computed only once during deployment, reducing the gas cost associated with repeated computations at runtime. This approach leads to more efficient execution, conserving resources for users, and allowing for smoother contract interactions, ultimately benefiting the overall performance and user experience.

There are 5 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");
...
60	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

GitHub : 57, 60


- contracts/ousg/rOUSG.sol
93	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
94	  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
...
95	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 93, 94, 95

[N-02]<a name="n-02"></a> Private and internal state variables should have a preceding _ in their name unless they are constants

Add a preceding underscore to the state variable name, take care to refactor where there variables are read/wrote

There are 4 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
72	  mapping(address => uint256) private shares;
...
75	  mapping(address => mapping(address => uint256)) private allowances;
...
78	  uint256 private totalShares;

GitHub : 72, 75, 78


- contracts/ousg/rOUSGFactory.sol
40	  address internal immutable guardian;

GitHub : 40

[N-03]<a name="n-03"></a> Emits without msg.sender parameter

If msg.sender play a part in the functionality of a function, any emits of this function should include msg.sender to ensure transparency with users

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
// @audit Current function use `msg.sender`, but do not emit it
639	    emit TransferShares(_account, address(0), ousgSharesAmount);

GitHub : 639

[N-04]<a name="n-04"></a> NatSpec @param is missing

There are 30 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
388	  function _redeem(
389	    uint256 ousgAmountIn
390	  ) internal returns (uint256 usdcAmountOut) {
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
...
731	  /**
732	   * @notice Scale provided amount up by `decimalsMultiplier`
733	   *
734	   * @dev This helper is used for converting a USDC amount's decimals
735	   *      representation to the rOUSG/OUSG decimals representation.
736	   */
737	  function _scaleUp(uint256 amount) internal view returns (uint256) {
...
741	  /**
742	   * @notice Scale provided amount down by `decimalsMultiplier`
743	   *
744	   * @dev This helper is used for converting an rOUSG/OUSG amount's decimals
745	   *      representation to the USDC decimals representation.
746	   */
747	  function _scaleDown(uint256 amount) internal view returns (uint256) {
...
794	  function multiexcall(
795	    ExCallData[] calldata exCallData
796	  )
797	    external
798	    payable
799	    override
800	    onlyRole(DEFAULT_ADMIN_ROLE)
801	    returns (bytes[] memory results)
802	  {

GitHub : 278..281, 388..390, 458, 731..737, 741..747, 794..802


- contracts/ousg/rOUSG.sol
102	  function initialize(
103	    address _kycRegistry,
104	    uint256 requirementGroup,
105	    address _ousg,
106	    address guardian,
107	    address _oracle
108	  ) public virtual initializer {
...
112	  function __rOUSG_init(
113	    address _kycRegistry,
114	    uint256 requirementGroup,
115	    address _ousg,
116	    address guardian,
117	    address _oracle
118	  ) internal onlyInitializing {
...
128	  function __rOUSG_init_unchained(
129	    address _kycRegistry,
130	    uint256 _requirementGroup,
131	    address _ousg,
132	    address guardian,
133	    address _oracle
134	  ) internal onlyInitializing {
...
193	  /**
194	   * @return the amount of tokens owned by the `_account`.
195	   *
196	   * @dev Balances are dynamic and equal the `_account`'s OUSG shares multiplied
197	   *      by the price of OUSG
198	   */
199	  function balanceOf(address _account) public view returns (uint256) {

GitHub : 102..108, 112..118, 128..134, 193..199, 205..220, 225..234, 238..251, 256..280, 289..305, 314..331, 351..356, 360..365, 370..373, 382..400, 445..454, 461..476, 484..487, 491..505, 521..532, 544..557, 572..590, 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 47

[N-05]<a name="n-05"></a> Use multiple require() and if statements instead of &&

Using multiple require() and if improves code readability and makes it easier to debug.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
593	    if (from != msg.sender && to != msg.sender) {
594	      require(_getKYCStatus(msg.sender), "rOUSG: 'sender' address not KYC'd");
595	    }

GitHub : 593..595

[N-06]<a name="n-06"></a> Import only specific files

Using import declarations of the form import {<identifier_name>} from "some/file.sol" avoids polluting the symbol namespace making flattened files smaller, and speeds up compilation

There are 23 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
18	import "contracts/external/openzeppelin/contracts/access/AccessControlEnumerable.sol";
...
19	import "contracts/external/openzeppelin/contracts/security/ReentrancyGuard.sol";
...
20	import "contracts/external/openzeppelin/contracts/token/IERC20Metadata.sol";
...
21	import "contracts/ousg/rOUSG.sol";
...
22	import "contracts/interfaces/IRWALike.sol";
...
23	import "contracts/interfaces/IBUIDLRedeemer.sol";
...
24	import "contracts/InstantMintTimeBasedRateLimiter.sol";
...
25	import "contracts/interfaces/IOUSGInstantManager.sol";
...
26	import "contracts/interfaces/IMulticall.sol";
...
27	import "contracts/interfaces/IInvestorBasedRateLimiter.sol";

GitHub : 18, 19, 20, 21, 22, 23, 24, 25, 26, 27


- contracts/ousg/rOUSG.sol

GitHub : 18, 19, 20, 21, 22, 23, 24, 25, 26


- contracts/ousg/rOUSGFactory.sol

GitHub : 19, 20, 21, 22

[N-07]<a name="n-07"></a> Constants in comparisons should appear on the left side

Doing so will prevent typo bugs

There are 33 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
164	      address(_usdc) != address(0),
...
168	      address(_usdcReciever) != address(0),
...
172	      address(_feeReceiver) != address(0),
...
176	      address(_ousgOracle) != address(0),
...
179	    require(_ousg != address(0), "OUSGInstantManager: OUSG cannot be 0x0");
...
180	    require(_rousg != address(0), "OUSGInstantManager: rOUSG cannot be 0x0");
...
181	    require(_buidl != address(0), "OUSGInstantManager: BUIDL cannot be 0x0");
...
183	      address(_buidlRedeemer) != address(0),
...
283	      IERC20Metadata(address(usdc)).decimals() == 6,
...
291	    if (address(investorBasedRateLimiter) != address(0)) {

GitHub : 164, 168, 172, 176, 179, 180, 181, 183, 283, 291, 311, 316, 392, 396, 408, 418, 450, 557, 570, 653, 689, 704


- contracts/ousg/rOUSG.sol

GitHub : 367, 416, 432, 477, 478, 506, 507, 533, 558, 597, 602

[N-08]<a name="n-08"></a> Consider using delete rather than assigning zero/false to clear values

The delete keyword more closely matches the semantics of what is being done, and draws more attention to the changing of state, which may lead to a more thorough audit of its associated logic

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
775	    mintPaused = false;
...
787	    redeemPaused = false;

GitHub : 775, 787

[N-09]<a name="n-09"></a> Unused contract variables

Note that there may be cases where a variable appears to be used, but this is only because there are multiple definitions of the varible in different files. In such cases, the variable definition should be moved into a separate file. The instances below are the unused variables.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSGFactory.sol
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 38

[N-10]<a name="n-10"></a> Remaining eth may not be refunded to users

When a contract function accepts Ethereum and executes a .call() or similar function that also forwards Ethereum value, it's important to check for and refund any remaining balance. This is because some of the supplied value may not be used during the call execution due to gas constraints, a revert in the called contract, or simply because not all the value was needed.

If you do not account for this remaining balance, it can become "locked" in the contract. It's crucial to either return the remaining balance to the sender or handle it in a way that ensures it is not permanently stuck. Neglecting to do so can lead to loss of funds and degradation of the contract's reliability. Furthermore, it's good practice to ensure fairness and trust with your users by returning unused funds.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
794	  function multiexcall(
795	    ExCallData[] calldata exCallData
796	  )
797	    external
798	    payable
799	    override
800	    onlyRole(DEFAULT_ADMIN_ROLE)
801	    returns (bytes[] memory results)
802	  {

GitHub : 794..802


- contracts/ousg/rOUSGFactory.sol
121	  function multiexcall(
122	    ExCallData[] calldata exCallData
123	  ) external payable override onlyGuardian returns (bytes[] memory results) {

GitHub : 121..123

[N-11]<a name="n-11"></a> External calls in an un-bounded for-loop may result in a DOS

Consider limiting the number of iterations in for-loops that make external calls

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
804	    for (uint256 i = 0; i < exCallData.length; ++i) {
805	      (bool success, bytes memory ret) = address(exCallData[i].target).call{
806	        value: exCallData[i].value
807	      }(exCallData[i].data);
808	      require(success, "Call Failed");
809	      results[i] = ret;
810	    }

GitHub : 804..810


- contracts/ousg/rOUSGFactory.sol
125	    for (uint256 i = 0; i < exCallData.length; ++i) {
126	      (bool success, bytes memory ret) = address(exCallData[i].target).call{
127	        value: exCallData[i].value
128	      }(exCallData[i].data);
129	      require(success, "Call Failed");
130	      results[i] = ret;
131	    }

GitHub : 125..131

[N-12]<a name="n-12"></a> constructor missing zero address check

It is important to ensure that the constructor does not allow zero address to be set. This is a common mistake that can lead to loss of funds or redeployment of the contract.

There are 2 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
144	  constructor(
145	    address defaultAdmin,
146	    address _usdc,
147	    address _usdcReciever,
148	    address _feeReceiver,
149	    address _ousgOracle,
150	    address _ousg,
151	    address _rousg,
152	    address _buidl,
153	    address _buidlRedeemer,
154	    RateLimiterConfig memory rateLimiterConfig
155	  )
156	    InstantMintTimeBasedRateLimiter(
157	      rateLimiterConfig.mintLimitDuration,
158	      rateLimiterConfig.redeemLimitDuration,
159	      rateLimiterConfig.mintLimit,
160	      rateLimiterConfig.redeemLimit
161	    )
162	  {

GitHub : 144..162


- contracts/ousg/rOUSGFactory.sol
47	  constructor(address _guardian) {

GitHub : 47

[N-13]<a name="n-13"></a> Large multiples of ten should use scientific notation

Use a scientific notation rather than decimal literals (e.g. 1e6 instead of 1000000), for better code readability. The same for exponentiation (e.g. 1e18 instead of 10**18): although the compiler is capable of optimizing it, it is considered good coding practice to utilize idioms that don't rely on compiler optimization, whenever possible.

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
66	  uint256 public constant FEE_GRANULARITY = 10_000;
...
69	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;

GitHub : 66, 69


- contracts/ousg/rOUSG.sol
87	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;

GitHub : 87

[N-14]<a name="n-14"></a> Complex math should be split into multiple steps

Consider splitting long arithmetic calculations into multiple steps to improve the code readability.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
367	      (_rOUSGAmount * 1e18 * OUSG_TO_ROUSG_SHARES_MULTIPLIER) / getOUSGPrice();

GitHub : 367

[N-15]<a name="n-15"></a> Duplicated require/if statements should be refactored

These statements should be refactored to a separate function, as there are multiple parts of the codebase that use the same logic, to improve the code readability and reduce code duplication.

There are 19 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
283	      IERC20Metadata(address(usdc)).decimals() == 6,
...
291	    if (address(investorBasedRateLimiter) != address(0)) {
...
392	      IERC20Metadata(address(usdc)).decimals() == 6,
...
396	      IERC20Metadata(address(buidl)).decimals() == 6,
...
408	    if (address(investorBasedRateLimiter) != address(0)) {
...
164	      address(_usdc) != address(0),
...
168	      address(_usdcReciever) != address(0),
...
172	      address(_feeReceiver) != address(0),
...
176	      address(_ousgOracle) != address(0),
...
183	      address(_buidlRedeemer) != address(0),

GitHub : 283, 291, 392, 396, 408, 164, 168, 172, 176, 183, 187, 191


- contracts/ousg/rOUSG.sol

GitHub : 434, 507, 533, 594, 599, 604, 629

[N-16]<a name="n-16"></a> Variable names don't follow the Solidity naming convention

Use mixedCase for local and state variables that are not constants, and add a trailing underscore for internal variables. Documentation

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
455	    uint256 _sharesToTransfer = getSharesByROUSG(_amount);

GitHub : 455

[N-17]<a name="n-17"></a> Contract functions should use an interface

All external/public functions should extend an interface. This is useful to make sure that the whole API is extracted.

There are 22 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
479	  function getOUSGPrice() public view returns (uint256 price) {
...
768	  function pauseMint() external onlyRole(PAUSER_ROLE) {
...
774	  function unpauseMint() external onlyRole(DEFAULT_ADMIN_ROLE) {
...
780	  function pauseRedeem() external onlyRole(PAUSER_ROLE) {
...
786	  function unpauseRedeem() external onlyRole(DEFAULT_ADMIN_ROLE) {
...
819	  function retrieveTokens(
820	    address token,
821	    address to,
822	    uint256 amount
823	  ) external onlyRole(DEFAULT_ADMIN_ROLE) {

GitHub : 479, 768, 774, 780, 786, 819..823


- contracts/ousg/rOUSG.sol
102	  function initialize(
103	    address _kycRegistry,
104	    uint256 requirementGroup,
105	    address _ousg,
106	    address guardian,
107	    address _oracle
108	  ) public virtual initializer {
...
302	  function increaseAllowance(
303	    address _spender,
304	    uint256 _addedValue
305	  ) public returns (bool) {
...
328	  function decreaseAllowance(
329	    address _spender,
330	    uint256 _subtractedValue
331	  ) public returns (bool) {
...
347	  function getTotalShares() public view returns (uint256) {

GitHub : 102..108, 302..305, 328..331, 347, 356, 363..365, 373, 378, 397..400, 415, 431, 613, 624..627, 642, 646


- contracts/ousg/rOUSGFactory.sol

GitHub : 70..75

[N-18]<a name="n-18"></a> State variables should include comments

Consider adding some comments on critical state variables to explain what they are supposed to do: this will help for future code reviews.

There are 34 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");
...
60	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
63	  uint256 public constant MINIMUM_OUSG_PRICE = 105e18;
...
66	  uint256 public constant FEE_GRANULARITY = 10_000;
...
69	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;
...
72	  IERC20 public immutable usdc; // 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
...
75	  IRWALike public immutable ousg;
...
78	  ROUSG public immutable rousg;
...
81	  IERC20 public immutable buidl;
...
84	  IBUIDLRedeemer public immutable buidlRedeemer;

GitHub : 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 106, 110, 113, 116, 120, 123


- contracts/ousg/rOUSG.sol

GitHub : 78, 81, 84, 87, 94, 95


- contracts/ousg/rOUSGFactory.sol

GitHub : 38, 40, 41, 42, 43, 45

[N-19]<a name="n-19"></a> Missing NatSpec from contract declarations

Some contracts miss a @dev/@notice NatSpec, which should be a best practice to add as a documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
28	/**
29	 * @title Interest-bearing ERC20-like token for OUSG.
30	 *
31	 * rOUSG balances are dynamic and represent the holder's share of the underlying OUSG
32	 * controlled by the protocol. To calculate each account's balance, we do
33	 *
34	 *   shares[account] * ousgPrice
35	 *
36	 * For example, assume that we have:
37	 *
38	 *   ousgPrice = 100.505
39	 *   sharesOf(user1) -> 100
40	 *   sharesOf(user2) -> 400
41	 *
42	 * Therefore:
43	 *
44	 *   balanceOf(user1) -> 105 tokens which corresponds 105 rOUSG
45	 *   balanceOf(user2) -> 420 tokens which corresponds 420 rOUSG
46	 *
47	 * Since balances of all token holders change when the price of OUSG changes, this
48	 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
49	 * upon explicit transfer between holders. In contrast, when total amount of pooled
50	 * Cash increases, no `Transfer` events are generated: doing so would require emitting
51	 * an event for each token holder and thus running an unbounded loop.
52	 *
53	 */
54	
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 28..72

[N-20]<a name="n-20"></a> Inconsistent usage of require/error

Some parts of the codebase use require statements, while others use custom errors. Consider refactoring the code to use the same approach: the following findings represent the minority of require vs error, and they show the first occurance in each file, for brevity.

There are 17 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
435	      revert UnwrapTooSmall();
...
630	      revert UnwrapTooSmall();
...
282	    require(currentAllowance >= _amount, "TRANSFER_AMOUNT_EXCEEDS_ALLOWANCE");
...
333	    require(
334	      currentAllowance >= _subtractedValue,
335	      "DECREASED_ALLOWANCE_BELOW_ZERO"
336	    );
...
416	    require(_OUSGAmount > 0, "rOUSG: can't wrap zero OUSG tokens");
...
432	    require(_rOUSGAmount > 0, "rOUSG: can't unwrap zero rOUSG tokens");
...
477	    require(_owner != address(0), "APPROVE_FROM_ZERO_ADDRESS");
...
478	    require(_spender != address(0), "APPROVE_TO_ZERO_ADDRESS");
...
506	    require(_sender != address(0), "TRANSFER_FROM_THE_ZERO_ADDRESS");
...
507	    require(_recipient != address(0), "TRANSFER_TO_THE_ZERO_ADDRESS");

GitHub : 435, 630, 282, 333..336, 416, 432, 477, 478, 506, 507, 512..515, 533, 558, 563, 594, 599, 604

[N-21]<a name="n-21"></a> Use of override is unnecessary

Starting with Solidity version 0.8.8, using the override keyword when the function solely overrides an interface function, and the function doesn't exist in multiple base contracts, is unnecessary.

There are 20 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
230	  function mint(
231	    uint256 usdcAmountIn
232	  )
233	    external
234	    override
235	    nonReentrant
236	    whenMintNotPaused
237	    returns (uint256 ousgAmountOut)
238	  {
...
254	  function mintRebasingOUSG(
255	    uint256 usdcAmountIn
256	  )
257	    external
258	    override
259	    nonReentrant
260	    whenMintNotPaused
261	    returns (uint256 rousgAmountOut)
262	  {
...
335	  function redeem(
336	    uint256 ousgAmountIn
337	  )
338	    external
339	    override
340	    nonReentrant
341	    whenRedeemNotPaused
342	    returns (uint256 usdcAmountOut)
343	  {
...
362	  function redeemRebasingOUSG(
363	    uint256 rousgAmountIn
364	  )
365	    external
366	    override
367	    nonReentrant
368	    whenRedeemNotPaused
369	    returns (uint256 usdcAmountOut)
370	  {
...
498	  function setInstantMintLimit(
499	    uint256 _instantMintLimit
500	  ) external override onlyRole(CONFIGURER_ROLE) {
...
512	  function setInstantRedemptionLimit(
513	    uint256 _instantRedemptionLimit
514	  ) external override onlyRole(CONFIGURER_ROLE) {
...
526	  function setInstantMintLimitDuration(
527	    uint256 _instantMintLimitDuration
528	  ) external override onlyRole(CONFIGURER_ROLE) {
...
540	  function setInstantRedemptionLimitDuration(
541	    uint256 _instantRedemptionLimitDuratioin
542	  ) external override onlyRole(CONFIGURER_ROLE) {
...
554	  function setMintFee(
555	    uint256 _mintFee
556	  ) external override onlyRole(CONFIGURER_ROLE) {
...
567	  function setRedeemFee(
568	    uint256 _redeemFee
569	  ) external override onlyRole(CONFIGURER_ROLE) {

GitHub : 230..238, 254..262, 335..343, 362..370, 498..500, 512..514, 526..528, 540..542, 554..556, 567..569, 581..583, 599..601, 622..624, 638..640, 650..652, 663..665, 794..802


- contracts/ousg/rOUSG.sol

GitHub : 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 121..123

[N-22]<a name="n-22"></a> Modifier declarations should have NatSpec descriptions

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSGFactory.sol
149	  modifier onlyGuardian() {
150	    require(msg.sender == guardian, "ROUSGFactory: You are not the Guardian");
151	    _;
152	  }

GitHub : 149..152

[N-23]<a name="n-23"></a> Function declarations should have NatSpec descriptions

There are 13 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
388	  function _redeem(
389	    uint256 ousgAmountIn
390	  ) internal returns (uint256 usdcAmountOut) {
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
...
794	  function multiexcall(
795	    ExCallData[] calldata exCallData
796	  )
797	    external
798	    payable
799	    override
800	    onlyRole(DEFAULT_ADMIN_ROLE)
801	    returns (bytes[] memory results)
802	  {

GitHub : 278..281, 388..390, 458, 794..802


- contracts/ousg/rOUSG.sol
102	  function initialize(
103	    address _kycRegistry,
104	    uint256 requirementGroup,
105	    address _ousg,
106	    address guardian,
107	    address _oracle
108	  ) public virtual initializer {
...
112	  function __rOUSG_init(
113	    address _kycRegistry,
114	    uint256 requirementGroup,
115	    address _ousg,
116	    address guardian,
117	    address _oracle
118	  ) internal onlyInitializing {
...
128	  function __rOUSG_init_unchained(
129	    address _kycRegistry,
130	    uint256 _requirementGroup,
131	    address _ousg,
132	    address guardian,
133	    address _oracle
134	  ) internal onlyInitializing {
...
378	  function getOUSGPrice() public view returns (uint256 price) {
...
642	  function pause() external onlyRole(PAUSER_ROLE) {
...
646	  function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {

GitHub : 102..108, 112..118, 128..134, 378, 642, 646, 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 47

[N-24]<a name="n-24"></a> Function names should use lowerCamelCase

According to the Solidity style guide function names should be in mixedCase (lowerCamelCase)

There are 13 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
254	  function mintRebasingOUSG(
255	    uint256 usdcAmountIn
256	  )
257	    external
258	    override
259	    nonReentrant
260	    whenMintNotPaused
261	    returns (uint256 rousgAmountOut)
262	  {
...
362	  function redeemRebasingOUSG(
363	    uint256 rousgAmountIn
364	  )
365	    external
366	    override
367	    nonReentrant
368	    whenRedeemNotPaused
369	    returns (uint256 usdcAmountOut)
370	  {
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
...
479	  function getOUSGPrice() public view returns (uint256 price) {
...
622	  function setMinimumBUIDLRedemptionAmount(
623	    uint256 _minimumBUIDLRedemptionAmount
624	  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {

GitHub : 254..262, 362..370, 458, 479, 622..624


- contracts/ousg/rOUSG.sol
112	  function __rOUSG_init(
113	    address _kycRegistry,
114	    uint256 requirementGroup,
115	    address _ousg,
116	    address guardian,
117	    address _oracle
118	  ) internal onlyInitializing {
...
128	  function __rOUSG_init_unchained(
129	    address _kycRegistry,
130	    uint256 _requirementGroup,
131	    address _ousg,
132	    address guardian,
133	    address _oracle
134	  ) internal onlyInitializing {
...
363	  function getSharesByROUSG(
364	    uint256 _rOUSGAmount
365	  ) public view returns (uint256) {
...
373	  function getROUSGByShares(uint256 _shares) public view returns (uint256) {
...
378	  function getOUSGPrice() public view returns (uint256 price) {

GitHub : 112..118, 128..134, 363..365, 373, 378, 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 70..75

[N-25]<a name="n-25"></a> Setters should prevent re-setting of the same value

This especially problematic when the setter also emits the same value, which may be confusing to offline parsers

There are 11 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
554	  function setMintFee(
555	    uint256 _mintFee
556	  ) external override onlyRole(CONFIGURER_ROLE) {
...
567	  function setRedeemFee(
568	    uint256 _redeemFee
569	  ) external override onlyRole(CONFIGURER_ROLE) {
...
581	  function setMinimumDepositAmount(
582	    uint256 _minimumDepositAmount
583	  ) external override onlyRole(CONFIGURER_ROLE) {
...
599	  function setMinimumRedemptionAmount(
600	    uint256 _minimumRedemptionAmount
601	  ) external override onlyRole(CONFIGURER_ROLE) {
...
622	  function setMinimumBUIDLRedemptionAmount(
623	    uint256 _minimumBUIDLRedemptionAmount
624	  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
...
638	  function setOracle(
639	    address _oracle
640	  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
...
650	  function setFeeReceiver(
651	    address _feeReceiver
652	  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {
...
663	  function setInvestorBasedRateLimiter(
664	    address _investorBasedRateLimiter
665	  ) external override onlyRole(DEFAULT_ADMIN_ROLE) {

GitHub : 554..556, 567..569, 581..583, 599..601, 622..624, 638..640, 650..652, 663..665


- contracts/ousg/rOUSG.sol
128	  function __rOUSG_init_unchained(
129	    address _kycRegistry,
130	    uint256 _requirementGroup,
131	    address _ousg,
132	    address guardian,
133	    address _oracle
134	  ) internal onlyInitializing {
...
472	  function _approve(
473	    address _owner,
474	    address _spender,
475	    uint256 _amount
476	  ) internal whenNotPaused {

GitHub : 128..134, 472..476, 613

[N-26]<a name="n-26"></a> Consider disallowing transfers to address(this)

There are 4 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
819	  function retrieveTokens(
820	    address token,
821	    address to,
822	    uint256 amount
823	  ) external onlyRole(DEFAULT_ADMIN_ROLE) {

GitHub : 819..823


- contracts/ousg/rOUSG.sol
220	  function transfer(address _recipient, uint256 _amount) public returns (bool) {
...
431	  function unwrap(uint256 _rOUSGAmount) external whenNotPaused {
...
624	  function burn(
625	    address _account,
626	    uint256 _amount
627	  ) external onlyRole(BURNER_ROLE) {

GitHub : 220, 431, 624..627

[N-27]<a name="n-27"></a> Named imports of parent contracts are missing

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
49	contract OUSGInstantManager is
50	  ReentrancyGuard,
51	  InstantMintTimeBasedRateLimiter,
52	  AccessControlEnumerable,
53	  IOUSGInstantManager,
54	  IMulticall
55	{
56	  // Role to configure the contract
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 49..57


- contracts/ousg/rOUSG.sol
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 55..72


- contracts/ousg/rOUSGFactory.sol
37	contract ROUSGFactory is IMulticall {
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 37..38

[N-28]<a name="n-28"></a> Events may be emitted out of order due to reentrancy

Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls

There are 9 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
254	  function mintRebasingOUSG(
255	    uint256 usdcAmountIn
256	  )
257	    external
258	    override
259	    nonReentrant
260	    whenMintNotPaused
261	    returns (uint256 rousgAmountOut)
262	  {
...
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
335	  function redeem(
336	    uint256 ousgAmountIn
337	  )
338	    external
339	    override
340	    nonReentrant
341	    whenRedeemNotPaused
342	    returns (uint256 usdcAmountOut)
343	  {
...
362	  function redeemRebasingOUSG(
363	    uint256 rousgAmountIn
364	  )
365	    external
366	    override
367	    nonReentrant
368	    whenRedeemNotPaused
369	    returns (uint256 usdcAmountOut)
370	  {
...
388	  function _redeem(
389	    uint256 ousgAmountIn
390	  ) internal returns (uint256 usdcAmountOut) {

GitHub : 254..262, 278..281, 335..343, 362..370, 388..390


- contracts/ousg/rOUSG.sol
415	  function wrap(uint256 _OUSGAmount) external whenNotPaused {
...
431	  function unwrap(uint256 _rOUSGAmount) external whenNotPaused {
...
624	  function burn(
625	    address _account,
626	    uint256 _amount
627	  ) external onlyRole(BURNER_ROLE) {

GitHub : 415, 431, 624..627


- contracts/ousg/rOUSGFactory.sol
70	  function deployRebasingOUSG(
71	    address kycRegistry,
72	    uint256 requirementGroup,
73	    address ousg,
74	    address oracle
75	  ) external onlyGuardian returns (address, address, address) {

GitHub : 70..75

[N-29]<a name="n-29"></a> Missing zero address check in initializer

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
102	  function initialize(
103	    address _kycRegistry,
104	    uint256 requirementGroup,
105	    address _ousg,
106	    address guardian,
107	    address _oracle
108	  ) public virtual initializer {

GitHub : 102..108

[N-30]<a name="n-30"></a> Missing NatSpec @author from contract declaration

Some contract definitions have an incomplete NatSpec: add a @author notation to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
28	/**
29	 * @title Interest-bearing ERC20-like token for OUSG.
30	 *
31	 * rOUSG balances are dynamic and represent the holder's share of the underlying OUSG
32	 * controlled by the protocol. To calculate each account's balance, we do
33	 *
34	 *   shares[account] * ousgPrice
35	 *
36	 * For example, assume that we have:
37	 *
38	 *   ousgPrice = 100.505
39	 *   sharesOf(user1) -> 100
40	 *   sharesOf(user2) -> 400
41	 *
42	 * Therefore:
43	 *
44	 *   balanceOf(user1) -> 105 tokens which corresponds 105 rOUSG
45	 *   balanceOf(user2) -> 420 tokens which corresponds 420 rOUSG
46	 *
47	 * Since balances of all token holders change when the price of OUSG changes, this
48	 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
49	 * upon explicit transfer between holders. In contrast, when total amount of pooled
50	 * Cash increases, no `Transfer` events are generated: doing so would require emitting
51	 * an event for each token holder and thus running an unbounded loop.
52	 *
53	 */
54	
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 28..72

[N-31]<a name="n-31"></a> Missing NatSpec @dev from contract declaration

Some contract definitions have an incomplete NatSpec: add a @dev notation to improve the code documentation.

There are 2 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
28	/**
29	 * @title Interest-bearing ERC20-like token for OUSG.
30	 *
31	 * rOUSG balances are dynamic and represent the holder's share of the underlying OUSG
32	 * controlled by the protocol. To calculate each account's balance, we do
33	 *
34	 *   shares[account] * ousgPrice
35	 *
36	 * For example, assume that we have:
37	 *
38	 *   ousgPrice = 100.505
39	 *   sharesOf(user1) -> 100
40	 *   sharesOf(user2) -> 400
41	 *
42	 * Therefore:
43	 *
44	 *   balanceOf(user1) -> 105 tokens which corresponds 105 rOUSG
45	 *   balanceOf(user2) -> 420 tokens which corresponds 420 rOUSG
46	 *
47	 * Since balances of all token holders change when the price of OUSG changes, this
48	 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
49	 * upon explicit transfer between holders. In contrast, when total amount of pooled
50	 * Cash increases, no `Transfer` events are generated: doing so would require emitting
51	 * an event for each token holder and thus running an unbounded loop.
52	 *
53	 */
54	
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 28..72


- contracts/ousg/rOUSGFactory.sol
24	/**
25	 * @title ROUSGFactory
26	 * @author Ondo Finance
27	 * @notice This contract serves as a Factory for the upgradable rOUSG token contract.
28	 *         Upon calling `deployRebasingOUSG` the `guardian` address (set in constructor) will
29	 *         deploy the following:
30	 *         1) rOUSG - The implementation contract, ERC20 contract with the initializer disabled
31	 *         2) ProxyAdmin - OZ ProxyAdmin contract, used to upgrade the proxy instance.
32	 *                         @notice Owner is set to `guardian` address.
33	 *         3) TransparentUpgradeableProxy - OZ, proxy contract. Admin is set to `address(proxyAdmin)`.
34	 *                                          `_logic' is set to `address(rOUSG)`.
35	 * @notice `guardian` address in constructor is a msig.
36	 */
37	contract ROUSGFactory is IMulticall {
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 24..38

[N-32]<a name="n-32"></a> Missing NatSpec @notice from contract declaration

Some contract definitions have an incomplete NatSpec: add a @notice notation to describe the contract to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
28	/**
29	 * @title Interest-bearing ERC20-like token for OUSG.
30	 *
31	 * rOUSG balances are dynamic and represent the holder's share of the underlying OUSG
32	 * controlled by the protocol. To calculate each account's balance, we do
33	 *
34	 *   shares[account] * ousgPrice
35	 *
36	 * For example, assume that we have:
37	 *
38	 *   ousgPrice = 100.505
39	 *   sharesOf(user1) -> 100
40	 *   sharesOf(user2) -> 400
41	 *
42	 * Therefore:
43	 *
44	 *   balanceOf(user1) -> 105 tokens which corresponds 105 rOUSG
45	 *   balanceOf(user2) -> 420 tokens which corresponds 420 rOUSG
46	 *
47	 * Since balances of all token holders change when the price of OUSG changes, this
48	 * token cannot fully implement ERC20 standard: it only emits `Transfer` events
49	 * upon explicit transfer between holders. In contrast, when total amount of pooled
50	 * Cash increases, no `Transfer` events are generated: doing so would require emitting
51	 * an event for each token holder and thus running an unbounded loop.
52	 *
53	 */
54	
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 28..72

[N-33]<a name="n-33"></a> Missing NatSpec from error declaration

Consider adding some comments on error declarations to explain what they are supposed to do: this will help for future code reviews.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
90	  error UnwrapTooSmall();

GitHub : 90

[N-34]<a name="n-34"></a> Missing NatSpec @dev from error declaration

Some errors have an incomplete NatSpec: add a @dev notation to describe the error to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
90	  error UnwrapTooSmall();

GitHub : 90

[N-35]<a name="n-35"></a> Missing NatSpec @notice from error declaration

Some errors have an incomplete NatSpec: add a @notice notation to describe the error to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
90	  error UnwrapTooSmall();

GitHub : 90

[N-36]<a name="n-36"></a> Missing NatSpec @param from error declaration

Some errors have an incomplete NatSpec: add a @param notation to describe the error parameters to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
90	  error UnwrapTooSmall();

GitHub : 90

[N-37]<a name="n-37"></a> Missing NatSpec @dev from event declaration

Some events have an incomplete NatSpec: add a @dev notation to describe the event to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
155	  /**
156	   * @notice Emitted when the oracle address is set
157	   *
158	   * @param oldOracle The address of the old oracle
159	   * @param newOracle The address of the new oracle
160	   */
161	  event OracleSet(address indexed oldOracle, address indexed newOracle);

GitHub : 155..161

[N-38]<a name="n-38"></a> Missing NatSpec @notice from event declaration

Some events have an incomplete NatSpec: add a @notice notation to describe the event to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSGFactory.sol
134	  /**
135	   * @dev Event emitted when upgradable rOUSG is deployed
136	   *
137	   * @param proxy             The address for the proxy contract
138	   * @param proxyAdmin        The address for the proxy admin contract
139	   * @param implementation    The address for the implementation contract
140	   */
141	  event rOUSGDeployed(

GitHub : 134..141

[N-39]<a name="n-39"></a> Missing NatSpec @param from event declaration

Some events have an incomplete NatSpec: add a @param notation to describe the event to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
144	  /**
145	   * @notice An executed shares transfer from `sender` to `recipient`.
146	   *
147	   * @dev emitted in pair with an ERC20-defined `Transfer` event.
148	   */
149	  event TransferShares(

GitHub : 144..149

[N-40]<a name="n-40"></a> Missing NatSpec from modifiers definitions

Consider adding some comments on modifier declarations to explain what they are supposed to do: this will help for future code reviews.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSGFactory.sol
149	  modifier onlyGuardian() {

GitHub : 149

[N-41]<a name="n-41"></a> Missing NatSpec @dev from modifier declaration

Some modifiers have an incomplete NatSpec: add a @dev notation to describe the modifier to improve the code documentation.

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
755	  /// @notice Ensure that the mint functionality is not paused
756	  modifier whenMintNotPaused() {
...
761	  /// @notice Ensure that the redeem functionality is not paused
762	  modifier whenRedeemNotPaused() {

GitHub : 755..756, 761..762


- contracts/ousg/rOUSGFactory.sol
149	  modifier onlyGuardian() {

GitHub : 149

[N-42]<a name="n-42"></a> Missing NatSpec @notice from modifier declaration

Some modifiers have an incomplete NatSpec: add a @notice notation to describe the modifier to improve the code documentation.

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSGFactory.sol
149	  modifier onlyGuardian() {

GitHub : 149

[N-43]<a name="n-43"></a> Missing NatSpec @param from modifier declaration

Some modifiers have an incomplete NatSpec: add a @param notation to describe the modifier parameters to improve the code documentation.

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
755	  /// @notice Ensure that the mint functionality is not paused
756	  modifier whenMintNotPaused() {
...
761	  /// @notice Ensure that the redeem functionality is not paused
762	  modifier whenRedeemNotPaused() {

GitHub : 755..756, 761..762


- contracts/ousg/rOUSGFactory.sol
149	  modifier onlyGuardian() {

GitHub : 149

[N-44]<a name="n-44"></a> Missing NatSpec @dev from function declaration

Some functions have an incomplete NatSpec: add a @dev notation to describe the function to improve the code documentation.

There are 48 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
388	  function _redeem(
389	    uint256 ousgAmountIn
390	  ) internal returns (uint256 usdcAmountOut) {
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
...
490	  /**
491	   * @notice Set the mintLimit constraint inside the InstantMintTimeBasedRateLimiter
492	   *         base contract
493	   *
494	   * @param _instantMintLimit New limit that dicates how much USDC can be transfered
495	   *                     for minting in a specified duration
496	   *                     (in 6 decimals per the USDC contract)
497	   */
498	  function setInstantMintLimit(
499	    uint256 _instantMintLimit
500	  ) external override onlyRole(CONFIGURER_ROLE) {
...
504	  /**
505	   * @notice Set the redeemLimit constraint inside the InstantMintTimeBasedRateLimiter
506	   *         base contract
507	   *
508	   * @param _instantRedemptionLimit New limit that dicates how much USDC
509	   *                       can be redeemed in a specified duration
510	   *                       (in 6 decimals per the USDC contract)
511	   */
512	  function setInstantRedemptionLimit(
513	    uint256 _instantRedemptionLimit
514	  ) external override onlyRole(CONFIGURER_ROLE) {
...
518	  /**
519	   * @notice Sets mintLimitDuration constraint inside the InstantMintTimeBasedRateLimiter
520	   *         base contract
521	   *
522	   * @param _instantMintLimitDuration New limit that specifies the interval
523	   *                             (in seconds) in which only `mintLimit` USDC
524	   *                             can be used for minting within
525	   */
526	  function setInstantMintLimitDuration(
527	    uint256 _instantMintLimitDuration
528	  ) external override onlyRole(CONFIGURER_ROLE) {
...
532	  /**
533	   * @notice Sets redeemLimitDuration inside the InstantMintTimeBasedRateLimiter
534	   *         base contract
535	   *
536	   * @param _instantRedemptionLimitDuratioin New limit that specifies the interval
537	   *                               (in seconds) in which only `redeemLimit` USDC
538	   *                               can be redeemed within
539	   */
540	  function setInstantRedemptionLimitDuration(
541	    uint256 _instantRedemptionLimitDuratioin
542	  ) external override onlyRole(CONFIGURER_ROLE) {
...
549	  /**
550	   * @notice Sets the mint fee
551	   *
552	   * @param _mintFee new mint fee specified in basis points
553	   */
554	  function setMintFee(
555	    uint256 _mintFee
556	  ) external override onlyRole(CONFIGURER_ROLE) {
...
562	  /**
563	   * @notice Sets the redeem fee.
564	   *
565	   * @param _redeemFee new redeem fee specified in basis points
566	   */
567	  function setRedeemFee(
568	    uint256 _redeemFee
569	  ) external override onlyRole(CONFIGURER_ROLE) {
...
575	  /**
576	   * @notice Admin function to set the minimum amount required for a deposit
577	   *
578	   * @param _minimumDepositAmount The minimum amount required to submit a deposit
579	   *                          request
580	   */
581	  function setMinimumDepositAmount(
582	    uint256 _minimumDepositAmount
583	  ) external override onlyRole(CONFIGURER_ROLE) {

GitHub : 278..281, 388..390, 458, 490..500, 504..514, 518..528, 532..542, 549..556, 562..569, 575..583, 593..601, 617..624, 632..640, 645..652, 658..665, 679..688, 693..702, 707..715, 719..727, 767..768, 773..774, 779..780, 785..786, 794..802, 813..823


- contracts/ousg/rOUSG.sol

GitHub : 97..98, 102..108, 112..118, 128..134, 163..166, 170..174, 178..181, 185..188, 289..305, 314..331, 360..365, 370..373, 378, 445..454, 461..476, 484..487, 491..505, 521..532, 642, 646, 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 47

[N-45]<a name="n-45"></a> Missing NatSpec @notice from function declaration

Some functions have an incomplete NatSpec: add a @notice notation to describe the function to improve the code documentation.

There are 26 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
278	  function _mint(
279	    uint256 usdcAmountIn,
280	    address to
281	  ) internal returns (uint256 ousgAmountOut) {
...
388	  function _redeem(
389	    uint256 ousgAmountIn
390	  ) internal returns (uint256 usdcAmountOut) {
...
458	  function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
...
794	  function multiexcall(
795	    ExCallData[] calldata exCallData
796	  )
797	    external
798	    payable
799	    override
800	    onlyRole(DEFAULT_ADMIN_ROLE)
801	    returns (bytes[] memory results)
802	  {

GitHub : 278..281, 388..390, 458, 794..802


- contracts/ousg/rOUSG.sol
97	  /// @custom:oz-upgrades-unsafe-allow constructor
98	  constructor() {
...
102	  function initialize(
103	    address _kycRegistry,
104	    uint256 requirementGroup,
105	    address _ousg,
106	    address guardian,
107	    address _oracle
108	  ) public virtual initializer {
...
112	  function __rOUSG_init(
113	    address _kycRegistry,
114	    uint256 requirementGroup,
115	    address _ousg,
116	    address guardian,
117	    address _oracle
118	  ) internal onlyInitializing {
...
128	  function __rOUSG_init_unchained(
129	    address _kycRegistry,
130	    uint256 _requirementGroup,
131	    address _ousg,
132	    address guardian,
133	    address _oracle
134	  ) internal onlyInitializing {
...
163	  /**
164	   * @return the name of the token.
165	   */
166	  function name() public pure returns (string memory) {
...
170	  /**
171	   * @return the symbol of the token, usually a shorter version of the
172	   * name.
173	   */
174	  function symbol() public pure returns (string memory) {

GitHub : 97..98, 102..108, 112..118, 128..134, 163..166, 170..174, 178..181, 185..188, 193..199, 225..234, 341..347, 351..356, 360..365, 370..373, 378, 484..487, 572..590, 642, 646, 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 47

[N-46]<a name="n-46"></a> Simplify complex require statements

Simplifying complex require statements with local variables and if(or revert) statements can improve readability, make debugging easier, and promote modularity and reusability in the code.

There are 1 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
466	    require(
467	      usdc.balanceOf(address(this)) == usdcBalanceBefore + buidlAmountToRedeem,
468	      "OUSGInstantManager::_redeemBUIDL: BUIDL:USDC not 1:1"
469	    );

GitHub : 466..469

[N-47]<a name="n-47"></a> Events should be emitted before external calls

Ensure that events follow the best practice of check-effects-interaction, and are emitted before external calls

There are 14 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @audit functions: `approve`, `wrap`, `getROUSGByShares`, `transfer` called before this event
270	    emit InstantMintRebasingOUSG(
271	      msg.sender,
272	      usdcAmountIn,
273	      ousgAmountOut,
274	      rousgAmountOut
275	    );
...
// @audit functions: `decimals`, `checkAndUpdateMintLimit`, `allowance`, `transferFrom`, `transferFrom` called before this event
321	    emit MintFeesDeducted(msg.sender, feeReceiver, usdcfees, usdcAmountIn);
...
// @audit functions: `allowance`, `transferFrom` called before this event
350	    emit InstantRedemptionOUSG(msg.sender, ousgAmountIn, usdcAmountOut);
...
// @audit functions: `allowance`, `transferFrom`, `unwrap`, `getSharesByROUSG` called before this event
380	    emit InstantRedemptionRebasingOUSG(
381	      msg.sender,
382	      rousgAmountIn,
383	      ousgAmountIn,
384	      usdcAmountOut
385	    );
...
// @audit functions: `decimals`, `decimals`, `checkAndUpdateRedeemLimit`, `burn`, `balanceOf` called before this event
443	      emit BUIDLRedemptionSkipped(
444	        msg.sender,
445	        usdcAmountToRedeem,
446	        usdcBalance - usdcAmountToRedeem
447	      );
...
// @audit functions: `decimals`, `decimals`, `checkAndUpdateRedeemLimit`, `burn`, `balanceOf` called before this event
435	      emit MinimumBUIDLRedemption(
436	        msg.sender,
437	        minBUIDLRedeemAmount,
438	        usdcBalance + minBUIDLRedeemAmount - usdcAmountToRedeem
439	      );
...
// @audit functions: `decimals`, `decimals`, `checkAndUpdateRedeemLimit`, `burn`, `balanceOf`, `transfer` called before this event
453	    emit RedeemFeesDeducted(msg.sender, feeReceiver, usdcFees, usdcAmountOut);

GitHub : 270..275, 321, 350, 380..385, 443..447, 435..439, 453


- contracts/ousg/rOUSG.sol
// @audit functions: `transferFrom` called before this event
420	    emit Transfer(address(0), msg.sender, getROUSGByShares(ousgSharesAmount));
...
// @audit functions: `transferFrom` called before this event
421	    emit TransferShares(address(0), msg.sender, ousgSharesAmount);
...
// @audit functions: `transfer` called before this event
441	    emit Transfer(msg.sender, address(0), _rOUSGAmount);

GitHub : 420, 421, 441, 442, 638, 639


- contracts/ousg/rOUSGFactory.sol

GitHub : 96..102

[N-48]<a name="n-48"></a> Use @inheritdoc for overridden functions

It is recommended to use @inheritdoc for overridden functions.

There are 20 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
219	  /**
220	   * @notice Calculates fees and triggers minting OUSG for a given amount of USDC
221	   *
222	   * @dev Please note that the fees are accumulated in `feeReceiver`
223	   *
224	   * @param usdcAmountIn amount of USDC exchanged for OUSG (in whatever decimals
225	   *                     specifed by usdc token contract)
226	   *
227	   * @return ousgAmountOut The quantity of OUSG minted for the user
228	   *                       (18 decimals per OUSG contract)
229	   */
230	  function mint(
231	    uint256 usdcAmountIn
232	  )
233	    external
234	    override
235	    nonReentrant
236	    whenMintNotPaused
237	    returns (uint256 ousgAmountOut)
238	  {
...
243	  /**
244	   * @notice Calculates fees and triggers minting rOUSG for a given amount of USDC
245	   *
246	   * @dev Please note that the fees are accumulated in `feeReceiver`
247	   *
248	   * @param usdcAmountIn amount of USDC exchanged for rOUSG (in whatever decimals
249	   *                     specifed by usdc token contract)
250	   *
251	   * @return rousgAmountOut The quantity of rOUSG minted for the user
252	   *                        (18 decimals per rOUSG contract)
253	   */
254	  function mintRebasingOUSG(
255	    uint256 usdcAmountIn
256	  )
257	    external
258	    override
259	    nonReentrant
260	    whenMintNotPaused
261	    returns (uint256 rousgAmountOut)
262	  {
...
326	  /**
327	   * @notice Calculates fees and triggers a redemption of OUSG for a given amount of USDC
328	   *
329	   * @dev Please note that the fees are accumulated in `feeReceiver`
330	   *
331	   * @param ousgAmountIn Amount of OUSG to redeem
332	   *
333	   * @return usdcAmountOut The amount of USDC returned to the user
334	   */
335	  function redeem(
336	    uint256 ousgAmountIn
337	  )
338	    external
339	    override
340	    nonReentrant
341	    whenRedeemNotPaused
342	    returns (uint256 usdcAmountOut)
343	  {
...
353	  /**
354	   * @notice Calculates fees and triggers minting rOUSG for a given amount of USDC
355	   *
356	   * @dev Please note that the fees are actually accumulated in `feeReceiver`
357	   *
358	   * @param rousgAmountIn Amount of rOUSG to redeem
359	   *
360	   * @return usdcAmountOut The amount of USDC returned to the user
361	   */
362	  function redeemRebasingOUSG(
363	    uint256 rousgAmountIn
364	  )
365	    external
366	    override
367	    nonReentrant
368	    whenRedeemNotPaused
369	    returns (uint256 usdcAmountOut)
370	  {
...
490	  /**
491	   * @notice Set the mintLimit constraint inside the InstantMintTimeBasedRateLimiter
492	   *         base contract
493	   *
494	   * @param _instantMintLimit New limit that dicates how much USDC can be transfered
495	   *                     for minting in a specified duration
496	   *                     (in 6 decimals per the USDC contract)
497	   */
498	  function setInstantMintLimit(
499	    uint256 _instantMintLimit
500	  ) external override onlyRole(CONFIGURER_ROLE) {
...
504	  /**
505	   * @notice Set the redeemLimit constraint inside the InstantMintTimeBasedRateLimiter
506	   *         base contract
507	   *
508	   * @param _instantRedemptionLimit New limit that dicates how much USDC
509	   *                       can be redeemed in a specified duration
510	   *                       (in 6 decimals per the USDC contract)
511	   */
512	  function setInstantRedemptionLimit(
513	    uint256 _instantRedemptionLimit
514	  ) external override onlyRole(CONFIGURER_ROLE) {
...
518	  /**
519	   * @notice Sets mintLimitDuration constraint inside the InstantMintTimeBasedRateLimiter
520	   *         base contract
521	   *
522	   * @param _instantMintLimitDuration New limit that specifies the interval
523	   *                             (in seconds) in which only `mintLimit` USDC
524	   *                             can be used for minting within
525	   */
526	  function setInstantMintLimitDuration(
527	    uint256 _instantMintLimitDuration
528	  ) external override onlyRole(CONFIGURER_ROLE) {
...
532	  /**
533	   * @notice Sets redeemLimitDuration inside the InstantMintTimeBasedRateLimiter
534	   *         base contract
535	   *
536	   * @param _instantRedemptionLimitDuratioin New limit that specifies the interval
537	   *                               (in seconds) in which only `redeemLimit` USDC
538	   *                               can be redeemed within
539	   */
540	  function setInstantRedemptionLimitDuration(
541	    uint256 _instantRedemptionLimitDuratioin
542	  ) external override onlyRole(CONFIGURER_ROLE) {
...
549	  /**
550	   * @notice Sets the mint fee
551	   *
552	   * @param _mintFee new mint fee specified in basis points
553	   */
554	  function setMintFee(
555	    uint256 _mintFee
556	  ) external override onlyRole(CONFIGURER_ROLE) {
...
562	  /**
563	   * @notice Sets the redeem fee.
564	   *
565	   * @param _redeemFee new redeem fee specified in basis points
566	   */
567	  function setRedeemFee(
568	    uint256 _redeemFee
569	  ) external override onlyRole(CONFIGURER_ROLE) {

GitHub : 219..238, 243..262, 326..343, 353..370, 490..500, 504..514, 518..528, 532..542, 549..556, 562..569, 575..583, 593..601, 617..624, 632..640, 645..652, 658..665, 794..802


- contracts/ousg/rOUSG.sol

GitHub : 650..652, 656..658


- contracts/ousg/rOUSGFactory.sol

GitHub : 110..123

[N-49]<a name="n-49"></a> Missing NatSpec from variable declarations

Some variables miss a NatSpec, which should be a best practice to add as a documentation.

Even if Natspec for internal and private variables is not parsed (but this may change in the future, according to the official docs), it still helps while reviewing the codebase.

There are 34 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");
...
60	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
63	  uint256 public constant MINIMUM_OUSG_PRICE = 105e18;
...
66	  uint256 public constant FEE_GRANULARITY = 10_000;
...
69	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;
...
72	  IERC20 public immutable usdc; // 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
...
75	  IRWALike public immutable ousg;
...
78	  ROUSG public immutable rousg;
...
81	  IERC20 public immutable buidl;
...
84	  IBUIDLRedeemer public immutable buidlRedeemer;

GitHub : 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99, 102, 106, 110, 113, 116, 120, 123


- contracts/ousg/rOUSG.sol

GitHub : 78, 81, 84, 87, 94, 95


- contracts/ousg/rOUSGFactory.sol

GitHub : 38, 40, 41, 42, 43, 45

[N-50]<a name="n-50"></a> Contract name does not follow the Solidity Style Guide

According to the Solidity Style Guide, contracts and libraries should be named using the CapWords style.

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
49	contract OUSGInstantManager is
50	  ReentrancyGuard,
51	  InstantMintTimeBasedRateLimiter,
52	  AccessControlEnumerable,
53	  IOUSGInstantManager,
54	  IMulticall
55	{
56	  // Role to configure the contract
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 49..57


- contracts/ousg/rOUSG.sol
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 55..72


- contracts/ousg/rOUSGFactory.sol
37	contract ROUSGFactory is IMulticall {
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 37..38

[N-51]<a name="n-51"></a> Variable names for immutables should use UPPER_CASE_WITH_UNDERSCORES

For immutable variable names, each word should use all capital letters, with underscores separating each word (UPPER_CASE_WITH_UNDERSCORES)

There are 8 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
72	  IERC20 public immutable usdc; // 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
...
75	  IRWALike public immutable ousg;
...
78	  ROUSG public immutable rousg;
...
81	  IERC20 public immutable buidl;
...
84	  IBUIDLRedeemer public immutable buidlRedeemer;
...
87	  uint256 public immutable decimalsMultiplier;
...
90	  address public immutable usdcReceiver;

GitHub : 72, 75, 78, 81, 84, 87, 90


- contracts/ousg/rOUSGFactory.sol
40	  address internal immutable guardian;

GitHub : 40

[N-52]<a name="n-52"></a> Use a single file for system wide constants

Consider grouping all the system constants under a single file.

There are 10 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");
...
60	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
63	  uint256 public constant MINIMUM_OUSG_PRICE = 105e18;
...
66	  uint256 public constant FEE_GRANULARITY = 10_000;
...
69	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;

GitHub : 57, 60, 63, 66, 69


- contracts/ousg/rOUSG.sol
87	  uint256 public constant OUSG_TO_ROUSG_SHARES_MULTIPLIER = 10_000;
...
93	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
94	  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
...
95	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 87, 93, 94, 95


- contracts/ousg/rOUSGFactory.sol
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 38

[N-53]<a name="n-53"></a> Contracts should have full test coverage

A 100% test coverage is not foolproof, but it helps immensely in reducing the amount of bugs that may occur.

There are 1 instance(s) of this issue:

GitHub : project\2024-03-ondo-finance

[N-54]<a name="n-54"></a> Large or complicated code bases should implement invariant tests

This includes: large code bases, or code with lots of inline-assembly, complicated math, or complicated interactions between multiple contracts.

Invariant fuzzers such as Echidna require the test writer to come up with invariants which should not be violated under any circumstances, and the fuzzer tests various inputs and function calls to ensure that the invariants always hold.

Even code with 100% code coverage can still have bugs due to the order of the operations a user performs, and invariant fuzzers may help significantly.

There are 1 instance(s) of this issue:

GitHub : project\2024-03-ondo-finance

[N-55]<a name="n-55"></a> Codebase should implement formal verification testing

Formal verification is the act of proving or disproving the correctness of intended algorithms underlying a system with respect to a certain formal specification/property/invariant, using formal methods of mathematics.

Some tools that are currently available to perform these tests on smart contracts are SMTChecker and Certora Prover.

There are 1 instance(s) of this issue:

GitHub : project\2024-03-ondo-finance

[N-56]<a name="n-56"></a> State variables not capped at reasonable values

Consider adding minimum/maximum value checks to ensure that the state variables below can never be used to excessively harm users, including via griefing

There are 1 instance(s) of this issue:


- contracts/ousg/rOUSG.sol
// @audit: parameter `_amount` is not limited in function body
626	    uint256 _amount

GitHub : 626

[N-57]<a name="n-57"></a> Consider adding a block/deny-list

Doing so will significantly increase centralization, but will help to prevent hackers from using stolen tokens

There are 3 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
49	contract OUSGInstantManager is
50	  ReentrancyGuard,
51	  InstantMintTimeBasedRateLimiter,
52	  AccessControlEnumerable,
53	  IOUSGInstantManager,
54	  IMulticall
55	{
56	  // Role to configure the contract
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 49..57


- contracts/ousg/rOUSG.sol
55	contract ROUSG is
56	  Initializable,
57	  ContextUpgradeable,
58	  PausableUpgradeable,
59	  AccessControlEnumerableUpgradeable,
60	  KYCRegistryClientUpgradeable,
61	  IERC20Upgradeable,
62	  IERC20MetadataUpgradeable
63	{
64	  /**
65	   * @dev rOUSG balances are dynamic and are calculated based on the accounts' shares (OUSG)
66	   * and the the price of OUSG. Account shares aren't
67	   * normalized, so the contract also stores the sum of all shares to calculate
68	   * each account's token balance which equals to:
69	   *
70	   *   shares[account] * ousgPrice
71	   */
72	  mapping(address => uint256) private shares;

GitHub : 55..72


- contracts/ousg/rOUSGFactory.sol
37	contract ROUSGFactory is IMulticall {
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 37..38

[N-58]<a name="n-58"></a> Expressions for constant values should use immutable rather than constant

While it does not save gas for some simple binary expressions because the compiler knows that developers often make this mistake, it's still best to use the right tool for the task at hand. There is a difference between constant variables and immutable variables, and they should each be used in their appropriate contexts. constants should be used for literal values written into the code, and immutable variables should be used for expressions, or values calculated in, or passed into the constructor.

There are 6 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
57	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");
...
60	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");

GitHub : 57, 60


- contracts/ousg/rOUSG.sol
93	  bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
...
94	  bytes32 public constant BURNER_ROLE = keccak256("BURN_ROLE");
...
95	  bytes32 public constant CONFIGURER_ROLE = keccak256("CONFIGURER_ROLE");

GitHub : 93, 94, 95


- contracts/ousg/rOUSGFactory.sol
38	  bytes32 public constant DEFAULT_ADMIN_ROLE = bytes32(0);

GitHub : 38

[N-59]<a name="n-59"></a> Lines are too long

Maximum suggested line length is 120 characters according to the documentation.

There are 15 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
6	 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
...
7	██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
...
8	██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
...
9	╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
...
10	 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`

GitHub : 6, 7, 8, 9, 10


- contracts/ousg/rOUSG.sol
6	 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
...
7	██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
...
8	██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
...
9	╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
...
10	 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`

GitHub : 6, 7, 8, 9, 10


- contracts/ousg/rOUSGFactory.sol
6	 ██ ,██¬ ▄████▄  ▀█▄ ╙█▄      ▄███▀▀███▄   ███▄    ██  ███▀▀▀███▄    ▄███▀▀███,
...
7	██  ██ ╒█▀'   ╙█▌ ╙█▌ ██     ▐██      ███  █████,  ██  ██▌    └██▌  ██▌     └██▌
...
8	██ ▐█▌ ██      ╟█  █▌ ╟█     ██▌      ▐██  ██ └███ ██  ██▌     ╟██ j██       ╟██
...
9	╟█  ██ ╙██    ▄█▀ ▐█▌ ██     ╙██      ██▌  ██   ╙████  ██▌    ▄██▀  ██▌     ,██▀
...
10	 ██ "██, ╙▀▀███████████⌐      ╙████████▀   ██     ╙██  ███████▀▀     ╙███████▀`

GitHub : 6, 7, 8, 9, 10

[N-60]<a name="n-60"></a> Typos

There are 1 instance(s) of this issue:

error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:42:32
   |
42 |  *      OUSG, rOUSG, USDC, and BUIDL. This contract multiplies
   |                                ^^^^^
   |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:80:6
   |
80 |   // BUIDL token contract
   |      ^^^^^
   |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:81:27
   |
81 |   IERC20 public immutable buidl;
   |                           ^^^^^
   |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:83:56
   |
83 |   // Redeemer contract used for instant redemptions of BUIDL
   |                                                        ^^^^^
   |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:84:35
   |
84 |   IBUIDLRedeemer public immutable buidlRedeemer;
   |                                   ^^^^^
   |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:118:28
    |
118 |   // The minimum amount of BUIDL that must be redeemed in a single redemption
    |                            ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:119:15
    |
119 |   // with the BUIDLRedeemer contract
    |               ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:120:21
    |
120 |   uint256 public minBUIDLRedeemAmount = 250_000e6;
    |                     ^^^^^
    |
error: `Reciever` should be `Receiver`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:130:18
    |
130 |    * @param _usdcReciever       Address that receives USDC during minting
    |                  ^^^^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:135:14
    |
135 |    * @param _buidl              BUIDL token contract address
    |              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:135:33
    |
135 |    * @param _buidl              BUIDL token contract address
    |                                 ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:136:14
    |
136 |    * @param _buidlRedeemer      Contract address used for instant redemptions of BUIDL
    |              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:136:82
    |
136 |    * @param _buidlRedeemer      Contract address used for instant redemptions of BUIDL
    |                                                                                  ^^^^^
    |
error: `Reciever` should be `Receiver`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:147:18
    |
147 |     address _usdcReciever,
    |                  ^^^^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:152:14
    |
152 |     address _buidl,
    |              ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:153:14
    |
153 |     address _buidlRedeemer,
    |              ^^^^^
    |
error: `Reciever` should be `Receiver`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:168:20
    |
168 |       address(_usdcReciever) != address(0),
    |                    ^^^^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:181:14
    |
181 |     require(_buidl != address(0), "OUSGInstantManager: BUIDL cannot be 0x0");
    |              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:181:56
    |
181 |     require(_buidl != address(0), "OUSGInstantManager: BUIDL cannot be 0x0");
    |                                                        ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:183:16
    |
183 |       address(_buidlRedeemer) != address(0),
    |                ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:184:28
    |
184 |       "OUSGInstantManager: BUIDL Redeemer cannot be 0x0"
    |                            ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:191:59
    |
191 |       IERC20Metadata(_usdc).decimals() == IERC20Metadata(_buidl).decimals(),
    |                                                           ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:192:59
    |
192 |       "OUSGInstantManager: USDC decimals must be equal to BUIDL decimals"
    |                                                           ^^^^^
    |
error: `Reciever` should be `Receiver`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:195:25
    |
195 |     usdcReceiver = _usdcReciever;
    |                         ^^^^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:200:5
    |
200 |     buidl = IERC20(_buidl);
    |     ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:200:21
    |
200 |     buidl = IERC20(_buidl);
    |                     ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:201:5
    |
201 |     buidlRedeemer = IBUIDLRedeemer(_buidlRedeemer);
    |     ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:201:37
    |
201 |     buidlRedeemer = IBUIDLRedeemer(_buidlRedeemer);
    |                                     ^^^^^
    |
error: `specifed` should be `specified`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:225:26
    |
225 |    *                     specifed by usdc token contract)
    |                          ^^^^^^^^
    |
error: `specifed` should be `specified`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:249:26
    |
249 |    *                     specifed by usdc token contract)
    |                          ^^^^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:396:30
    |
396 |       IERC20Metadata(address(buidl)).decimals() == 6,
    |                              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:397:37
    |
397 |       "OUSGInstantManager::_redeem: BUIDL decimals must be 6"
    |                                     ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:426:34
    |
426 |     if (usdcAmountToRedeem >= minBUIDLRedeemAmount) {
    |                                  ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:427:43
    |
427 |       // amount of USDC needed is over minBUIDLRedeemAmount, do a BUIDL redemption
    |                                           ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:427:67
    |
427 |       // amount of USDC needed is over minBUIDLRedeemAmount, do a BUIDL redemption
    |                                                                   ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:429:14
    |
429 |       _redeemBUIDL(usdcAmountToRedeem);
    |              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:432:26
    |
432 |       // so we perform a BUIDL redemption of BUIDL's minimum required amount.
    |                          ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:432:46
    |
432 |       // so we perform a BUIDL redemption of BUIDL's minimum required amount.
    |                                              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:434:14
    |
434 |       _redeemBUIDL(minBUIDLRedeemAmount);
    |              ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:434:23
    |
434 |       _redeemBUIDL(minBUIDLRedeemAmount);
    |                       ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:435:19
    |
435 |       emit MinimumBUIDLRedemption(
    |                   ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:437:12
    |
437 |         minBUIDLRedeemAmount,
    |            ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:438:26
    |
438 |         usdcBalance + minBUIDLRedeemAmount - usdcAmountToRedeem
    |                          ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:442:66
    |
442 |       // to cover the redemption and fees without redeeming more BUIDL.
    |                                                                  ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:443:12
    |
443 |       emit BUIDLRedemptionSkipped(
    |            ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:458:19
    |
458 |   function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
    |                   ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:458:33
    |
458 |   function _redeemBUIDL(uint256 buidlAmountToRedeem) internal {
    |                                 ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:460:7
    |
460 |       buidl.balanceOf(address(this)) >= minBUIDLRedeemAmount,
    |       ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:460:44
    |
460 |       buidl.balanceOf(address(this)) >= minBUIDLRedeemAmount,
    |                                            ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:461:35
    |
461 |       "OUSGInstantManager::_redeemBUIDL: Insufficient BUIDL balance"
    |                                   ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:461:55
    |
461 |       "OUSGInstantManager::_redeemBUIDL: Insufficient BUIDL balance"
    |                                                       ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:464:5
    |
464 |     buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);
    |     ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:464:27
    |
464 |     buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);
    |                           ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:464:43
    |
464 |     buidl.approve(address(buidlRedeemer), buidlAmountToRedeem);
    |                                           ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:465:5
    |
465 |     buidlRedeemer.redeem(buidlAmountToRedeem);
    |     ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:465:26
    |
465 |     buidlRedeemer.redeem(buidlAmountToRedeem);
    |                          ^^^^^
    |
error: `buidl` should be `build`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:467:60
    |
467 |       usdc.balanceOf(address(this)) == usdcBalanceBefore + buidlAmountToRedeem,
    |                                                            ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:468:35
    |
468 |       "OUSGInstantManager::_redeemBUIDL: BUIDL:USDC not 1:1"
    |                                   ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:468:42
    |
468 |       "OUSGInstantManager::_redeemBUIDL: BUIDL:USDC not 1:1"
    |                                          ^^^^^
    |
error: `dicates` should be `dictates`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:494:46
    |
494 |    * @param _instantMintLimit New limit that dicates how much USDC can be transfered
    |                                              ^^^^^^^
    |
error: `transfered` should be `transferred`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:494:75
    |
494 |    * @param _instantMintLimit New limit that dicates how much USDC can be transfered
    |                                                                           ^^^^^^^^^^
    |
error: `dicates` should be `dictates`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:508:52
    |
508 |    * @param _instantRedemptionLimit New limit that dicates how much USDC
    |                                                    ^^^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:618:74
    |
618 |    * @notice Admin function to set the minimum amount required to redeem BUIDL
    |                                                                          ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:620:21
    |
620 |    * @param _minimumBUIDLRedemptionAmount The minimum amount required to redeem BUIDL
    |                     ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:620:81
    |
620 |    * @param _minimumBUIDLRedemptionAmount The minimum amount required to redeem BUIDL
    |                                                                                 ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:622:22
    |
622 |   function setMinimumBUIDLRedemptionAmount(
    |                      ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:623:21
    |
623 |     uint256 _minimumBUIDLRedemptionAmount
    |                     ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:625:17
    |
625 |     emit MinimumBUIDLRedemptionAmountSet(
    |                 ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:626:10
    |
626 |       minBUIDLRedeemAmount,
    |          ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:627:15
    |
627 |       _minimumBUIDLRedemptionAmount
    |               ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:629:8
    |
629 |     minBUIDLRedeemAmount = _minimumBUIDLRedemptionAmount;
    |        ^^^^^
    |
error: `BUIDL` should be `BUILD`
  --> project\2024-03-ondo-finance\contracts\ousg\ousgInstantManager.sol:629:36
    |
629 |     minBUIDLRedeemAmount = _minimumBUIDLRedemptionAmount;
    |                                    ^^^^^
    |

GitHub : 1

proof: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/README.md?plain=1#L113-L114

There are 4 instance(s) of this issue:


- contracts/ousg/ousgInstantManager.sol
// @proof: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/README.md?plain=1#L113-L114
// @context 
317	      usdc.transferFrom(msg.sender, feeReceiver, usdcfees);
...
// @proof: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/README.md?plain=1#L113-L114
// @context 
319	    usdc.transferFrom(msg.sender, usdcReceiver, usdcAmountAfterFee);
...
// @proof: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/README.md?plain=1#L113-L114
// @context 
348	    ousg.transferFrom(msg.sender, address(this), ousgAmountIn);

GitHub : 317, 319, 348


- contracts/ousg/rOUSG.sol
// @proof: https://github.com/code-423n4/2024-03-ondo-finance/blob/78779c30bebfd46e6f416b03066c55d587e8b30b/README.md?plain=1#L113-L114
// @context 
419	    ousg.transferFrom(msg.sender, address(this), _OUSGAmount);

GitHub : 419

#0 - 0xRobocop

2024-04-05T05:13:21Z

Bot findings

#1 - c4-pre-sort

2024-04-05T05:13:24Z

0xRobocop marked the issue as insufficient quality report

#2 - c4-judge

2024-04-09T15:52:10Z

3docSec marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter