From a90bc8da834f590ef453fa5764ec8e0934a76905 Mon Sep 17 00:00:00 2001 From: Aditya Arora Date: Fri, 1 Aug 2025 13:13:33 -0400 Subject: [PATCH 1/3] chore Add some more tezt --- package-lock.json | 4 +- package.json | 5 +- .../interfaces/IMorphoPythOracleFactory.sol | 2 +- test/MorphoPythOracleTest.sol | 87 +++++++++++++++---- 4 files changed, 75 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 043e607..07f0cf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "morpho-blue-oracles", + "name": "pyth-morpho-wrapper", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "morpho-blue-oracles", + "name": "pyth-morpho-wrapper", "version": "1.0.0", "license": "ISC", "dependencies": { diff --git a/package.json b/package.json index 3d54a17..b839f56 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,10 @@ "test": "test" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "forge test", + "test:verbose": "forge test -vvv", + "test:gas": "forge test --gas-report", + "coverage": "forge coverage" }, "keywords": [], "author": "", diff --git a/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol b/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol index bc67e62..094a38e 100644 --- a/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol +++ b/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol @@ -43,7 +43,7 @@ interface IMorphoPythOracleFactory { /// @param quoteFeed2 Second quote feed. Pass bytes32(0) if the price = 1. We recommend using stablecoin feeds /// instead of passing 1. /// @param quoteTokenDecimals Quote token decimals. - /// @param priceFeedMaxAge The maximum age in secondsfor the oracles prices to be considered valid. + /// @param priceFeedMaxAge The maximum age in seconds for the oracles prices to be considered valid. We have only one price feed max age for all price feed. /// @param salt The salt to use for the CREATE2. /// @dev The base asset should be the collateral token and the quote asset the loan token. function createMorphoPythOracle( diff --git a/test/MorphoPythOracleTest.sol b/test/MorphoPythOracleTest.sol index 719fa67..73a5966 100644 --- a/test/MorphoPythOracleTest.sol +++ b/test/MorphoPythOracleTest.sol @@ -13,6 +13,29 @@ contract MorphoPythOracleTest is Test { function setUp() public { mockPyth = new MockPyth(60, 1); + // Update the price feed + mockPyth.updatePriceFeeds{value: 2}(getPriceUpdateData()); + + assertEq(mockPyth.getPriceUnsafe(pythWbtcUsdFeed).price, 30000 * 1e8); + assertEq(mockPyth.getPriceUnsafe(pythUsdtUsdFeed).price, 1 * 1e8); + + oracle = new MorphoPythOracle( + address(mockPyth), + vaultZero, + 1, + pythWbtcUsdFeed, + pythFeedZero, + pythWbtcUsdTokenDecimals, + vaultZero, + 1, + pythUsdtUsdFeed, + pythFeedZero, + pythUsdtUsdTokenDecimals, + oneMinute + ); + } + + function getPriceUpdateData() internal view returns (bytes[] memory) { // Create price feed update data for WBTC/USD bytes[] memory updateData = new bytes[](2); updateData[0] = mockPyth.createPriceFeedUpdateData( @@ -36,25 +59,7 @@ contract MorphoPythOracleTest is Test { uint64(block.timestamp), uint64(block.timestamp) ); - // Update the price feed - mockPyth.updatePriceFeeds{value: 2}(updateData); - assertEq(mockPyth.getPriceUnsafe(pythWbtcUsdFeed).price, 30000 * 1e8); - assertEq(mockPyth.getPriceUnsafe(pythUsdtUsdFeed).price, 1 * 1e8); - - oracle = new MorphoPythOracle( - address(mockPyth), - vaultZero, - 1, - pythWbtcUsdFeed, - pythFeedZero, - pythWbtcUsdTokenDecimals, - vaultZero, - 1, - pythUsdtUsdFeed, - pythFeedZero, - pythUsdtUsdTokenDecimals, - oneHour - ); + return updateData; } function testInitialSetup() public { @@ -88,4 +93,48 @@ contract MorphoPythOracleTest is Test { ) / uint256(int256(mockPyth.getPriceUnsafe(pythUsdtUsdFeed).price)) ); } + + function testPriceFeedAgeValidation() public { + // This should work - price is current + uint256 price = oracle.price(); + assertTrue(price > 0); + } + + function testPriceFeedStalePrice() public { + vm.warp(block.timestamp + oneMinute + 1); + // This should revert due to stale price + vm.expectRevert(bytes4(0x19abf40e)); // StalePrice error + oracle.price(); + } + + function testZeroPriceHandling() public { + // Set up price feeds with zero price + bytes[] memory updateData = new bytes[](2); + updateData[0] = mockPyth.createPriceFeedUpdateData( + pythWbtcUsdFeed, + 0, // Zero price + 1000000, // Confidence interval + -6, // Expo (-6 means price is multiplied by 10^-6) + 0, // EMA price + 0, // EMA Confidence interval + uint64(block.timestamp) + 1, + uint64(block.timestamp) + 1 + ); + + updateData[1] = mockPyth.createPriceFeedUpdateData( + pythUsdtUsdFeed, + 1 * 1e8, // Price of 1 USD + 0, // Confidence interval + -6, // Expo (-6 means price is multiplied by 10^-6) + 1 * 1e8, // EMA price + 0, // EMA Confidence interval + uint64(block.timestamp) + 1, + uint64(block.timestamp) + 1 + ); + mockPyth.updatePriceFeeds{value: 2}(updateData); + + // Zero price should be valid (price = 0 is allowed, only negative is rejected) + uint256 price = oracle.price(); + assertEq(price, 0); + } } From a505bd13c9229482ebb730b23b382954d82a8568 Mon Sep 17 00:00:00 2001 From: Aditya Arora Date: Fri, 1 Aug 2025 13:15:46 -0400 Subject: [PATCH 2/3] fmt --- src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol | 3 ++- test/MorphoPythOracleTest.sol | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol b/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol index 094a38e..47a94a9 100644 --- a/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol +++ b/src/morpho-pyth/interfaces/IMorphoPythOracleFactory.sol @@ -43,7 +43,8 @@ interface IMorphoPythOracleFactory { /// @param quoteFeed2 Second quote feed. Pass bytes32(0) if the price = 1. We recommend using stablecoin feeds /// instead of passing 1. /// @param quoteTokenDecimals Quote token decimals. - /// @param priceFeedMaxAge The maximum age in seconds for the oracles prices to be considered valid. We have only one price feed max age for all price feed. + /// @param priceFeedMaxAge The maximum age in seconds for the oracles prices to be considered valid. We have only + /// one price feed max age for all price feed. /// @param salt The salt to use for the CREATE2. /// @dev The base asset should be the collateral token and the quote asset the loan token. function createMorphoPythOracle( diff --git a/test/MorphoPythOracleTest.sol b/test/MorphoPythOracleTest.sol index 73a5966..c9d1e06 100644 --- a/test/MorphoPythOracleTest.sol +++ b/test/MorphoPythOracleTest.sol @@ -94,13 +94,13 @@ contract MorphoPythOracleTest is Test { ); } - function testPriceFeedAgeValidation() public { + function testPriceFeedAgeValidation() public { // This should work - price is current uint256 price = oracle.price(); assertTrue(price > 0); } - function testPriceFeedStalePrice() public { + function testPriceFeedStalePrice() public { vm.warp(block.timestamp + oneMinute + 1); // This should revert due to stale price vm.expectRevert(bytes4(0x19abf40e)); // StalePrice error From 6b567b21c99a5d0b3f78dc9f907d0799a042799b Mon Sep 17 00:00:00 2001 From: Aditya Arora Date: Fri, 1 Aug 2025 13:36:51 -0400 Subject: [PATCH 3/3] requested changes --- test/MorphoPythOracleTest.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/MorphoPythOracleTest.sol b/test/MorphoPythOracleTest.sol index c9d1e06..e3fb88c 100644 --- a/test/MorphoPythOracleTest.sol +++ b/test/MorphoPythOracleTest.sol @@ -123,10 +123,10 @@ contract MorphoPythOracleTest is Test { updateData[1] = mockPyth.createPriceFeedUpdateData( pythUsdtUsdFeed, - 1 * 1e8, // Price of 1 USD + 1 * 1e6, // Price of 1 USD 0, // Confidence interval -6, // Expo (-6 means price is multiplied by 10^-6) - 1 * 1e8, // EMA price + 1 * 1e6, // EMA price 0, // EMA Confidence interval uint64(block.timestamp) + 1, uint64(block.timestamp) + 1