Skip to content

Conversation

@rudewalt
Copy link
Contributor

@rudewalt rudewalt commented Jun 19, 2024

Summary by CodeRabbit

  • New Features

    • Introduced PendleMarketOracle contract for retrieving price information from the Pendle market.
    • Added methods to set token pairs and update time-weighted average price (TWAP) duration.
    • Introduced functions to calculate balance and margin call prices for token pairs.
    • Added MarginlyCompositeOracle contract to manage multiple price oracles and retrieve prices.
  • Tests

    • Added comprehensive tests for MarginlyCompositeOracle and related oracles to handle various token pairs and price scenarios.
    • Included new test cases for price retrieval using Pendle and Curve oracles.
    • Streamlined import statements in test files for better organization.

@rudewalt rudewalt requested a review from optifat June 19, 2024 11:41
@coderabbitai
Copy link

coderabbitai bot commented Jun 19, 2024

Walkthrough

The changes introduce the PendleMarketOracle contract, which implements the IPriceOracle interface to manage price data for Pendle tokens and their corresponding Ib tokens. Additionally, new Hardhat configuration files for Arbitrum and Ethereum forking are created or updated. The integration testing scripts are modified to accommodate these configurations, and several test files are updated to reflect changes in import paths due to a directory restructuring. New tests for the MarginlyCompositeOracle are also added to ensure proper functionality.

Changes

