@@ -5,6 +5,7 @@ pragma solidity ^0.8.0;
5
5
import "forge-std/Test.sol " ;
6
6
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol " ;
7
7
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol " ;
8
+ import "./utils/PulseTestUtils.t.sol " ;
8
9
import "../contracts/pulse/PulseUpgradeable.sol " ;
9
10
import "../contracts/pulse/IPulse.sol " ;
10
11
import "../contracts/pulse/PulseState.sol " ;
@@ -84,7 +85,7 @@ contract CustomErrorPulseConsumer is IPulseConsumer {
84
85
}
85
86
86
87
// FIXME: this shouldn't be IPulseConsumer.
87
- contract PulseTest is Test , PulseEvents , IPulseConsumer {
88
+ contract PulseTest is Test , PulseEvents , IPulseConsumer , PulseTestUtils {
88
89
ERC1967Proxy public proxy;
89
90
PulseUpgradeable public pulse;
90
91
MockPulseConsumer public consumer;
@@ -97,20 +98,6 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
97
98
uint128 constant DEFAULT_PROVIDER_FEE_PER_GAS = 1 wei ;
98
99
uint128 constant DEFAULT_PROVIDER_BASE_FEE = 1 wei ;
99
100
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
114
101
115
102
function setUp () public {
116
103
owner = address (1 );
@@ -139,102 +126,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
139
126
consumer = new MockPulseConsumer (address (proxy));
140
127
}
141
128
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
-
203
129
// Helper function to calculate total fee
204
130
// FIXME: I think this helper probably needs to take some arguments.
205
131
function calculateTotalFee () internal view returns (uint128 ) {
206
132
return
207
133
pulse.getFee (defaultProvider, CALLBACK_GAS_LIMIT, createPriceIds ());
208
134
}
209
135
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
-
238
136
function testRequestPriceUpdate () public {
239
137
// Set a realistic gas price
240
138
vm.txGasPrice (30 gwei);
@@ -334,7 +232,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
334
232
publishTime
335
233
);
336
234
// FIXME: this test doesn't ensure the Pyth fee is paid.
337
- mockParsePriceFeedUpdates (priceFeeds);
235
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
338
236
339
237
// Create arrays for expected event data
340
238
int64 [] memory expectedPrices = new int64 [](2 );
@@ -405,12 +303,16 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
405
303
uint64 sequenceNumber ,
406
304
bytes32 [] memory priceIds ,
407
305
uint256 publishTime
408
- ) = setupConsumerRequest (address (failingConsumer));
306
+ ) = setupConsumerRequest (
307
+ pulse,
308
+ defaultProvider,
309
+ address (failingConsumer)
310
+ );
409
311
410
312
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
411
313
publishTime
412
314
);
413
- mockParsePriceFeedUpdates (priceFeeds);
315
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
414
316
bytes [] memory updateData = createMockUpdateData (priceFeeds);
415
317
416
318
vm.expectEmit ();
@@ -440,12 +342,16 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
440
342
uint64 sequenceNumber ,
441
343
bytes32 [] memory priceIds ,
442
344
uint256 publishTime
443
- ) = setupConsumerRequest (address (failingConsumer));
345
+ ) = setupConsumerRequest (
346
+ pulse,
347
+ defaultProvider,
348
+ address (failingConsumer)
349
+ );
444
350
445
351
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
446
352
publishTime
447
353
);
448
- mockParsePriceFeedUpdates (priceFeeds);
354
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
449
355
bytes [] memory updateData = createMockUpdateData (priceFeeds);
450
356
451
357
vm.expectEmit ();
@@ -472,13 +378,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
472
378
uint64 sequenceNumber ,
473
379
bytes32 [] memory priceIds ,
474
380
uint256 publishTime
475
- ) = setupConsumerRequest (address (consumer));
381
+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
476
382
477
383
// Setup mock data
478
384
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
479
385
publishTime
480
386
);
481
- mockParsePriceFeedUpdates (priceFeeds);
387
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
482
388
bytes [] memory updateData = createMockUpdateData (priceFeeds);
483
389
484
390
// Try executing with only 100K gas when 1M is required
@@ -508,7 +414,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
508
414
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
509
415
futureTime // Mock price feeds with future timestamp
510
416
);
511
- mockParsePriceFeedUpdates (priceFeeds); // This will make parsePriceFeedUpdates return future-dated prices
417
+ mockParsePriceFeedUpdates (pyth, priceFeeds); // This will make parsePriceFeedUpdates return future-dated prices
512
418
bytes [] memory updateData = createMockUpdateData (priceFeeds);
513
419
514
420
vm.prank (defaultProvider);
@@ -555,12 +461,12 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
555
461
uint64 sequenceNumber ,
556
462
bytes32 [] memory priceIds ,
557
463
uint256 publishTime
558
- ) = setupConsumerRequest (address (consumer));
464
+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
559
465
560
466
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
561
467
publishTime
562
468
);
563
- mockParsePriceFeedUpdates (priceFeeds);
469
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
564
470
bytes [] memory updateData = createMockUpdateData (priceFeeds);
565
471
566
472
// First execution
@@ -747,7 +653,11 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
747
653
uint256 publishTime = block .timestamp ;
748
654
749
655
// Setup request
750
- (uint64 sequenceNumber , , ) = setupConsumerRequest (address (consumer));
656
+ (uint64 sequenceNumber , , ) = setupConsumerRequest (
657
+ pulse,
658
+ defaultProvider,
659
+ address (consumer)
660
+ );
751
661
752
662
// Create different priceIds
753
663
bytes32 [] memory wrongPriceIds = new bytes32 [](2 );
@@ -757,7 +667,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
757
667
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
758
668
publishTime
759
669
);
760
- mockParsePriceFeedUpdates (priceFeeds);
670
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
761
671
bytes [] memory updateData = createMockUpdateData (priceFeeds);
762
672
763
673
// Should revert when trying to execute with wrong priceIds
@@ -923,13 +833,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
923
833
uint64 sequenceNumber ,
924
834
bytes32 [] memory priceIds ,
925
835
uint256 publishTime
926
- ) = setupConsumerRequest (address (consumer));
836
+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
927
837
928
838
// Setup mock data
929
839
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
930
840
publishTime
931
841
);
932
- mockParsePriceFeedUpdates (priceFeeds);
842
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
933
843
bytes [] memory updateData = createMockUpdateData (priceFeeds);
934
844
935
845
// Try to execute with second provider during exclusivity period
@@ -965,13 +875,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
965
875
uint64 sequenceNumber ,
966
876
bytes32 [] memory priceIds ,
967
877
uint256 publishTime
968
- ) = setupConsumerRequest (address (consumer));
878
+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
969
879
970
880
// Setup mock data
971
881
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
972
882
publishTime
973
883
);
974
- mockParsePriceFeedUpdates (priceFeeds);
884
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
975
885
bytes [] memory updateData = createMockUpdateData (priceFeeds);
976
886
977
887
// Wait for exclusivity period to end
@@ -1006,13 +916,13 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
1006
916
uint64 sequenceNumber ,
1007
917
bytes32 [] memory priceIds ,
1008
918
uint256 publishTime
1009
- ) = setupConsumerRequest (address (consumer));
919
+ ) = setupConsumerRequest (pulse, defaultProvider, address (consumer));
1010
920
1011
921
// Setup mock data
1012
922
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
1013
923
publishTime
1014
924
);
1015
- mockParsePriceFeedUpdates (priceFeeds);
925
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
1016
926
bytes [] memory updateData = createMockUpdateData (priceFeeds);
1017
927
1018
928
// Try at 29 seconds (should fail for second provider)
@@ -1080,7 +990,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
1080
990
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
1081
991
SafeCast.toUint64 (block .timestamp )
1082
992
);
1083
- mockParsePriceFeedUpdates (priceFeeds);
993
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
1084
994
updateData = createMockUpdateData (priceFeeds);
1085
995
1086
996
vm.deal (defaultProvider, 2 ether); // Increase ETH allocation to prevent OutOfFunds
@@ -1208,7 +1118,7 @@ contract PulseTest is Test, PulseEvents, IPulseConsumer {
1208
1118
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds (
1209
1119
publishTime
1210
1120
);
1211
- mockParsePriceFeedUpdates (priceFeeds);
1121
+ mockParsePriceFeedUpdates (pyth, priceFeeds);
1212
1122
bytes [] memory updateData = createMockUpdateData (priceFeeds);
1213
1123
1214
1124
// Create 20 requests with some gaps
0 commit comments