Skip to content

feat: add setAssetOracle to Pool and add AggregatorRETH contract#1526

Open
MilGard91 wants to merge 3 commits intorelease-candidatefrom
chore/new-rETH-oracle
Open

feat: add setAssetOracle to Pool and add AggregatorRETH contract#1526
MilGard91 wants to merge 3 commits intorelease-candidatefrom
chore/new-rETH-oracle

Conversation

@MilGard91
Copy link
Copy Markdown
Contributor

@MilGard91 MilGard91 commented Mar 24, 2026

Description

  • Add a method to change the oracle address for the asset
  • Add a proxy contract that will read rETH/ETH ratio from the token contract

Testing

Explain how you tested your changes to ensure they work as expected.

Checklist

  • Performed a self-review of my own code
  • Made corresponding changes to the documentation

Summary by CodeRabbit

Release Notes

  • New Features
    • Added oracle configuration capability for registered assets with comprehensive validation for decimal compatibility between aggregators and assets.
    • Introduced rETH oracle aggregator support to enable enhanced pricing accuracy for rETH assets.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 88c28146-6934-48c3-bb10-3eb05d64aaa1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds oracle configuration functionality to the Pool contract. Introduces setAssetOracle function with aggregator type and decimals validation, a new AggregatorRETH contract for rETH pricing, mock implementation, and comprehensive unit and fork tests.

Changes

Cohort / File(s) Summary
Interface Updates
contracts/interfaces/IPool.sol
Added setAssetOracle function declaration and new AggregatorAssetMustNotBeETH error, extending oracle configuration capabilities.
Oracle Aggregator Implementation
contracts/modules/capital/AggregatorRETH.sol
New contract implementing rETH oracle aggregation with IRETH interface to fetch exchange rates and expose latestAnswer() function with 18 decimals.
Pool Implementation
contracts/modules/capital/Pool.sol
Implemented setAssetOracle with governor-restricted access, validation for non-zero addresses, ETH prevention, asset registration checks, and aggregator-decimals compatibility enforcement.
Mock Implementation
contracts/mocks/generic/PoolGeneric.sol
Added stubbed setAssetOracle virtual function that reverts with "unsupported" message, maintaining consistency with mock pattern.
Unit Tests
test/unit/Pool/setAssetOracle.js
Comprehensive test suite covering access control, input validation (zero addresses, ETH rejection, decimals compatibility), asset registration requirements, and positive oracle configuration scenarios.
Fork Tests
test/fork/rEthOracle/rETH.js, test/fork/rEthOracle/rETHAbi.json
Integration tests validating oracle configuration through governance proposal execution, pool upgrade, rETH oracle setup, and state verification with rETH contract ABI support.

Sequence Diagram

sequenceDiagram
    participant Gov as Governor
    participant Pool as Pool Contract
    participant Agg as AggregatorRETH
    participant rETH as rETH Token
    
    Gov->>Pool: Execute setAssetOracle(rETH, aggregator, ETH, 18)
    activate Pool
    Pool->>Pool: Validate assetAddress != 0
    Pool->>Pool: Validate aggregator != 0
    Pool->>Pool: Validate asset is registered
    Pool->>Pool: Validate decimals (18 for ETH type)
    Pool->>Pool: Update oracles[rETH] mapping
    deactivate Pool
    
    Gov->>Agg: Deploy AggregatorRETH(rETH)
    activate Agg
    Agg->>Agg: Store rETH reference
    deactivate Agg
    
    Agg->>rETH: latestAnswer() queries
    activate rETH
    rETH-->>Agg: getExchangeRate()
    deactivate rETH
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

test

Suggested reviewers

  • roxdanila
  • shark0der
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding setAssetOracle functionality to Pool and introducing the AggregatorRETH contract.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/new-rETH-oracle
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch chore/new-rETH-oracle

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
contracts/modules/capital/AggregatorRETH.sol (2)

14-16: Consider adding zero-address validation in constructor.

If _rETH is passed as the zero address, latestAnswer() calls will revert with an unhelpful error. Adding explicit validation provides a clearer failure mode at deployment time.

