22pragma solidity ^ 0.8.12 ;
33
44import {Initializable} from "@openzeppelin-upgrades/contracts/proxy/utils/Initializable.sol " ;
5- import {OwnableUpgradeable} from "@openzeppelin-upgrades/contracts/access/OwnableUpgradeable.sol " ;
65import {UUPSUpgradeable} from "@openzeppelin-upgrades/contracts/proxy/utils/UUPSUpgradeable.sol " ;
6+ import {AccessControlUpgradeable} from "@openzeppelin-upgrades/contracts/access/AccessControlUpgradeable.sol " ;
77
88/**
99 * @title AggregationModePaymentService
1010 * @author Aligned Layer
1111 * @notice Handles deposits that grant time-limited access to aggregation services.
1212 */
13- contract AggregationModePaymentService is Initializable , OwnableUpgradeable , UUPSUpgradeable {
13+ contract AggregationModePaymentService is Initializable , UUPSUpgradeable , AccessControlUpgradeable {
14+
15+ bytes32 public constant OWNER_ROLE = keccak256 ("OWNER_ROLE " );
16+ bytes32 public constant ADMIN_ROLE = keccak256 ("ADMIN_ROLE " );
17+
1418 /// @notice for how much time the payment is valid in seconds
1519 uint256 public paymentExpirationTimeSeconds;
1620
@@ -20,6 +24,20 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
2024 /// @notice The address where the payment funds will be sent.
2125 address public paymentFundsRecipient;
2226
27+ /// @notice The limit of subscriptions for different addresses
28+ uint256 public subscriptionLimit;
29+
30+ /// @notice Number of current subscriptions
31+ uint256 public activeSubscriptionsAmount;
32+
33+ /// @notice Maximum amount of time (in seconds) an address can be subscribed ahead of the current block timestamp.
34+ /// Prevents stacking multiple short subscriptions and paying them over an extended period.
35+ uint256 public maxSubscriptionTimeAhead;
36+
37+ /// @notice Number of addresses currently subscribed.
38+ /// @dev `expirationTime` is a Unix timestamp (UTC seconds) compared against block timestamps.
39+ mapping (address subscriber = > uint256 expirationTime ) public subscribedAddresses;
40+
2341 /**
2442 * @notice Emitted when a user deposits funds to purchase service time.
2543 * @param user Address that sent the payment.
@@ -37,6 +55,18 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
3755 /// @param newAmountToPay the new amount to pay for a subscription in wei.
3856 event AmountToPayUpdated (uint256 indexed newAmountToPay );
3957
58+ /// @notice Event emitted when the subscription limit is updated
59+ /// @param newSubscriptionLimit the new subscription limit.
60+ event SubscriptionLimitUpdated (uint256 indexed newSubscriptionLimit );
61+
62+ /// @notice Event emitted when the subscription amount is updated
63+ /// @param newSubscriptionsAmount the new subscriptions amount.
64+ event ActiveSubscriptionsAmountUpdated (uint256 indexed newSubscriptionsAmount );
65+
66+ /// @notice Event emitted when the max subscription time ahead is updated
67+ /// @param newMaxSubscriptionTimeAhead the max time allowed to subscribe ahead the current timestamp.
68+ event MaxSubscriptionTimeAheadUpdated (uint256 indexed newMaxSubscriptionTimeAhead );
69+
4070 /// @notice Event emitted when the funds recipient is updated
4171 /// @param newFundsRecipient the new address for receiving the funds on withdrawal.
4272 event FundsRecipientUpdated (address indexed newFundsRecipient );
@@ -48,6 +78,10 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
4878
4979 error InvalidDepositAmount (uint256 amountReceived , uint256 amountRequired );
5080
81+ error SubscriptionLimitReached (uint256 subscriptionLimit );
82+
83+ error SubscriptionTimeExceedsLimit (uint256 newSubscriptionTime , uint256 timeLimit );
84+
5185 /**
5286 * @notice Disables initializers for the implementation contract.
5387 */
@@ -58,15 +92,31 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
5892 /**
5993 * @notice Initializes the contract and transfers ownership to the provided address.
6094 * @param _owner Address that becomes the contract owner.
95+ * @param _admin Address that becomes the contract admin.
96+ * @param _paymentFundsRecipient Address that will receive the withdrawal funds.
97+ * @param _amountToPayInWei Amount to pay in wei for the subscription.
98+ * @param _paymentExpirationTimeSeconds The time in seconds that the subscription takes to expire.
99+ * @param _subscriptionLimit The maximum subscribers that can be subscribed at the same time.
100+ *
61101 */
62- function initialize (address _owner , address _paymentFundsRecipient , uint256 _amountToPayInWei , uint256 _paymentExpirationTimeSeconds ) public initializer {
63- __Ownable_init ();
102+ function initialize (
103+ address _owner ,
104+ address _admin ,
105+ address _paymentFundsRecipient ,
106+ uint256 _amountToPayInWei ,
107+ uint256 _paymentExpirationTimeSeconds ,
108+ uint256 _subscriptionLimit ,
109+ uint256 _maxSubscriptionTimeAhead
110+ ) public initializer {
64111 __UUPSUpgradeable_init ();
65- _transferOwnership (_owner);
112+ _grantRole (OWNER_ROLE, _owner);
113+ _grantRole (ADMIN_ROLE, _admin);
66114
67115 paymentExpirationTimeSeconds = _paymentExpirationTimeSeconds;
68116 amountToPayInWei = _amountToPayInWei;
69117 paymentFundsRecipient = _paymentFundsRecipient;
118+ subscriptionLimit = _subscriptionLimit;
119+ maxSubscriptionTimeAhead = _maxSubscriptionTimeAhead;
70120 }
71121
72122 /**
@@ -76,14 +126,14 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
76126 function _authorizeUpgrade (address newImplementation )
77127 internal
78128 override
79- onlyOwner // solhint-disable-next-line no-empty-blocks
129+ onlyRole (OWNER_ROLE) // solhint-disable-next-line no-empty-blocks
80130 {}
81131
82132 /**
83133 * @notice Sets the new expiration time. Only callable by the owner
84134 * @param newExpirationTimeInSeconds The new expiration time for the users payments in seconds.
85135 */
86- function setPaymentExpirationTimeSeconds (uint256 newExpirationTimeInSeconds ) public onlyOwner ( ) {
136+ function setPaymentExpirationTimeSeconds (uint256 newExpirationTimeInSeconds ) public onlyRole (OWNER_ROLE ) {
87137 paymentExpirationTimeSeconds = newExpirationTimeInSeconds;
88138
89139 emit PaymentExpirationTimeUpdated (newExpirationTimeInSeconds);
@@ -93,7 +143,7 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
93143 * @notice Sets the new amount to pay. Only callable by the owner
94144 * @param newRecipient The new address for receiving the funds on withdrawal.
95145 */
96- function setFundsRecipientAddress (address newRecipient ) public onlyOwner ( ) {
146+ function setFundsRecipientAddress (address newRecipient ) public onlyRole (OWNER_ROLE ) {
97147 paymentFundsRecipient = newRecipient;
98148
99149 emit FundsRecipientUpdated (newRecipient);
@@ -103,12 +153,61 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
103153 * @notice Sets the new amount to pay. Only callable by the owner
104154 * @param newAmountToPay The new amount to pay for subscription in wei.
105155 */
106- function setAmountToPay (uint256 newAmountToPay ) public onlyOwner ( ) {
156+ function setAmountToPay (uint256 newAmountToPay ) public onlyRole (OWNER_ROLE ) {
107157 amountToPayInWei = newAmountToPay;
108158
109159 emit AmountToPayUpdated (newAmountToPay);
110160 }
111161
162+ /**
163+ * @notice Sets the new subscription limit. Only callable by the owner
164+ * @param newSubscriptionLimit The new subscription limit.
165+ */
166+ function setSubscriptionLimit (uint256 newSubscriptionLimit ) public onlyRole (OWNER_ROLE) {
167+ subscriptionLimit = newSubscriptionLimit;
168+
169+ emit SubscriptionLimitUpdated (newSubscriptionLimit);
170+ }
171+
172+ /**
173+ * @notice Sets the subscriptions counter to the value received by parameter. Only callable by the owner
174+ * @param newSubscriptionsAmount The new subscriptions amount.
175+ */
176+ function setActiveSubscriptionsAmount (uint256 newSubscriptionsAmount ) public onlyRole (ADMIN_ROLE) {
177+ activeSubscriptionsAmount = newSubscriptionsAmount;
178+
179+ emit ActiveSubscriptionsAmountUpdated (newSubscriptionsAmount);
180+ }
181+
182+ /**
183+ * @notice Sets the max subscription time ahead to the value received by parameter. Only callable by the owner
184+ * @param newMaxSubscriptionTimeAhead max time allowed to subscribe ahead the current timestamp.
185+ */
186+ function setMaxSubscriptionTimeAhead (uint256 newMaxSubscriptionTimeAhead ) public onlyRole (OWNER_ROLE) {
187+ maxSubscriptionTimeAhead = newMaxSubscriptionTimeAhead;
188+
189+ emit MaxSubscriptionTimeAheadUpdated (newMaxSubscriptionTimeAhead);
190+ }
191+
192+ /**
193+ * @notice Adds an array of addresses to the payment map and emits the Payment event.
194+ * @param addressesToAdd the addresses to be subscribed
195+ * @param expirationTimestamp the expiration timestamp (UTC seconds) for that subscriptions
196+ * Note: this method adds the subscriptions without checking if the final amount of subscriptions surpasses
197+ * the subscriptionLimit
198+ */
199+ function addSubscriptions (address [] memory addressesToAdd , uint256 expirationTimestamp ) public onlyRole (ADMIN_ROLE) {
200+ for (uint256 i= 0 ; i < addressesToAdd.length ; ++ i) {
201+ address addressToAdd = addressesToAdd[i];
202+
203+ subscribedAddresses[addressToAdd] = expirationTimestamp;
204+
205+ ++ activeSubscriptionsAmount;
206+
207+ emit UserPayment (addressToAdd, amountToPayInWei, block .timestamp , expirationTimestamp);
208+ }
209+ }
210+
112211 /**
113212 * @notice Accepts payments and validates they meet the minimum requirement.
114213 */
@@ -119,13 +218,33 @@ contract AggregationModePaymentService is Initializable, OwnableUpgradeable, UUP
119218 revert InvalidDepositAmount (amount, amountToPayInWei);
120219 }
121220
221+ if (activeSubscriptionsAmount >= subscriptionLimit) {
222+ revert SubscriptionLimitReached (subscriptionLimit);
223+ }
224+
225+ if (subscribedAddresses[msg .sender ] < block .timestamp ) {
226+ // Subscription is inactive/expired: start a new period from now.
227+ subscribedAddresses[msg .sender ] = block .timestamp + paymentExpirationTimeSeconds;
228+ } else {
229+ // Subscription is still active: extend the current expiry by one period.
230+ subscribedAddresses[msg .sender ] = subscribedAddresses[msg .sender ] + paymentExpirationTimeSeconds;
231+ }
232+
233+ uint256 newExpiration = subscribedAddresses[msg .sender ];
234+
235+ if (newExpiration - block .timestamp > maxSubscriptionTimeAhead) {
236+ revert SubscriptionTimeExceedsLimit (newExpiration, maxSubscriptionTimeAhead);
237+ }
238+
239+ ++ activeSubscriptionsAmount;
240+
122241 emit UserPayment (msg .sender , amount, block .timestamp , block .timestamp + paymentExpirationTimeSeconds);
123242 }
124243
125244 /**
126245 * @notice Withdraws the contract balance to the recipient address.
127246 */
128- function withdraw () external onlyOwner {
247+ function withdraw () external onlyRole (OWNER_ROLE) {
129248 uint256 balance = address (this ).balance;
130249 payable (paymentFundsRecipient).transfer (balance);
131250 emit FundsWithdrawn (paymentFundsRecipient, balance);
0 commit comments