@@ -311,15 +311,16 @@ abstract contract Pyth is
311311 function parseTwapPriceFeedUpdates (
312312 bytes [2 ][] calldata updateData ,
313313 bytes32 [] calldata priceIds ,
314- unit8 windowSize
314+ uint8 windowSize
315315 )
316316 external
317317 payable
318- override
319318 returns (PythStructs.TwapPriceFeed[] memory twapPriceFeeds )
320319 {
321320 {
322- revert (updateData.length != 2 , PythErrors.InvalidUpdateData ());
321+ if (updateData.length != 2 ) {
322+ revert PythErrors.InvalidUpdateData ();
323+ }
323324 uint requiredFee = getUpdateFee (updateData[0 ]);
324325
325326 // Check if the two updateData contains the same number of priceUpdates
@@ -330,6 +331,7 @@ abstract contract Pyth is
330331 }
331332
332333 // Parse the updateData
334+ unchecked {
333335 twapPriceFeeds = new PythStructs.TwapPriceFeed [](priceIds.length );
334336 for (uint i = 0 ; i < updateData[0 ].length ; i++ ) {
335337 if (
@@ -341,6 +343,7 @@ abstract contract Pyth is
341343 ACCUMULATOR_MAGIC)
342344 ) {
343345 // Parse the accumulator update
346+ // I believe the offset will be same for both updateData[0][i] and updateData[1][i]
344347 uint offsetFirst;
345348 uint offsetSecond;
346349 {
@@ -364,7 +367,7 @@ abstract contract Pyth is
364367 (
365368 offsetFirst,
366369 digestFirst,
367- numUpdates ,
370+ numUpdatesFirst ,
368371 encodedFirst
369372 ) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedFromAccumulatorUpdate (
370373 updateData[0 ][i],
@@ -373,12 +376,12 @@ abstract contract Pyth is
373376 (
374377 offsetSecond,
375378 digestSecond,
376- numUpdates ,
379+ numUpdatesSecond ,
377380 encodedSecond
378381 ) = extractWormholeMerkleHeaderDigestAndNumUpdatesAndEncodedFromAccumulatorUpdate (
379382 updateData[1 ][i],
380383 offsetSecond);
381-
384+ // I believe this check is redundant
382385 if (numUpdatesFirst != numUpdatesSecond) {
383386 revert PythErrors.InvalidUpdateData ();
384387 }
@@ -389,15 +392,86 @@ abstract contract Pyth is
389392 bytes32 priceIdFirst;
390393 bytes32 priceIdSecond;
391394
392- (offsetFirst, twapPriceInfoFirst, priceIdFirst) = extractPriceInfoFromMerkleProof (digestFirst, encodedFirst, offsetFirst);
393- (offsetSecond, twapPriceInfoSecond, priceIdSecond) = extractPriceInfoFromMerkleProof (digestSecond, encodedSecond, offsetSecond);
394- }
395+ (offsetFirst, twapPriceInfoFirst, priceIdFirst) = extractTwapPriceInfoFromMerkleProof (digestFirst, encodedFirst, offsetFirst);
396+ (offsetSecond, twapPriceInfoSecond, priceIdSecond) = extractTwapPriceInfoFromMerkleProof (digestSecond, encodedSecond, offsetSecond);
395397
398+ require (priceIdFirst == priceIdSecond, PythErrors.InvalidTwapUpdateDataSet ());
396399
400+ // No Updates here.
401+ // check whether caller requested for this data
402+ uint k = findIndexOfPriceId (priceIds, priceIdFirst);
403+ if (k == priceIds.length || twapPriceFeeds[k].id != 0 ) {
404+ continue ;
405+ }
406+
407+ // Since we have already validated the twap price info, we can directly use it
408+ validateTwapPriceInfo (twapPriceInfoFirst, twapPriceInfoSecond);
409+
410+ // Now we will calcualte the cumulative price and cumulative conf
411+ // for the first and second priceId
412+ // I believe the cumulative price and cumulative conf will be same for both priceIdFirst and priceIdSecond
413+ // because they are both from the same accumulator update
414+ uint64 slotDiff = twapPriceInfoSecond.publishSlot - twapPriceInfoFirst.publishSlot;
415+ int128 priceDiff = twapPriceInfoSecond.price - twapPriceInfoFirst.price;
416+ uint128 confDiff = twapPriceInfoSecond.conf - twapPriceInfoFirst.conf;
417+
418+ // Now we will calculate the twap price and twap conf
419+ // for the first and second priceId
420+ int128 twapPrice = priceDiff / slotDiff;
421+ uint128 twapConf = confDiff / slotDiff;
422+
423+ twapPriceFeeds[k].id = priceIdFirst;
424+ twapPriceFeeds[k].twap.price = twapPrice;
425+ twapPriceFeeds[k].twap.conf = twapConf;
426+ twapPriceFeeds[k].twap.expo = twapPriceInfoFirst.expo;
427+ twapPriceFeeds[k].twap.publishTime = twapPriceInfoSecond.publishTime;
428+ twapPriceFeeds[k].startTime = twapPriceInfoFirst.publishTime;
429+ twapPriceFeeds[k].endTime = twapPriceInfoSecond.publishTime;
430+ //TODO: Calculate the downSlotRatio
431+ }
432+ if (offsetFirst != encodedFirst.length ) {
433+ revert PythErrors.InvalidTwapUpdateData ();
434+ }
435+ if (offsetSecond != encodedSecond.length ) {
436+ revert PythErrors.InvalidTwapUpdateData ();
437+ }
438+ if (offsetFirst != offsetSecond) {
439+ revert PythErrors.InvalidTwapUpdateData ();
440+ } else {
441+ revert PythErrors.InvalidUpdateData ();
442+ }
397443 } else {
398444 revert PythErrors.InvalidUpdateData ();
399445 }
400446 }
447+
448+ for (uint k = 0 ; k < priceIds.length ; k++ ) {
449+ if (twapPriceFeeds[k].id == 0 ) {
450+ revert PythErrors.PriceFeedNotFoundWithinRange ();
451+ }
452+ }
453+ }
454+ }
455+
456+ function validateTwapPriceInfo (
457+ PythInternalStructs.TwapPriceInfo memory twapPriceInfoFirst ,
458+ PythInternalStructs.TwapPriceInfo memory twapPriceInfoSecond
459+ ) private pure {
460+ if (twapPriceInfoFirst.expo != twapPriceInfoSecond.expo) {
461+ revert PythErrors.InvalidTwapUpdateDataSet ();
462+ }
463+ if (twapPriceInfoFirst.publishSlot > twapPriceInfoSecond.publishSlot) {
464+ revert PythErrors.InvalidTwapUpdateDataSet ();
465+ }
466+ if (twapPriceInfoFirst.prevPublishTime > twapPriceInfoFirst.publishTime) {
467+ revert PythErrors.InvalidTwapUpdateData ();
468+ }
469+ if (twapPriceInfoSecond.prevPublishTime > twapPriceInfoSecond.publishTime) {
470+ revert PythErrors.InvalidTwapUpdateDataSet ();
471+ }
472+ if (twapPriceInfoFirst.publishTime > twapPriceInfoSecond.publishTime) {
473+ revert PythErrors.InvalidTwapUpdateDataSet ();
474+ }
401475 }
402476
403477 function parsePriceFeedUpdatesUnique (
0 commit comments