diff --git a/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol b/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol index b5222bc55a..a5d9160ba6 100644 --- a/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol +++ b/target_chains/ethereum/contracts/contracts/pulse/Scheduler.sol @@ -531,7 +531,7 @@ abstract contract Scheduler is IScheduler, SchedulerState { subscriptionId ]; - if (!status.isActive) { + if (!params.isActive) { revert InactiveSubscription(); } @@ -542,6 +542,16 @@ abstract contract Scheduler is IScheduler, SchedulerState { } status.balanceInWei += msg.value; + + // If subscription is active, ensure minimum balance is maintained + if (params.isActive) { + uint256 minimumBalance = this.getMinimumBalance( + uint8(params.priceIds.length) + ); + if (status.balanceInWei < minimumBalance) { + revert InsufficientBalance(); + } + } } function withdrawFunds( diff --git a/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol b/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol index 10352ccda9..888f9ba569 100644 --- a/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol +++ b/target_chains/ethereum/contracts/forge-test/PulseScheduler.t.sol @@ -541,6 +541,134 @@ contract SchedulerTest is Test, SchedulerEvents, PulseSchedulerTestUtils { "Balance should match initial balance plus added funds" ); } + + + + function testAddFundsEnforcesMinimumBalance() public { + // First add a subscription with minimum balance + uint256 subscriptionId = addTestSubscription( + scheduler, + address(reader) + ); + + // Get subscription parameters and initial balance + (SchedulerState.SubscriptionParams memory params, SchedulerState.SubscriptionStatus memory status) = scheduler + .getSubscription(subscriptionId); + + // Calculate minimum balance + uint256 minimumBalance = scheduler.getMinimumBalance( + uint8(params.priceIds.length) + ); + + // Verify initial balance is at minimum + assertEq( + status.balanceInWei, + minimumBalance, + "Initial balance should equal minimum balance" + ); + + // Add some funds to the subscription + uint256 additionalFunds = 0.1 ether; + scheduler.addFunds{value: additionalFunds}(subscriptionId); + + // Verify funds were added + (, SchedulerState.SubscriptionStatus memory statusAfterAdd) = scheduler + .getSubscription(subscriptionId); + assertEq( + statusAfterAdd.balanceInWei, + minimumBalance + additionalFunds, + "Balance should be increased by the added funds" + ); + + // Now create a new subscription but don't fund it fully + SchedulerState.SubscriptionParams memory newParams = createDefaultSubscriptionParams( + 2, + address(reader) + ); + + // Calculate minimum balance for this new subscription + uint256 newMinimumBalance = scheduler.getMinimumBalance( + uint8(newParams.priceIds.length) + ); + + // Try to create with insufficient funds + uint256 insufficientFunds = newMinimumBalance - 1 wei; + vm.expectRevert(abi.encodeWithSelector(InsufficientBalance.selector)); + scheduler.createSubscription{value: insufficientFunds}(newParams); + + // Create with sufficient funds + uint256 newSubscriptionId = scheduler.createSubscription{value: newMinimumBalance}(newParams); + + // Verify subscription was created with minimum balance + (, SchedulerState.SubscriptionStatus memory newStatus) = scheduler + .getSubscription(newSubscriptionId); + assertEq( + newStatus.balanceInWei, + newMinimumBalance, + "New subscription balance should equal minimum balance" + ); + } + + function testAddFundsEnforcesMinimumBalanceForPermanentSubscription() public { + // Create a non-permanent subscription first + uint256 subscriptionId = addTestSubscription( + scheduler, + address(reader) + ); + + // Get subscription parameters and initial balance + (SchedulerState.SubscriptionParams memory params, SchedulerState.SubscriptionStatus memory status) = scheduler + .getSubscription(subscriptionId); + + // Calculate minimum balance + uint256 minimumBalance = scheduler.getMinimumBalance( + uint8(params.priceIds.length) + ); + + // Verify initial balance is at minimum + assertEq( + status.balanceInWei, + minimumBalance, + "Initial balance should equal minimum balance" + ); + + // Make it permanent + params.isPermanent = true; + scheduler.updateSubscription(subscriptionId, params); + + // Try to add funds - this should succeed since we're adding to a permanent subscription + uint256 additionalFunds = 0.1 ether; + scheduler.addFunds{value: additionalFunds}(subscriptionId); + + // Verify funds were added + (, SchedulerState.SubscriptionStatus memory statusAfter) = scheduler + .getSubscription(subscriptionId); + assertEq( + statusAfter.balanceInWei, + minimumBalance + additionalFunds, + "Balance should be increased by the added funds" + ); + + // Now test the deposit limit for permanent subscriptions + uint256 maxDepositLimit = 100 ether; // MAX_DEPOSIT_LIMIT from SchedulerState + + // Try to add funds exceeding the deposit limit + vm.expectRevert(abi.encodeWithSelector(MaxDepositLimitExceeded.selector)); + scheduler.addFunds{value: maxDepositLimit + 1}(subscriptionId); + + // Add funds within the deposit limit + uint256 validDeposit = 1 ether; + scheduler.addFunds{value: validDeposit}(subscriptionId); + + // Verify funds were added + (, SchedulerState.SubscriptionStatus memory statusAfterValidDeposit) = scheduler + .getSubscription(subscriptionId); + assertEq( + statusAfterValidDeposit.balanceInWei, + minimumBalance + additionalFunds + validDeposit, + "Balance should be increased by the valid deposit" + ); + } function testWithdrawFunds() public { // Add a subscription and get the parameters