@@ -239,17 +239,21 @@ abstract contract Scheduler is IScheduler, SchedulerState {
239
239
// Parse price feed updates with an expected timestamp range of [-10s, now]
240
240
// We will validate the trigger conditions and timestamps ourselves
241
241
// 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 {
245
248
value: pythFee
246
249
}(updateData, priceIds, minPublishTime, maxPublishTime);
247
250
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 ();
253
257
}
254
258
}
255
259
@@ -291,7 +295,6 @@ abstract contract Scheduler is IScheduler, SchedulerState {
291
295
292
296
/**
293
297
* @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.
295
298
* @param subscriptionId The ID of the subscription (needed for reading previous prices).
296
299
* @param params The subscription's parameters struct.
297
300
* @param status The subscription's status struct.
@@ -303,9 +306,16 @@ abstract contract Scheduler is IScheduler, SchedulerState {
303
306
SubscriptionStatus storage status ,
304
307
PythStructs.PriceFeed[] memory priceFeeds
305
308
) 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
+ }
309
319
310
320
// Reject updates if they're older than the latest stored ones
311
321
if (
0 commit comments