Krzysztof Szubiczuk
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # NM-0081-Ondo Finance ## List of status for each finding - **Fixed:** Issue has been fixed in the code; - **Acknowledged:** Client acknowledges that he/she is aware of the situation, but no action will be taken now (since they depend on future decisions, require more planning, etc.) - **Mitigated:** The issue is still in the code, but some measures have been taken to mitigate the issue (by changing the user interface, updating user-facing documentation, or any other activities outside of this codebase); - **Not Fixed:** The issue is not fixed and the client has not engaged in any action to solve it; - **False-positive:** The client does not agree with the issue. **Commit Hash:** `6face44a82b7300be928cba30cd970636a77d93a` ### [Medium] Rounding issue when calculating absolute changes in basis points (BPS) **File(s)**: [`RWAOracleRateCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleRateCheck.sol), [`RWAOracleExternalComparisonCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol) **Description**: In the RWA oracle, there is a `MAX_CHANGE_DIFF_BPS` parameter that limits the change in the new price. This is done by calculating the difference between the new and old prices and dividing it by the old price. However, rounding down when calculating `changeBps` could allow price change to be higher than `MAX_CHANGE_DIFF_BPS`. **Example of an exploited scenario**: 1. The admin sets `MAX_CHANGE_DIFF_BPS = 10`. 2. If the old price is `previousPrice = 10011` and the new price is `newPrice = 10000`, the calculation will be: ```solidity= change = newPrice - previousPrice = 10000 - 10011 = -11 changeBps = (change * BPS_DENOMINATOR) / previousPrice = (-11 * 10000) / 10011 = -10.99 ``` 3. Since Solidity performs calculations using integers, `changeBps` will round down to `-10`, making it still valid to set even though the change is more than `10 BPS`. **Recommendation(s)**: Consider rounding up when calculating the absolute changes in basis points. Here is a [reference implementation](https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol#L53) for rounding up. **Status**: Acknowledged **Update from the client**: This is a great call out, but exclusively rounding up or down would have a loosening or tightening effect based on the situation. The slightly more correct option here would be to conditionally round up or down depending on which action would result in the tightest possible constraint. Due to how loose this constraint already is, we do not expect to ever be operating at these bounds. This decision should have no tangible outcome for the goals of the contract. We have decided not to implement “conditional” rounding does not worth the complexity. ***Example where rounding down loosens constraint.*** If RWA bps change is 84.9, and the CL bps change is 10, the 84.9 gets truncated to 84. Because 84-10 = 74, the price setting would succeed. ***Example where rounding down tightens constraint*** If RWA bps change is -85 and the CL bps change is -10.9, The -10.9 gets truncated to -10. Because -85 - -10 = -75, the price setting would fail. (If we rounded up it would have “loosened” constraint and passed). Please let us know if you believe we are missing anything here. **Update from Nethermind**: We agree with your perspective that rounding should be situation-dependent. However, we believe it remains an issue, not a false positive. The provided examples occur because `RWAOracleExternalComparisonCheck` employs two rounds of rounding (one for RWA BPS and one for Chainlink BPS), while in the case of `RWAOracleRateCheck`, the change is rounded only once, and rounding up always tightens the constraints. **Update from Client**: PR to round up for `RWAOracleRateCheck`: [a998c11559457b4d789cfda75903ee489f827cb9](https://github.com/ondoprotocol/flux/commit/a998c11559457b4d789cfda75903ee489f827cb9). --- ### [Medium] No guarantee that `rwaPrice` and `chainlinkPrice` are close **File(s)**: [`RWAOracleExternalComparisonCheck.sol`]() **Description**: In contract `RWAOracleExternalComparisonCheck`, every time a new price is set it will check with the recent Chainlink price to ensure it is not too far away from each other. However, there is no guarantee that `rwaPrice` and `chainlinkPrice` are close from the beginning or during operation. Consider the example below: 1. Initialization, `rwaPrice = 100000` and `chainlinkPrice = 100000`. They are exactly the same, so there is no problem. 2. During operation, Chainlink oracle went wrong `chainlinkPrice = 2000000`. As we can see, the difference between the new price and the old price of Chainlink is too large, it is ignored but still recorded. 4. Now, the setter calls the function `setPrice(...)` with `newPrice = 100100` (1% change). And now Chainlink oracle return price is `chainlinkPrice = 2002000` (1% change too). Their `changeDifferenceBps = 1% - 1% = 0`, thus still allowing the price to be updated. **Recommendation(s)**: Consider reviewing the mechanism of RWAOracleExternalComparisonCheck again. **Status**: False-positive **Update from the client**: The RWA and Chainlink prices are not the same. Currently, OUSG (the RWA token whose price we plan to set) is $100.8 while the SHV ETF price (from the chainlink feed is ~$110). The key insight behind the design is as that because the RWA price is backed by a fund comprised of the ETF with the CL price feed, they should move in similar directions over **short** time horizons." As for the specific example provided, when Chainlink reports an unrealistic price (more than 274 bps in change up or down), the code effectively ignores the Chainlink price change and solely relies on the “absolute check” (plus or minus 200 bps for setting a price. On the subsequent setting of the price, the operator of the contract will need to evaluate whether using this contract and CL is still feasible. The resolution may be that this was a one-time bug from CL and we can continue as normal. It may also be that CL is no longer reliable anymore and a vote should be required to utilize some new pricing mechanism. It is hard to predict what the problem and resolution should be. What is important here is that the invariant of the contract is not broken and the operators of the contract are not able to set the price in some dramatic fashion and rug pull or falsely liquidate investors in Flux Finance. **Update from Nethermind**: We agree that RWA price and Chainlink price are different and don't need to be similar in value. Therefore this issue is false positive. --- ### [Medium] `setPrice(...)` function does not guarantee the latest price **File(s)**: [`RWAOracleExternalComparisonCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol) **Description**: In the `RWAOracleExternalComparisonCheck` contract, the `setPrice(...)` function can update the price only if there is a difference between the latest `roundId` from Chainlink data and the current `roundId`. This check ensures that the answer is updated. Below we present this check: ```solidity= /////////////////////////////////////////////////////////// // @audit This check does not guarantee the latest price // /////////////////////////////////////////////////////////// if (round.roundId == lastSetRound.roundId) revert ChainlinkRoundNotUpdated(); ``` There is a scenario where the current `roundId` differs from the previous one, but the answer remains unchanged. This can occur when the `Aggregator` implementation is modified, causing the `phaseId` to change while the answer stays the same. The `roundId` is derived from the `phaseId` and `aggregatorRoundId`, so it changes when either of these values is modified. Refer for more information here: https://docs.chain.link/data-feeds/historical-data. **Recommendation(s)**: Verify that `answeredInRound` is equal to `roundId`. This ensures that the answer has been updated and is not being carried over. **Status**: False-positive **Update from the client**: Per recent Chainlink Oracle updates, it is impossible for roundID to be different from answeredInRound. See [OffchainAggregator.sol](https://etherscan.deth.net/address/0x9E1320991057c1246cba9F02c79f272a4Da892b3) L883. To double-check this invariant, we have standardized all CL queries by having them sanitize responses by requiring `roundId==AnsweredInRound`. **Update from Nethermind**: The provided address `0x9E1320991057c1246cba9F02c79f272a4Da892b3` corresponds to the `AccessControlledOffchainAggregator` contract for the SHV/USD pair. However, it is recommended to utilize the `EACAggregatorProxy` contract located at `0xc04611C43842220fd941515F86d1DDdB15F04e46`. This contract is listed on the official Chainlink page for the SHV/USD pair (https://docs.chain.link/data-feeds/price-feeds/addresses). The proxy contract directs to the current aggregator contract, allowing for upgrades without service interruption to consuming contracts. Consuming data from the proxy contract is advisable to ensure that the response originates from the current working aggregator, which is not guaranteed when consuming data directly from the aggregator. If the Ondo team decides to consume data from the proxy contract, it is recommended to verify that `answeredInRound` equals `roundId`. If the team chooses to consume data directly from the aggregator, this check is not necessary. However, additional logic must be implemented in the `RWAOracleExternalComparisonCheck` contract to allow for a change of the price feed address for the SHV/USD pair, as the aggregator contract used by Chainlink may change. **Update from Client**: We will be interacting directly with the Proxy and understand that we will have to upgrade and do a vote if CL changes their implementation such that `roundId != answeredInRound`. **Update from Nethermind**: During an internal discussion and a closer look at the CL codebase, we understood that such a condition could happen in the older Chainlink model, but it is not the case in the current implementation used by the RWA Oracle. After learning that answeredInRound is a deprecated variable, we decided to mark the issue as false-positive since we don’t believe it poses any security concern for Oracle. --- ### [Low] `_setFTokenToChainlinkOracle(...)` does not check oracle decimals **File(s)**: [`contracts/lending/fluxOracles/FluxOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fluxOracles/FluxOracle.sol) **Description**: The `_setFTokenToChainlinkOracle(...)` function assigns a specified Chainlink oracle to an `fToken`. However, there is no validation process in place to ensure that the number of decimals returned by the Chainlink oracle falls within an acceptable range. The function is presented below: ```solidity= function _setFTokenToChainlinkOracle( address fToken, address chainlinkOracle, uint256 maxChainlinkOracleTimeDelay ) private { if (fTokenToOracleType[fToken] != OracleType.CHAINLINK) { revert InvalidOracleType( OracleType.CHAINLINK, fTokenToOracleType[fToken] ); } address underlying = ICTokenLike(fToken).underlying(); fTokenToChainlinkOracle[fToken].scaleFactor = (10 ** (36 - uint256(IERC20Like(underlying).decimals()) - ////////////////////////////////////////////////////////////// // @audit The decimals from Oracle are not checked ////////////////////////////////////////////////////////////// uint256(AggregatorV3Interface(chainlinkOracle).decimals()))); fTokenToChainlinkOracle[fToken].oracle = AggregatorV3Interface( chainlinkOracle ); fTokenToChainlinkOracle[fToken] .maxChainlinkOracleTimeDelay = maxChainlinkOracleTimeDelay; } ``` **Recommendation(s)**: Verify the number of decimals returned by the Chainlink oracle. **Status**: Acknowledged **Update from the client**: The contract works as intended in cases where `underlyingDecimals + chainlinkDecimals <= 36`. If the sum is > 36, the function will underflow. If the sum is == 36, the `scaleFactor` will be 1. If the sum is < 36, the scaleFactor will be some value >= 1e2. A check is not needed. **Update from Nethermind**: We believe that a check for decimals should be performed to ensure that the Chainlink response is not corrupted. This check would be consistent with the other checks for Chainlink response decimals in the `fTokenOracle` and `RWAOracleExternalComparisonCheck` contracts. --- ### [Low] Changing the Oracle type does not remove previous Oracle data **File(s)**: [`contracts/lending/fluxOracles/FluxOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fluxOracles/FluxOracle.sol) **Description**: The `FluxOracle` contract allows for the oracle type to be changed for each asset. However, when the oracle type is changed, the data previously stored in the `fTokenToHardcodedPrice`, `fTokenToRWAOracle`, and `fTokenToChainlinkOracle` mappings for the given asset is not removed. This can result in unintended behavior. It is good practice to remove unused data to prevent such issues and to save on gas costs. Below we present `setFTokenToOracleType(...)` function responsible for changing the oracle type: ```solidity= function setFTokenToOracleType( address fToken, OracleType oracleType ) external override onlyRole(DEFAULT_ADMIN_ROLE) { fTokenToOracleType[fToken] = oracleType; ///////////////////////////////////////////////////////////////////////////////// // @audit Data from mappings fTokenToHardcodedPrice, fTokenToRWAOracle, and // fTokenToChainlinkOracle is not removed. ///////////////////////////////////////////////////////////////////////////////// emit FTokenToOracleTypeSet(fToken, oracleType); } ``` **Recommendation(s)**: Consider removing any unused data when changing the oracle type for a given asset. **Status**: Fixed **Update from the client**: Cleared stale data in this PR: [c39b3532de80600b201d801f28a699ae93bd07de](https://github.com/ondoprotocol/flux/commit/c39b3532de80600b201d801f28a699ae93bd07de). --- ### [Low] Lack of a two-step process for transferring ownership **File(s)**: [`contracts/lending/fTokenOracle/fTokenOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fTokenOracle/fTokenOracle.sol) **Description**: The `fTokenOracle` contract inherits OpenZeppelin’s `Ownable` contract, which enables ownership to be transferred in a single step. However, this one-step transfer process can be prone to errors and may cause significant damage to the protocol if a mistake occurs. The function is presented below: ```solidity= ////////////////////////////////////////////////////////////////////////////////////////// // @audit Providing an incorrect address will result in the immediate loss of ownership // ////////////////////////////////////////////////////////////////////////////////////////// function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } ``` **Recommendation(s)**: We recommend implementing a two-step ownership transfer process, such as the propose-accept scheme. You can refer to the [Ownable2Step.sol](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable2Step.sol) contract from OpenZeppelin for an example. **Status**: Fixed **Update from the client**: Added Ownable2Step in this PR: [0b86a1b554a8c345faef8b22045c718e52e2d533](https://github.com/ondoprotocol/flux/commit/0b86a1b554a8c345faef8b22045c718e52e2d533). --- ### [Low] Use AccessControlDefaultAdminRules **File(s)**: [`contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol), [`contracts/lending/rwaOracles/RWAOracleRateCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleRateCheck.sol), [`contracts/lending/fluxOracles/FluxOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fluxOracles/FluxOracle.sol) **Description**: The `FluxOracle`, `RWAOracleRateCheck`, and `RWAOracleExternalComparisonCheck` contracts inherit from [`AccessControl`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControl.sol). However, the `AccessControl` contract allows for potentially insecure behavior with the `DEFAULT_ADMIN_ROLE`, as multiple addresses can be granted this role and it is its own admin, meaning it has permission to grant and revoke the default admin role. A more secure solution is the [`AccessControlDefaultAdminRules`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/AccessControlDefaultAdminRules.sol) contract, which implements additional risk mitigations on top of `AccessControl`: - only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced - enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account. - enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted. - the delay can be changed by scheduling - it is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`. **Recommendation(s)**: Consider using `AccessControlDefaultAdminRules` to increase the security of role management. **Status**: Acknowledged **Update from the client**: Understand the benefits of `AccessControlDefaultAdminRules`; however, we don't want to believe we need the added features/complexity at this point in time. --- ### [Info] No check for `fToken` validity **File(s)**: [`contracts/lending/fluxOracles/FluxOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fluxOracles/FluxOracle.sol) **Description**: In the `FluxOracle` contract, the oracle type can be assigned to an `fToken` through the `fTokenToOracleType` mapping. However, there is no check in place to ensure that the `fToken` is a valid address. This could be implemented in a similar manner to the `fTokenOracle` contract: ```solidity= if (!IFToken(_fToken).isCToken()) { revert InvalidFToken(); } ``` **Recommendation(s)**: Consider checking the validity of the `fToken` when assigning the oracle type to the asset. **Status**: Fixed **Update from the client**: Check added in this PR: [f19e5a38b78153ea436632f480b73194cdcda930](https://github.com/ondoprotocol/flux/commit/f19e5a38b78153ea436632f480b73194cdcda930). --- ### [Info] Inconsistent behavior on `price == 0` **File(s)**: [`contracts/lending/fluxOracles/FluxOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fluxOracles/FluxOracle.sol) **Description**: The `FluxOracle` contract supports three types of oracles: hardcoded, RWA, and Chainlink. However, these oracle types do not exhibit consistent behavior when the price is equal to `0`. Here is a summary of the behavior for each oracle type: - Hardcoded: The price can be set to and retrieved as `0`. - RWA: The oracle cannot be set if returned price is `0`. The price can be retrieved as `0`. - Chainlink: The price can be retrieved as `0` (but reverts if less than `0`). This inconsistent behavior with zero values may lead to incorrect assumptions when using the oracles. While checking for a zero value when setting the RWA oracle is good practice, it creates an assumption that this oracle will always work correctly and does not check the returned price on a get-call. Additionally, the price set for the hardcoded oracle is not checked and can be `0`. The Chainlink price is only considered invalid if it is less than `0`, allowing for `0` to be a valid price. **Recommendation(s)**: Consider establishing a unified definition of a zero price for all types of oracles and implementing the set and get functions to behave consistently when the price is zero. **Status**: Fixed **Update from the client**: FluxOracle contract now prevents setting price=0 for Hardcoded types and retrieving zero prices for all Oracle types. Fixed in: [48d5c4052795b082e876926f18f884927bb653b9](https://github.com/ondoprotocol/flux/commit/48d5c4052795b082e876926f18f884927bb653b9). --- ### [Info] Events in RWA oracles differ **File(s)**: [`RWAOracleRateCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleRateCheck.sol), [`RWAOracleExternalComparisonCheck.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol) **Description**: The `RWAOracleRateCheck` and `RWAOracleExternalComparisonCheck` contracts can both be assigned as oracles to a `fToken` with the same specified in the `FluxOracle`. As such, they are interchangeable. However, it should be noted that the events emitted when the price is set to contain a varying number of fields. This discrepancy may pose a problem if these logs are utilized by off-chain infrastructure. The events in both contracts are presented below: ```solidity= //////////////////////// // RWAOracleRateCheck // //////////////////////// event RWAPriceSet( int256 oldPrice, int256 newPrice, uint256 timestamp ); ////////////////////////////////////// // RWAOracleExternalComparisonCheck // ////////////////////////////////////// event RWAPriceSet( int256 oldChainlinkPrice, uint80 indexed oldRoundId, int256 newChainlinkPrice, uint80 indexed newRoundId, int256 oldRWAPrice, int256 newRWAPrice ); ``` **Recommendation(s)**: Maintain consistency in the number of fields included in the RWAPriceSet event. Alternatively, change the event names to correspond to the contracts that emit them. **Status**: Fixed **Update from the client**: We have changed the name of the event in `RWAOracleExternalComparisonCheck` in this commit. [22716a82e7382ab2479d6fe74f79d59e43879a65](https://github.com/ondoprotocol/flux/commit/22716a82e7382ab2479d6fe74f79d59e43879a65). --- ### [Info] Incorrect check for decimals **File(s)**: [`RWAOracleExternalComparisonCheck`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/rwaOracles/RWAOracleExternalComparisonCheck.sol) **Description**: The constructor includes a check to ensure that the Chainlink oracle returns at least 4 decimals. The relevant code is shown below: ```solidity= constructor( int256 initialPrice, address _chainlinkOracle, string memory _description, address admin, address _setter ) { chainlinkOracle = AggregatorV3Interface(_chainlinkOracle); if (chainlinkOracle.decimals() < 4) revert CorruptedChainlinkResponse(); .... } ``` However, it should be noted that the current minimum number of decimals returned by Chainlink is 8 for non-ETH pairs. In the event that the returned decimals are less than 8 but greater than 3, which could indicate a malfunction of Chainlink, this would not be detected by the contract. **Recommendation(s)**: Consider checking if the decimals returned by chainlink is 8. **Status**: Fixed **Update from the client**: We have changed the check to `decimals==8` in this commit. [f983d5487e568016ad1ae2e4db72c2ec50d2b74d](https://github.com/ondoprotocol/flux/commit/f983d5487e568016ad1ae2e4db72c2ec50d2b74d). --- ### [Best Practice] `renounceOwnership(...)` function should be disabled **File(s)**: [`contracts/lending/fTokenOracle/fTokenOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fTokenOracle/fTokenOracle.sol) **Description**: The `fTokenOracle` contract, which is inherited from the `Ownable` contract, includes the `renounceOwnership(...)` function. This function allows the owner to be removed from the contract. Below is a presentation of the function: ```solidity= ////////////////////////////////////////////////////////////////////////// // @audit Renouncing ownership will leave the contract without an owner // ////////////////////////////////////////////////////////////////////////// function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } ``` **Recommendation(s)**: If the `fTokenOracle` is intended to operate with an owner, consider disabling the `renounceOwnership(...)` function. **Status**: Acknowledged **Update from the client**: --- ### [Best Practice] Use of magic numbers **File(s)**: [`fTokenOracle.sol`](https://github.com/ondoprotocol/flux/blob/6face44a82b7300be928cba30cd970636a77d93a/contracts/lending/fTokenOracle/fTokenOracle.sol) **Description**: The `fTokenOracle` contract calculates the `scaleFactor` using the following equation: ```solidity= ///////////////////////////////////////////////////////// // @audit Constant 8 is not explained ///////////////////////////////////////////////////////// scaleFactor = 10 ** (priceFeedDecimals - 8 + fTokenUnderlyingDecimals); ``` The constant `8` is used in the calculation, but its purpose is not explained. **Recommendation(s)**: It is recommended to store constants as variables and provide a description that explains their purpose. This would improve the readability and maintainability of the code. **Status**: Fixed **Update from the client**: Removed the magic number in this PR: [0b86a1b554a8c345faef8b22045c718e52e2d533](https://github.com/ondoprotocol/flux/commit/0b86a1b554a8c345faef8b22045c718e52e2d533). ---

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully