MockPyth.sol 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // SPDX-License-Identifier: Apache-2.0
  2. pragma solidity ^0.8.0;
  3. import "./AbstractPyth.sol";
  4. import "./PythStructs.sol";
  5. import "./PythErrors.sol";
  6. contract MockPyth is AbstractPyth {
  7. mapping(bytes32 => PythStructs.PriceFeed) priceFeeds;
  8. uint64 sequenceNumber;
  9. uint singleUpdateFeeInWei;
  10. uint validTimePeriod;
  11. constructor(uint _validTimePeriod, uint _singleUpdateFeeInWei) {
  12. singleUpdateFeeInWei = _singleUpdateFeeInWei;
  13. validTimePeriod = _validTimePeriod;
  14. }
  15. function queryPriceFeed(
  16. bytes32 id
  17. ) public view override returns (PythStructs.PriceFeed memory priceFeed) {
  18. if (priceFeeds[id].id == 0) revert PythErrors.PriceFeedNotFound();
  19. return priceFeeds[id];
  20. }
  21. function priceFeedExists(bytes32 id) public view override returns (bool) {
  22. return (priceFeeds[id].id != 0);
  23. }
  24. function getValidTimePeriod() public view override returns (uint) {
  25. return validTimePeriod;
  26. }
  27. // Takes an array of encoded price feeds and stores them.
  28. // You can create this data either by calling createPriceFeedUpdateData or
  29. // by using web3.js or ethers abi utilities.
  30. function updatePriceFeeds(
  31. bytes[] calldata updateData
  32. ) public payable override {
  33. uint requiredFee = getUpdateFee(updateData);
  34. if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
  35. // Chain ID is id of the source chain that the price update comes from. Since it is just a mock contract
  36. // We set it to 1.
  37. uint16 chainId = 1;
  38. for (uint i = 0; i < updateData.length; i++) {
  39. PythStructs.PriceFeed memory priceFeed = abi.decode(
  40. updateData[i],
  41. (PythStructs.PriceFeed)
  42. );
  43. uint lastPublishTime = priceFeeds[priceFeed.id].price.publishTime;
  44. if (lastPublishTime < priceFeed.price.publishTime) {
  45. // Price information is more recent than the existing price information.
  46. priceFeeds[priceFeed.id] = priceFeed;
  47. emit PriceFeedUpdate(
  48. priceFeed.id,
  49. uint64(priceFeed.price.publishTime),
  50. priceFeed.price.price,
  51. priceFeed.price.conf
  52. );
  53. }
  54. }
  55. // In the real contract, the input of this function contains multiple batches that each contain multiple prices.
  56. // This event is emitted when a batch is processed. In this mock contract we consider there is only one batch of prices.
  57. // Each batch has (chainId, sequenceNumber) as it's unique identifier. Here chainId is set to 1 and an increasing sequence number is used.
  58. emit BatchPriceFeedUpdate(chainId, sequenceNumber);
  59. sequenceNumber += 1;
  60. }
  61. function getUpdateFee(
  62. bytes[] calldata updateData
  63. ) public view override returns (uint feeAmount) {
  64. return singleUpdateFeeInWei * updateData.length;
  65. }
  66. function parsePriceFeedUpdatesInternal(
  67. bytes[] calldata updateData,
  68. bytes32[] calldata priceIds,
  69. uint64 minPublishTime,
  70. uint64 maxPublishTime,
  71. bool unique
  72. ) internal returns (PythStructs.PriceFeed[] memory feeds) {
  73. uint requiredFee = getUpdateFee(updateData);
  74. if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
  75. feeds = new PythStructs.PriceFeed[](priceIds.length);
  76. for (uint i = 0; i < priceIds.length; i++) {
  77. for (uint j = 0; j < updateData.length; j++) {
  78. uint64 prevPublishTime;
  79. (feeds[i], prevPublishTime) = abi.decode(
  80. updateData[j],
  81. (PythStructs.PriceFeed, uint64)
  82. );
  83. uint publishTime = feeds[i].price.publishTime;
  84. if (priceFeeds[feeds[i].id].price.publishTime < publishTime) {
  85. priceFeeds[feeds[i].id] = feeds[i];
  86. emit PriceFeedUpdate(
  87. feeds[i].id,
  88. uint64(publishTime),
  89. feeds[i].price.price,
  90. feeds[i].price.conf
  91. );
  92. }
  93. if (feeds[i].id == priceIds[i]) {
  94. if (
  95. minPublishTime <= publishTime &&
  96. publishTime <= maxPublishTime &&
  97. (!unique || prevPublishTime < minPublishTime)
  98. ) {
  99. break;
  100. } else {
  101. feeds[i].id = 0;
  102. }
  103. }
  104. }
  105. if (feeds[i].id != priceIds[i])
  106. revert PythErrors.PriceFeedNotFoundWithinRange();
  107. }
  108. }
  109. function parsePriceFeedUpdates(
  110. bytes[] calldata updateData,
  111. bytes32[] calldata priceIds,
  112. uint64 minPublishTime,
  113. uint64 maxPublishTime
  114. ) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
  115. return
  116. parsePriceFeedUpdatesInternal(
  117. updateData,
  118. priceIds,
  119. minPublishTime,
  120. maxPublishTime,
  121. false
  122. );
  123. }
  124. function parsePriceFeedUpdatesUnique(
  125. bytes[] calldata updateData,
  126. bytes32[] calldata priceIds,
  127. uint64 minPublishTime,
  128. uint64 maxPublishTime
  129. ) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
  130. return
  131. parsePriceFeedUpdatesInternal(
  132. updateData,
  133. priceIds,
  134. minPublishTime,
  135. maxPublishTime,
  136. true
  137. );
  138. }
  139. function createPriceFeedUpdateData(
  140. bytes32 id,
  141. int64 price,
  142. uint64 conf,
  143. int32 expo,
  144. int64 emaPrice,
  145. uint64 emaConf,
  146. uint64 publishTime,
  147. uint64 prevPublishTime
  148. ) public pure returns (bytes memory priceFeedData) {
  149. PythStructs.PriceFeed memory priceFeed;
  150. priceFeed.id = id;
  151. priceFeed.price.price = price;
  152. priceFeed.price.conf = conf;
  153. priceFeed.price.expo = expo;
  154. priceFeed.price.publishTime = publishTime;
  155. priceFeed.emaPrice.price = emaPrice;
  156. priceFeed.emaPrice.conf = emaConf;
  157. priceFeed.emaPrice.expo = expo;
  158. priceFeed.emaPrice.publishTime = publishTime;
  159. priceFeedData = abi.encode(priceFeed, prevPublishTime);
  160. }
  161. }