File(s) Change Summary
packages/periphery/contracts/oracles/PendleMarketOracle.sol Introduced PendleMarketOracle contract implementing IPriceOracle for Pendle market price retrieval.
packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol Added MarginlyCompositeOracle contract for managing multiple price oracles.
packages/periphery/hardhat-configs/arb-fork.config.ts Added configuration for Hardhat to fork from the Arbitrum blockchain.
packages/periphery/hardhat-configs/eth-fork.config.ts Updated Hardhat network forking settings including RPC URL and block number.
packages/periphery/package.json Removed "test:int" script and added "test:int-arb" and "test:int-eth" scripts for integration tests.
packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts Added tests for MarginlyCompositeOracle, checking price retrieval for PT-wstUSR/USR token pair.
packages/periphery/test/int/arb/others/*.spec.ts Updated import paths for several test files due to directory restructuring.

Possibly related PRs

  • Feature/pendle pt to asset adapter #207: The changes in the main PR, which introduce the PendleMarketOracle contract and its associated functions for managing price oracles, are related to the retrieved PR that adds the PendlePtToAssetAdapter contract, as both involve the implementation and management of price-related functionalities within the Pendle ecosystem.
  • added pendle market oracle #185: The changes in the main PR are directly related to the modifications made in the retrieved PR, as both involve the implementation and functionality of the PendleMarketOracle contract, including the same functions and structures.
  • small fixes in deploy script, deployed weth pools #196: The changes in the main PR, which introduce the PendleMarketOracle contract and its associated functions for managing price oracles, are related to the retrieved PR, which modifies the deployment scripts and configurations for various price oracles, including settings for the weth token that may interact with the new oracle.

Suggested reviewers

  • optifat

Poem

In the realm of prices, where tokens align,
Pendle's new oracle now helps us define.
With scripts for forking and tests in the fray,
We measure and manage, come what may.
A hop through the code, with paths to explore,
In the world of finance, we’re ready for more!
🐇✨📊


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cffa950 and 749d667.

📒 Files selected for processing (3)
  • packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (1 hunks)
  • packages/periphery/test/PendleOracle.spec.ts (1 hunks)
  • packages/periphery/test/shared/fixtures.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol
🔇 Additional comments (4)
packages/periphery/test/PendleOracle.spec.ts (1)

3-3: LGTM! Import consolidation improves code organization.

Moving the constants to a centralized location in fixtures.ts improves maintainability.

packages/periphery/test/shared/fixtures.ts (3)

40-41: LGTM! Constants are properly exported.

The constants are correctly defined and exported for reuse across test files.


1134-1137: LGTM! Clean factory function implementation.

The function provides a simple way to create an empty oracle instance.


1139-1146: LGTM! Well-structured type definition.

The type clearly defines all components needed for the composite oracle setup.

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 3

Outside diff range and nitpick comments (1)
packages/periphery/test/int/others/MarginlyCompositeOracle.spec.ts (1)

9-82: The tests are well-structured and cover multiple composite scenarios. However, ensure that the print statements are intended for debugging purposes only and consider removing them before deployment to maintain clean and professional code.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 3126dbb and b70806e.

Files selected for processing (7)
  • packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (1 hunks)
  • packages/periphery/contracts/test/MockPriceOracleV2.sol (1 hunks)
  • packages/periphery/hardhat-configs/fork.config.ts (1 hunks)
  • packages/periphery/test/MarginlyCompositionOracle.spec.ts (1 hunks)
  • packages/periphery/test/UniswapV2Oracle.spec.ts (1 hunks)
  • packages/periphery/test/int/others/MarginlyCompositeOracle.spec.ts (1 hunks)
  • packages/periphery/test/shared/fixtures.ts (2 hunks)
Additional comments not posted (9)
packages/periphery/hardhat-configs/fork.config.ts (1)

11-12: The updated forking configuration appears correct. Ensure that the new RPC URL and block number are consistent with your intended test environment.

packages/periphery/contracts/test/MockPriceOracleV2.sol (1)

21-27: The getter functions for prices are straightforward and follow Solidity best practices.

packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (3)

26-65: The implementation of setPair is robust with comprehensive error checks and state updates. Consider adding comments to explain the logic, especially how token pairs are stored bidirectionally.


67-76: The getBalancePrice function correctly handles uninitialized pairs and performs safe arithmetic operations to calculate the composite price. Good use of the Math.mulDiv for precision.


78-87: The getMargincallPrice function is consistent with getBalancePrice in handling errors and calculating prices, ensuring uniformity across different types of price retrievals.

packages/periphery/test/shared/fixtures.ts (4)

18-18: The import of MockPriceOracleV2 is correctly placed within the import block, maintaining alphabetical order.


19-19: The import of MarginlyCompositeOracle is correctly placed within the import block, maintaining alphabetical order.


1137-1144: The type definition MarginlyCompositeOracleData is well-structured and provides a clear interface for handling oracle data. This should facilitate easier manipulation and testing of oracle-related data.


1132-1135: The function createEmptyMarginlyCompositeOracle is concise and correctly uses the factory pattern to instantiate MarginlyCompositeOracle. Ensure that the contract deployment is covered by appropriate tests to verify its initialization state.

Verification successful

The previous shell script did not return any results, which suggests that there might not be any direct test cases for createEmptyMarginlyCompositeOracle. However, to ensure thorough verification, we should extend our search to include any references to createEmptyMarginlyCompositeOracle within the test files. This will help us identify if the function is indirectly tested.


The search results indicate that createEmptyMarginlyCompositeOracle is indeed referenced within the test files, particularly in MarginlyCompositionOracle.spec.ts. This suggests that the function is being utilized in tests, which implies that the contract deployment is covered by these tests.

  • packages/periphery/test/MarginlyCompositionOracle.spec.ts: The function createEmptyMarginlyCompositeOracle is imported and used within this test file.
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify that tests cover the deployment of MarginlyCompositeOracle.

# Test: Search for the test cases for `createEmptyMarginlyCompositeOracle`. Expect: At least one test case.
rg --type typescript $'describe\(.*createEmptyMarginlyCompositeOracle.*\)'

Length of output: 110



Script:

#!/bin/bash
# Description: Verify that tests cover the deployment of MarginlyCompositeOracle.

# Test: Search for the test cases for `createEmptyMarginlyCompositeOracle`. Expect: At least one test case.
# Fixing the file type issue by specifying extensions for TypeScript files
rg 'describe\(.*createEmptyMarginlyCompositeOracle.*\)' --glob '*.ts' --glob '*.tsx'

Length of output: 86



Script:

#!/bin/bash
# Description: Verify that tests cover the deployment of MarginlyCompositeOracle.

# Test: Search for any references to `createEmptyMarginlyCompositeOracle` within the test files.
rg 'createEmptyMarginlyCompositeOracle' --glob '*.ts' --glob '*.tsx'

Length of output: 626

Comment on lines +5 to +87
describe('MarginlyCompositeOracle', () => {
it('should fail when zero address passed', async () => {
const oracle = await loadFixture(createEmptyMarginlyCompositeOracle);
const quote = '0x0000000000000000000000000000000000000001';
const interm = '0x0000000000000000000000000000000000000002';
const base = '0x0000000000000000000000000000000000000003';
const oracle1 = '0x0000000000000000000000000000000000000004';

await expect(oracle.setPair(ZERO_ADDRESS, interm, base, ZERO_ADDRESS, ZERO_ADDRESS)).to.be.revertedWithCustomError(
oracle,
'ZeroAddress'
);

await expect(oracle.setPair(quote, ZERO_ADDRESS, base, ZERO_ADDRESS, ZERO_ADDRESS)).to.be.revertedWithCustomError(
oracle,
'ZeroAddress'
);

await expect(oracle.setPair(quote, interm, ZERO_ADDRESS, ZERO_ADDRESS, ZERO_ADDRESS)).to.be.revertedWithCustomError(
oracle,
'ZeroAddress'
);

await expect(oracle.setPair(quote, interm, base, ZERO_ADDRESS, oracle1)).to.be.revertedWithCustomError(
oracle,
'ZeroAddress'
);

await expect(oracle.setPair(quote, interm, base, oracle1, ZERO_ADDRESS)).to.be.revertedWithCustomError(
oracle,
'ZeroAddress'
);
});

it('should return price weth/usdc with arb intermediate', async () => {
const { oracle, quoteToken: usdc, baseToken: weth } = await loadFixture(createMarginlyCompositeOracle);

const balancePrice = await oracle.getBalancePrice(usdc.address, weth.address);
console.log(balancePrice);

const mcPrice = await oracle.getMargincallPrice(usdc.address, weth.address);
console.log(mcPrice);
});

it('should return price usdc/weth with usdc intermediate', async () => {
const { oracle, quoteToken: usdc, baseToken: weth } = await loadFixture(createMarginlyCompositeOracle);

const balancePrice = await oracle.getBalancePrice(weth.address, usdc.address);
console.log(balancePrice);

const mcPrice = await oracle.getMargincallPrice(weth.address, usdc.address);
console.log(mcPrice);
});

it('should fail when pair not initialized', async () => {
const {
oracle,
quoteToken: usdc,
baseToken: weth,
intermediateToken: arb,
} = await loadFixture(createMarginlyCompositeOracle);

await expect(oracle.getBalancePrice(arb.address, usdc.address)).to.be.revertedWithCustomError(
oracle,
'NotInitialized'
);

await expect(oracle.getBalancePrice(usdc.address, arb.address)).to.be.revertedWithCustomError(
oracle,
'NotInitialized'
);

await expect(oracle.getMargincallPrice(arb.address, usdc.address)).to.be.revertedWithCustomError(
oracle,
'NotInitialized'
);

await expect(oracle.getMargincallPrice(usdc.address, arb.address)).to.be.revertedWithCustomError(
oracle,
'NotInitialized'
);
});
});
Copy link

Choose a reason for hiding this comment

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

The test cases comprehensively cover scenarios including zero address checks and uninitialized pair handling. Consider adding tests for boundary conditions and unexpected inputs to further enhance the robustness of the oracle.

Copy link

@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: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between b70806e and 59592a1.

Files selected for processing (1)
  • packages/periphery/test/UniswapV2Oracle.spec.ts (2 hunks)
Files skipped from review as they are similar to previous changes (1)
  • packages/periphery/test/UniswapV2Oracle.spec.ts

Copy link

@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: 2

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 59592a1 and e961947.

Files selected for processing (1)
  • packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (1 hunks)
Additional comments not posted (6)
packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (6)

1-1: Ensure SPDX license compatibility.

The SPDX license identifier is set to GPL-2.0-or-later. Make sure this is compatible with other licenses used in your project, particularly for dependencies and linked contracts.


4-7: Review import statements for correctness and necessity.

Ensure that all imported contracts and libraries (Ownable2Step, Math, IPriceOracle, CompositeOracle) are used within the file and are necessary for the functionality of MarginlyCompositeOracle.


14-17: Error handling: Custom error definitions.

The custom errors (ZeroPrice, ZeroAddress, NotInitialized, PairAlreadyExists) are well-defined and enhance clarity and gas efficiency. Good practice in Solidity.


19-19: Constant usage: X96ONE.

The constant X96ONE is defined but ensure that its usage in multiplication and division operations is clear to maintainers, possibly by adding a comment explaining its purpose and significance.


36-40: Input validation using revert with ZeroAddress.

Proper use of input validation to prevent zero address parameters. This is crucial for security in smart contracts to avoid unintended behavior.


70-79: Functions getBalancePrice and getMargincallPrice.

Both functions perform similar operations with slight variations. They effectively use the Math.mulDiv for price calculations, which is a safe way to handle overflow issues. Ensure that the division by X96ONE is always safe and consider edge cases where firstPrice or secondPrice could be extremely large.

Also applies to: 81-90

Comment on lines 29 to 68
function setPair(
address quoteToken,
address intermediateToken,
address baseToken,
IPriceOracle quoteIntermediateOracle,
IPriceOracle interMediateBaseOracle
) external onlyOwner {
if (quoteToken == address(0)) revert ZeroAddress();
if (intermediateToken == address(0)) revert ZeroAddress();
if (baseToken == address(0)) revert ZeroAddress();
if (address(quoteIntermediateOracle) == address(0)) revert ZeroAddress();
if (address(interMediateBaseOracle) == address(0)) revert ZeroAddress();

OracleParams memory params = getParams[quoteToken][baseToken];
if (params.intermediateToken != address(0)) revert PairAlreadyExists();

if (quoteIntermediateOracle.getBalancePrice(quoteToken, intermediateToken) == 0) revert ZeroPrice();
if (quoteIntermediateOracle.getBalancePrice(intermediateToken, quoteToken) == 0) revert ZeroPrice();

if (quoteIntermediateOracle.getMargincallPrice(quoteToken, intermediateToken) == 0) revert ZeroPrice();
if (quoteIntermediateOracle.getMargincallPrice(intermediateToken, quoteToken) == 0) revert ZeroPrice();

if (interMediateBaseOracle.getBalancePrice(intermediateToken, baseToken) == 0) revert ZeroPrice();
if (interMediateBaseOracle.getBalancePrice(baseToken, intermediateToken) == 0) revert ZeroPrice();

if (interMediateBaseOracle.getMargincallPrice(intermediateToken, baseToken) == 0) revert ZeroPrice();
if (interMediateBaseOracle.getMargincallPrice(baseToken, intermediateToken) == 0) revert ZeroPrice();

getParams[quoteToken][baseToken] = OracleParams({
intermediateToken: intermediateToken,
quoteIntermediateOracle: quoteIntermediateOracle,
interMediateBaseOracle: interMediateBaseOracle
});

getParams[baseToken][quoteToken] = OracleParams({
intermediateToken: intermediateToken,
quoteIntermediateOracle: interMediateBaseOracle,
interMediateBaseOracle: quoteIntermediateOracle
});
}
Copy link

Choose a reason for hiding this comment

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

Complex function: setPair.

This function handles a lot of logic including input validation, price checks, and state updates. Consider breaking it down into smaller functions or adding helper functions to improve readability and maintainability.

Comment on lines 45 to 55
if (quoteIntermediateOracle.getBalancePrice(quoteToken, intermediateToken) == 0) revert ZeroPrice();
if (quoteIntermediateOracle.getBalancePrice(intermediateToken, quoteToken) == 0) revert ZeroPrice();

if (quoteIntermediateOracle.getMargincallPrice(quoteToken, intermediateToken) == 0) revert ZeroPrice();
if (quoteIntermediateOracle.getMargincallPrice(intermediateToken, quoteToken) == 0) revert ZeroPrice();

if (interMediateBaseOracle.getBalancePrice(intermediateToken, baseToken) == 0) revert ZeroPrice();
if (interMediateBaseOracle.getBalancePrice(baseToken, intermediateToken) == 0) revert ZeroPrice();

if (interMediateBaseOracle.getMargincallPrice(intermediateToken, baseToken) == 0) revert ZeroPrice();
if (interMediateBaseOracle.getMargincallPrice(baseToken, intermediateToken) == 0) revert ZeroPrice();
Copy link

Choose a reason for hiding this comment

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

Price validation checks.

The repeated checks for zero prices are good for ensuring valid oracle responses. However, consider abstracting these checks into a separate function to reduce redundancy and improve code clarity.

Copy link

@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 (2)
packages/periphery/contracts/oracles/PendleMarketOracle.sol (1)

129-153: Minor naming nitpick for clarity.
The boolean parameter isMargincallPrice could be more descriptive, e.g. forLiquidation. This helps reflect its purpose more directly at call sites.

packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts (1)

7-16: Consider extracting hardcoded values to constants file.

Contract addresses and configuration values should be moved to a separate constants file for better maintainability and reusability across tests.

Consider creating a new file like test/constants/addresses.ts:

export const ETHEREUM_ADDRESSES = {
  PYTH_CONTRACT: '0x4305fb66699c3b2702d4d05cf36551390a4c69c6',
  WST_USR: '0x1202f5c7b4b9e47a1a484e8b270be34dbbc75055',
  // ... other addresses
};

export const ORACLE_CONFIG = {
  PYTH_MAX_PRICE_AGE: 1800,
  PENDLE_MARKET_ORACLE_SECONDS_AGO: 1800,
  // ... other config values
};
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e961947 and 48a6eb2.

📒 Files selected for processing (11)
  • packages/periphery/contracts/oracles/PendleMarketOracle.sol (1 hunks)
  • packages/periphery/hardhat-configs/arb-fork.config.ts (1 hunks)
  • packages/periphery/hardhat-configs/eth-fork.config.ts (1 hunks)
  • packages/periphery/package.json (1 hunks)
  • packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts (1 hunks)
  • packages/periphery/test/int/arb/others/ChainlinkOracle.spec.ts (1 hunks)
  • packages/periphery/test/int/arb/others/CurveOracle.spec.ts (1 hunks)
  • packages/periphery/test/int/arb/others/MarginlyCompositeOracle.spec.ts (1 hunks)
  • packages/periphery/test/int/arb/others/PythOracle.spec.ts (1 hunks)
  • packages/periphery/test/int/arb/others/UniswapV2Oracle.spec.ts (1 hunks)
  • packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/periphery/test/int/arb/others/CurveOracle.spec.ts
  • packages/periphery/test/int/arb/others/UniswapV2Oracle.spec.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts

[error] 5-5: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)

🔇 Additional comments (9)
packages/periphery/test/int/arb/others/ChainlinkOracle.spec.ts (1)

4-5: LGTM! Import paths correctly updated.

The import paths have been properly adjusted to reflect the new directory structure while maintaining the same functionality.

packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts (1)

2-3: LGTM! Import paths correctly updated.

The import paths have been properly adjusted to reflect the new directory structure while maintaining the correct relative paths to the imported modules.

Let's verify that the new import paths are correct and the imported modules exist:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Description: Verify that the imported modules exist at the specified paths

# Check if the oracle contracts exist
fd -t f "AlgebraTickOracle|AlgebraTickOracleDouble" typechain-types/contracts/oracles

# Check if the common utilities exist
fd -t f "common.ts" shared

Length of output: 329


Attention: Manual Verification Needed for Import Paths

The updated import paths in packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts appear to correctly reflect the intended restructuring. However, our initial file system search did not yield results because the search command used directory paths that do not exist in the repository structure. Please manually verify or run updated shell scripts to ensure that:

  • The modules AlgebraTickOracle and AlgebraTickOracleDouble exist at the location corresponding to the relative path ../../../../typechain-types/contracts/oracles
  • The utilities getDecimalsDiff and printPrices are present at the relative path ../../../shared/common

For further confidence, you can run a new script to search for the directories by name from the repository root:

Please verify that the output confirms the correct directory placement relative to packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts.


🏁 Script executed:

#!/bin/bash
echo "Searching for directories named 'typechain-types' and 'shared':"
fd -t d "typechain-types"
fd -t d "shared"

Length of output: 432


Below is an updated script to search for the relevant files across the repository. Please run it and verify that the files are located where the updated import paths in packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts expect them to be:

Once you confirm the search results match the expected locations relative to the file (../../../../typechain-types/contracts/oracles and ../../../shared/common), please update the review accordingly.


🏁 Script executed:

#!/bin/bash
echo "Searching for files containing 'AlgebraTickOracle' in the repository:"
fd -t f "AlgebraTickOracle"

echo "Searching for files containing 'AlgebraTickOracleDouble' in the repository:"
fd -t f "AlgebraTickOracleDouble"

echo "Searching for files containing 'getDecimalsDiff' in the repository:"
fd -t f "getDecimalsDiff"

echo "Searching for files containing 'printPrices' in the repository:"
fd -t f "printPrices"

Length of output: 1036


Action Required: Manual Verification Needed for Import Paths

The updated import paths in packages/periphery/test/int/arb/others/AlgebraOracle.spec.ts reference modules that we couldn’t conclusively locate via our automated searches:

  • The Oracle contracts are imported from a relative path ending in typechain-types/contracts/oracles, but our search only revealed the Solidity sources under packages/periphery/contracts/oracles. It’s likely that the typechain-types folder is generated (and hence not present in the repository), so please confirm that the generated TypeChain files for AlgebraTickOracle and AlgebraTickOracleDouble are indeed output to the expected location relative to this test file.
  • The utilities (getDecimalsDiff and printPrices) are imported from a path resolving to packages/periphery/test/shared/common (or a similar file), but our search for these strings didn’t return results. Note that the shared directory exists, so please verify manually that the required file (likely common.ts or similar) containing these utilities is present in that directory.

To assist further, please run this additional script from the repository root to list files in the expected directories:

Based on the outcome of these checks, please manually verify that the import paths correctly resolve to the generated oracle types and the shared utility functions.


🏁 Script executed:

#!/bin/bash
echo "Listing directories for generated types (if any):"
find . -type d -iname "typechain-types"

echo "Listing files in packages/periphery/test/shared (expected location for common file):"
find packages/periphery/test/shared -type f

Length of output: 657


LGTM! Import paths correctly updated.

  • The relative import for the common utility now properly targets packages/periphery/test/shared/common.ts.
  • The Oracle contracts’ generated TypeChain files are expected to reside in a typechain-types folder outside of versioned source files; the absence in file searches is typical for generated outputs.

Overall, the updates reflect the intended directory restructuring.

packages/periphery/contracts/oracles/PendleMarketOracle.sol (3)

26-35: Good use of descriptive custom errors.
No issues found with your approach here, as leveraging custom errors improves clarity and reduces bytecode size.


36-40: Constructor check looks correct.
Requiring a non-zero _pendle address is sufficient for initialization here, and storing it in an immutable variable is a good practice.


95-117: Verify new TWAP durations are properly initialized for liquidation.
When updating secondsAgoLiquidation to a new value, you may inadvertently set it to a range the oracle has not yet initialized. If intended, this is fine. Otherwise, repeating _assertOracleIsInitialized for the updated secondsAgoLiquidation helps prevent future retrieval errors.

Would you like a script to locate and confirm all usage of updated TWAP durations, ensuring the oracle can handle both durations?

packages/periphery/hardhat-configs/eth-fork.config.ts (1)

11-12: Confirm correctness of forking block number.
Please ensure that block 21814800 meets your testing or debugging requirements. Using environment variables for the RPC URL and block number may offer more flexibility across different environments.

packages/periphery/hardhat-configs/arb-fork.config.ts (1)

1-19: Check Arbitrum RPC and fork block number.
Everything looks fine for a dedicated Arbitrum forking config. However, verifying that block 221775851 is current enough for your testing or aligning it with a stable environment variable is recommended.

packages/periphery/test/int/arb/others/PythOracle.spec.ts (1)

2-3: LGTM! Import paths updated correctly.

The import paths have been properly updated to reflect the new directory structure.

packages/periphery/package.json (1)

15-16: LGTM! Well-organized test scripts.

The integration tests have been properly split into network-specific commands, improving organization and clarity.

Comment on lines +41 to +93
/// @notice Create token pair oracle price params. Can be called only once per pair.
/// @param quoteToken address of IbToken or PtToken
/// @param baseToken address of PtToken or IbToken
/// @param pendleMarket Address of PendleMarket contract with PtToken and IbToken
/// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means
/// @param secondsAgoLiquidation Same as `secondsAgo`, but for liquidation case
function setPair(
address quoteToken,
address baseToken,
address pendleMarket,
uint16 secondsAgo,
uint16 secondsAgoLiquidation
) external onlyOwner {
if (secondsAgo == 0 || secondsAgoLiquidation == 0) revert WrongValue();
if (secondsAgo < secondsAgoLiquidation) revert WrongValue();
if (quoteToken == address(0) || baseToken == address(0) || pendleMarket == address(0)) {
revert ZeroAddress();
}

if (getParams[quoteToken][baseToken].pendleMarket != address(0)) revert PairAlreadyExist();

_assertOracleIsInitialized(pendleMarket, secondsAgo);

(IStandardizedYield sy, IPPrincipalToken pt, ) = PendleMarketV3(pendleMarket).readTokens();
address ibToken;
if (baseToken == address(pt)) {
ibToken = quoteToken;
} else if (quoteToken == address(pt)) {
ibToken = baseToken;
} else {
revert WrongPtAddress();
}

if (!sy.isValidTokenIn(ibToken) || !sy.isValidTokenOut(ibToken)) revert WrongIbTokenAddress();

uint8 ptDecimals = IERC20Metadata(baseToken).decimals();
uint8 syDecimals = IERC20Metadata(address(sy)).decimals();
uint8 ibDecimals = IERC20Metadata(ibToken).decimals();

//We assume that sy ib ratio is 1:1 and decimals for both tokens are equals
if (syDecimals != ibDecimals) revert WrongIbSyDecimals();

OracleParams memory oracleParams = OracleParams({
pendleMarket: pendleMarket,
ibToken: ibToken,
secondsAgo: secondsAgo,
secondsAgoLiquidation: secondsAgoLiquidation,
ptSyDecimalsDelta: PRICE_DECIMALS + ptDecimals - syDecimals
});

getParams[quoteToken][baseToken] = oracleParams;
getParams[baseToken][quoteToken] = oracleParams;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure oracle is initialized for both normal and liquidation scenarios.
Currently, _assertOracleIsInitialized is only called with secondsAgo. Consider doing the same check for secondsAgoLiquidation to confirm the oracle is adequately initialized for liquidation as well.

Apply this diff after line 62 to verify the oracle’s readiness for liquidation durations:

     _assertOracleIsInitialized(pendleMarket, secondsAgo);
+    _assertOracleIsInitialized(pendleMarket, secondsAgoLiquidation);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// @notice Create token pair oracle price params. Can be called only once per pair.
/// @param quoteToken address of IbToken or PtToken
/// @param baseToken address of PtToken or IbToken
/// @param pendleMarket Address of PendleMarket contract with PtToken and IbToken
/// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means
/// @param secondsAgoLiquidation Same as `secondsAgo`, but for liquidation case
function setPair(
address quoteToken,
address baseToken,
address pendleMarket,
uint16 secondsAgo,
uint16 secondsAgoLiquidation
) external onlyOwner {
if (secondsAgo == 0 || secondsAgoLiquidation == 0) revert WrongValue();
if (secondsAgo < secondsAgoLiquidation) revert WrongValue();
if (quoteToken == address(0) || baseToken == address(0) || pendleMarket == address(0)) {
revert ZeroAddress();
}
if (getParams[quoteToken][baseToken].pendleMarket != address(0)) revert PairAlreadyExist();
_assertOracleIsInitialized(pendleMarket, secondsAgo);
(IStandardizedYield sy, IPPrincipalToken pt, ) = PendleMarketV3(pendleMarket).readTokens();
address ibToken;
if (baseToken == address(pt)) {
ibToken = quoteToken;
} else if (quoteToken == address(pt)) {
ibToken = baseToken;
} else {
revert WrongPtAddress();
}
if (!sy.isValidTokenIn(ibToken) || !sy.isValidTokenOut(ibToken)) revert WrongIbTokenAddress();
uint8 ptDecimals = IERC20Metadata(baseToken).decimals();
uint8 syDecimals = IERC20Metadata(address(sy)).decimals();
uint8 ibDecimals = IERC20Metadata(ibToken).decimals();
//We assume that sy ib ratio is 1:1 and decimals for both tokens are equals
if (syDecimals != ibDecimals) revert WrongIbSyDecimals();
OracleParams memory oracleParams = OracleParams({
pendleMarket: pendleMarket,
ibToken: ibToken,
secondsAgo: secondsAgo,
secondsAgoLiquidation: secondsAgoLiquidation,
ptSyDecimalsDelta: PRICE_DECIMALS + ptDecimals - syDecimals
});
getParams[quoteToken][baseToken] = oracleParams;
getParams[baseToken][quoteToken] = oracleParams;
}
/// @notice Create token pair oracle price params. Can be called only once per pair.
/// @param quoteToken address of IbToken or PtToken
/// @param baseToken address of PtToken or IbToken
/// @param pendleMarket Address of PendleMarket contract with PtToken and IbToken
/// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means
/// @param secondsAgoLiquidation Same as `secondsAgo`, but for liquidation case
function setPair(
address quoteToken,
address baseToken,
address pendleMarket,
uint16 secondsAgo,
uint16 secondsAgoLiquidation
) external onlyOwner {
if (secondsAgo == 0 || secondsAgoLiquidation == 0) revert WrongValue();
if (secondsAgo < secondsAgoLiquidation) revert WrongValue();
if (quoteToken == address(0) || baseToken == address(0) || pendleMarket == address(0)) {
revert ZeroAddress();
}
if (getParams[quoteToken][baseToken].pendleMarket != address(0)) revert PairAlreadyExist();
_assertOracleIsInitialized(pendleMarket, secondsAgo);
_assertOracleIsInitialized(pendleMarket, secondsAgoLiquidation);
(IStandardizedYield sy, IPPrincipalToken pt, ) = PendleMarketV3(pendleMarket).readTokens();
address ibToken;
if (baseToken == address(pt)) {
ibToken = quoteToken;
} else if (quoteToken == address(pt)) {
ibToken = baseToken;
} else {
revert WrongPtAddress();
}
if (!sy.isValidTokenIn(ibToken) || !sy.isValidTokenOut(ibToken)) revert WrongIbTokenAddress();
uint8 ptDecimals = IERC20Metadata(baseToken).decimals();
uint8 syDecimals = IERC20Metadata(address(sy)).decimals();
uint8 ibDecimals = IERC20Metadata(ibToken).decimals();
//We assume that sy ib ratio is 1:1 and decimals for both tokens are equals
if (syDecimals != ibDecimals) revert WrongIbSyDecimals();
OracleParams memory oracleParams = OracleParams({
pendleMarket: pendleMarket,
ibToken: ibToken,
secondsAgo: secondsAgo,
secondsAgoLiquidation: secondsAgoLiquidation,
ptSyDecimalsDelta: PRICE_DECIMALS + ptDecimals - syDecimals
});
getParams[quoteToken][baseToken] = oracleParams;
getParams[baseToken][quoteToken] = oracleParams;
}

import { MarginlyCompositeOracle, PendleMarketOracle, PythOracle } from '../../../typechain-types/contracts/oracles';
import { getDecimalsDiff, printPrices } from '../../shared/common';

describe.only('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove .only from the test suite.

The .only modifier prevents other tests from running. This is typically used during development and should be removed before merging.

-describe.only('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
+describe('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
describe.only('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
describe('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
// ... rest of your test implementation remains unchanged
});
🧰 Tools
🪛 Biome (1.9.4)

[error] 5-5: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)

Comment on lines +73 to +74
const wethArbMcPrice = await uniswapV3Oracle.getBalancePrice(arb, weth);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect price retrieval method.

Similar to the previous issue, the margin call price is being retrieved using getBalancePrice instead of getMargincallPrice.

-    const wethArbMcPrice = await uniswapV3Oracle.getBalancePrice(arb, weth);
+    const wethArbMcPrice = await uniswapV3Oracle.getMargincallPrice(arb, weth);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const wethArbMcPrice = await uniswapV3Oracle.getBalancePrice(arb, weth);
const wethArbMcPrice = await uniswapV3Oracle.getMargincallPrice(arb, weth);

Comment on lines +67 to +68
const wbtcWethMcPrice = await camelotOracle.getBalancePrice(weth, wbtc);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect price retrieval method.

The margin call price is being retrieved using getBalancePrice instead of getMargincallPrice.

-    const wbtcWethMcPrice = await camelotOracle.getBalancePrice(weth, wbtc);
+    const wbtcWethMcPrice = await camelotOracle.getMargincallPrice(weth, wbtc);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const wbtcWethMcPrice = await camelotOracle.getBalancePrice(weth, wbtc);
const wbtcWethMcPrice = await camelotOracle.getMargincallPrice(weth, wbtc);

Copy link

@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: 0

♻️ Duplicate comments (2)
packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts (2)

71-71: 🛠️ Refactor suggestion

Remove the .only focus on this suite.
Having .only will skip all other tests. This is typically used for debugging and should be removed before merging to ensure full test coverage.

-describe.only('Composite oracle Spectra PT-wstUSR/USR, Pyth wstUSR/USR', () => {
+describe('Composite oracle Spectra PT-wstUSR/USR, Pyth wstUSR/USR', () => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 71-71: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)


6-6: 🛠️ Refactor suggestion

Remove the .only focus on this suite.
Having .only will skip all other tests. This is typically used for debugging and should be removed before merging to ensure full test coverage.

-describe.only('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
+describe('Composite oracle PT-wstUSR/USR with Pendle for PT-wstUSR/wstUSR, Pyth wstUSR/USR', () => {
🧰 Tools
🪛 Biome (1.9.4)

[error] 6-6: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)

🧹 Nitpick comments (4)
packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (2)

9-13: Enforce consistent time settings for combined oracles.
The doc comment warns that both oracles must have the same time settings, yet there is no logic to enforce this requirement. Consider adding checks or clarifications to ensure both IPriceOracle instances align on timing parameters.


23-24: Rename interMediateBaseOracle for consistency.
Use a unified naming style to match quoteIntermediateOracle. For example, rename interMediateBaseOracle to intermediateBaseOracle to maintain readability.

- IPriceOracle interMediateBaseOracle;
+ IPriceOracle intermediateBaseOracle;
packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts (2)

63-64: Rename variables referencing “wbtcArb.”
The variable names wbtcArbBalancePrice and wbtcArbMcPrice do not reflect their underlying tokens (usrAddress and ptWstUsr27Mar2025). Consider renaming them for clarity.

- const wbtcArbBalancePrice = await compositeOracle.getBalancePrice(usrAddress, ptWstUsr27Mar2025);
- const wbtcArbMcPrice = await compositeOracle.getMargincallPrice(usrAddress, ptWstUsr27Mar2025);
+ const ptWstUsrBalancePrice = await compositeOracle.getBalancePrice(usrAddress, ptWstUsr27Mar2025);
+ const ptWstUsrMcPrice = await compositeOracle.getMargincallPrice(usrAddress, ptWstUsr27Mar2025);

103-103: Test name mismatch with the actual token address.
The test description references "27Mar2025," but it uses ptWstUsr26Jun2025 in the code. Consider updating the description or the token address variable to maintain consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48a6eb2 and cffa950.

📒 Files selected for processing (2)
  • packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (1 hunks)
  • packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/periphery/test/int/eth/MarginlyCompositeOracle.spec.ts

[error] 6-6: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)


[error] 71-71: Don't focus the test.

The 'only' method is often used for debugging or during implementation. It should be removed before deploying to production.
Consider removing 'only' to ensure all tests are executed.
Unsafe fix: Remove focus from test.

(lint/suspicious/noFocusedTests)

🔇 Additional comments (2)
packages/periphery/contracts/oracles/MarginlyCompositeOracle.sol (2)

45-75: Function setPair remains relatively complex.
Although this function is not excessively large, it performs several distinct steps: input validation, price checks, plus forward and reverse pair mapping. Splitting it into smaller helper functions can simplify logic and ease future maintenance.


77-85: Repetitive zero checks between _validateOracle and retrieval methods.
You are verifying zero prices in _validateOracle, getBalancePrice, and getMargincallPrice, which could be somewhat repetitive. Consider centralizing or reusing a shared validation method if you want to reduce duplication.

@rudewalt rudewalt merged commit 18b13be into main Feb 18, 2025
1 check passed
@rudewalt rudewalt deleted the feature/composite-oracle branch February 18, 2025 14:06
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.

2 participants