MockPyth.sol 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. uint singleUpdateFeeInWei;
  9. uint validTimePeriod;
  10. constructor(uint _validTimePeriod, uint _singleUpdateFeeInWei) {
  11. singleUpdateFeeInWei = _singleUpdateFeeInWei;
  12. validTimePeriod = _validTimePeriod;
  13. }
  14. function queryPriceFeed(
  15. bytes32 id
  16. ) public view override returns (PythStructs.PriceFeed memory priceFeed) {
  17. if (priceFeeds[id].id == 0) revert PythErrors.PriceFeedNotFound();
  18. return priceFeeds[id];
  19. }
  20. function priceFeedExists(bytes32 id) public view override returns (bool) {
  21. return (priceFeeds[id].id != 0);
  22. }
  23. function getValidTimePeriod() public view override returns (uint) {
  24. return validTimePeriod;
  25. }
  26. // Takes an array of encoded price feeds and stores them.
  27. // You can create this data either by calling createPriceFeedUpdateData or
  28. // by using web3.js or ethers abi utilities.
  29. // @note: The updateData expected here is different from the one used in the main contract.
  30. // In particular, the expected format is:
  31. // [
  32. // abi.encode(
  33. // PythStructs.PriceFeed(
  34. // bytes32 id,
  35. // PythStructs.Price price,
  36. // PythStructs.Price emaPrice
  37. // ),
  38. // uint64 prevPublishTime
  39. // )
  40. // ]
  41. function updatePriceFeeds(
  42. bytes[] calldata updateData
  43. ) public payable override {
  44. uint requiredFee = getUpdateFee(updateData);
  45. if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
  46. for (uint i = 0; i < updateData.length; i++) {
  47. PythStructs.PriceFeed memory priceFeed = abi.decode(
  48. updateData[i],
  49. (PythStructs.PriceFeed)
  50. );
  51. uint lastPublishTime = priceFeeds[priceFeed.id].price.publishTime;
  52. if (lastPublishTime < priceFeed.price.publishTime) {
  53. // Price information is more recent than the existing price information.
  54. priceFeeds[priceFeed.id] = priceFeed;
  55. emit PriceFeedUpdate(
  56. priceFeed.id,
  57. uint64(priceFeed.price.publishTime),
  58. priceFeed.price.price,
  59. priceFeed.price.conf
  60. );
  61. }
  62. }
  63. }
  64. function getUpdateFee(
  65. bytes[] calldata updateData
  66. ) public view override returns (uint feeAmount) {
  67. return singleUpdateFeeInWei * updateData.length;
  68. }
  69. function parsePriceFeedUpdatesInternal(
  70. bytes[] calldata updateData,
  71. bytes32[] calldata priceIds,
  72. uint64 minPublishTime,
  73. uint64 maxPublishTime,
  74. bool unique
  75. ) internal returns (PythStructs.PriceFeed[] memory feeds) {
  76. uint requiredFee = getUpdateFee(updateData);
  77. if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
  78. feeds = new PythStructs.PriceFeed[](priceIds.length);
  79. for (uint i = 0; i < priceIds.length; i++) {
  80. for (uint j = 0; j < updateData.length; j++) {
  81. uint64 prevPublishTime;
  82. (feeds[i], prevPublishTime) = abi.decode(
  83. updateData[j],
  84. (PythStructs.PriceFeed, uint64)
  85. );
  86. uint publishTime = feeds[i].price.publishTime;
  87. if (priceFeeds[feeds[i].id].price.publishTime < publishTime) {
  88. priceFeeds[feeds[i].id] = feeds[i];
  89. emit PriceFeedUpdate(
  90. feeds[i].id,
  91. uint64(publishTime),
  92. feeds[i].price.price,
  93. feeds[i].price.conf
  94. );
  95. }
  96. if (feeds[i].id == priceIds[i]) {
  97. if (
  98. minPublishTime <= publishTime &&
  99. publishTime <= maxPublishTime &&
  100. (!unique || prevPublishTime < minPublishTime)
  101. ) {
  102. break;
  103. } else {
  104. feeds[i].id = 0;
  105. }
  106. }
  107. }
  108. if (feeds[i].id != priceIds[i])
  109. revert PythErrors.PriceFeedNotFoundWithinRange();
  110. }
  111. }
  112. function parsePriceFeedUpdates(
  113. bytes[] calldata updateData,
  114. bytes32[] calldata priceIds,
  115. uint64 minPublishTime,
  116. uint64 maxPublishTime
  117. ) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
  118. return
  119. parsePriceFeedUpdatesInternal(
  120. updateData,
  121. priceIds,
  122. minPublishTime,
  123. maxPublishTime,
  124. false
  125. );
  126. }
  127. function parsePriceFeedUpdatesUnique(
  128. bytes[] calldata updateData,
  129. bytes32[] calldata priceIds,
  130. uint64 minPublishTime,
  131. uint64 maxPublishTime
  132. ) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
  133. return
  134. parsePriceFeedUpdatesInternal(
  135. updateData,
  136. priceIds,
  137. minPublishTime,
  138. maxPublishTime,
  139. true
  140. );
  141. }
  142. function createPriceFeedUpdateData(
  143. bytes32 id,
  144. int64 price,
  145. uint64 conf,
  146. int32 expo,
  147. int64 emaPrice,
  148. uint64 emaConf,
  149. uint64 publishTime,
  150. uint64 prevPublishTime
  151. ) public pure returns (bytes memory priceFeedData) {
  152. PythStructs.PriceFeed memory priceFeed;
  153. priceFeed.id = id;
  154. priceFeed.price.price = price;
  155. priceFeed.price.conf = conf;
  156. priceFeed.price.expo = expo;
  157. priceFeed.price.publishTime = publishTime;
  158. priceFeed.emaPrice.price = emaPrice;
  159. priceFeed.emaPrice.conf = emaConf;
  160. priceFeed.emaPrice.expo = expo;
  161. priceFeed.emaPrice.publishTime = publishTime;
  162. priceFeedData = abi.encode(priceFeed, prevPublishTime);
  163. }
  164. }