Skip to content

Commit 7f91566

Browse files
committed
Merge branch 'main' of github.com:pyth-network/pyth-crosschain into tb/pulse/enforce-min-bal-for-all-active-updated-subs
2 parents a0b53a6 + b44aea5 commit 7f91566

File tree

4 files changed

+26
-52
lines changed

4 files changed

+26
-52
lines changed

target_chains/ethereum/contracts/contracts/pulse/IScheduler.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ interface IScheduler is SchedulerEvents {
4949

5050
/**
5151
* @notice Updates price feeds for a subscription.
52-
* @dev Internally, this verifies the updateData using the Pyth contract and validates update conditions.
52+
* @dev The updateData must contain all price feeds for the subscription, not a subset or superset.
53+
* @dev Internally, the updateData is verified using the Pyth contract and validates update conditions.
54+
* The call will only succeed if the update conditions for the subscription are met.
5355
* @param subscriptionId The ID of the subscription
5456
* @param updateData The price update data from Pyth
55-
* @param priceIds The IDs of the price feeds to update
5657
*/
5758
function updatePriceFeeds(
5859
uint256 subscriptionId,
59-
bytes[] calldata updateData,
60-
bytes32[] calldata priceIds
60+
bytes[] calldata updateData
6161
) external;
6262

6363
/** @notice Returns the price of a price feed without any sanity checks.

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

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,7 @@ abstract contract Scheduler is IScheduler, SchedulerState {
244244

245245
function updatePriceFeeds(
246246
uint256 subscriptionId,
247-
bytes[] calldata updateData,
248-
bytes32[] calldata priceIds
247+
bytes[] calldata updateData
249248
) external override {
250249
uint256 startGas = gasleft();
251250

@@ -260,21 +259,6 @@ abstract contract Scheduler is IScheduler, SchedulerState {
260259
revert InactiveSubscription();
261260
}
262261

263-
// Verify price IDs match subscription length
264-
if (priceIds.length != params.priceIds.length) {
265-
revert InvalidPriceIdsLength(
266-
priceIds.length,
267-
params.priceIds.length
268-
);
269-
}
270-
271-
// Keepers must provide priceIds in the exact same order as defined in the subscription
272-
for (uint8 i = 0; i < priceIds.length; i++) {
273-
if (priceIds[i] != params.priceIds[i]) {
274-
revert InvalidPriceId(priceIds[i], params.priceIds[i]);
275-
}
276-
}
277-
278262
// Get the Pyth contract and parse price updates
279263
IPyth pyth = IPyth(_state.pyth);
280264
uint256 pythFee = pyth.getUpdateFee(updateData);
@@ -292,7 +276,7 @@ abstract contract Scheduler is IScheduler, SchedulerState {
292276
uint64[] memory slots
293277
) = pyth.parsePriceFeedUpdatesWithSlots{value: pythFee}(
294278
updateData,
295-
priceIds,
279+
params.priceIds,
296280
curTime > PAST_TIMESTAMP_MAX_VALIDITY_PERIOD
297281
? curTime - PAST_TIMESTAMP_MAX_VALIDITY_PERIOD
298282
: 0,
@@ -326,7 +310,7 @@ abstract contract Scheduler is IScheduler, SchedulerState {
326310

327311
_storePriceUpdates(subscriptionId, priceFeeds);
328312

329-
_processFeesAndPayKeeper(status, startGas, priceIds.length);
313+
_processFeesAndPayKeeper(status, startGas, params.priceIds.length);
330314

331315
emit PricesUpdated(subscriptionId, latestPublishTime);
332316
}

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

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
252252
bytes[] memory updateData = createMockUpdateData(initialPriceFeeds);
253253

254254
vm.prank(pusher);
255-
scheduler.updatePriceFeeds(subscriptionId, updateData, initialPriceIds);
255+
scheduler.updatePriceFeeds(subscriptionId, updateData);
256256

257257
// Verify initial state: All 3 feeds should be readable
258258
assertTrue(
@@ -830,7 +830,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
830830
emit PricesUpdated(subscriptionId, publishTime1);
831831
vm.prank(pusher);
832832

833-
scheduler.updatePriceFeeds(subscriptionId, updateData1, priceIds);
833+
scheduler.updatePriceFeeds(subscriptionId, updateData1);
834834

835835
// Verify first update
836836
(, SchedulerState.SubscriptionStatus memory status1) = scheduler
@@ -881,7 +881,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
881881
emit PricesUpdated(subscriptionId, publishTime2);
882882
vm.prank(pusher);
883883

884-
scheduler.updatePriceFeeds(subscriptionId, updateData2, priceIds);
884+
scheduler.updatePriceFeeds(subscriptionId, updateData2);
885885

886886
// Verify second update
887887
(, SchedulerState.SubscriptionStatus memory status2) = scheduler
@@ -948,7 +948,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
948948

949949
// Perform update
950950
vm.prank(pusher);
951-
scheduler.updatePriceFeeds(subscriptionId, updateData, params.priceIds);
951+
scheduler.updatePriceFeeds(subscriptionId, updateData);
952952

953953
// Get state after
954954
(, SchedulerState.SubscriptionStatus memory statusAfter) = scheduler
@@ -1050,7 +1050,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
10501050
// Expect revert due to insufficient balance for total fee
10511051
vm.expectRevert(abi.encodeWithSelector(InsufficientBalance.selector));
10521052
vm.prank(pusher);
1053-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1053+
scheduler.updatePriceFeeds(subscriptionId, updateData);
10541054
}
10551055

10561056
function testUpdatePriceFeedsRevertsOnHeartbeatUpdateConditionNotMet()
@@ -1073,15 +1073,14 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
10731073
uint256 fundAmount = 1 ether;
10741074
scheduler.addFunds{value: fundAmount}(subscriptionId);
10751075
// First update to set initial timestamp
1076-
bytes32[] memory priceIds = createPriceIds();
10771076
uint64 publishTime1 = SafeCast.toUint64(block.timestamp);
10781077
PythStructs.PriceFeed[] memory priceFeeds1;
10791078
uint64[] memory slots1;
10801079
(priceFeeds1, slots1) = createMockPriceFeedsWithSlots(publishTime1, 2);
10811080
mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots1);
10821081
bytes[] memory updateData1 = createMockUpdateData(priceFeeds1);
10831082
vm.prank(pusher);
1084-
scheduler.updatePriceFeeds(subscriptionId, updateData1, priceIds);
1083+
scheduler.updatePriceFeeds(subscriptionId, updateData1);
10851084

10861085
// Prepare second update within heartbeat interval
10871086
vm.warp(block.timestamp + 30); // Advance time by 30 seconds (less than 60)
@@ -1097,7 +1096,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
10971096
abi.encodeWithSelector(UpdateConditionsNotMet.selector)
10981097
);
10991098
vm.prank(pusher);
1100-
scheduler.updatePriceFeeds(subscriptionId, updateData2, priceIds);
1099+
scheduler.updatePriceFeeds(subscriptionId, updateData2);
11011100
}
11021101

11031102
function testUpdatePriceFeedsRevertsOnDeviationUpdateConditionNotMet()
@@ -1121,15 +1120,14 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
11211120
scheduler.addFunds{value: fundAmount}(subscriptionId);
11221121

11231122
// First update to set initial price
1124-
bytes32[] memory priceIds = createPriceIds();
11251123
uint64 publishTime1 = SafeCast.toUint64(block.timestamp);
11261124
PythStructs.PriceFeed[] memory priceFeeds1;
11271125
uint64[] memory slots;
11281126
(priceFeeds1, slots) = createMockPriceFeedsWithSlots(publishTime1, 2);
11291127
mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds1, slots);
11301128
bytes[] memory updateData1 = createMockUpdateData(priceFeeds1);
11311129
vm.prank(pusher);
1132-
scheduler.updatePriceFeeds(subscriptionId, updateData1, priceIds);
1130+
scheduler.updatePriceFeeds(subscriptionId, updateData1);
11331131

11341132
// Prepare second update with price deviation less than threshold (e.g., 50 bps)
11351133
vm.warp(block.timestamp + 1000); // Advance time significantly (doesn't matter for deviation)
@@ -1160,7 +1158,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
11601158
abi.encodeWithSelector(UpdateConditionsNotMet.selector)
11611159
);
11621160
vm.prank(pusher);
1163-
scheduler.updatePriceFeeds(subscriptionId, updateData2, priceIds);
1161+
scheduler.updatePriceFeeds(subscriptionId, updateData2);
11641162
}
11651163

11661164
function testUpdatePriceFeedsRevertsOnOlderTimestamp() public {
@@ -1173,7 +1171,6 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
11731171
scheduler.addFunds{value: fundAmount}(subscriptionId);
11741172

11751173
// First update to establish last updated timestamp
1176-
bytes32[] memory priceIds = createPriceIds();
11771174
uint64 publishTime1 = SafeCast.toUint64(block.timestamp);
11781175
PythStructs.PriceFeed[] memory priceFeeds1;
11791176
uint64[] memory slots1;
@@ -1182,7 +1179,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
11821179
bytes[] memory updateData1 = createMockUpdateData(priceFeeds1);
11831180

11841181
vm.prank(pusher);
1185-
scheduler.updatePriceFeeds(subscriptionId, updateData1, priceIds);
1182+
scheduler.updatePriceFeeds(subscriptionId, updateData1);
11861183

11871184
// Prepare second update with an older timestamp
11881185
uint64 publishTime2 = publishTime1 - 10; // Timestamp older than the first update
@@ -1204,7 +1201,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
12041201

12051202
// Attempt to update price feeds
12061203
vm.prank(pusher);
1207-
scheduler.updatePriceFeeds(subscriptionId, updateData2, priceIds);
1204+
scheduler.updatePriceFeeds(subscriptionId, updateData2);
12081205
}
12091206

12101207
function testUpdatePriceFeedsRevertsOnMismatchedSlots() public {
@@ -1217,7 +1214,6 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
12171214
scheduler.addFunds{value: fundAmount}(subscriptionId);
12181215

12191216
// Create two price feeds with same timestamp but different slots
1220-
bytes32[] memory priceIds = createPriceIds(2);
12211217
uint64 publishTime = SafeCast.toUint64(block.timestamp);
12221218
PythStructs.PriceFeed[] memory priceFeeds = new PythStructs.PriceFeed[](
12231219
2
@@ -1239,7 +1235,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
12391235

12401236
// Attempt to update price feeds
12411237
vm.prank(pusher);
1242-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1238+
scheduler.updatePriceFeeds(subscriptionId, updateData);
12431239
}
12441240

12451241
function testUpdateSubscriptionEnforcesMinimumBalance() public {
@@ -1425,7 +1421,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
14251421
bytes[] memory updateData = createMockUpdateData(priceFeeds);
14261422

14271423
vm.prank(pusher);
1428-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1424+
scheduler.updatePriceFeeds(subscriptionId, updateData);
14291425

14301426
// Get all latest prices (empty priceIds array)
14311427
bytes32[] memory emptyPriceIds = new bytes32[](0);
@@ -1467,7 +1463,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
14671463
bytes[] memory updateData = createMockUpdateData(priceFeeds);
14681464

14691465
vm.prank(pusher);
1470-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1466+
scheduler.updatePriceFeeds(subscriptionId, updateData);
14711467

14721468
// Get only the first price feed
14731469
bytes32[] memory selectedPriceIds = new bytes32[](1);
@@ -1520,10 +1516,9 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
15201516
(priceFeeds, slots) = createMockPriceFeedsWithSlots(publishTime, 2);
15211517
mockParsePriceFeedUpdatesWithSlots(pyth, priceFeeds, slots);
15221518
bytes[] memory updateData = createMockUpdateData(priceFeeds);
1523-
bytes32[] memory priceIds = params.priceIds;
15241519

15251520
vm.prank(pusher);
1526-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1521+
scheduler.updatePriceFeeds(subscriptionId, updateData);
15271522

15281523
// Try to access from a non-whitelisted address (should succeed)
15291524
address randomUser = address(0xdead);
@@ -1567,7 +1562,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
15671562
bytes[] memory updateData = createMockUpdateData(priceFeeds);
15681563

15691564
vm.prank(pusher);
1570-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1565+
scheduler.updatePriceFeeds(subscriptionId, updateData);
15711566

15721567
// Try to access from a non-whitelisted address (should fail)
15731568
vm.startPrank(address(0xdead));
@@ -1634,7 +1629,7 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils {
16341629
bytes[] memory updateData = createMockUpdateData(priceFeeds);
16351630

16361631
vm.prank(pusher);
1637-
scheduler.updatePriceFeeds(subscriptionId, updateData, priceIds);
1632+
scheduler.updatePriceFeeds(subscriptionId, updateData);
16381633

16391634
// Get EMA prices
16401635
bytes32[] memory emptyPriceIds = new bytes32[](0);

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ contract PulseSchedulerGasBenchmark is Test, PulseSchedulerTestUtils {
7676
uint256 startGas = gasleft();
7777
scheduler.updatePriceFeeds(
7878
subscriptionId,
79-
createMockUpdateData(newPriceFeeds),
80-
params.priceIds
79+
createMockUpdateData(newPriceFeeds)
8180
);
8281
uint256 updateGasUsed = startGas - gasleft();
8382

@@ -114,10 +113,6 @@ contract PulseSchedulerGasBenchmark is Test, PulseSchedulerTestUtils {
114113
address(manager)
115114
);
116115

117-
// Fetch the price IDs
118-
(SchedulerState.SubscriptionParams memory params, ) = scheduler
119-
.getSubscription(subscriptionId);
120-
121116
// Create initial price feed updates
122117
uint64 publishTime = SafeCast.toUint64(block.timestamp);
123118
PythStructs.PriceFeed[] memory priceFeeds;
@@ -133,7 +128,7 @@ contract PulseSchedulerGasBenchmark is Test, PulseSchedulerTestUtils {
133128

134129
// Update the price feeds. We should have enough balance to cover the update
135130
// because we funded the subscription with the minimum balance during creation.
136-
scheduler.updatePriceFeeds(subscriptionId, updateData, params.priceIds);
131+
scheduler.updatePriceFeeds(subscriptionId, updateData);
137132
return subscriptionId;
138133
}
139134

0 commit comments

Comments
 (0)