11// SPDX-License-Identifier: MIT
22pragma solidity >= 0.8.0 ;
33
4+ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol " ;
5+ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol " ;
6+
47import "./DirectPaymentsPool.sol " ;
8+ import "./ProvableNFT.sol " ;
59
6- library DirectPayemntsLibrary {
7- function _updateMemberLimits (
8- DirectPaymentsPool.LimitsData storage memberStats ,
9- uint128 reward ,
10- uint64 curMonth
11- ) internal {
10+ library DirectPaymentsLibrary {
11+ using SafeERC20Upgradeable for IERC20Upgradeable ;
12+ event NOT_MEMBER_OR_WHITELISTED_OR_LIMITS (address contributer );
13+ event EventRewardClaimed (
14+ uint256 indexed tokenId ,
15+ uint16 eventType ,
16+ uint32 eventTimestamp ,
17+ uint256 eventQuantity ,
18+ string eventUri ,
19+ address [] contributers ,
20+ uint256 rewardPerContributer
21+ );
22+ event NFTClaimed (uint256 indexed tokenId , uint256 totalRewards );
23+ error OVER_MEMBER_LIMITS (address );
24+ error OVER_GLOBAL_LIMITS ();
25+ error NO_BALANCE ();
26+
27+ function _updateMemberLimits (DirectPaymentsPool.LimitsData storage memberStats , uint128 reward ) public {
1228 if (memberStats.lastReward + 60 * 60 * 24 < block .timestamp ) //more than a day passed since last reward
1329 {
1430 memberStats.daily = reward;
1531 } else {
1632 memberStats.daily += reward;
1733 }
1834
19- if (memberStats.lastMonth < curMonth ) //month switched
35+ if (memberStats.lastMonth < _month () ) //month switched
2036 {
2137 memberStats.monthly = reward;
2238 } else {
@@ -25,22 +41,28 @@ library DirectPayemntsLibrary {
2541
2642 memberStats.total += reward;
2743 memberStats.lastReward = uint64 (block .timestamp );
28- memberStats.lastMonth = curMonth ;
44+ memberStats.lastMonth = _month () ;
2945 }
3046
31- function _updateGlobalLimits (
47+ /**
48+ * @dev Updates the global limits with the new reward.
49+ * @param globalLimits The global limits data to update.
50+ * @param limits The safety limits to check against.
51+ * @param reward The amount of rewards to update.
52+ */
53+ function _enforceAndUpdateGlobalLimits (
3254 DirectPaymentsPool.LimitsData storage globalLimits ,
33- uint128 reward ,
34- uint64 curMonth
35- ) internal {
55+ DirectPaymentsPool.SafetyLimits memory limits ,
56+ uint128 reward
57+ ) public {
3658 if (globalLimits.lastReward + 60 * 60 * 24 < block .timestamp ) //more than a day passed since last reward
3759 {
3860 globalLimits.daily = reward;
3961 } else {
4062 globalLimits.daily += reward;
4163 }
4264
43- if (globalLimits.lastMonth < curMonth ) //month switched
65+ if (globalLimits.lastMonth < _month () ) //month switched
4466 {
4567 globalLimits.monthly = reward;
4668 } else {
@@ -49,6 +71,138 @@ library DirectPayemntsLibrary {
4971
5072 globalLimits.total += reward;
5173 globalLimits.lastReward = uint64 (block .timestamp );
52- globalLimits.lastMonth = curMonth;
74+ globalLimits.lastMonth = _month ();
75+
76+ if (globalLimits.monthly > limits.maxTotalPerMonth) revert OVER_GLOBAL_LIMITS ();
77+ }
78+
79+ /**
80+ * @dev Enforces and updates the reward limits for the specified member. if the member is not a valid member, or past limit it returns false and member will not get rewards.
81+ * @param memberStats The member's limits data to update.
82+ * @param limits The safety limits to check against.
83+ * @param member The address of the member to enforce and update limits for.
84+ * @param reward The amount of rewards to enforce and update limits for.
85+ */
86+ function _enforceAndUpdateMemberLimits (
87+ DirectPaymentsPool.LimitsData storage memberStats ,
88+ DirectPaymentsPool.SafetyLimits memory limits ,
89+ address member ,
90+ uint128 reward
91+ ) public returns (bool ) {
92+ if (
93+ DirectPaymentsPool (address (this )).hasRole (DirectPaymentsPool (address (this )).MEMBER_ROLE (), member) == false
94+ ) {
95+ return false ;
96+ }
97+ _updateMemberLimits (memberStats, reward);
98+
99+ if (memberStats.daily > limits.maxMemberPerDay || memberStats.monthly > limits.maxMemberPerMonth) return false ;
100+
101+ return true ;
102+ }
103+
104+ /**
105+ * @dev Sends rewards to the specified recipients.
106+ * @param recipients The addresses of the recipients to send rewards to.
107+ * @param reward The total amount of rewards to send.
108+ */
109+ function _sendReward (
110+ DirectPaymentsPool.LimitsData storage globalLimits ,
111+ DirectPaymentsPool.SafetyLimits memory limits ,
112+ mapping (address => DirectPaymentsPool.LimitsData) storage memberLimits ,
113+ DirectPaymentsPool.PoolSettings memory settings ,
114+ address [] memory recipients ,
115+ uint128 reward
116+ ) public {
117+ uint128 perReward = uint128 (reward / recipients.length );
118+ uint128 totalSent;
119+ for (uint i = 0 ; i < recipients.length ; i++ ) {
120+ bool valid = _enforceAndUpdateMemberLimits (memberLimits[recipients[i]], limits, recipients[i], perReward);
121+ if (valid) {
122+ settings.rewardToken.safeTransfer (recipients[i], perReward);
123+ totalSent += perReward;
124+ } else {
125+ emit NOT_MEMBER_OR_WHITELISTED_OR_LIMITS (recipients[i]);
126+ }
127+ }
128+ _enforceAndUpdateGlobalLimits (globalLimits, limits, totalSent);
129+ }
130+
131+ /**
132+ * @dev Claims rewards for the specified NFT ID.
133+ * @param globalLimits The global limits data to check against.
134+ * @param limits The safety limits to check against.
135+ * @param memberLimits The mapping of member limits to check against.
136+ * @param settings The pool settings containing the reward token and other configurations.
137+ * @param _nftId The ID of the NFT to claim rewards for.
138+ * @param _data The NFTData struct containing data about the NFT.
139+ */
140+ function _claim (
141+ DirectPaymentsPool.LimitsData storage globalLimits ,
142+ DirectPaymentsPool.SafetyLimits memory limits ,
143+ mapping (address => DirectPaymentsPool.LimitsData) storage memberLimits ,
144+ DirectPaymentsPool.PoolSettings memory settings ,
145+ uint256 _nftId ,
146+ ProvableNFT.NFTData memory _data
147+ ) public {
148+ uint totalRewards;
149+ uint rewardsBalance = settings.rewardToken.balanceOf (address (this ));
150+
151+ bool allowRewardOverride = settings.allowRewardOverride;
152+ for (uint256 i = 0 ; i < _data.events.length ; i++ ) {
153+ uint reward = (
154+ allowRewardOverride && _data.events[i].rewardOverride > 0
155+ ? _data.events[i].rewardOverride
156+ : _eventReward (settings, _data.events[i].subtype)
157+ ) * _data.events[i].quantity;
158+ if (reward > 0 ) {
159+ totalRewards += reward;
160+ if (totalRewards > rewardsBalance) revert NO_BALANCE ();
161+ rewardsBalance -= totalRewards;
162+
163+ _sendReward (
164+ globalLimits,
165+ limits,
166+ memberLimits,
167+ settings,
168+ _data.events[i].contributers,
169+ uint128 (reward)
170+ );
171+ emit EventRewardClaimed (
172+ _nftId,
173+ _data.events[i].subtype,
174+ _data.events[i].timestamp,
175+ _data.events[i].quantity,
176+ _data.events[i].eventUri,
177+ _data.events[i].contributers,
178+ uint128 (reward / _data.events[i].contributers.length )
179+ );
180+ }
181+ }
182+
183+ emit NFTClaimed (_nftId, totalRewards);
184+ }
185+
186+ /**
187+ * @dev Returns the reward amount for the specified event type.
188+ * @param _eventType The type of the event to get the reward for.
189+ * @return reward amount for the specified event type.
190+ */
191+ function _eventReward (
192+ DirectPaymentsPool.PoolSettings memory settings ,
193+ uint16 _eventType
194+ ) internal pure returns (uint128 reward ) {
195+ for (uint i = 0 ; i < settings.validEvents.length ; i++ ) {
196+ if (_eventType == settings.validEvents[i]) return settings.rewardPerEvent[i];
197+ }
198+ return 0 ;
199+ }
200+
201+ /**
202+ * @dev Returns the current month.
203+ * @return month current month as a uint64 value.
204+ */
205+ function _month () internal view returns (uint64 month ) {
206+ return uint64 (block .timestamp / (60 * 60 * 24 * 30 ));
53207 }
54208}
0 commit comments