From 518306182c8fb8022084c0dd0f51aa9a12407115 Mon Sep 17 00:00:00 2001 From: 0xchin Date: Thu, 10 Oct 2024 11:35:58 -0300 Subject: [PATCH] refactor: remove subscriptions --- src/contracts/Grateful.sol | 150 ++------------------------------ src/interfaces/IGrateful.sol | 113 +----------------------- test/integration/Grateful.t.sol | 135 +--------------------------- 3 files changed, 9 insertions(+), 389 deletions(-) diff --git a/src/contracts/Grateful.sol b/src/contracts/Grateful.sol index 1006289..0e58390 100644 --- a/src/contracts/Grateful.sol +++ b/src/contracts/Grateful.sol @@ -39,14 +39,9 @@ contract Grateful is IGrateful, Ownable2Step { /// @inheritdoc IGrateful mapping(address => mapping(address => uint256)) public shares; - mapping(uint256 => Subscription) public subscriptions; - /// @inheritdoc IGrateful mapping(address => bool) public oneTimePayments; - /// @inheritdoc IGrateful - uint256 public subscriptionCount; - /// @inheritdoc IGrateful uint256 public fee; @@ -140,16 +135,7 @@ contract Grateful is IGrateful, Ownable2Step { uint256 _amount, uint256 _id ) external onlyWhenTokenWhitelisted(_token) { - _processPayment( - msg.sender, - _merchant, - _token, - _amount, - _id, - 0, // 0 because no subscription is involved - new address[](0), - new uint256[](0) - ); + _processPayment(msg.sender, _merchant, _token, _amount, _id, new address[](0), new uint256[](0)); } /// @inheritdoc IGrateful @@ -161,129 +147,7 @@ contract Grateful is IGrateful, Ownable2Step { address[] calldata _recipients, uint256[] calldata _percentages ) external onlyWhenTokenWhitelisted(_token) { - _processPayment( - msg.sender, - _merchant, - _token, - _amount, - _id, - 0, // 0 because no subscription is involved - _recipients, - _percentages - ); - } - - /// @inheritdoc IGrateful - function subscribe( - address _token, - address _receiver, - uint256 _amount, - uint256 _subscriptionPlanId, - uint40 _interval, - uint16 _paymentsAmount, - address[] memory _recipients, - uint256[] memory _percentages - ) public onlyWhenTokenWhitelisted(_token) returns (uint256 subscriptionId) { - if (_recipients.length != _percentages.length) { - revert Grateful_MismatchedArrays(); - } - - subscriptionId = subscriptionCount++; - - // Store the subscription details - Subscription storage subscription = subscriptions[subscriptionId]; - subscription.token = _token; - subscription.sender = msg.sender; - subscription.amount = _amount; - subscription.subscriptionPlanId = _subscriptionPlanId; - subscription.receiver = _receiver; - subscription.interval = _interval; - subscription.paymentsAmount = _paymentsAmount - 1; - subscription.lastPaymentTime = uint40(block.timestamp); - subscription.recipients = _recipients; - subscription.percentages = _percentages; - - // Precompute paymentId - uint256 paymentId = calculateId(msg.sender, _receiver, _token, _amount); - - emit SubscriptionCreated(subscriptionId, msg.sender, _receiver, _amount, _subscriptionPlanId); - - // Call _processPayment - _processPayment(msg.sender, _receiver, _token, _amount, paymentId, subscriptionId, _recipients, _percentages); - } - - /// @inheritdoc IGrateful - function subscribe( - address _token, - address _receiver, - uint256 _amount, - uint256 _subscriptionPlanId, - uint40 _interval, - uint16 _paymentsAmount - ) external onlyWhenTokenWhitelisted(_token) returns (uint256 subscriptionId) { - return subscribe( - _token, _receiver, _amount, _subscriptionPlanId, _interval, _paymentsAmount, new address[](0), new uint256[](0) - ); - } - - /// @inheritdoc IGrateful - function processSubscription( - uint256 subscriptionId - ) external { - Subscription storage subscription = subscriptions[subscriptionId]; - - if (subscription.amount == 0) { - revert Grateful_SubscriptionDoesNotExist(); - } - if (block.timestamp < subscription.lastPaymentTime + subscription.interval) { - revert Grateful_TooEarlyForNextPayment(); - } - if (subscription.paymentsAmount == 0) { - revert Grateful_PaymentsAmountReached(); - } - - _processPayment( - subscription.sender, - subscription.receiver, - subscription.token, - subscription.amount, - calculateId(subscription.sender, subscription.receiver, subscription.token, subscription.amount), - subscriptionId, - subscription.recipients, - subscription.percentages - ); - subscription.lastPaymentTime = uint40(block.timestamp); - subscription.paymentsAmount--; - } - - /// @inheritdoc IGrateful - function extendSubscription(uint256 subscriptionId, uint16 additionalPayments) external { - Subscription storage subscription = subscriptions[subscriptionId]; - - if (subscription.amount == 0) { - revert Grateful_SubscriptionDoesNotExist(); - } - if (subscription.sender != msg.sender) { - revert Grateful_OnlySenderCanExtendSubscription(); - } - - subscription.paymentsAmount += additionalPayments; - } - - /// @inheritdoc IGrateful - function cancelSubscription( - uint256 subscriptionId - ) external { - Subscription storage subscription = subscriptions[subscriptionId]; - - if (subscription.amount == 0) { - revert Grateful_SubscriptionDoesNotExist(); - } - if (subscription.sender != msg.sender && subscription.receiver != msg.sender) { - revert Grateful_OnlySenderOrReceiverCanCancelSubscription(); - } - - delete subscriptions[subscriptionId]; + _processPayment(msg.sender, _merchant, _token, _amount, _id, _recipients, _percentages); } /// @inheritdoc IGrateful @@ -316,7 +180,7 @@ contract Grateful is IGrateful, Ownable2Step { if (!oneTimePayments[msg.sender]) { revert Grateful_OneTimeNotFound(); } - _processPayment(msg.sender, _merchant, _token, _amount, _paymentId, 0, _recipients, _percentages); + _processPayment(msg.sender, _merchant, _token, _amount, _paymentId, _recipients, _percentages); } /// @inheritdoc IGrateful @@ -368,7 +232,7 @@ contract Grateful is IGrateful, Ownable2Step { for (uint256 i = 0; i < _tokens.length; i++) { if (_tokens[i].balanceOf(msg.sender) >= _amount) { _processPayment( - msg.sender, _merchant, address(_tokens[i]), _amount, _paymentId, 0, new address[](0), new uint256[](0) + msg.sender, _merchant, address(_tokens[i]), _amount, _paymentId, new address[](0), new uint256[](0) ); } } @@ -427,8 +291,7 @@ contract Grateful is IGrateful, Ownable2Step { * @param _merchant Address of the merchant. * @param _token Address of the token. * @param _amount Amount of the token. - * @param _paymentId ID of the payment. - * @param _subscriptionId ID of the subscription, 0 if it is one-time. + * @param _paymentId ID of the payment * @param _recipients List of recipients for payment splitting. * @param _percentages Corresponding percentages for each recipient. */ @@ -438,7 +301,6 @@ contract Grateful is IGrateful, Ownable2Step { address _token, uint256 _amount, uint256 _paymentId, - uint256 _subscriptionId, address[] memory _recipients, uint256[] memory _percentages ) internal { @@ -504,6 +366,6 @@ contract Grateful is IGrateful, Ownable2Step { } } - emit PaymentProcessed(_sender, _merchant, _token, _amount, yieldingFunds[_merchant], _paymentId, _subscriptionId); + emit PaymentProcessed(_sender, _merchant, _token, _amount, yieldingFunds[_merchant], _paymentId); } } diff --git a/src/interfaces/IGrateful.sol b/src/interfaces/IGrateful.sol index 2fd8e13..ecb273d 100644 --- a/src/interfaces/IGrateful.sol +++ b/src/interfaces/IGrateful.sol @@ -11,32 +11,6 @@ import {AaveV3ERC4626, IPool} from "yield-daddy/aave-v3/AaveV3ERC4626.sol"; * @notice Interface for the Grateful contract that allows payments in whitelisted tokens with optional yield via AAVE. */ interface IGrateful { - /*////////////////////////////////////////////////////////////// - STRUCTS - //////////////////////////////////////////////////////////////*/ - - struct Subscription { - address token; - address sender; - uint256 amount; - uint256 subscriptionPlanId; - address receiver; - uint40 interval; - uint16 paymentsAmount; - uint40 lastPaymentTime; - address[] recipients; - uint256[] percentages; - } - - struct PaymentDetails { - address merchant; - address token; - uint256 amount; - uint256 id; - address[] recipients; - uint256[] percentages; - } - /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ @@ -48,8 +22,7 @@ interface IGrateful { * @param token Address of the token. * @param amount Amount of the token. * @param yielded Indicates if the payment was yielded. - * @param paymentId ID of the payment. - * @param subscriptionId ID of the subscription. + * @param paymentId ID of the payment */ event PaymentProcessed( address indexed sender, @@ -57,8 +30,7 @@ interface IGrateful { address indexed token, uint256 amount, bool yielded, - uint256 paymentId, - uint256 subscriptionId + uint256 paymentId ); /** @@ -69,14 +41,6 @@ interface IGrateful { */ event OneTimePaymentCreated(address indexed merchant, address[] indexed tokens, uint256 amount); - event SubscriptionCreated( - uint256 indexed subscriptionId, - address indexed sender, - address indexed receiver, - uint256 amount, - uint256 subscriptionPlanId - ); - /*/////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ @@ -96,24 +60,9 @@ interface IGrateful { /// @notice Thrown when a token transfer fails. error Grateful_TransferFailed(); - /// @notice Thrown when the subscription does not exist. - error Grateful_SubscriptionDoesNotExist(); - - /// @notice Thrown when it's too early for the next subscription payment. - error Grateful_TooEarlyForNextPayment(); - - /// @notice Thrown when the maximum number of payments has been reached. - error Grateful_PaymentsAmountReached(); - /// @notice Thrown when the one-time payment is not found. error Grateful_OneTimeNotFound(); - /// @notice Thrown when only the sender or receiver can cancel the subscription. - error Grateful_OnlySenderOrReceiverCanCancelSubscription(); - - /// @notice Thrown when only the sender can extend subscription. - error Grateful_OnlySenderCanExtendSubscription(); - /*/////////////////////////////////////////////////////////////// VARIABLES //////////////////////////////////////////////////////////////*/ @@ -155,10 +104,6 @@ interface IGrateful { address _address ) external view returns (bool); - /// @notice Returns the total number of subscriptions. - /// @return Number of subscriptions. - function subscriptionCount() external view returns (uint256); - /// @notice Returns the fee applied to the payments. /// @return Fee in basis points (10000 = 100%). function fee() external view returns (uint256); @@ -209,60 +154,6 @@ interface IGrateful { uint256[] calldata _percentages ) external; - /** - * @notice Subscribes to a service with recurring payments. - * @param _token Address of the token. - * @param _receiver Address of the payment receiver. - * @param _amount Amount per payment. - * @param _interval Interval in seconds between payments. - * @param _paymentsAmount Total number of payments. - * @param _recipients List of recipients for payment splitting. - * @param _percentages Corresponding percentages for each recipient. - * @return subscriptionId ID of the created subscription. - */ - function subscribe( - address _token, - address _receiver, - uint256 _amount, - uint256 _subscriptionPlanId, - uint40 _interval, - uint16 _paymentsAmount, - address[] calldata _recipients, - uint256[] calldata _percentages - ) external returns (uint256 subscriptionId); - - /** - * @notice Subscribes to a service with recurring payments. - * @param _token Address of the token. - * @param _receiver Address of the payment receiver. - * @param _amount Amount per payment. - * @param _interval Interval in seconds between payments. - * @param _paymentsAmount Total number of payments. - * @return subscriptionId ID of the created subscription. - */ - function subscribe( - address _token, - address _receiver, - uint256 _amount, - uint256 _subscriptionPlanId, - uint40 _interval, - uint16 _paymentsAmount - ) external returns (uint256 subscriptionId); - - function cancelSubscription( - uint256 subscriptionId - ) external; - - function extendSubscription(uint256 subscriptionId, uint16 additionalPayments) external; - - /** - * @notice Processes a subscription payment. - * @param subscriptionId ID of the subscription. - */ - function processSubscription( - uint256 subscriptionId - ) external; - /** * @notice Creates a one-time payment. * @param _merchant Address of the merchant. diff --git a/test/integration/Grateful.t.sol b/test/integration/Grateful.t.sol index 8426744..4b3e99f 100644 --- a/test/integration/Grateful.t.sol +++ b/test/integration/Grateful.t.sol @@ -38,94 +38,6 @@ contract IntegrationGreeter is IntegrationBase { assertGt(_usdc.balanceOf(_merchant), _grateful.applyFee(_amount)); } - function test_Subscription() public { - vm.startPrank(_usdcWhale); - _usdc.approve(address(_grateful), _amount * 2); - - vm.expectEmit(address(_grateful)); - - emit IGrateful.SubscriptionCreated( - 0, // Because it is the first subscription - _usdcWhale, - _merchant, - _amount, - _subscriptionPlanId - ); - - uint256 subscriptionId = _grateful.subscribe(address(_usdc), _merchant, _amount, _subscriptionPlanId, 30 days, 2); - vm.stopPrank(); - - // When subscription is created, a initial payment is made - assertEq(_usdc.balanceOf(_merchant), _grateful.applyFee(_amount)); - - // Shouldn't be able to process the subscription before 30 days have passed - vm.expectRevert(IGrateful.Grateful_TooEarlyForNextPayment.selector); - _grateful.processSubscription(subscriptionId); - - // Fast forward 30 days - vm.warp(block.timestamp + 30 days); - - _grateful.processSubscription(subscriptionId); - - assertEq(_usdc.balanceOf(_merchant), _grateful.applyFee(_amount) * 2); - - // Should revert if the payments amount has been reached - - // Fast forward 30 days - vm.warp(block.timestamp + 30 days); - - vm.expectRevert(IGrateful.Grateful_PaymentsAmountReached.selector); - _grateful.processSubscription(subscriptionId); - - // Now, the sender extends the subscription - vm.startPrank(_usdcWhale); - - // Approve additional funds for the extended payments - _usdc.approve(address(_grateful), _amount * 2); - - // Extend the subscription by 2 additional payments - _grateful.extendSubscription(subscriptionId, 2); - - vm.stopPrank(); - - // Process the extended payments - - // Fast forward 30 days - vm.warp(block.timestamp + 30 days); - - _grateful.processSubscription(subscriptionId); - - // The merchant should have received three payments in total - assertEq(_usdc.balanceOf(_merchant), _grateful.applyFee(_amount) * 3); - - // Fast forward another 30 days - vm.warp(block.timestamp + 30 days); - - _grateful.processSubscription(subscriptionId); - - // The merchant should have received four payments in total - assertEq(_usdc.balanceOf(_merchant), _grateful.applyFee(_amount) * 4); - - // Should revert if the payments amount has been reached again - - // Fast forward 30 days - vm.warp(block.timestamp + 30 days); - - vm.expectRevert(IGrateful.Grateful_PaymentsAmountReached.selector); - _grateful.processSubscription(subscriptionId); - - // Now, test cancellation by the sender - - // Sender cancels the subscription - vm.prank(_usdcWhale); - _grateful.cancelSubscription(subscriptionId); - - // Attempt to process the subscription after cancellation - vm.warp(block.timestamp + 30 days); - vm.expectRevert(IGrateful.Grateful_SubscriptionDoesNotExist.selector); - _grateful.processSubscription(subscriptionId); - } - function test_OneTimePayment() public { // 1. Calculate payment id uint256 paymentId = _grateful.calculateId(_usdcWhale, _merchant, address(_usdc), _amount); @@ -166,6 +78,7 @@ contract IntegrationGreeter is IntegrationBase { // 5. Grateful automation calls api to make one time payment to his address vm.prank(_gratefulAutomation); _grateful.createOneTimePayment(_merchant, _tokens, _amount, 4, paymentId, precomputed); + // 6. Advance time vm.warp(block.timestamp + 1 days); @@ -252,50 +165,4 @@ contract IntegrationGreeter is IntegrationBase { uint256 feeAmount = _amount - amountAfterFee; assertEq(_usdc.balanceOf(_owner), feeAmount); } - - function test_SubscriptionSplit() public { - // 1. Define recipients and percentages - address[] memory recipients = new address[](2); - recipients[0] = makeAddr("recipient1"); // Recipient 1 - recipients[1] = makeAddr("recipient2"); // Recipient 2 - - uint256[] memory percentages = new uint256[](2); - percentages[0] = 7000; // 70% - percentages[1] = 3000; // 30% - - // 2. Subscribe to a plan - vm.startPrank(_usdcWhale); - _usdc.approve(address(_grateful), _amount * 2); - - uint256 subscriptionId = - _grateful.subscribe(address(_usdc), _merchant, _amount, _subscriptionPlanId, 30 days, 2, recipients, percentages); - vm.stopPrank(); - - // 3. Calculate expected amounts after fee - uint256 amountAfterFee = _grateful.applyFee(_amount); - uint256 expectedAmountRecipient0 = (amountAfterFee * percentages[0]) / 10_000; - uint256 expectedAmountRecipient1 = (amountAfterFee * percentages[1]) / 10_000; - - // 4. Check balances of recipients - assertEq(_usdc.balanceOf(recipients[0]), expectedAmountRecipient0); - assertEq(_usdc.balanceOf(recipients[1]), expectedAmountRecipient1); - - // Ensure owner received the fee - uint256 feeAmount = _amount - amountAfterFee; - assertEq(_usdc.balanceOf(_owner), feeAmount); - - // 5. Fast forward time - vm.warp(block.timestamp + 30 days); - - // 6. Process subscription - vm.prank(_gratefulAutomation); - _grateful.processSubscription(subscriptionId); - - // 7. Check balances of recipients - assertEq(_usdc.balanceOf(recipients[0]), expectedAmountRecipient0 * 2); - assertEq(_usdc.balanceOf(recipients[1]), expectedAmountRecipient1 * 2); - - // Ensure owner received the fee - assertEq(_usdc.balanceOf(_owner), feeAmount * 2); - } }