@@ -239,17 +239,21 @@ abstract contract Scheduler is IScheduler, SchedulerState {
239239 // Parse price feed updates with an expected timestamp range of [-10s, now]
240240 // We will validate the trigger conditions and timestamps ourselves
241241 // using the returned PriceFeeds.
242- uint64 maxPublishTime = SafeCast.toUint64 (block .timestamp );
243- uint64 minPublishTime = maxPublishTime - 10 seconds ;
244- PythStructs.PriceFeed[] memory priceFeeds = pyth.parsePriceFeedUpdates {
242+ uint64 curTime = SafeCast.toUint64 (block .timestamp );
243+ uint64 maxPublishTime = curTime + FUTURE_TIMESTAMP_MAX_VALIDITY_PERIOD;
244+ uint64 minPublishTime = curTime - PAST_TIMESTAMP_MAX_VALIDITY_PERIOD;
245+ PythStructs.PriceFeed[] memory priceFeeds;
246+ uint64 [] memory slots;
247+ (priceFeeds, slots) = pyth.parsePriceFeedUpdatesWithSlots {
245248 value: pythFee
246249 }(updateData, priceIds, minPublishTime, maxPublishTime);
247250
248- // Verify all price feeds have the same timestamp
249- uint256 timestamp = priceFeeds[0 ].price.publishTime;
250- for (uint8 i = 1 ; i < priceFeeds.length ; i++ ) {
251- if (priceFeeds[i].price.publishTime != timestamp) {
252- revert PriceTimestampMismatch ();
251+ // Verify all price feeds have the same Pythnet slot.
252+ // All feeds in a subscription must be updated at the same time.
253+ uint64 slot = slots[0 ];
254+ for (uint8 i = 1 ; i < slots.length ; i++ ) {
255+ if (slots[i] != slot) {
256+ revert PriceSlotMismatch ();
253257 }
254258 }
255259
@@ -291,7 +295,6 @@ abstract contract Scheduler is IScheduler, SchedulerState {
291295
292296 /**
293297 * @notice Validates whether the update trigger criteria is met for a subscription. Reverts if not met.
294- * @dev This function assumes that all updates in priceFeeds have the same timestamp. The caller is expected to enforce this invariant.
295298 * @param subscriptionId The ID of the subscription (needed for reading previous prices).
296299 * @param params The subscription's parameters struct.
297300 * @param status The subscription's status struct.
@@ -303,9 +306,16 @@ abstract contract Scheduler is IScheduler, SchedulerState {
303306 SubscriptionStatus storage status ,
304307 PythStructs.PriceFeed[] memory priceFeeds
305308 ) internal view returns (bool ) {
306- // SECURITY NOTE: this check assumes that all updates in priceFeeds have the same timestamp.
307- // The caller is expected to enforce this invariant.
308- uint256 updateTimestamp = priceFeeds[0 ].price.publishTime;
309+ // Use the most recent timestamp, as some asset markets may be closed.
310+ // Closed markets will have a publishTime from their last trading period.
311+ // Since we verify all updates share the same Pythnet slot, we still ensure
312+ // that all price feeds are synchronized from the same update cycle.
313+ uint256 updateTimestamp = 0 ;
314+ for (uint8 i = 0 ; i < priceFeeds.length ; i++ ) {
315+ if (priceFeeds[i].price.publishTime > updateTimestamp) {
316+ updateTimestamp = priceFeeds[i].price.publishTime;
317+ }
318+ }
309319
310320 // Reject updates if they're older than the latest stored ones
311321 if (
0 commit comments