Skip to content

Commit 0237566

Browse files
Update PulseScheduler tests to match new implementation
Co-Authored-By: Tejas Badadare <[email protected]>
1 parent 5c5b30a commit 0237566

File tree

2 files changed

+149
-39
lines changed

2 files changed

+149
-39
lines changed

target_chains/ethereum/contracts/contracts/pulse/scheduler/Scheduler.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,17 @@ abstract contract Scheduler is IScheduler, SchedulerState {
512512
return (subscriptionIds, subscriptionParams, totalCount);
513513
}
514514

515+
/**
516+
* @notice Returns the minimum balance an active subscription of a given size needs to hold.
517+
* @param numPriceFeeds The number of price feeds in the subscription.
518+
*/
519+
function getMinimumBalance(
520+
uint8 numPriceFeeds
521+
) external view override returns (uint256 minimumBalance) {
522+
// Simple implementation - minimum balance is 0.01 ETH per price feed
523+
return numPriceFeeds * 0.01 ether;
524+
}
525+
515526
// ACCESS CONTROL MODIFIERS
516527

517528
modifier onlyManager(uint256 subscriptionId) {

target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol

Lines changed: 138 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,32 +19,37 @@ contract MockReader {
1919
_scheduler = scheduler;
2020
}
2121

22-
function getLatestPrices(
22+
function getPricesUnsafe(
2323
uint256 subscriptionId,
2424
bytes32[] memory priceIds
25-
) external view returns (PythStructs.PriceFeed[] memory) {
26-
return IScheduler(_scheduler).getLatestPrices(subscriptionId, priceIds);
25+
) external view returns (PythStructs.Price[] memory) {
26+
return IScheduler(_scheduler).getPricesUnsafe(subscriptionId, priceIds);
27+
}
28+
29+
function getEmaPriceUnsafe(
30+
uint256 subscriptionId,
31+
bytes32[] memory priceIds
32+
) external view returns (PythStructs.Price[] memory) {
33+
return IScheduler(_scheduler).getEmaPriceUnsafe(subscriptionId, priceIds);
2734
}
2835

2936
function verifyPriceFeeds(
3037
uint256 subscriptionId,
3138
bytes32[] memory priceIds,
3239
PythStructs.PriceFeed[] memory expectedFeeds
3340
) external view returns (bool) {
34-
PythStructs.PriceFeed[] memory actualFeeds = IScheduler(_scheduler)
35-
.getLatestPrices(subscriptionId, priceIds);
41+
PythStructs.Price[] memory actualPrices = IScheduler(_scheduler)
42+
.getPricesUnsafe(subscriptionId, priceIds);
3643

37-
if (actualFeeds.length != expectedFeeds.length) {
44+
if (actualPrices.length != expectedFeeds.length) {
3845
return false;
3946
}
4047

41-
for (uint i = 0; i < actualFeeds.length; i++) {
48+
for (uint i = 0; i < actualPrices.length; i++) {
4249
if (
43-
actualFeeds[i].id != expectedFeeds[i].id ||
44-
actualFeeds[i].price.price != expectedFeeds[i].price.price ||
45-
actualFeeds[i].price.conf != expectedFeeds[i].price.conf ||
46-
actualFeeds[i].price.publishTime !=
47-
expectedFeeds[i].price.publishTime
50+
actualPrices[i].price != expectedFeeds[i].price.price ||
51+
actualPrices[i].conf != expectedFeeds[i].price.conf ||
52+
actualPrices[i].publishTime != expectedFeeds[i].price.publishTime
4853
) {
4954
return false;
5055
}
@@ -100,8 +105,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
100105
});
101106

102107
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
103-
maxGasPrice: 100 gwei,
104-
maxGasLimit: 1_000_000
108+
maxGasMultiplierCapPct: 10_000,
109+
maxFeeMultiplierCapPct: 10_000
105110
});
106111

107112
SchedulerState.SubscriptionParams memory params = SchedulerState
@@ -152,9 +157,9 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
152157
"Deviation threshold mismatch"
153158
);
154159
assertEq(
155-
storedParams.gasConfig.maxGasPrice,
156-
100 gwei,
157-
"Max gas price mismatch"
160+
storedParams.gasConfig.maxGasMultiplierCapPct,
161+
10_000,
162+
"Max gas multiplier mismatch"
158163
);
159164

160165
assertTrue(status.isActive, "Subscription should be active");
@@ -181,8 +186,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
181186

182187
SchedulerState.GasConfig memory newGasConfig = SchedulerState
183188
.GasConfig({
184-
maxGasPrice: 200 gwei, // Changed from 100 gwei
185-
maxGasLimit: 2_000_000 // Changed from 1_000_000
189+
maxGasMultiplierCapPct: 20_000, // Changed from 10_000
190+
maxFeeMultiplierCapPct: 20_000 // Changed from 10_000
186191
});
187192

188193
SchedulerState.SubscriptionParams memory newParams = SchedulerState
@@ -230,9 +235,9 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
230235
"Deviation threshold mismatch"
231236
);
232237
assertEq(
233-
storedParams.gasConfig.maxGasPrice,
234-
200 gwei,
235-
"Max gas price mismatch"
238+
storedParams.gasConfig.maxGasMultiplierCapPct,
239+
20_000,
240+
"Max gas multiplier mismatch"
236241
);
237242
}
238243

