@@ -5,6 +5,7 @@ pragma solidity ^0.8.0;
55import "forge-std/Test.sol " ;
66import "@pythnetwork/pyth-sdk-solidity/IPyth.sol " ;
77import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol " ;
8+ import "./utils/PulseTestUtils.t.sol " ;
89import "../contracts/pulse/PulseUpgradeable.sol " ;
910import "../contracts/pulse/IPulse.sol " ;
1011import "../contracts/pulse/PulseState.sol " ;
@@ -84,7 +85,7 @@ contract CustomErrorPulseConsumer is IPulseConsumer {
8485}
8586
8687// FIXME: this shouldn't be IPulseConsumer.
87- contract PulseTest is Test , PulseEvents , IPulseConsumer {
88+ contract PulseTest is Test , PulseEvents , IPulseConsumer , PulseTestUtils {
8889 ERC1967Proxy public proxy;
8990 PulseUpgradeable public pulse;
9091 MockPulseConsumer public consumer;
@@ -97,20 +98,6 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
9798 uint128 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei ;
9899 uint128 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei ;
99100 uint128 constant DEFAULT_PROVIDER_FEE_PER_FEED = 10 wei ;
100- uint constant MOCK_PYTH_FEE_PER_FEED = 10 wei ;
101-
102- uint128 constant CALLBACK_GAS_LIMIT = 1_000_000 ;
103- bytes32 constant BTC_PRICE_FEED_ID =
104- 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43 ;
105- bytes32 constant ETH_PRICE_FEED_ID =
106- 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace ;
107-
108- // Price feed constants
109- int8 constant MOCK_PRICE_FEED_EXPO = - 8 ;
110- int64 constant MOCK_BTC_PRICE = 5_000_000_000_000 ; // $50,000
111- int64 constant MOCK_ETH_PRICE = 300_000_000_000 ; // $3,000
112- uint64 constant MOCK_BTC_CONF = 10_000_000_000 ; // $100
113- uint64 constant MOCK_ETH_CONF = 5_000_000_000 ; // $50
114101
115102 function setUp () public {
116103 owner = address (1 );
@@ -139,102 +126,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
139126 consumer = new MockPulseConsumer (address (proxy));
140127 }
141128
142- // Helper function to create price IDs array
143- function createPriceIds () internal pure returns (bytes32 [] memory ) {
144- bytes32 [] memory priceIds = new bytes32 [](2 );
145- priceIds[0 ] = BTC_PRICE_FEED_ID;
146- priceIds[1 ] = ETH_PRICE_FEED_ID;
147- return priceIds;
148- }
149-
150- // Helper function to create mock price feeds
151- function createMockPriceFeeds (
152- uint256 publishTime
153- ) internal pure returns (PythStructs.PriceFeed[] memory ) {
154- PythStructs.PriceFeed[] memory priceFeeds = new PythStructs.PriceFeed [](
155- 2
156- );
157-
158- priceFeeds[0 ].id = BTC_PRICE_FEED_ID;
159- priceFeeds[0 ].price.price = MOCK_BTC_PRICE;
160- priceFeeds[0 ].price.conf = MOCK_BTC_CONF;
161- priceFeeds[0 ].price.expo = MOCK_PRICE_FEED_EXPO;
162- priceFeeds[0 ].price.publishTime = publishTime;
163-
164- priceFeeds[1 ].id = ETH_PRICE_FEED_ID;
165- priceFeeds[1 ].price.price = MOCK_ETH_PRICE;
166- priceFeeds[1 ].price.conf = MOCK_ETH_CONF;
167- priceFeeds[1 ].price.expo = MOCK_PRICE_FEED_EXPO;
168- priceFeeds[1 ].price.publishTime = publishTime;
169-
170- return priceFeeds;
171- }
172-
173- // Helper function to mock Pyth response
174- function mockParsePriceFeedUpdates (
175- PythStructs.PriceFeed[] memory priceFeeds
176- ) internal {
177- uint expectedFee = MOCK_PYTH_FEE_PER_FEED * priceFeeds.length ;
178-
179- vm.mockCall (
180- address (pyth),
181- abi.encodeWithSelector (IPyth.getUpdateFee.selector ),
182- abi.encode (expectedFee)
183- );
184-
185- vm.mockCall (
186- address (pyth),
187- expectedFee,
188- abi.encodeWithSelector (IPyth.parsePriceFeedUpdates.selector ),
189- abi.encode (priceFeeds)
190- );
191- }
192-
193- // Helper function to create mock update data
194- function createMockUpdateData (
195- PythStructs.PriceFeed[] memory priceFeeds
196- ) internal pure returns (bytes [] memory ) {
197- bytes [] memory updateData = new bytes [](2 );
198- updateData[0 ] = abi.encode (priceFeeds[0 ]);
199- updateData[1 ] = abi.encode (priceFeeds[1 ]);
200- return updateData;
201- }
202-
203129 // Helper function to calculate total fee
204130 // FIXME: I think this helper probably needs to take some arguments.
205131 function calculateTotalFee () internal view returns (uint128 ) {
206132 return
207133 pulse.getFee (defaultProvider, CALLBACK_GAS_LIMIT, createPriceIds ());
208134 }
209135
210- // Helper function to setup consumer request
211- function setupConsumerRequest (
212- address consumerAddress
213- )
214- internal
215- returns (
216- uint64 sequenceNumber ,
217- bytes32 [] memory priceIds ,
218- uint64 publishTime
219- )
220- {
221- priceIds = createPriceIds ();
222- publishTime = SafeCast.toUint64 (block .timestamp );
223- vm.deal (consumerAddress, 1 gwei);
224-
225- uint128 totalFee = calculateTotalFee ();
226-
227- vm.prank (consumerAddress);
228- sequenceNumber = pulse.requestPriceUpdatesWithCallback {value: totalFee}(
229- defaultProvider,
230- publishTime,
231- priceIds,
232- CALLBACK_GAS_LIMIT
233- );
234-
235- return (sequenceNumber, priceIds, publishTime);
236- }
237-
238136 function testRequestPriceUpdate () public {
239137 // Set a realistic gas price
240138 vm.txGasPrice (30 gwei);
@@ -334,7 +232,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
334232 publishTime
335233 );
336234 // FIXME: this test doesn't ensure the Pyth fee is paid.
337- mockParsePriceFeedUpdates (priceFeeds);
235+ mockParsePriceFeedUpdates (pyth, priceFeeds);
338236
339237 // Create arrays for expected event data
340238 int64 [] memory expectedPrices = new int64 [](2 );
@@ -405,12 +303,16 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
405303 uint64 sequenceNumber ,
406304 bytes32 [] memory priceIds ,
407305 uint256 publishTime
408- ) = setupConsumerRequest (address (failingConsumer));
306+ ) = setupConsumerRequest (
307+ pulse,
308+ defaultProvider,
309+ address (failingConsumer)
310+ );
409311
410312 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
411313 publishTime
412314 );
413- mockParsePriceFeedUpdates (priceFeeds);
315+ mockParsePriceFeedUpdates (pyth, priceFeeds);
414316 bytes [] memory updateData = createMockUpdateData (priceFeeds);
415317
416318 vm.expectEmit ();
@@ -440,12 +342,16 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
440342 uint64 sequenceNumber ,
441343 bytes32 [] memory priceIds ,
442344 uint256 publishTime
443- ) = setupConsumerRequest (address (failingConsumer));
345+ ) = setupConsumerRequest (
346+ pulse,
347+ defaultProvider,
348+ address (failingConsumer)
349+ );
444350
445351 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
446352 publishTime
447353 );
448- mockParsePriceFeedUpdates (priceFeeds);
354+ mockParsePriceFeedUpdates (pyth, priceFeeds);
449355 bytes [] memory updateData = createMockUpdateData (priceFeeds);
450356
451357 vm.expectEmit ();
@@ -472,13 +378,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
472378 uint64 sequenceNumber ,
473379 bytes32 [] memory priceIds ,
474380 uint256 publishTime
475- ) = setupConsumerRequest (address (consumer));
381+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
476382
477383 // Setup mock data
478384 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
479385 publishTime
480386 );
481- mockParsePriceFeedUpdates (priceFeeds);
387+ mockParsePriceFeedUpdates (pyth, priceFeeds);
482388 bytes [] memory updateData = createMockUpdateData (priceFeeds);
483389
484390 // Try executing with only 100K gas when 1M is required
@@ -508,7 +414,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
508414 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
509415 futureTime // Mock price feeds with future timestamp
510416 );
511- mockParsePriceFeedUpdates (priceFeeds); // This will make parsePriceFeedUpdates return future-dated prices
417+ mockParsePriceFeedUpdates (pyth, priceFeeds); // This will make parsePriceFeedUpdates return future-dated prices
512418 bytes [] memory updateData = createMockUpdateData (priceFeeds);
513419
514420 vm.prank (defaultProvider);
@@ -555,12 +461,12 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
555461 uint64 sequenceNumber ,
556462 bytes32 [] memory priceIds ,
557463 uint256 publishTime
558- ) = setupConsumerRequest (address (consumer));
464+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
559465
560466 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
561467 publishTime
562468 );
563- mockParsePriceFeedUpdates (priceFeeds);
469+ mockParsePriceFeedUpdates (pyth, priceFeeds);
564470 bytes [] memory updateData = createMockUpdateData (priceFeeds);
565471
566472 // First execution
@@ -747,7 +653,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
747653 uint256 publishTime = block .timestamp ;
748654
749655 // Setup request
750- (uint64 sequenceNumber , , ) = setupConsumerRequest (address (consumer));
656+ (uint64 sequenceNumber , , ) = setupConsumerRequest (
657+ pulse,
658+ defaultProvider,
659+ address (consumer)
660+ );
751661
752662 // Create different priceIds
753663 bytes32 [] memory wrongPriceIds = new bytes32 [](2 );
@@ -757,7 +667,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
757667 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
758668 publishTime
759669 );
760- mockParsePriceFeedUpdates (priceFeeds);
670+ mockParsePriceFeedUpdates (pyth, priceFeeds);
761671 bytes [] memory updateData = createMockUpdateData (priceFeeds);
762672
763673 // Should revert when trying to execute with wrong priceIds
@@ -923,13 +833,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
923833 uint64 sequenceNumber ,
924834 bytes32 [] memory priceIds ,
925835 uint256 publishTime
926- ) = setupConsumerRequest (address (consumer));
836+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
927837
928838 // Setup mock data
929839 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
930840 publishTime
931841 );
932- mockParsePriceFeedUpdates (priceFeeds);
842+ mockParsePriceFeedUpdates (pyth, priceFeeds);
933843 bytes [] memory updateData = createMockUpdateData (priceFeeds);
934844
935845 // Try to execute with second provider during exclusivity period
@@ -965,13 +875,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
965875 uint64 sequenceNumber ,
966876 bytes32 [] memory priceIds ,
967877 uint256 publishTime
968- ) = setupConsumerRequest (address (consumer));
878+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
969879
970880 // Setup mock data
971881 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
972882 publishTime
973883 );
974- mockParsePriceFeedUpdates (priceFeeds);
884+ mockParsePriceFeedUpdates (pyth, priceFeeds);
975885 bytes [] memory updateData = createMockUpdateData (priceFeeds);
976886
977887 // Wait for exclusivity period to end
@@ -1006,13 +916,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
1006916 uint64 sequenceNumber ,
1007917 bytes32 [] memory priceIds ,
1008918 uint256 publishTime
1009- ) = setupConsumerRequest (address (consumer));
919+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
1010920
1011921 // Setup mock data
1012922 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
1013923 publishTime
1014924 );
1015- mockParsePriceFeedUpdates (priceFeeds);
925+ mockParsePriceFeedUpdates (pyth, priceFeeds);
1016926 bytes [] memory updateData = createMockUpdateData (priceFeeds);
1017927
1018928 // Try at 29 seconds (should fail for second provider)
@@ -1080,7 +990,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
1080990 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
1081991 SafeCast.toUint64 (block .timestamp )
1082992 );
1083- mockParsePriceFeedUpdates (priceFeeds);
993+ mockParsePriceFeedUpdates (pyth, priceFeeds);
1084994 updateData = createMockUpdateData (priceFeeds);
1085995
1086996 vm.deal (defaultProvider, 2 ether); // Increase ETH allocation to prevent OutOfFunds
@@ -1208,7 +1118,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
12081118 PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
12091119 publishTime
12101120 );
1211- mockParsePriceFeedUpdates (priceFeeds);
1121+ mockParsePriceFeedUpdates (pyth, priceFeeds);
12121122 bytes [] memory updateData = createMockUpdateData (priceFeeds);
12131123
12141124 // Create 20 requests with some gaps
0 commit comments