GasBenchmark.t.sol 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // SPDX-License-Identifier: Apache 2
  2. pragma solidity ^0.8.0;
  3. import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
  4. import "forge-std/Test.sol";
  5. import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
  6. import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
  7. import "./utils/WormholeTestUtils.t.sol";
  8. import "./utils/PythTestUtils.t.sol";
  9. contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
  10. // 19, current mainnet number of guardians, is used to have gas estimates
  11. // close to our mainnet transactions.
  12. uint8 constant NUM_GUARDIANS = 19;
  13. // 2/3 of the guardians should sign a message for a VAA which is 13 out of 19 guardians.
  14. // It is possible to have more signers but the median seems to be 13.
  15. uint8 constant NUM_GUARDIAN_SIGNERS = 13;
  16. // We use 5 prices to form a batch of 5 prices, close to our mainnet transactions.
  17. uint8 constant NUM_PRICES = 5;
  18. IPyth public pyth;
  19. bytes32[] priceIds;
  20. // Cached prices are populated in the setUp
  21. PythStructs.Price[] cachedPrices;
  22. bytes[] cachedPricesUpdateData;
  23. uint cachedPricesUpdateFee;
  24. uint64[] cachedPricesPublishTimes;
  25. // Fresh prices are different prices that can be used
  26. // as a fresh price to update the prices
  27. PythStructs.Price[] freshPrices;
  28. bytes[] freshPricesUpdateData;
  29. uint freshPricesUpdateFee;
  30. uint64[] freshPricesPublishTimes;
  31. uint64 sequence;
  32. uint randSeed;
  33. function setUp() public {
  34. pyth = IPyth(setUpPyth(setUpWormhole(NUM_GUARDIANS)));
  35. priceIds = new bytes32[](NUM_PRICES);
  36. priceIds[0] = bytes32(0x1000000000000000000000000000000000000000000000000000000000000f00);
  37. for (uint i = 1; i < NUM_PRICES; ++i) {
  38. priceIds[i] = bytes32(uint256(priceIds[i-1])+1);
  39. }
  40. for (uint i = 0; i < NUM_PRICES; ++i) {
  41. uint64 publishTime = uint64(getRand() % 10);
  42. cachedPrices.push(PythStructs.Price(
  43. int64(uint64(getRand() % 1000)), // Price
  44. uint64(getRand() % 100), // Confidence
  45. -5, // Expo
  46. publishTime
  47. ));
  48. cachedPricesPublishTimes.push(publishTime);
  49. publishTime += uint64(getRand() % 10);
  50. freshPrices.push(PythStructs.Price(
  51. int64(uint64(getRand() % 1000)), // Price
  52. uint64(getRand() % 100), // Confidence
  53. -5, // Expo
  54. publishTime
  55. ));
  56. freshPricesPublishTimes.push(publishTime);
  57. }
  58. // Populate the contract with the initial prices
  59. (cachedPricesUpdateData, cachedPricesUpdateFee) = generateUpdateDataAndFee(cachedPrices);
  60. pyth.updatePriceFeeds{value: cachedPricesUpdateFee}(cachedPricesUpdateData);
  61. (freshPricesUpdateData, freshPricesUpdateFee) = generateUpdateDataAndFee(freshPrices);
  62. }
  63. function getRand() internal returns (uint val) {
  64. ++randSeed;
  65. val = uint(keccak256(abi.encode(randSeed)));
  66. }
  67. function generateUpdateDataAndFee(PythStructs.Price[] memory prices) internal returns (bytes[] memory updateData, uint updateFee) {
  68. bytes memory vaa = generatePriceFeedUpdateVAA(
  69. priceIds,
  70. prices,
  71. sequence,
  72. NUM_GUARDIAN_SIGNERS
  73. );
  74. ++sequence;
  75. updateData = new bytes[](1);
  76. updateData[0] = vaa;
  77. updateFee = pyth.getUpdateFee(updateData);
  78. }
  79. function testBenchmarkUpdatePriceFeedsFresh() public {
  80. pyth.updatePriceFeeds{value: freshPricesUpdateFee}(freshPricesUpdateData);
  81. }
  82. function testBenchmarkUpdatePriceFeedsNotFresh() public {
  83. pyth.updatePriceFeeds{value: cachedPricesUpdateFee}(cachedPricesUpdateData);
  84. }
  85. function testBenchmarkUpdatePriceFeedsIfNecessaryFresh() public {
  86. // Since the prices have advanced, the publishTimes are newer than one in
  87. // the contract and hence, the call should succeed.
  88. pyth.updatePriceFeedsIfNecessary{value: freshPricesUpdateFee}(freshPricesUpdateData, priceIds, freshPricesPublishTimes);
  89. }
  90. function testBenchmarkUpdatePriceFeedsIfNecessaryNotFresh() public {
  91. // Since the price is not advanced, the publishTimes are the same as the
  92. // ones in the contract.
  93. vm.expectRevert(bytes("no prices in the submitted batch have fresh prices, so this update will have no effect"));
  94. pyth.updatePriceFeedsIfNecessary{value: cachedPricesUpdateFee}(cachedPricesUpdateData, priceIds, cachedPricesPublishTimes);
  95. }
  96. function testBenchmarkGetPrice() public {
  97. // Set the block timestamp to 0. As prices have < 10 timestamp and staleness
  98. // is set to 60 seconds, the getPrice should work as expected.
  99. vm.warp(0);
  100. pyth.getPrice(priceIds[0]);
  101. }
  102. function testBenchmarkGetUpdateFee() public view {
  103. pyth.getUpdateFee(freshPricesUpdateData);
  104. }
  105. }