-
Notifications
You must be signed in to change notification settings - Fork 305
refactor: Optional storage flag when parsing price feed updates #2727
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
4fe1222
552a34f
b3cf5d3
2db25fc
a1822f8
2a5b2ec
6b4b0c8
2e79358
f1183f1
53690d2
0636cb1
f604a13
660717e
27bf014
02fe840
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -333,35 +333,43 @@ abstract contract Pyth is | |
| uint64[] memory slots | ||
| ) | ||
| { | ||
| { | ||
| uint requiredFee = getUpdateFee(updateData); | ||
| if (msg.value < requiredFee) revert PythErrors.InsufficientFee(); | ||
| } | ||
| if (msg.value < getUpdateFee(updateData)) revert PythErrors.InsufficientFee(); | ||
|
|
||
| // Create the context struct that holds all shared parameters | ||
| PythInternalStructs.UpdateParseContext memory context; | ||
| context.priceIds = priceIds; | ||
| context.minAllowedPublishTime = minAllowedPublishTime; | ||
| context.maxAllowedPublishTime = maxAllowedPublishTime; | ||
| context.checkUniqueness = checkUniqueness; | ||
| context.checkUpdateDataIsMinimal = checkUpdateDataIsMinimal; | ||
| context.priceFeeds = new PythStructs.PriceFeed[](priceIds.length); | ||
| context.slots = new uint64[](priceIds.length); | ||
| PythInternalStructs.UpdateParseContext memory context = PythInternalStructs.UpdateParseContext({ | ||
| priceIds: priceIds, | ||
| minAllowedPublishTime: minAllowedPublishTime, | ||
| maxAllowedPublishTime: maxAllowedPublishTime, | ||
| checkUniqueness: checkUniqueness, | ||
| checkUpdateDataIsMinimal: checkUpdateDataIsMinimal, | ||
| priceFeeds: new PythStructs.PriceFeed[](priceIds.length), | ||
| slots: new uint64[](priceIds.length) | ||
| }); | ||
|
|
||
| // Track total updates for minimal update data check | ||
| uint64 totalUpdatesAcrossBlobs = 0; | ||
|
|
||
| unchecked { | ||
| // Process each update, passing the context struct | ||
| // Parsed results will be filled in context.priceFeeds and context.slots | ||
| for (uint i = 0; i < updateData.length; i++) { | ||
| for (uint i = 0; i < updateData.length; ++i) { | ||
| totalUpdatesAcrossBlobs += _processSingleUpdateDataBlob( | ||
| updateData[i], | ||
| context | ||
| ); | ||
| if (storeUpdatesIfFresh) { | ||
| bytes32 curPriceId = context.priceIds[i]; | ||
| updateLatestPriceIfNecessary(curPriceId, _state.latestPriceInfo[curPriceId]); | ||
| } | ||
|
|
||
| for (uint j = 0; j < priceIds.length; ++j) { | ||
| PythStructs.PriceFeed memory pf = context.priceFeeds[j]; | ||
| if (storeUpdatesIfFresh && pf.id != 0) { | ||
| updateLatestPriceIfNecessary(priceIds[j], PythInternalStructs.PriceInfo({ | ||
| publishTime: uint64(pf.price.publishTime), | ||
| expo: pf.price.expo, | ||
| price: pf.price.price, | ||
| conf: pf.price.conf, | ||
| emaPrice: pf.emaPrice.price, | ||
| emaConf: pf.emaPrice.conf | ||
| })); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -375,7 +383,7 @@ abstract contract Pyth is | |
| } | ||
|
|
||
| // Check all price feeds were found | ||
| for (uint k = 0; k < priceIds.length; k++) { | ||
| for (uint k = 0; k < priceIds.length; ++k) { | ||
|
||
| if (context.priceFeeds[k].id == 0) { | ||
| revert PythErrors.PriceFeedNotFoundWithinRange(); | ||
| } | ||
|
|
@@ -384,6 +392,7 @@ abstract contract Pyth is | |
| // Return results | ||
| return (context.priceFeeds, context.slots); | ||
| } | ||
|
|
||
| function parsePriceFeedUpdates( | ||
| bytes[] calldata updateData, | ||
| bytes32[] calldata priceIds, | ||
|
|
@@ -402,36 +411,10 @@ abstract contract Pyth is | |
| maxPublishTime, | ||
| false, | ||
| false, | ||
| true | ||
| false | ||
| ); | ||
| } | ||
|
|
||
| function parsePriceFeedUpdatesWithSlotsStrict( | ||
| bytes[] calldata updateData, | ||
| bytes32[] calldata priceIds, | ||
| uint64 minPublishTime, | ||
| uint64 maxPublishTime | ||
| ) | ||
| external | ||
| payable | ||
| override | ||
| returns ( | ||
| PythStructs.PriceFeed[] memory priceFeeds, | ||
| uint64[] memory slots | ||
| ) | ||
| { | ||
| return | ||
| parsePriceFeedUpdatesWithConfig( | ||
| updateData, | ||
| priceIds, | ||
| minPublishTime, | ||
| maxPublishTime, | ||
| false, | ||
| true, | ||
| true | ||
| ); | ||
| } | ||
|
|
||
| function extractTwapPriceInfos( | ||
| bytes calldata updateData | ||
| ) | ||
|
|
@@ -639,7 +622,7 @@ abstract contract Pyth is | |
| maxPublishTime, | ||
| true, | ||
| false, | ||
| true | ||
| false | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -278,6 +278,74 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { | |
| } | ||
| } | ||
|
|
||
| function testParsePriceFeedUpdatesWithConfigIfStorageTrue(uint seed) public { | ||
| setRandSeed(seed); | ||
| uint numMessages = 1 + (getRandUint() % 10); | ||
| ( | ||
| bytes32[] memory priceIds, | ||
| PriceFeedMessage[] memory messages | ||
| ) = generateRandomPriceMessages(numMessages); | ||
|
|
||
| ( | ||
| bytes[] memory updateData, | ||
| uint updateFee | ||
| ) = createBatchedUpdateDataFromMessages(messages); | ||
|
|
||
| (PythStructs.PriceFeed[] memory priceFeeds,) = pyth.parsePriceFeedUpdatesWithConfig{ | ||
| value: updateFee | ||
| }(updateData, priceIds, 0, MAX_UINT64, false, true, true); | ||
|
|
||
| for (uint i = 0; i < numMessages; i++) { | ||
| // Validating that returned priceIds are equal | ||
| assertEq(priceFeeds[i].id, priceIds[i]); | ||
| assertEq(priceFeeds[i].price.price, messages[i].price); | ||
| assertEq(priceFeeds[i].price.conf, messages[i].conf); | ||
| assertEq(priceFeeds[i].price.expo, messages[i].expo); | ||
| assertEq(priceFeeds[i].price.publishTime, messages[i].publishTime); | ||
| assertEq(priceFeeds[i].emaPrice.price, messages[i].emaPrice); | ||
| assertEq(priceFeeds[i].emaPrice.conf, messages[i].emaConf); | ||
| assertEq(priceFeeds[i].emaPrice.expo, messages[i].expo); | ||
| assertEq( | ||
| priceFeeds[i].emaPrice.publishTime, | ||
| messages[i].publishTime | ||
| ); | ||
|
|
||
| // Validating that prices are stored on chain | ||
| PythStructs.Price memory curPrice = pyth.getPriceUnsafe( | ||
| messages[i].priceId | ||
| ); | ||
|
|
||
| assertEq(priceFeeds[i].price.price, curPrice.price); | ||
| assertEq(priceFeeds[i].price.conf, curPrice.conf); | ||
| assertEq(priceFeeds[i].price.expo, curPrice.expo); | ||
| assertEq(priceFeeds[i].price.publishTime, curPrice.publishTime); | ||
| } | ||
| } | ||
|
|
||
| function testParsePriceFeedUpdatesWithConfigIfStorageFalse(uint seed) public { | ||
| setRandSeed(seed); | ||
| uint numMessages = 1 + (getRandUint() % 10); | ||
| ( | ||
| bytes32[] memory priceIds, | ||
| PriceFeedMessage[] memory messages | ||
| ) = generateRandomPriceMessages(numMessages); | ||
|
|
||
| ( | ||
| bytes[] memory updateData, | ||
| uint updateFee | ||
| ) = createBatchedUpdateDataFromMessages(messages); | ||
|
|
||
| pyth.parsePriceFeedUpdatesWithConfig{ | ||
| value: updateFee | ||
| }(updateData, priceIds, 0, MAX_UINT64, false, true, false); | ||
|
|
||
| // validate that stored prices of each priceId are still unpopulated | ||
| for (uint i = 0; i < numMessages; i++) { | ||
| vm.expectRevert(PythErrors.PriceFeedNotFound.selector); | ||
| pyth.getPriceUnsafe(priceIds[i]); | ||
| } | ||
| } | ||
|
|
||
| function testParsePriceFeedUpdatesWithSlotsStrictWorks(uint seed) public { | ||
| setRandSeed(seed); | ||
| uint numMessages = 1 + (getRandUint() % 10); | ||
|
|
@@ -293,11 +361,14 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { | |
| ( | ||
| PythStructs.PriceFeed[] memory priceFeeds, | ||
| uint64[] memory slots | ||
| ) = pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( | ||
| ) = pyth.parsePriceFeedUpdatesWithConfig{value: updateFee}( | ||
| updateData, | ||
| priceIds, | ||
| 0, | ||
| MAX_UINT64 | ||
| MAX_UINT64, | ||
| false, | ||
| true, | ||
| false | ||
| ); | ||
|
|
||
| assertEq(priceFeeds.length, numMessages); | ||
|
|
@@ -459,11 +530,14 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { | |
|
|
||
| // Should revert in strict mode | ||
| vm.expectRevert(PythErrors.InvalidArgument.selector); | ||
| pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( | ||
| pyth.parsePriceFeedUpdatesWithConfig{value: updateFee}( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we update the names of these tests please |
||
| updateData, | ||
| requestedPriceIds, | ||
| 0, | ||
| MAX_UINT64 | ||
| MAX_UINT64, | ||
| false, | ||
| true, | ||
| false | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -496,11 +570,14 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { | |
|
|
||
| // Should revert in strict mode because we have fewer updates than price IDs | ||
| vm.expectRevert(PythErrors.InvalidArgument.selector); | ||
| pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( | ||
| pyth.parsePriceFeedUpdatesWithConfig{value: updateFee}( | ||
| updateData, | ||
| requestedPriceIds, | ||
| 0, | ||
| MAX_UINT64 | ||
| MAX_UINT64, | ||
| false, | ||
| true, | ||
| false | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -136,21 +136,6 @@ abstract contract AbstractPyth is IPyth { | |
| override | ||
| returns (PythStructs.PriceFeed[] memory priceFeeds); | ||
|
|
||
| function parsePriceFeedUpdatesWithSlotsStrict( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did we squeeze within the size limit with this? :) |
||
| bytes[] calldata updateData, | ||
| bytes32[] calldata priceIds, | ||
| uint64 minPublishTime, | ||
| uint64 maxPublishTime | ||
| ) | ||
| external | ||
| payable | ||
| virtual | ||
| override | ||
| returns ( | ||
| PythStructs.PriceFeed[] memory priceFeeds, | ||
| uint64[] memory slots | ||
| ); | ||
|
|
||
| function parseTwapPriceFeedUpdates( | ||
| bytes[] calldata updateData, | ||
| bytes32[] calldata priceIds | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's bump the version() to 1.4.5-alpha.1