diff --git a/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol b/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol index 4aa5e17f97..3898d4db41 100644 --- a/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol +++ b/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol @@ -273,7 +273,7 @@ abstract contract Scheduler is IScheduler, SchedulerState { ( PythStructs.PriceFeed[] memory priceFeeds, uint64[] memory slots - ) = pyth.parsePriceFeedUpdatesWithSlots{value: pythFee}( + ) = pyth.parsePriceFeedUpdatesWithSlotsStrict{value: pythFee}( updateData, params.priceIds, 0, // We enforce the past max validity ourselves in _validateShouldUpdatePrices diff --git a/target_chains/ethereum/contracts/contracts/pyth/Pyth.sol b/target_chains/ethereum/contracts/contracts/pyth/Pyth.sol index d899474237..dcf92125b9 100644 --- a/target_chains/ethereum/contracts/contracts/pyth/Pyth.sol +++ b/target_chains/ethereum/contracts/contracts/pyth/Pyth.sol @@ -239,10 +239,10 @@ abstract contract Pyth is if (k < context.priceIds.length && context.priceFeeds[k].id == 0) { uint publishTime = uint(priceInfo.publishTime); if ( - publishTime >= context.config.minPublishTime && - publishTime <= context.config.maxPublishTime && - (!context.config.checkUniqueness || - context.config.minPublishTime > prevPublishTime) + publishTime >= context.minAllowedPublishTime && + publishTime <= context.maxAllowedPublishTime && + (!context.checkUniqueness || + context.minAllowedPublishTime > prevPublishTime) ) { context.priceFeeds[k].id = priceId; context.priceFeeds[k].price.price = priceInfo.price; @@ -262,7 +262,7 @@ abstract contract Pyth is function _processSingleUpdateDataBlob( bytes calldata singleUpdateData, PythInternalStructs.UpdateParseContext memory context - ) internal view { + ) internal view returns (uint64 numUpdates) { // Check magic number and length first if ( singleUpdateData.length <= 4 || @@ -312,12 +312,18 @@ abstract contract Pyth is if (offset != encoded.length) { revert PythErrors.InvalidUpdateData(); } + + // Return the number of updates in this blob for tracking + return merkleData.numUpdates; } function parsePriceFeedUpdatesInternal( bytes[] calldata updateData, bytes32[] calldata priceIds, - PythInternalStructs.ParseConfig memory config + uint64 minAllowedPublishTime, + uint64 maxAllowedPublishTime, + bool checkUniqueness, + bool checkUpdateDataIsMinimal ) internal returns ( @@ -333,18 +339,35 @@ abstract contract Pyth is // Create the context struct that holds all shared parameters PythInternalStructs.UpdateParseContext memory context; context.priceIds = priceIds; - context.config = config; + 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); + // 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++) { - _processSingleUpdateDataBlob(updateData[i], context); + totalUpdatesAcrossBlobs += _processSingleUpdateDataBlob( + updateData[i], + context + ); } } + // In minimal update data mode, revert if we have more or less updates than price IDs + if ( + checkUpdateDataIsMinimal && + totalUpdatesAcrossBlobs != priceIds.length + ) { + revert PythErrors.InvalidArgument(); + } + // Check all price feeds were found for (uint k = 0; k < priceIds.length; k++) { if (context.priceFeeds[k].id == 0) { @@ -369,15 +392,14 @@ abstract contract Pyth is (priceFeeds, ) = parsePriceFeedUpdatesInternal( updateData, priceIds, - PythInternalStructs.ParseConfig( - minPublishTime, - maxPublishTime, - false - ) + minPublishTime, + maxPublishTime, + false, + false ); } - function parsePriceFeedUpdatesWithSlots( + function parsePriceFeedUpdatesWithSlotsStrict( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64 minPublishTime, @@ -395,11 +417,10 @@ abstract contract Pyth is parsePriceFeedUpdatesInternal( updateData, priceIds, - PythInternalStructs.ParseConfig( - minPublishTime, - maxPublishTime, - false - ) + minPublishTime, + maxPublishTime, + false, + true ); } @@ -606,11 +627,10 @@ abstract contract Pyth is (priceFeeds, ) = parsePriceFeedUpdatesInternal( updateData, priceIds, - PythInternalStructs.ParseConfig( - minPublishTime, - maxPublishTime, - true - ) + minPublishTime, + maxPublishTime, + true, + false ); } @@ -683,7 +703,7 @@ abstract contract Pyth is } function version() public pure returns (string memory) { - return "1.4.4"; + return "1.4.5-alpha.1"; } /// @notice Calculates TWAP from two price points diff --git a/target_chains/ethereum/contracts/contracts/pyth/PythInternalStructs.sol b/target_chains/ethereum/contracts/contracts/pyth/PythInternalStructs.sol index 94818263cd..95da234a8e 100644 --- a/target_chains/ethereum/contracts/contracts/pyth/PythInternalStructs.sol +++ b/target_chains/ethereum/contracts/contracts/pyth/PythInternalStructs.sol @@ -9,18 +9,18 @@ import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol"; contract PythInternalStructs { using BytesLib for bytes; - struct ParseConfig { - uint64 minPublishTime; - uint64 maxPublishTime; - bool checkUniqueness; - } - /// Internal struct to hold parameters for update processing /// @dev Storing these variable in a struct rather than local variables /// helps reduce stack depth when passing arguments to functions. struct UpdateParseContext { bytes32[] priceIds; - ParseConfig config; + uint64 minAllowedPublishTime; + uint64 maxAllowedPublishTime; + bool checkUniqueness; + /// When checkUpdateDataIsMinimal is true, parsing will revert + /// if the number of passed in updates exceeds or is less than + /// the length of priceIds. + bool checkUpdateDataIsMinimal; PythStructs.PriceFeed[] priceFeeds; uint64[] slots; } diff --git a/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol b/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol index ee06ff171f..47d2b7653e 100644 --- a/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol +++ b/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol @@ -256,7 +256,11 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { numInitialFeeds ); - mockParsePriceFeedUpdatesWithSlots(pyth, initialPriceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict( + pyth, + initialPriceFeeds, + slots + ); bytes[] memory updateData = createMockUpdateData(initialPriceFeeds); vm.prank(pusher); @@ -830,7 +834,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { priceIds.length ); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds1, slots); bytes[] memory updateData1 = createMockUpdateData(priceFeeds1); // Perform first update @@ -881,7 +885,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { priceFeeds2[i].emaPrice.publishTime = publishTime2; } - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds2, slots); // Mock for the second call + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds2, slots); // Mock for the second call bytes[] memory updateData2 = createMockUpdateData(priceFeeds2); // Perform second update @@ -942,7 +946,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { ); uint256 mockPythFee = MOCK_PYTH_FEE_PER_FEED * params.priceIds.length; - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Get state before @@ -1027,7 +1031,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { priceIds.length ); uint256 mockPythFee = MOCK_PYTH_FEE_PER_FEED * priceIds.length; - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Calculate minimum keeper fee (overhead + feed-specific fee) @@ -1085,7 +1089,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds1; uint64[] memory slots1; (priceFeeds1, slots1) = createMockPriceFeedsWithSlots(publishTime1, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots1); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds1, slots1); bytes[] memory updateData1 = createMockUpdateData(priceFeeds1); vm.prank(pusher); scheduler.updatePriceFeeds(subscriptionId, updateData1); @@ -1096,7 +1100,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds2; uint64[] memory slots2; (priceFeeds2, slots2) = createMockPriceFeedsWithSlots(publishTime2, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds2, slots2); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds2, slots2); bytes[] memory updateData2 = createMockUpdateData(priceFeeds2); // Expect revert because heartbeat condition is not met @@ -1132,7 +1136,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds1; uint64[] memory slots; (priceFeeds1, slots) = createMockPriceFeedsWithSlots(publishTime1, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds1, slots); bytes[] memory updateData1 = createMockUpdateData(priceFeeds1); vm.prank(pusher); scheduler.updatePriceFeeds(subscriptionId, updateData1); @@ -1158,7 +1162,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { priceFeeds2[i].price.publishTime = publishTime2; } - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds2, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds2, slots); bytes[] memory updateData2 = createMockUpdateData(priceFeeds2); // Expect revert because deviation condition is not met @@ -1183,7 +1187,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds1; uint64[] memory slots1; (priceFeeds1, slots1) = createMockPriceFeedsWithSlots(publishTime1, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots1); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds1, slots1); bytes[] memory updateData1 = createMockUpdateData(priceFeeds1); vm.prank(pusher); @@ -1195,7 +1199,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { uint64[] memory slots2; (priceFeeds2, slots2) = createMockPriceFeedsWithSlots(publishTime2, 2); // Mock Pyth response to return feeds with the older timestamp - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds2, slots2); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds2, slots2); bytes[] memory updateData2 = createMockUpdateData(priceFeeds2); // Expect revert with TimestampOlderThanLastUpdate (checked in _validateShouldUpdatePrices) @@ -1235,7 +1239,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { slots[1] = 200; // Different slot // Mock Pyth response to return these feeds with mismatched slots - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Expect revert with PriceSlotMismatch error @@ -1350,7 +1354,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds_reduce, uint64[] memory slots_reduce ) = createMockPriceFeedsWithSlots(publishTime + (i * 60), 2); - mockParsePriceFeedUpdatesWithSlots( + mockParsePriceFeedUpdatesWithSlotsStrict( pyth, priceFeeds_reduce, slots_reduce @@ -1422,7 +1426,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds; uint64[] memory slots; (priceFeeds, slots) = createMockPriceFeedsWithSlots(publishTime, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); vm.prank(pusher); @@ -1464,7 +1468,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds; uint64[] memory slots; (priceFeeds, slots) = createMockPriceFeedsWithSlots(publishTime, 3); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); vm.prank(pusher); @@ -1519,7 +1523,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { PythStructs.PriceFeed[] memory priceFeeds; uint64[] memory slots; (priceFeeds, slots) = createMockPriceFeedsWithSlots(publishTime, 2); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); vm.prank(pusher); @@ -1563,7 +1567,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { publishTime, priceIds.length ); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); vm.prank(pusher); @@ -1630,7 +1634,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { priceFeeds[i].emaPrice.expo = priceFeeds[i].price.expo; } - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); vm.prank(pusher); @@ -1935,7 +1939,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { slots[1] = 100; // Same slot // Mock Pyth response (should succeed in the real world as minValidTime is 0) - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Expect PricesUpdated event with the latest valid timestamp @@ -1988,7 +1992,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { slots[1] = 100; // Same slot // Mock Pyth response (should succeed in the real world as minValidTime is 0) - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Expect revert with TimestampTooOld (checked in _validateShouldUpdatePrices) diff --git a/target_chains/ethereum/contracts/forge-test/PulseSchedulerGasBenchmark.t.sol b/target_chains/ethereum/contracts/forge-test/PulseSchedulerGasBenchmark.t.sol index 63783731f4..48fefda6c3 100644 --- a/target_chains/ethereum/contracts/forge-test/PulseSchedulerGasBenchmark.t.sol +++ b/target_chains/ethereum/contracts/forge-test/PulseSchedulerGasBenchmark.t.sol @@ -71,7 +71,7 @@ contract PulseSchedulerGasBenchmark is Test, PulseSchedulerTestUtils { ); // Mock Pyth response for the benchmark - mockParsePriceFeedUpdatesWithSlots(pyth, newPriceFeeds, newSlots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, newPriceFeeds, newSlots); // Actual benchmark: Measure gas for updating price feeds uint256 startGas = gasleft(); @@ -124,7 +124,7 @@ contract PulseSchedulerGasBenchmark is Test, PulseSchedulerTestUtils { numFeeds ); - mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots); + mockParsePriceFeedUpdatesWithSlotsStrict(pyth, priceFeeds, slots); bytes[] memory updateData = createMockUpdateData(priceFeeds); // Update the price feeds. We should have enough balance to cover the update diff --git a/target_chains/ethereum/contracts/forge-test/Pyth.t.sol b/target_chains/ethereum/contracts/forge-test/Pyth.t.sol index a9bab2548c..b883ba9e58 100644 --- a/target_chains/ethereum/contracts/forge-test/Pyth.t.sol +++ b/target_chains/ethereum/contracts/forge-test/Pyth.t.sol @@ -278,7 +278,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { } } - function testParsePriceFeedUpdatesWithSlotsWorks(uint seed) public { + function testParsePriceFeedUpdatesWithSlotsStrictWorks(uint seed) public { setRandSeed(seed); uint numMessages = 1 + (getRandUint() % 10); ( @@ -293,7 +293,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { ( PythStructs.PriceFeed[] memory priceFeeds, uint64[] memory slots - ) = pyth.parsePriceFeedUpdatesWithSlots{value: updateFee}( + ) = pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( updateData, priceIds, 0, @@ -434,6 +434,76 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils { ); } + function testParsePriceFeedUpdatesWithSlotsStrictRevertsWithExcessUpdateData() + public + { + // Create a price update with more price updates than requested price IDs + uint numPriceIds = 2; + uint numMessages = numPriceIds + 1; // One more than the number of price IDs + + ( + bytes32[] memory priceIds, + PriceFeedMessage[] memory messages + ) = generateRandomPriceMessages(numMessages); + + // Only use a subset of the price IDs to trigger the strict check + bytes32[] memory requestedPriceIds = new bytes32[](numPriceIds); + for (uint i = 0; i < numPriceIds; i++) { + requestedPriceIds[i] = priceIds[i]; + } + + ( + bytes[] memory updateData, + uint updateFee + ) = createBatchedUpdateDataFromMessages(messages); + + // Should revert in strict mode + vm.expectRevert(PythErrors.InvalidArgument.selector); + pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( + updateData, + requestedPriceIds, + 0, + MAX_UINT64 + ); + } + + function testParsePriceFeedUpdatesWithSlotsStrictRevertsWithFewerUpdateData() + public + { + // Create a price update with fewer price updates than requested price IDs + uint numPriceIds = 3; + uint numMessages = numPriceIds - 1; // One less than the number of price IDs + + ( + bytes32[] memory priceIds, + PriceFeedMessage[] memory messages + ) = generateRandomPriceMessages(numMessages); + + // Create a larger array of requested price IDs to trigger the strict check + bytes32[] memory requestedPriceIds = new bytes32[](numPriceIds); + for (uint i = 0; i < numMessages; i++) { + requestedPriceIds[i] = priceIds[i]; + } + // Add an extra price ID that won't be in the updates + requestedPriceIds[numMessages] = bytes32( + uint256(keccak256(abi.encodePacked("extra_id"))) + ); + + ( + bytes[] memory updateData, + uint updateFee + ) = createBatchedUpdateDataFromMessages(messages); + + // Should revert in strict mode because we have fewer updates than price IDs + vm.expectRevert(PythErrors.InvalidArgument.selector); + pyth.parsePriceFeedUpdatesWithSlotsStrict{value: updateFee}( + updateData, + requestedPriceIds, + 0, + MAX_UINT64 + ); + } + function testParsePriceFeedUpdatesRevertsIfUpdateSourceChainIsInvalid() public { diff --git a/target_chains/ethereum/contracts/forge-test/utils/MockPriceFeedTestUtils.sol b/target_chains/ethereum/contracts/forge-test/utils/MockPriceFeedTestUtils.sol index 05ed62dc0a..7488047587 100644 --- a/target_chains/ethereum/contracts/forge-test/utils/MockPriceFeedTestUtils.sol +++ b/target_chains/ethereum/contracts/forge-test/utils/MockPriceFeedTestUtils.sol @@ -170,7 +170,7 @@ abstract contract MockPriceFeedTestUtils is Test { } // Helper function to mock Pyth response with slots - function mockParsePriceFeedUpdatesWithSlots( + function mockParsePriceFeedUpdatesWithSlotsStrict( address pyth, PythStructs.PriceFeed[] memory priceFeeds, uint64[] memory slots @@ -187,7 +187,7 @@ abstract contract MockPriceFeedTestUtils is Test { pyth, expectedFee, abi.encodeWithSelector( - IPyth.parsePriceFeedUpdatesWithSlots.selector + IPyth.parsePriceFeedUpdatesWithSlotsStrict.selector ), abi.encode(priceFeeds, slots) ); diff --git a/target_chains/ethereum/contracts/foundry.toml b/target_chains/ethereum/contracts/foundry.toml index 57d77cec3d..df90e460ef 100644 --- a/target_chains/ethereum/contracts/foundry.toml +++ b/target_chains/ethereum/contracts/foundry.toml @@ -7,10 +7,7 @@ src = 'contracts' # truffle doesn't try to build them test = 'forge-test' -libs = [ - 'lib', - 'node_modules', -] +libs = ['lib', 'node_modules'] # This doesn't work on ../../../node_modules alone because the sources from # parent directories are also represented as absolute paths and won't doesn't @@ -20,5 +17,5 @@ libs = [ ignored_warnings_from = [ "lib", "node_modules/", - "/home/runner/work/pyth-crosschain/pyth-crosschain/node_modules" + "/home/runner/work/pyth-crosschain/pyth-crosschain/node_modules", ] diff --git a/target_chains/ethereum/sdk/solidity/AbstractPyth.sol b/target_chains/ethereum/sdk/solidity/AbstractPyth.sol index be76f7a3a5..dfa60ceb0b 100644 --- a/target_chains/ethereum/sdk/solidity/AbstractPyth.sol +++ b/target_chains/ethereum/sdk/solidity/AbstractPyth.sol @@ -136,7 +136,7 @@ abstract contract AbstractPyth is IPyth { override returns (PythStructs.PriceFeed[] memory priceFeeds); - function parsePriceFeedUpdatesWithSlots( + function parsePriceFeedUpdatesWithSlotsStrict( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64 minPublishTime, diff --git a/target_chains/ethereum/sdk/solidity/IPyth.sol b/target_chains/ethereum/sdk/solidity/IPyth.sol index 65fda5b6d9..5418504429 100644 --- a/target_chains/ethereum/sdk/solidity/IPyth.sol +++ b/target_chains/ethereum/sdk/solidity/IPyth.sol @@ -172,15 +172,14 @@ interface IPyth is IPythEvents { uint64 maxPublishTime ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds); - /// @dev Same as `parsePriceFeedUpdates`, but also returns the Pythnet slot - /// associated with each price update. + /// @dev Same as `parsePriceFeedUpdates`, but checks that the updateData is minimal and also returns the Pythnet slots. /// @param updateData Array of price update data. /// @param priceIds Array of price ids. /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`. /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`. /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order). /// @return slots Array of the Pythnet slot corresponding to the given `priceIds` (with the same order). - function parsePriceFeedUpdatesWithSlots( + function parsePriceFeedUpdatesWithSlotsStrict( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64 minPublishTime, diff --git a/target_chains/ethereum/sdk/solidity/MockPyth.sol b/target_chains/ethereum/sdk/solidity/MockPyth.sol index 200fe97159..e0a59f9cdd 100644 --- a/target_chains/ethereum/sdk/solidity/MockPyth.sol +++ b/target_chains/ethereum/sdk/solidity/MockPyth.sol @@ -170,7 +170,7 @@ contract MockPyth is AbstractPyth { ); } - function parsePriceFeedUpdatesWithSlots( + function parsePriceFeedUpdatesWithSlotsStrict( bytes[] calldata updateData, bytes32[] calldata priceIds, uint64 minPublishTime, diff --git a/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol b/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol index 9cfbb3ba64..8afe2289cc 100644 --- a/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol +++ b/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol @@ -31,7 +31,10 @@ contract PythAggregatorV3 { pyth.updatePriceFeeds{value: fee}(priceUpdateData); // refund remaining eth - payable(msg.sender).call{value: address(this).balance}(""); + // solhint-disable-next-line no-unused-vars + (bool success, ) = payable(msg.sender).call{ + value: address(this).balance + }(""); } function decimals() public view virtual returns (uint8) { diff --git a/target_chains/ethereum/sdk/solidity/abis/AbstractPyth.json b/target_chains/ethereum/sdk/solidity/abis/AbstractPyth.json index 6d1a2a8f08..b0c8f504d0 100644 --- a/target_chains/ethereum/sdk/solidity/abis/AbstractPyth.json +++ b/target_chains/ethereum/sdk/solidity/abis/AbstractPyth.json @@ -608,7 +608,7 @@ "type": "uint64" } ], - "name": "parsePriceFeedUpdatesWithSlots", + "name": "parsePriceFeedUpdatesWithSlotsStrict", "outputs": [ { "components": [ diff --git a/target_chains/ethereum/sdk/solidity/abis/IPyth.json b/target_chains/ethereum/sdk/solidity/abis/IPyth.json index 0516d39d97..cbb0717797 100644 --- a/target_chains/ethereum/sdk/solidity/abis/IPyth.json +++ b/target_chains/ethereum/sdk/solidity/abis/IPyth.json @@ -498,7 +498,7 @@ "type": "uint64" } ], - "name": "parsePriceFeedUpdatesWithSlots", + "name": "parsePriceFeedUpdatesWithSlotsStrict", "outputs": [ { "components": [ diff --git a/target_chains/ethereum/sdk/solidity/abis/MockPyth.json b/target_chains/ethereum/sdk/solidity/abis/MockPyth.json index 92cba70ce1..ed4a5b0854 100644 --- a/target_chains/ethereum/sdk/solidity/abis/MockPyth.json +++ b/target_chains/ethereum/sdk/solidity/abis/MockPyth.json @@ -747,7 +747,7 @@ "type": "uint64" } ], - "name": "parsePriceFeedUpdatesWithSlots", + "name": "parsePriceFeedUpdatesWithSlotsStrict", "outputs": [ { "components": [