MockPriceFeedTestUtils.sol 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // SPDX-License-Identifier: Apache 2
  2. pragma solidity ^0.8.0;
  3. import "forge-std/Test.sol";
  4. import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
  5. import "@openzeppelin/contracts/utils/math/SafeCast.sol";
  6. abstract contract MockPriceFeedTestUtils is Test {
  7. bytes32 constant BTC_PRICE_FEED_ID =
  8. 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43;
  9. bytes32 constant ETH_PRICE_FEED_ID =
  10. 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
  11. bytes32 constant SOL_PRICE_FEED_ID =
  12. 0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d;
  13. bytes32 constant AVAX_PRICE_FEED_ID =
  14. 0x93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7;
  15. bytes32 constant MELANIA_PRICE_FEED_ID =
  16. 0x8fef7d52c7f4e3a6258d663f9d27e64a1b6fd95ab5f7d545dbf9a515353d0064;
  17. bytes32 constant PYTH_PRICE_FEED_ID =
  18. 0x0bbf28e9a841a1cc788f6a361b17ca072d0ea3098a1e5df1c3922d06719579ff;
  19. bytes32 constant UNI_PRICE_FEED_ID =
  20. 0x78d185a741d07edb3412b09008b7c5cfb9bbbd7d568bf00ba737b456ba171501;
  21. bytes32 constant AAVE_PRICE_FEED_ID =
  22. 0x2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445;
  23. bytes32 constant DOGE_PRICE_FEED_ID =
  24. 0xdcef50dd0a4cd2dcc17e45df1676dcb336a11a61c69df7a0299b0150c672d25c;
  25. bytes32 constant ADA_PRICE_FEED_ID =
  26. 0x2a01deaec9e51a579277b34b122399984d0bbf57e2458a7e42fecd2829867a0d;
  27. // Price feed constants
  28. int8 constant MOCK_PRICE_FEED_EXPO = -8;
  29. int64 constant MOCK_BTC_PRICE = 5_000_000_000_000; // $50,000
  30. int64 constant MOCK_ETH_PRICE = 300_000_000_000; // $3,000
  31. uint64 constant MOCK_BTC_CONF = 10_000_000_000; // $100
  32. uint64 constant MOCK_ETH_CONF = 5_000_000_000; // $50
  33. // Fee charged by the Pyth oracle per price feed
  34. uint constant MOCK_PYTH_FEE_PER_FEED = 10 wei;
  35. uint32 constant CALLBACK_GAS_LIMIT = 1_000_000;
  36. // Helper function to create price IDs array with default 2 feeds
  37. function createPriceIds() internal pure returns (bytes32[] memory) {
  38. return createPriceIds(2);
  39. }
  40. // Helper function to create price IDs array with variable number of feeds
  41. function createPriceIds(
  42. uint256 numFeeds
  43. ) internal pure returns (bytes32[] memory) {
  44. bytes32[] memory priceIds = new bytes32[](numFeeds);
  45. // First assign our predefined price feed IDs
  46. uint256 predefinedCount = 10;
  47. uint256 assignCount = numFeeds < predefinedCount
  48. ? numFeeds
  49. : predefinedCount;
  50. if (assignCount > 0) priceIds[0] = BTC_PRICE_FEED_ID;
  51. if (assignCount > 1) priceIds[1] = ETH_PRICE_FEED_ID;
  52. if (assignCount > 2) priceIds[2] = SOL_PRICE_FEED_ID;
  53. if (assignCount > 3) priceIds[3] = AVAX_PRICE_FEED_ID;
  54. if (assignCount > 4) priceIds[4] = MELANIA_PRICE_FEED_ID;
  55. if (assignCount > 5) priceIds[5] = PYTH_PRICE_FEED_ID;
  56. if (assignCount > 6) priceIds[6] = UNI_PRICE_FEED_ID;
  57. if (assignCount > 7) priceIds[7] = AAVE_PRICE_FEED_ID;
  58. if (assignCount > 8) priceIds[8] = DOGE_PRICE_FEED_ID;
  59. if (assignCount > 9) priceIds[9] = ADA_PRICE_FEED_ID;
  60. // For any additional feeds beyond our predefined ones, generate derived IDs
  61. for (uint256 i = predefinedCount; i < numFeeds; i++) {
  62. // Derive new price IDs by incrementing the last predefined price ID
  63. priceIds[i] = bytes32(uint256(ADA_PRICE_FEED_ID) + (i - 9));
  64. }
  65. return priceIds;
  66. }
  67. // Helper function to create a single mock price feed
  68. function createSingleMockPriceFeed(
  69. uint256 publishTime
  70. ) internal pure returns (PythStructs.PriceFeed memory) {
  71. return createMockPriceFeeds(publishTime, 1)[0];
  72. }
  73. // Helper function to create mock price feeds with slots
  74. function createMockPriceFeedsWithSlots(
  75. uint256 publishTime,
  76. uint256 numFeeds
  77. )
  78. internal
  79. pure
  80. returns (
  81. PythStructs.PriceFeed[] memory priceFeeds,
  82. uint64[] memory slots
  83. )
  84. {
  85. priceFeeds = createMockPriceFeeds(publishTime, numFeeds);
  86. slots = new uint64[](numFeeds);
  87. // Set all slots to the publishTime as a mock value
  88. for (uint256 i = 0; i < numFeeds; i++) {
  89. slots[i] = uint64(publishTime);
  90. }
  91. return (priceFeeds, slots);
  92. }
  93. // Helper function to create mock price feeds with default 2 feeds
  94. function createMockPriceFeeds(
  95. uint256 publishTime
  96. ) internal pure returns (PythStructs.PriceFeed[] memory) {
  97. return createMockPriceFeeds(publishTime, 2);
  98. }
  99. // Helper function to create mock price feeds with variable number of feeds
  100. function createMockPriceFeeds(
  101. uint256 publishTime,
  102. uint256 numFeeds
  103. ) internal pure returns (PythStructs.PriceFeed[] memory) {
  104. PythStructs.PriceFeed[] memory priceFeeds = new PythStructs.PriceFeed[](
  105. numFeeds
  106. );
  107. bytes32[] memory priceIds = createPriceIds(numFeeds);
  108. for (uint256 i = 0; i < numFeeds; i++) {
  109. priceFeeds[i].id = priceIds[i];
  110. // Use appropriate price and conf based on the price ID
  111. if (priceIds[i] == BTC_PRICE_FEED_ID) {
  112. priceFeeds[i].price.price = MOCK_BTC_PRICE;
  113. priceFeeds[i].price.conf = MOCK_BTC_CONF;
  114. } else if (priceIds[i] == ETH_PRICE_FEED_ID) {
  115. priceFeeds[i].price.price = MOCK_ETH_PRICE;
  116. priceFeeds[i].price.conf = MOCK_ETH_CONF;
  117. } else {
  118. // Default to BTC price for other feeds
  119. priceFeeds[i].price.price = MOCK_BTC_PRICE;
  120. priceFeeds[i].price.conf = MOCK_BTC_CONF;
  121. }
  122. priceFeeds[i].price.expo = MOCK_PRICE_FEED_EXPO;
  123. priceFeeds[i].price.publishTime = publishTime;
  124. }
  125. return priceFeeds;
  126. }
  127. // Helper function to mock Pyth response
  128. function mockParsePriceFeedUpdates(
  129. address pyth,
  130. PythStructs.PriceFeed[] memory priceFeeds
  131. ) internal {
  132. uint expectedFee = MOCK_PYTH_FEE_PER_FEED * priceFeeds.length;
  133. vm.mockCall(
  134. pyth,
  135. abi.encodeWithSelector(IPyth.getUpdateFee.selector),
  136. abi.encode(expectedFee)
  137. );
  138. vm.mockCall(
  139. pyth,
  140. expectedFee,
  141. abi.encodeWithSelector(IPyth.parsePriceFeedUpdates.selector),
  142. abi.encode(priceFeeds)
  143. );
  144. }
  145. // Helper function to mock Pyth response with slots
  146. function mockParsePriceFeedUpdatesWithSlotsStrict(
  147. address pyth,
  148. PythStructs.PriceFeed[] memory priceFeeds,
  149. uint64[] memory slots
  150. ) internal {
  151. uint expectedFee = MOCK_PYTH_FEE_PER_FEED * priceFeeds.length;
  152. vm.mockCall(
  153. pyth,
  154. abi.encodeWithSelector(IPyth.getUpdateFee.selector),
  155. abi.encode(expectedFee)
  156. );
  157. vm.mockCall(
  158. pyth,
  159. expectedFee,
  160. abi.encodeWithSelector(
  161. IPyth.parsePriceFeedUpdatesWithConfig.selector
  162. ),
  163. abi.encode(priceFeeds, slots)
  164. );
  165. }
  166. // Helper function to create mock update data for variable feeds
  167. function createMockUpdateData(
  168. PythStructs.PriceFeed[] memory priceFeeds
  169. ) internal pure returns (bytes[] memory) {
  170. uint256 numFeeds = priceFeeds.length;
  171. bytes[] memory updateData = new bytes[](numFeeds);
  172. for (uint256 i = 0; i < numFeeds; i++) {
  173. updateData[i] = abi.encode(priceFeeds[i]);
  174. }
  175. return updateData;
  176. }
  177. }