@@ -7,7 +7,6 @@ import "@pythnetwork/pyth-sdk-solidity/AbstractPyth.sol";
7
7
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol " ;
8
8
9
9
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol " ;
10
- import "@pythnetwork/pyth-sdk-solidity/PythUtils.sol " ;
11
10
import "./PythAccumulator.sol " ;
12
11
import "./PythGetters.sol " ;
13
12
import "./PythSetters.sol " ;
@@ -628,16 +627,53 @@ abstract contract Pyth is
628
627
return "1.4.4-alpha.5 " ;
629
628
}
630
629
630
+ /// @notice Calculates TWAP from two price points
631
+ /// @dev The calculation is done by taking the difference of cumulative values and dividing by the time difference
632
+ /// @param priceId The price feed ID
633
+ /// @param twapPriceInfoStart The starting price point
634
+ /// @param twapPriceInfoEnd The ending price point
635
+ /// @return twapPriceFeed The calculated TWAP price feed
631
636
function calculateTwap (
632
637
bytes32 priceId ,
633
638
PythStructs.TwapPriceInfo memory twapPriceInfoStart ,
634
639
PythStructs.TwapPriceInfo memory twapPriceInfoEnd
635
- ) private pure returns (PythStructs.TwapPriceFeed memory ) {
636
- return
637
- PythUtils.calculateTwap (
638
- priceId,
639
- twapPriceInfoStart,
640
- twapPriceInfoEnd
641
- );
640
+ ) private pure returns (PythStructs.TwapPriceFeed memory twapPriceFeed ) {
641
+ twapPriceFeed.id = priceId;
642
+ twapPriceFeed.startTime = twapPriceInfoStart.publishTime;
643
+ twapPriceFeed.endTime = twapPriceInfoEnd.publishTime;
644
+
645
+ // Calculate differences between start and end points for slots and cumulative values
646
+ uint64 slotDiff = twapPriceInfoEnd.publishSlot -
647
+ twapPriceInfoStart.publishSlot;
648
+ int128 priceDiff = twapPriceInfoEnd.cumulativePrice -
649
+ twapPriceInfoStart.cumulativePrice;
650
+ uint128 confDiff = twapPriceInfoEnd.cumulativeConf -
651
+ twapPriceInfoStart.cumulativeConf;
652
+
653
+ // Calculate time-weighted average price (TWAP) and confidence by dividing
654
+ // the difference in cumulative values by the number of slots between data points
655
+ int128 twapPrice = priceDiff / int128 (uint128 (slotDiff));
656
+ uint128 twapConf = confDiff / uint128 (slotDiff);
657
+
658
+ // The conversion from int128 to int64 is safe because:
659
+ // 1. Individual prices fit within int64 by protocol design
660
+ // 2. TWAP is essentially an average price over time (cumulativePrice₂-cumulativePrice₁)/slotDiff
661
+ // 3. This average must be within the range of individual prices that went into the calculation
662
+ // We use int128 only as an intermediate type to safely handle cumulative sums
663
+ twapPriceFeed.twap.price = int64 (twapPrice);
664
+ twapPriceFeed.twap.conf = uint64 (twapConf);
665
+ twapPriceFeed.twap.expo = twapPriceInfoStart.expo;
666
+ twapPriceFeed.twap.publishTime = twapPriceInfoEnd.publishTime;
667
+
668
+ // Calculate downSlotsRatio as a value between 0 and 1,000,000
669
+ // 0 means no slots were missed, 1,000,000 means all slots were missed
670
+ uint64 totalDownSlots = twapPriceInfoEnd.numDownSlots -
671
+ twapPriceInfoStart.numDownSlots;
672
+ uint64 downSlotsRatio = (totalDownSlots * 1_000_000 ) / slotDiff;
673
+
674
+ // Safely downcast to uint32 (sufficient for value range 0-1,000,000)
675
+ twapPriceFeed.downSlotsRatio = uint32 (downSlotsRatio);
676
+
677
+ return twapPriceFeed;
642
678
}
643
679
}
0 commit comments