@@ -583,7 +588,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
583588
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
584589
}
585590

586-
function testGetLatestPricesAllFeeds() public {
591+
function testGetPricesUnsafeAllFeeds() public {
587592
// First add a subscription, funds, and update price feeds
588593
uint256 subscriptionId = addTestSubscription();
589594
uint256 fundAmount = 1 ether;
@@ -602,7 +607,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
602607

603608
// Get all latest prices (empty priceIds array)
604609
bytes32[] memory emptyPriceIds = new bytes32[](0);
605-
PythStructs.PriceFeed[] memory latestPrices = scheduler.getLatestPrices(
610+
PythStructs.Price[] memory latestPrices = scheduler.getPricesUnsafe(
606611
subscriptionId,
607612
emptyPriceIds
608613
);
@@ -621,7 +626,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
621626
);
622627
}
623628

624-
function testGetLatestPricesSelectiveFeeds() public {
629+
function testGetPricesUnsafeSelectiveFeeds() public {
625630
// First add a subscription with 3 price feeds, funds, and update price feeds
626631
uint256 subscriptionId = addTestSubscriptionWithFeeds(3);
627632
uint256 fundAmount = 1 ether;
@@ -643,7 +648,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
643648
bytes32[] memory selectedPriceIds = new bytes32[](1);
644649
selectedPriceIds[0] = priceIds[0];
645650

646-
PythStructs.PriceFeed[] memory latestPrices = scheduler.getLatestPrices(
651+
PythStructs.Price[] memory latestPrices = scheduler.getPricesUnsafe(
647652
subscriptionId,
648653
selectedPriceIds
649654
);
@@ -681,8 +686,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
681686
});
682687

683688
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
684-
maxGasPrice: 100 gwei,
685-
maxGasLimit: 1_000_000
689+
maxGasMultiplierCapPct: 10_000,
690+
maxFeeMultiplierCapPct: 10_000
686691
});
687692

688693
SchedulerState.SubscriptionParams memory params = SchedulerState
@@ -717,7 +722,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
717722

718723
// Should not revert since whitelist is disabled
719724
// We'll just check that it doesn't revert
720-
scheduler.getLatestPrices(subscriptionId, emptyPriceIds);
725+
scheduler.getPricesUnsafe(subscriptionId, emptyPriceIds);
721726
vm.stopPrank();
722727

723728
// Verify the data is correct
@@ -726,6 +731,61 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
726731
"Price feeds verification failed"
727732
);
728733
}
734+
735+
function testGetEmaPriceUnsafe() public {
736+
// First add a subscription, funds, and update price feeds
737+
uint256 subscriptionId = addTestSubscription();
738+
uint256 fundAmount = 1 ether;
739+
scheduler.addFunds{value: fundAmount}(subscriptionId);
740+
741+
bytes32[] memory priceIds = createPriceIds();
742+
uint64 publishTime = SafeCast.toUint64(block.timestamp);
743+
PythStructs.PriceFeed[] memory priceFeeds = createMockPriceFeeds(
744+
publishTime
745+
);
746+
747+
// Ensure EMA prices are set in the mock price feeds
748+
for (uint i = 0; i < priceFeeds.length; i++) {
749+
priceFeeds[i].emaPrice.price = priceFeeds[i].price.price * 2; // Make EMA price different for testing
750+
priceFeeds[i].emaPrice.conf = priceFeeds[i].price.conf;
751+
priceFeeds[i].emaPrice.publishTime = publishTime;
752+
priceFeeds[i].emaPrice.expo = priceFeeds[i].price.expo;
753+
}
754+
755+
mockParsePriceFeedUpdates(pyth, priceFeeds);
756+
bytes[] memory updateData = createMockUpdateData(priceFeeds);
757+
758+
vm.prank(pusher);
759+
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
760+
761+
// Get EMA prices
762+
bytes32[] memory emptyPriceIds = new bytes32[](0);
763+
PythStructs.Price[] memory emaPrices = scheduler.getEmaPriceUnsafe(
764+
subscriptionId,
765+
emptyPriceIds
766+
);
767+
768+
// Verify all EMA prices were returned
769+
assertEq(
770+
emaPrices.length,
771+
priceIds.length,
772+
"Should return all EMA prices"
773+
);
774+
775+
// Verify EMA price values
776+
for (uint i = 0; i < emaPrices.length; i++) {
777+
assertEq(
778+
emaPrices[i].price,
779+
priceFeeds[i].emaPrice.price,
780+
"EMA price value mismatch"
781+
);
782+
assertEq(
783+
emaPrices[i].publishTime,
784+
priceFeeds[i].emaPrice.publishTime,
785+
"EMA price publish time mismatch"
786+
);
787+
}
788+
}
729789