Proposed fix
  constructor(address _rETH) {
+   require(_rETH != address(0), "rETH address cannot be zero");
    rETH = IRETH(_rETH);
  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/modules/capital/AggregatorRETH.sol` around lines 14 - 16, Add an
explicit zero-address check in the constructor before assigning rETH: validate
that the incoming _rETH is not the zero address (require(_rETH != address(0),
"...")) and revert with a clear message if it is, then set rETH = IRETH(_rETH);
this ensures calls to IRETH.latestAnswer() fail fast with a clear deployment
error rather than later unhelpful reverts.

18-20: Return type mismatch with Aggregator interface.

The Aggregator interface expects latestAnswer() to return int, but this implementation returns uint256. When Pool._getAssetToEthRate calls oracle.aggregator.latestAnswer() and casts the result, this works because Solidity ABI decoding treats them as the same size, but it's fragile and non-standard.

Consider matching the interface signature for clarity and explicit safety.

Proposed fix
-  function latestAnswer() public view returns (uint256) {
-    return rETH.getExchangeRate();
+  function latestAnswer() public view returns (int256) {
+    return int256(rETH.getExchangeRate());
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/modules/capital/AggregatorRETH.sol` around lines 18 - 20, The
implementation of latestAnswer in AggregatorRETH.sol returns uint256 but the
Aggregator interface expects an int type; change the signature of function
latestAnswer() to return int256 and convert rETH.getExchangeRate() to int256
before returning (e.g. check that the uint256 value <= type(int256).max and then
return int256(value)). This makes the return type match the Aggregator interface
and avoids fragile ABI decoding when Pool._getAssetToEthRate calls
oracle.aggregator.latestAnswer().
test/unit/Pool/setAssetOracle.js (1)

110-126: Test description and hardcoded index.

  1. The test description says "add new aggregator" but semantically it's updating/changing an existing oracle configuration.
  2. Line 115 uses hardcoded index 3 - consider using getAssetId(token) to make the test more robust to setup changes.
Proposed improvements
-  it('should add new aggregator for an asset', async function () {
+  it('should update aggregator for an existing asset', async function () {
     const fixture = await loadFixture(setAssetOracleSetup);
     const { pool, governor, token, tokenAggregatorTwo } = fixture;

     await pool.connect(governor).setAssetOracle(token, tokenAggregatorTwo, 0, 18);
-    const tokenAsset = await pool.getAsset(3);
+    const assetId = await pool.getAssetId(token);
+    const tokenAsset = await pool.getAsset(assetId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/Pool/setAssetOracle.js` around lines 110 - 126, The test's
description and use of a hardcoded asset index are brittle: update the test
title to reflect that it's updating an existing oracle (e.g., "should update
aggregator for an asset"), replace the hardcoded getAsset(3) lookup with a
dynamic lookup using getAssetId(token) to fetch the asset id, and then call
pool.getAsset(assetId) and pool.oracles(token) to assert the updated aggregator,
aggregatorType, and assetDecimals after invoking
pool.connect(governor).setAssetOracle(token, tokenAggregatorTwo, 0, 18); ensure
references to setAssetOracle, getAsset, getAssetId and oracles are used so the
test no longer depends on a hardcoded index.
contracts/modules/capital/Pool.sol (1)

483-504: Consider emitting an event when the oracle is updated.

Other state-changing functions in the contract emit events (e.g., MCRUpdated, Payout). Adding an event for oracle changes would improve off-chain observability and audit trails.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@contracts/modules/capital/Pool.sol` around lines 483 - 504, Add an event and
emit it from setAssetOracle to record oracle changes: define an event (e.g.,
OracleUpdated(address asset, address aggregator, AggregatorType aggregatorType,
uint8 assetDecimals, address updatedBy)) and, at the end of the setAssetOracle
function, emit OracleUpdated with assetAddress, aggregator, aggregatorType,
assetDecimals and msg.sender (or tx originating contract) so every oracle update
to the oracles mapping is logged for off-chain observability; ensure the event
declaration uses the same AggregatorType enum and matches the oracles storage
fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@contracts/modules/capital/AggregatorRETH.sol`:
- Line 11: The decimals state variable is declared as uint but must match the
Aggregator interface signature (decimals() returns uint8); change the
declaration of decimals in AggregatorRETH.sol from uint public decimals = 18; to
uint8 public decimals = 18; and update any places that rely on decimals to
handle uint8 (or explicitly cast to uint) to avoid type-mismatch warnings when
the contract is used as an Aggregator per IPool.sol.

In `@contracts/modules/capital/Pool.sol`:
- Around line 491-497: The validation is checking assetDecimals instead of the
aggregator's own decimals; update the checks in the Pool asset-add logic to call
the aggregator's decimals (e.g., aggregator.decimals() or via the price
aggregator interface used elsewhere) and compare that value to 18 for
AggregatorType.ETH and 8 for AggregatorType.USD (the same approach used in
_addAsset). Locate the AggregatorType checks and replace assetDecimals with the
aggregator's decimals accessor (ensuring you use the same interface/type as
_addAsset) so the aggregator's latestAnswer interpretation is validated
correctly.

In `@test/fork/rEthOracle/rETH.js`:
- Around line 119-136: The test is reading state from the old Pool instance
(this.pool) before the upgrade, so move or repeat the pool re-acquisition to use
the upgraded Pool when reading post-upgrade values: after the governor upgrades
the Pool call ethers.getContractAt('Pool', pool.target) (or assign to a new
variable like upgradedPool) and then call upgradedPool.getAssets(),
upgradedPool.getMCR(), upgradedPool.getMCRRatio(), and
upgradedPool.oracles(this.rEth.target) (and use this.rEth.getExchangeRate() as
before) so the assertions comparing rEthOracle.aggregator to
this.rETHAggregator.target and other state reflect the new Pool, not the old
reference.

In `@test/unit/Pool/setAssetOracle.js`:
- Line 96: Update the test description in the failing 'it' block that currently
reads "reverts if asset doesnt exists" to use proper punctuation and grammar;
change it to "reverts if asset doesn't exist" in the 'it' test case in
setAssetOracle.js so the message is clear and grammatically correct.

---

Nitpick comments:
In `@contracts/modules/capital/AggregatorRETH.sol`:
- Around line 14-16: Add an explicit zero-address check in the constructor
before assigning rETH: validate that the incoming _rETH is not the zero address
(require(_rETH != address(0), "...")) and revert with a clear message if it is,
then set rETH = IRETH(_rETH); this ensures calls to IRETH.latestAnswer() fail
fast with a clear deployment error rather than later unhelpful reverts.
- Around line 18-20: The implementation of latestAnswer in AggregatorRETH.sol
returns uint256 but the Aggregator interface expects an int type; change the
signature of function latestAnswer() to return int256 and convert
rETH.getExchangeRate() to int256 before returning (e.g. check that the uint256
value <= type(int256).max and then return int256(value)). This makes the return
type match the Aggregator interface and avoids fragile ABI decoding when
Pool._getAssetToEthRate calls oracle.aggregator.latestAnswer().

In `@contracts/modules/capital/Pool.sol`:
- Around line 483-504: Add an event and emit it from setAssetOracle to record
oracle changes: define an event (e.g., OracleUpdated(address asset, address
aggregator, AggregatorType aggregatorType, uint8 assetDecimals, address
updatedBy)) and, at the end of the setAssetOracle function, emit OracleUpdated
with assetAddress, aggregator, aggregatorType, assetDecimals and msg.sender (or
tx originating contract) so every oracle update to the oracles mapping is logged
for off-chain observability; ensure the event declaration uses the same
AggregatorType enum and matches the oracles storage fields.

In `@test/unit/Pool/setAssetOracle.js`:
- Around line 110-126: The test's description and use of a hardcoded asset index
are brittle: update the test title to reflect that it's updating an existing
oracle (e.g., "should update aggregator for an asset"), replace the hardcoded
getAsset(3) lookup with a dynamic lookup using getAssetId(token) to fetch the
asset id, and then call pool.getAsset(assetId) and pool.oracles(token) to assert
the updated aggregator, aggregatorType, and assetDecimals after invoking
pool.connect(governor).setAssetOracle(token, tokenAggregatorTwo, 0, 18); ensure
references to setAssetOracle, getAsset, getAssetId and oracles are used so the
test no longer depends on a hardcoded index.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbbbeace-8f40-42f7-99d2-73ee6b0c9049

📥 Commits

Reviewing files that changed from the base of the PR and between ad21204 and 4bcfe2b.

📒 Files selected for processing (7)
  • contracts/interfaces/IPool.sol
  • contracts/mocks/generic/PoolGeneric.sol
  • contracts/modules/capital/AggregatorRETH.sol
  • contracts/modules/capital/Pool.sol
  • test/fork/rEthOracle/rETH.js
  • test/fork/rEthOracle/rETHAbi.json
  • test/unit/Pool/setAssetOracle.js

@roxdanila roxdanila linked an issue Mar 25, 2026 that may be closed by this pull request
4 tasks
@MilGard91 MilGard91 force-pushed the chore/new-rETH-oracle branch from 4bcfe2b to 60a4ac0 Compare March 25, 2026 13:19
@MilGard91 MilGard91 force-pushed the chore/new-rETH-oracle branch from 60a4ac0 to 265b23f Compare March 25, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace rETH oracle with the exchange rate feed

3 participants