Skip to content

Commit ca1200a

Browse files
feat(pulse-scheduler): add permanent subscriptions, allow permissionless funding, clean up tests (#2593)
* feat: add permanent subscriptions, clean up tests * fix: disallow config and whitelist updates for permanent subs * fix: disallow updating isActive for permanent subscriptions * doc: clarify comment
1 parent 4047cbd commit ca1200a

File tree

4 files changed

+412
-236
lines changed

4 files changed

+412
-236
lines changed

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

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ abstract contract Scheduler is IScheduler, SchedulerState {
9898
bool wasActive = currentParams.isActive;
9999
bool willBeActive = newParams.isActive;
100100

101+
// Check for permanent subscription restrictions
102+
if (currentParams.isPermanent) {
103+
_validatePermanentSubscriptionUpdate(currentParams, newParams);
104+
}
105+
101106
// If subscription is inactive and will remain inactive, no need to validate parameters
102107
if (!wasActive && !willBeActive) {
103108
// Update subscription parameters
@@ -385,6 +390,103 @@ abstract contract Scheduler is IScheduler, SchedulerState {
385390
revert UpdateConditionsNotMet();
386391
}
387392

393+
/**
394+
* @notice Internal helper to validate modifications to a permanent subscription.
395+
* @param currentParams The current subscription parameters (storage).
396+
* @param newParams The proposed new subscription parameters (memory).
397+
*/
398+
function _validatePermanentSubscriptionUpdate(
399+
SubscriptionParams storage currentParams,
400+
SubscriptionParams memory newParams
401+
) internal view {
402+
// Cannot disable isPermanent flag once set
403+
if (!newParams.isPermanent) {
404+
revert IllegalPermanentSubscriptionModification();
405+
}
406+
407+
// Cannot deactivate a permanent subscription
408+
if (!newParams.isActive) {
409+
revert IllegalPermanentSubscriptionModification();
410+
}
411+
412+
// Cannot remove price feeds from a permanent subscription
413+
if (newParams.priceIds.length < currentParams.priceIds.length) {
414+
revert IllegalPermanentSubscriptionModification();
415+
}
416+
417+
// Check that all existing price IDs are preserved (adding is allowed, not removing)
418+
for (uint i = 0; i < currentParams.priceIds.length; i++) {
419+
bool found = false;
420+
for (uint j = 0; j < newParams.priceIds.length; j++) {
421+
if (currentParams.priceIds[i] == newParams.priceIds[j]) {
422+
found = true;
423+
break;
424+
}
425+
}
426+
if (!found) {
427+
revert IllegalPermanentSubscriptionModification();
428+
}
429+
}
430+
431+
// Cannot change reader whitelist settings for permanent subscriptions
432+
if (newParams.whitelistEnabled != currentParams.whitelistEnabled) {
433+
revert IllegalPermanentSubscriptionModification();
434+
}
435+
436+
// Check if the set of addresses in the whitelist is the same
437+
if (
438+
newParams.readerWhitelist.length !=
439+
currentParams.readerWhitelist.length
440+
) {
441+
revert IllegalPermanentSubscriptionModification();
442+
}
443+
uint256 n = newParams.readerWhitelist.length;
444+
bool[] memory currentVisited = new bool[](n);
445+
uint256 matchesFound = 0;
446+
for (uint256 i = 0; i < n; i++) {
447+
bool foundInCurrent = false;
448+
for (uint256 j = 0; j < n; j++) {
449+
if (
450+
!currentVisited[j] &&
451+
newParams.readerWhitelist[i] ==
452+
currentParams.readerWhitelist[j]
453+
) {
454+
currentVisited[j] = true;
455+
foundInCurrent = true;
456+
matchesFound++;
457+
break;
458+
}
459+
}
460+
if (!foundInCurrent) {
461+
revert IllegalPermanentSubscriptionModification();
462+
}
463+
}
464+
465+
// Cannot change update criteria for permanent subscriptions
466+
if (
467+
newParams.updateCriteria.updateOnHeartbeat !=
468+
currentParams.updateCriteria.updateOnHeartbeat ||
469+
newParams.updateCriteria.heartbeatSeconds !=
470+
currentParams.updateCriteria.heartbeatSeconds ||
471+
newParams.updateCriteria.updateOnDeviation !=
472+
currentParams.updateCriteria.updateOnDeviation ||
473+
newParams.updateCriteria.deviationThresholdBps !=
474+
currentParams.updateCriteria.deviationThresholdBps
475+
) {
476+
revert IllegalPermanentSubscriptionModification();
477+
}
478+
479+
// Cannot change gas config for permanent subscriptions
480+
if (
481+
newParams.gasConfig.maxBaseFeeMultiplierCapPct !=
482+
currentParams.gasConfig.maxBaseFeeMultiplierCapPct ||
483+
newParams.gasConfig.maxPriorityFeeMultiplierCapPct !=
484+
currentParams.gasConfig.maxPriorityFeeMultiplierCapPct
485+
) {
486+
revert IllegalPermanentSubscriptionModification();
487+
}
488+
}
489+
388490
/// FETCH PRICES
389491

390492
/**
@@ -487,9 +589,7 @@ abstract contract Scheduler is IScheduler, SchedulerState {
487589

488590
/// BALANCE MANAGEMENT
489591

490-
function addFunds(
491-
uint256 subscriptionId
492-
) external payable override onlyManager(subscriptionId) {
592+
function addFunds(uint256 subscriptionId) external payable override {
493593
if (!_state.subscriptionParams[subscriptionId].isActive) {
494594
revert InactiveSubscription();
495595
}
@@ -508,6 +608,11 @@ abstract contract Scheduler is IScheduler, SchedulerState {
508608
subscriptionId
509609
];
510610

611+
// Prevent withdrawals from permanent subscriptions
612+
if (params.isPermanent) {
613+
revert IllegalPermanentSubscriptionModification();
614+
}
615+
511616
if (status.balanceInWei < amount) {
512617
revert InsufficientBalance();
513618
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ error InvalidGasConfig();
1212
error PriceSlotMismatch();
1313
error TooManyPriceIds(uint256 provided, uint256 maximum);
1414
error UpdateConditionsNotMet();
15+
error IllegalPermanentSubscriptionModification();
1516
error TimestampOlderThanLastUpdate(
1617
uint256 providedUpdateTimestamp,
1718
uint256 lastUpdatedAt

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ contract SchedulerState {
4141
address[] readerWhitelist;
4242
bool whitelistEnabled;
4343
bool isActive;
44+
bool isPermanent;
4445
UpdateCriteria updateCriteria;
4546
GasConfig gasConfig;
4647
}

0 commit comments

Comments
 (0)