730790
function testGetActiveSubscriptions() public {
731791
// Add multiple subscriptions with the test contract as manager
@@ -750,8 +810,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
750810
});
751811

752812
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
753-
maxGasPrice: 100 gwei,
754-
maxGasLimit: 1_000_000
813+
maxGasMultiplierCapPct: 10_000,
814+
maxFeeMultiplierCapPct: 10_000
755815
});
756816

757817
SchedulerState.SubscriptionParams memory params = SchedulerState
@@ -770,8 +830,9 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
770830
vm.prank(owner);
771831
(
772832
uint256[] memory activeIds,
773-
SchedulerState.SubscriptionParams[] memory activeParams
774-
) = scheduler.getActiveSubscriptions();
833+
SchedulerState.SubscriptionParams[] memory activeParams,
834+
uint256 totalCount
835+
) = scheduler.getActiveSubscriptions(0, 10); // Start at index 0, get up to 10 results
775836

776837
// Verify active subscriptions
777838
assertEq(activeIds.length, 3, "Should have 3 active subscriptions");
@@ -780,6 +841,11 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
780841
3,
781842
"Should have 3 active subscription params"
782843
);
844+
assertEq(
845+
totalCount,
846+
3,
847+
"Total count should be 3"
848+
);
783849

784850
// Verify subscription params
785851
for (uint i = 0; i < activeIds.length; i++) {
@@ -800,6 +866,39 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
800866
"Heartbeat seconds mismatch"
801867
);
802868
}
869+
870+
// Test pagination - get only the first subscription
871+
vm.prank(owner);
872+
(
873+
uint256[] memory firstPageIds,
874+
SchedulerState.SubscriptionParams[] memory firstPageParams,
875+
uint256 firstPageTotal
876+
) = scheduler.getActiveSubscriptions(0, 1);
877+
878+
assertEq(firstPageIds.length, 1, "Should have 1 subscription in first page");
879+
assertEq(firstPageTotal, 3, "Total count should still be 3");
880+
881+
// Test pagination - get the second page
882+
vm.prank(owner);
883+
(
884+
uint256[] memory secondPageIds,
885+
SchedulerState.SubscriptionParams[] memory secondPageParams,
886+
uint256 secondPageTotal
887+
) = scheduler.getActiveSubscriptions(1, 2);
888+
889+
assertEq(secondPageIds.length, 2, "Should have 2 subscriptions in second page");
890+
assertEq(secondPageTotal, 3, "Total count should still be 3");
891+
892+
// Test pagination - start index beyond total count
893+
vm.prank(owner);
894+
(
895+
uint256[] memory emptyPageIds,
896+
SchedulerState.SubscriptionParams[] memory emptyPageParams,
897+
uint256 emptyPageTotal
898+
) = scheduler.getActiveSubscriptions(10, 10);
899+
900+
assertEq(emptyPageIds.length, 0, "Should have 0 subscriptions when start index is beyond total");
901+
assertEq(emptyPageTotal, 3, "Total count should still be 3");
803902
}
804903

805904
// Helper function to add a test subscription
@@ -817,8 +916,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
817916
});
818917

819918
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
820-
maxGasPrice: 100 gwei,
821-
maxGasLimit: 1_000_000
919+
maxGasMultiplierCapPct: 10_000,
920+
maxFeeMultiplierCapPct: 10_000
822921
});
823922

824923
SchedulerState.SubscriptionParams memory params = SchedulerState
@@ -850,8 +949,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
850949
});
851950

852951
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
853-
maxGasPrice: 100 gwei,
854-
maxGasLimit: 1_000_000
952+
maxGasMultiplierCapPct: 10_000,
953+
maxFeeMultiplierCapPct: 10_000
855954
});
856955

857956
SchedulerState.SubscriptionParams memory params = SchedulerState
@@ -875,8 +974,8 @@ contract SchedulerTest is Test, SchedulerEvents, PulseTestUtils {
875974
readerWhitelist[0] = address(reader);
876975

877976
SchedulerState.GasConfig memory gasConfig = SchedulerState.GasConfig({
878-
maxGasPrice: 100 gwei,
879-
maxGasLimit: 1_000_000
977+
maxGasMultiplierCapPct: 10_000,
978+
maxFeeMultiplierCapPct: 10_000
880979
});
881980

882981
SchedulerState.SubscriptionParams memory params = SchedulerState

0 commit comments

Comments
 (0)