@gooddollar/goodcollective-contracts 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +14 -1
  2. package/contracts/DirectPayments/DirectPaymentsFactory.sol +23 -15
  3. package/contracts/DirectPayments/DirectPaymentsLibrary.sol +54 -0
  4. package/contracts/DirectPayments/DirectPaymentsPool.sol +25 -41
  5. package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +89 -61
  6. package/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +12 -0
  7. package/contracts/Interfaces.sol +25 -0
  8. package/contracts/UBI/UBIPool.sol +103 -90
  9. package/contracts/UBI/UBIPoolFactory.sol +24 -10
  10. package/contracts/test/HelperLibraryTest.sol +16 -0
  11. package/contracts/utils/HelperLibrary.sol +109 -8
  12. package/package.json +4 -3
  13. package/releases/deployment.json +22797 -431
  14. package/typechain-types/@uniswap/index.ts +0 -2
  15. package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +56 -9
  16. package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool.ts +187 -9
  17. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.ts +150 -6
  18. package/typechain-types/contracts/{UBI/MultiClaimModule.sol/IClaimable.ts → GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp.ts} +24 -23
  19. package/typechain-types/contracts/GoodCollective/{GoodCollectiveSuperApp.sol → IGoodCollectiveSuperApp.sol}/index.ts +1 -1
  20. package/typechain-types/contracts/GoodCollective/index.ts +3 -2
  21. package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IIdentityV2.ts +1 -1
  22. package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IMembersValidator.ts +1 -1
  23. package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/index.ts +0 -1
  24. package/typechain-types/contracts/UBI/{UBIPool.sol/UBIPool.ts → UBIPool.ts} +325 -78
  25. package/typechain-types/contracts/UBI/UBIPoolFactory.ts +95 -33
  26. package/typechain-types/contracts/UBI/index.ts +1 -4
  27. package/typechain-types/contracts/index.ts +4 -0
  28. package/typechain-types/contracts/test/HelperLibraryTest.ts +147 -0
  29. package/typechain-types/{@uniswap/v3-periphery/contracts/interfaces → contracts/test}/index.ts +1 -1
  30. package/typechain-types/contracts/utils/HelperLibrary.ts +46 -4
  31. package/typechain-types/factories/@uniswap/index.ts +0 -1
  32. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +25 -2
  33. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +128 -1
  34. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +105 -1
  35. package/typechain-types/factories/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp__factory.ts +48 -0
  36. package/typechain-types/factories/contracts/GoodCollective/{GoodCollectiveSuperApp.sol → IGoodCollectiveSuperApp.sol}/IRegistry__factory.ts +1 -1
  37. package/typechain-types/factories/contracts/GoodCollective/{GoodCollectiveSuperApp.sol → IGoodCollectiveSuperApp.sol}/index.ts +1 -1
  38. package/typechain-types/factories/contracts/GoodCollective/index.ts +2 -1
  39. package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IIdentityV2__factory.ts +1 -1
  40. package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IMembersValidator__factory.ts +1 -1
  41. package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/index.ts +0 -1
  42. package/typechain-types/factories/contracts/UBI/UBIPoolFactory__factory.ts +76 -4
  43. package/typechain-types/factories/contracts/UBI/UBIPool__factory.ts +2052 -0
  44. package/typechain-types/factories/contracts/UBI/index.ts +1 -2
  45. package/typechain-types/factories/contracts/index.ts +2 -0
  46. package/typechain-types/factories/contracts/test/HelperLibraryTest__factory.ts +154 -0
  47. package/typechain-types/{@uniswap/v3-periphery → factories/contracts/test}/index.ts +1 -2
  48. package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +31 -1
  49. package/typechain-types/hardhat.d.ts +18 -36
  50. package/typechain-types/index.ts +10 -14
  51. package/contracts/UBI/MultiClaimModule.sol +0 -78
  52. package/typechain-types/@gooddollar/goodprotocol/contracts/token/FeesFormula.sol/IFeesFormula.ts +0 -115
  53. package/typechain-types/@gooddollar/goodprotocol/contracts/token/FeesFormula.sol/index.ts +0 -4
  54. package/typechain-types/@uniswap/v3-periphery/contracts/index.ts +0 -5
  55. package/typechain-types/@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.ts +0 -369
  56. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp.ts +0 -1000
  57. package/typechain-types/contracts/UBI/MultiClaimModule.sol/IModule.ts +0 -196
  58. package/typechain-types/contracts/UBI/MultiClaimModule.sol/MultiClaimModule.ts +0 -242
  59. package/typechain-types/contracts/UBI/MultiClaimModule.sol/index.ts +0 -6
  60. package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/FeesFormula.sol/IFeesFormula__factory.ts +0 -60
  61. package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/FeesFormula.sol/index.ts +0 -4
  62. package/typechain-types/factories/@uniswap/v3-periphery/contracts/index.ts +0 -4
  63. package/typechain-types/factories/@uniswap/v3-periphery/contracts/interfaces/ISwapRouter__factory.ts +0 -263
  64. package/typechain-types/factories/@uniswap/v3-periphery/contracts/interfaces/index.ts +0 -4
  65. package/typechain-types/factories/@uniswap/v3-periphery/index.ts +0 -4
  66. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp__factory.ts +0 -728
  67. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IClaimable__factory.ts +0 -33
  68. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IModule__factory.ts +0 -84
  69. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/MultiClaimModule__factory.ts +0 -150
  70. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/index.ts +0 -6
  71. package/typechain-types/factories/contracts/UBI/UBIPool.sol/UBIPool__factory.ts +0 -1835
  72. /package/typechain-types/contracts/GoodCollective/{GoodCollectiveSuperApp.sol → IGoodCollectiveSuperApp.sol}/IRegistry.ts +0 -0
@@ -9,30 +9,20 @@ import { IERC721ReceiverUpgradeable } from "@openzeppelin/contracts-upgradeable/
9
9
 
10
10
  import "../GoodCollective/GoodCollectiveSuperApp.sol";
11
11
  import "./UBIPoolFactory.sol";
12
-
13
- interface IMembersValidator {
14
- function isMemberValid(
15
- address pool,
16
- address operator,
17
- address member,
18
- bytes memory extraData
19
- ) external returns (bool);
20
- }
21
-
22
- interface IIdentityV2 {
23
- function getWhitelistedRoot(address member) external view returns (address);
24
- }
12
+ import "../Interfaces.sol";
25
13
 
26
14
  contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgradeable {
27
15
  using SafeERC20Upgradeable for IERC20Upgradeable;
28
16
 
29
17
  error CLAIMFOR_DISABLED();
30
18
  error NOT_MEMBER(address claimer);
31
- error NOT_WHITELISTED(address claimer);
32
- error ALREADY_CLAIMED(address claimer);
19
+ error NOT_MANAGER(address manager);
20
+ error NOT_WHITELISTED(address whitelistedRoot);
21
+ error ALREADY_CLAIMED(address whitelistedRoot);
33
22
  error INVALID_0_VALUE();
34
23
  error EMPTY_MANAGER();
35
24
  error MAX_MEMBERS_REACHED();
25
+ error MAX_PERIOD_CLAIMERS_REACHED(uint256 claimers);
36
26
 
37
27
  bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
38
28
  bytes32 public constant MEMBER_ROLE = keccak256("MEMBER_ROLE");
@@ -52,13 +42,6 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
52
42
  event UBICycleCalculated(uint256 day, uint256 pool, uint256 cycleLength, uint256 dailyUBIPool);
53
43
 
54
44
  event UBIClaimed(address indexed whitelistedRoot, address indexed claimer, uint256 amount);
55
- // Define functions
56
- struct PoolSettings {
57
- address manager;
58
- IMembersValidator membersValidator;
59
- IIdentityV2 uniquenessValidator;
60
- IERC20Upgradeable rewardToken;
61
- }
62
45
 
63
46
  struct UBISettings {
64
47
  //number of days of each UBI pool cycle
@@ -69,8 +52,11 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
69
52
  uint32 minActiveUsers;
70
53
  // can you trigger claim for someone else
71
54
  bool claimForEnabled;
55
+ // max daily claim amount
72
56
  uint maxClaimAmount;
57
+ // max number of members in a pool
73
58
  uint32 maxMembers;
59
+ bool onlyMembers;
74
60
  }
75
61
 
76
62
  struct PoolStatus {
@@ -91,11 +77,21 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
91
77
  uint32 membersCount;
92
78
  }
93
79
 
80
+ struct ExtendedSettings {
81
+ // max number of members that can claim in a day maxPeriodClaimers <= maxMembers
82
+ uint32 maxPeriodClaimers;
83
+ // min daily claim amount, daily amount will be 0 if <minClaimAmount
84
+ uint minClaimAmount;
85
+ // fees taken from income to the pool manager
86
+ uint32 managerFeeBps;
87
+ }
88
+
94
89
  PoolSettings public settings;
95
90
  UBISettings public ubiSettings;
96
91
  PoolStatus public status;
97
92
 
98
93
  UBIPoolFactory public registry;
94
+ ExtendedSettings public extendedSettings;
99
95
 
100
96
  /// @custom:oz-upgrades-unsafe-allow constructor
101
97
  constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
@@ -110,6 +106,10 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
110
106
  return IRegistry(address(registry));
111
107
  }
112
108
 
109
+ function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) {
110
+ return (settings.manager, extendedSettings.managerFeeBps);
111
+ }
112
+
113
113
  /**
114
114
  * @dev Initializes the contract with the given settings and limits.
115
115
  * @param _settings The PoolSettings struct containing pool settings.
@@ -118,13 +118,16 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
118
118
  function initialize(
119
119
  PoolSettings memory _settings,
120
120
  UBISettings memory _ubiSettings,
121
+ ExtendedSettings memory _extendedSettings,
121
122
  UBIPoolFactory _registry
122
123
  ) external initializer {
123
124
  registry = _registry;
124
125
  settings = _settings;
125
126
  ubiSettings = _ubiSettings;
127
+ extendedSettings = _extendedSettings;
128
+ _verifyPoolSettings(_settings);
126
129
  _verifyUBISettings(_ubiSettings);
127
- _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
130
+ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // when using factory this gives factory role which then set role to the real msg.sender
128
131
  _setupRole(MANAGER_ROLE, _settings.manager);
129
132
  setSuperToken(ISuperToken(address(settings.rewardToken)));
130
133
  }
@@ -145,35 +148,22 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
145
148
  * the daily balance is determined by dividing current pool by the cycle length
146
149
  * @return The amount of GoodDollar the user can claim
147
150
  */
148
- function distributionFormula() internal returns (uint256) {
151
+ function distributionFormula() public returns (uint256) {
149
152
  // once every claim cycle
150
153
  uint256 currentDay = getCurrentDay();
151
- if (currentDay > status.currentDay + ubiSettings.claimPeriodDays) {
152
- status.currentDay = currentDay;
153
- uint32 cycleLength = ubiSettings.cycleLengthDays;
154
- uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
155
- //start early cycle if daily pool size is +%5 previous pool or not enough until end of cycle
156
- uint256 nextDailyPool = currentBalance / cycleLength;
157
- bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
158
- currentBalance < (status.dailyCyclePool * (cycleLength - currentDayInCycle()));
159
-
160
- if (
161
- currentDayInCycle() >= status.currentCycleLength || shouldStartEarlyCycle
162
- ) //start of cycle or first time
163
- {
154
+ if (currentDay >= status.currentDay + ubiSettings.claimPeriodDays) {
155
+ (uint256 nextDailyPool, uint256 nextDailyUbi, bool newCycle) = _calcNextDailyUBI();
156
+ if (newCycle) {
157
+ uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
164
158
  status.dailyCyclePool = nextDailyPool;
165
- status.currentCycleLength = cycleLength;
159
+ status.currentCycleLength = ubiSettings.cycleLengthDays;
166
160
  status.startOfCycle = currentDay;
167
- emit UBICycleCalculated(currentDay, currentBalance, cycleLength, nextDailyPool);
161
+ emit UBICycleCalculated(currentDay, currentBalance, ubiSettings.cycleLengthDays, nextDailyPool);
168
162
  }
169
163
 
170
- uint256 prevPeriodClaimers = status.periodClaimers;
171
- status.dailyUbi = min(
172
- ubiSettings.maxClaimAmount,
173
- status.dailyCyclePool / max((prevPeriodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
174
- );
175
- //update minActiveUsers as claimers grow
176
- ubiSettings.minActiveUsers = uint32(max(prevPeriodClaimers / 2, ubiSettings.minActiveUsers));
164
+ status.currentDay = currentDay;
165
+ status.dailyUbi = nextDailyUbi;
166
+ if (status.dailyUbi <= extendedSettings.minClaimAmount) status.dailyUbi = 0;
177
167
 
178
168
  emit UBICalculated(
179
169
  currentDay,
@@ -189,6 +179,32 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
189
179
  return status.dailyUbi;
190
180
  }
191
181
 
182
+ function _calcNextDailyUBI() internal view returns (uint256 nextPeriodPool, uint256 nextDailyUbi, bool newCycle) {
183
+ uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
184
+ //start early cycle if we can increase the daily UBI pool
185
+ uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays;
186
+ bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
187
+ (currentDayInCycle() <= status.currentCycleLength &&
188
+ currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle())));
189
+
190
+ nextPeriodPool = status.dailyCyclePool;
191
+ nextDailyUbi;
192
+ if (
193
+ (currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle
194
+ ) //start of cycle or first time
195
+ {
196
+ nextPeriodPool = currentBalance / ubiSettings.cycleLengthDays;
197
+ newCycle = true;
198
+ }
199
+
200
+ nextDailyUbi = min(
201
+ ubiSettings.maxClaimAmount,
202
+ nextPeriodPool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
203
+ );
204
+
205
+ if (nextDailyUbi < extendedSettings.minClaimAmount) nextDailyUbi = 0;
206
+ }
207
+
192
208
  /**
193
209
  * @dev returns the day count since start of current cycle
194
210
  */
@@ -225,9 +241,17 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
225
241
  function _claim(address claimer, bool sendToWhitelistedRoot) internal {
226
242
  address whitelistedRoot = IIdentityV2(settings.uniquenessValidator).getWhitelistedRoot(claimer);
227
243
  if (whitelistedRoot == address(0)) revert NOT_WHITELISTED(claimer);
228
- if (address(settings.membersValidator) != address(0) && hasRole(MEMBER_ROLE, claimer) == false)
244
+
245
+ // if open for anyone but has limits, we add the first claimers as members to handle the max claimers
246
+ if ((ubiSettings.maxMembers > 0 && ubiSettings.onlyMembers == false)) _grantRole(MEMBER_ROLE, claimer);
247
+
248
+ // check membership if has claimers limits or limited to members only
249
+ if ((ubiSettings.maxMembers > 0 || ubiSettings.onlyMembers) && hasRole(MEMBER_ROLE, claimer) == false)
229
250
  revert NOT_MEMBER(claimer);
230
251
 
252
+ if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers)
253
+ revert MAX_PERIOD_CLAIMERS_REACHED(status.periodClaimers);
254
+
231
255
  // calculats the formula up today ie on day 0 there are no active users, on day 1 any user
232
256
  // (new or active) will trigger the calculation with the active users count of the day before
233
257
  // and so on. the new or inactive users that will become active today, will not take into account
@@ -246,18 +270,6 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
246
270
  emit UBIClaimed(whitelistedRoot, claimer, dailyUbi);
247
271
  }
248
272
 
249
- function addMemberByManager(address member) external onlyRole(MANAGER_ROLE) returns (bool) {
250
- if (hasRole(MEMBER_ROLE, member)) return true;
251
-
252
- if (address(settings.uniquenessValidator) != address(0)) {
253
- address rootAddress = settings.uniquenessValidator.getWhitelistedRoot(member);
254
- if (rootAddress == address(0)) revert NOT_WHITELISTED(member);
255
- }
256
-
257
- _grantRole(MEMBER_ROLE, member);
258
- return true;
259
- }
260
-
261
273
  /**
262
274
  * @dev Adds a member to the contract.
263
275
  * @param member The address of the member to add.
@@ -265,37 +277,43 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
265
277
  */
266
278
 
267
279
  function addMember(address member, bytes memory extraData) external returns (bool isMember) {
268
- if (hasRole(MEMBER_ROLE, member)) return true;
269
-
270
280
  if (address(settings.uniquenessValidator) != address(0)) {
271
281
  address rootAddress = settings.uniquenessValidator.getWhitelistedRoot(member);
272
282
  if (rootAddress == address(0)) revert NOT_WHITELISTED(member);
273
283
  }
274
284
 
275
- // if no members validator then anyone can join the pool
276
- if (address(settings.membersValidator) != address(0)) {
285
+ if (address(settings.membersValidator) != address(0) && hasRole(MANAGER_ROLE, msg.sender) == false) {
277
286
  if (settings.membersValidator.isMemberValid(address(this), msg.sender, member, extraData) == false) {
278
- return false;
287
+ revert NOT_MEMBER(member);
279
288
  }
280
289
  }
290
+ // if no members validator then if members only only manager can add members
291
+ else if (ubiSettings.onlyMembers && hasRole(MANAGER_ROLE, msg.sender) == false) {
292
+ revert NOT_MANAGER(member);
293
+ }
281
294
 
282
295
  _grantRole(MEMBER_ROLE, member);
283
296
  return true;
284
297
  }
285
298
 
299
+ function removeMember(address member) external onlyRole(MANAGER_ROLE) {
300
+ _revokeRole(MEMBER_ROLE, member);
301
+ }
302
+
286
303
  function _grantRole(bytes32 role, address account) internal virtual override {
287
- if (role == MEMBER_ROLE) {
288
- registry.addMember(account);
304
+ if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account) == false) {
289
305
  if (ubiSettings.maxMembers > 0 && status.membersCount > ubiSettings.maxMembers)
290
306
  revert MAX_MEMBERS_REACHED();
307
+ registry.addMember(account);
291
308
  status.membersCount += 1;
292
309
  }
293
310
  super._grantRole(role, account);
294
311
  }
295
312
 
296
313
  function _revokeRole(bytes32 role, address account) internal virtual override {
297
- if (role == MEMBER_ROLE) {
314
+ if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account)) {
298
315
  status.membersCount -= 1;
316
+ registry.removeMember(account);
299
317
  }
300
318
  super._revokeRole(role, account);
301
319
  }
@@ -304,9 +322,13 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
304
322
  * @dev Sets the safety limits for the pool.
305
323
  * @param _ubiSettings The new safety limits.
306
324
  */
307
- function setUBISettings(UBISettings memory _ubiSettings) public onlyRole(MANAGER_ROLE) {
325
+ function setUBISettings(
326
+ UBISettings memory _ubiSettings,
327
+ ExtendedSettings memory _extendedSettings
328
+ ) public onlyRole(MANAGER_ROLE) {
308
329
  _verifyUBISettings(_ubiSettings);
309
330
  ubiSettings = _ubiSettings;
331
+ extendedSettings = _extendedSettings;
310
332
  emit UBISettingsChanged(_ubiSettings);
311
333
  }
312
334
 
@@ -324,7 +346,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
324
346
  * @param _settings The new pool settings.
325
347
  */
326
348
  function setPoolSettings(PoolSettings memory _settings) public onlyRole(MANAGER_ROLE) {
327
- if (_settings.manager == address(0)) revert EMPTY_MANAGER();
349
+ _verifyPoolSettings(_settings);
328
350
 
329
351
  if (_settings.manager != settings.manager) {
330
352
  _revokeRole(MANAGER_ROLE, settings.manager);
@@ -334,27 +356,16 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
334
356
  emit PoolSettingsChanged(_settings);
335
357
  }
336
358
 
337
- function estimateNextDailyUBI() public view returns (uint256) {
338
- uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
339
- //start early cycle if we can increase the daily UBI pool
340
- uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays;
341
- bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
342
- currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle()));
343
-
344
- uint256 _dailyCyclePool = status.dailyCyclePool;
345
- uint256 _dailyUbi;
359
+ function _verifyPoolSettings(PoolSettings memory _poolSettings) internal pure {
346
360
  if (
347
- (currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle
348
- ) //start of cycle or first time
349
- {
350
- _dailyCyclePool = currentBalance / ubiSettings.cycleLengthDays;
351
- }
361
+ _poolSettings.manager == address(0) ||
362
+ address(_poolSettings.uniquenessValidator) == address(0) ||
363
+ address(_poolSettings.rewardToken) == address(0)
364
+ ) revert INVALID_0_VALUE();
365
+ }
352
366
 
353
- _dailyUbi = min(
354
- ubiSettings.maxClaimAmount,
355
- _dailyCyclePool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
356
- );
357
- return _dailyUbi;
367
+ function estimateNextDailyUBI() public view returns (uint256 nextDailyUbi) {
368
+ (, nextDailyUbi, ) = _calcNextDailyUBI();
358
369
  }
359
370
 
360
371
  function checkEntitlement() public view returns (uint256) {
@@ -372,6 +383,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
372
383
  // current day has already been updated which means
373
384
  // that the dailyUbi has been updated
374
385
  if (status.currentDay == getCurrentDay() && status.dailyUbi > 0) {
386
+ if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers)
387
+ return 0;
375
388
  return hasClaimed(_member) ? 0 : status.dailyUbi;
376
389
  }
377
390
  return estimateNextDailyUBI();
@@ -379,10 +392,10 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
379
392
 
380
393
  function hasClaimed(address _member) public view returns (bool) {
381
394
  address whitelistedRoot = IIdentityV2(settings.uniquenessValidator).getWhitelistedRoot(_member);
382
- return status.lastClaimed[whitelistedRoot] == status.currentDay;
395
+ return status.lastClaimed[whitelistedRoot] == getCurrentDay();
383
396
  }
384
397
 
385
398
  function nextClaimTime() public view returns (uint256) {
386
- return getCurrentDay() * (1 days) - (12 hours);
399
+ return (getCurrentDay() + ubiSettings.claimPeriodDays) * (1 days) + 12 hours;
387
400
  }
388
401
  }
@@ -10,7 +10,9 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
10
10
  import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
11
11
  import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
12
12
 
13
- import "hardhat/console.sol";
13
+ import "../Interfaces.sol";
14
+
15
+ // import "hardhat/console.sol";
14
16
 
15
17
  contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
16
18
  error NOT_PROJECT_OWNER();
@@ -20,7 +22,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
20
22
  address indexed pool,
21
23
  string indexed projectId,
22
24
  string ipfs,
23
- UBIPool.PoolSettings poolSettings,
25
+ PoolSettings poolSettings,
24
26
  UBIPool.UBISettings poolLimits
25
27
  );
26
28
 
@@ -84,31 +86,34 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
84
86
  function createManagedPool(
85
87
  string memory _projectId,
86
88
  string memory _ipfs,
87
- UBIPool.PoolSettings memory _settings,
88
- UBIPool.UBISettings memory _limits
89
+ PoolSettings memory _settings,
90
+ UBIPool.UBISettings memory _limits,
91
+ UBIPool.ExtendedSettings memory _extendedSettings
89
92
  ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) {
90
- return _createPool(_projectId, _ipfs, _settings, _limits, true);
93
+ return _createPool(_projectId, _ipfs, _settings, _limits, _extendedSettings, true);
91
94
  }
92
95
 
93
96
  function createPool(
94
97
  string memory _projectId,
95
98
  string memory _ipfs,
96
- UBIPool.PoolSettings memory _settings,
97
- UBIPool.UBISettings memory _limits
99
+ PoolSettings memory _settings,
100
+ UBIPool.UBISettings memory _limits,
101
+ UBIPool.ExtendedSettings memory _extendedSettings
98
102
  ) external onlyProjectOwnerOrNon(_projectId) returns (UBIPool pool) {
99
- return _createPool(_projectId, _ipfs, _settings, _limits, false);
103
+ return _createPool(_projectId, _ipfs, _settings, _limits, _extendedSettings, false);
100
104
  }
101
105
 
102
106
  function _createPool(
103
107
  string memory _projectId,
104
108
  string memory _ipfs,
105
- UBIPool.PoolSettings memory _settings,
109
+ PoolSettings memory _settings,
106
110
  UBIPool.UBISettings memory _limits,
111
+ UBIPool.ExtendedSettings memory _extendedSettings,
107
112
  bool useBeacon
108
113
  ) internal returns (UBIPool pool) {
109
114
  //TODO: add check if msg.sender is whitelisted
110
115
 
111
- bytes memory initCall = abi.encodeWithSelector(UBIPool.initialize.selector, _settings, _limits, address(this));
116
+ bytes memory initCall = abi.encodeCall(UBIPool.initialize, (_settings, _limits, _extendedSettings, this));
112
117
 
113
118
  if (useBeacon) {
114
119
  pool = UBIPool(address(new BeaconProxy(address(impl), initCall)));
@@ -153,6 +158,15 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
153
158
  memberPools[account].push(msg.sender);
154
159
  }
155
160
 
161
+ function removeMember(address member) external onlyPool {
162
+ for (uint i = 0; i < memberPools[member].length; i++) {
163
+ if (memberPools[member][i] == msg.sender) {
164
+ memberPools[member][i] = memberPools[member][memberPools[member].length - 1];
165
+ memberPools[member].pop();
166
+ }
167
+ }
168
+ }
169
+
156
170
  function getMemberPools(address member) external view returns (address[] memory) {
157
171
  return memberPools[member];
158
172
  }
@@ -0,0 +1,16 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity >=0.8.0;
4
+
5
+ import "../utils/HelperLibrary.sol";
6
+
7
+ contract HelperLibraryTest {
8
+ function handleSwap(
9
+ IV3SwapRouter swapRouter,
10
+ HelperLibrary.SwapData memory _customData,
11
+ address outTokenIfNoPath,
12
+ address _sender
13
+ ) external returns (uint256 amountOut) {
14
+ return HelperLibrary.handleSwap(swapRouter, _customData, outTokenIfNoPath, _sender, _sender);
15
+ }
16
+ }
@@ -6,11 +6,15 @@ import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
6
6
  import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol";
7
7
  import { ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
8
8
  import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
9
+ import { CFAv1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/CFAv1Library.sol";
9
10
 
10
11
  import "../GoodCollective/IGoodCollectiveSuperApp.sol";
11
12
 
13
+ // import "hardhat/console.sol";
14
+
12
15
  library HelperLibrary {
13
16
  using SuperTokenV1Library for ISuperToken;
17
+ using CFAv1Library for CFAv1Library.InitData;
14
18
 
15
19
  /**
16
20
  * @dev A struct containing information about a token swap
@@ -33,7 +37,17 @@ library HelperLibrary {
33
37
  SwapData memory _customData,
34
38
  address outTokenIfNoPath,
35
39
  address _sender
36
- ) external {
40
+ ) external returns (uint256 amountOut) {
41
+ return handleSwap(swapRouter, _customData, outTokenIfNoPath, _sender, _sender);
42
+ }
43
+
44
+ function handleSwap(
45
+ IV3SwapRouter swapRouter,
46
+ SwapData memory _customData,
47
+ address outTokenIfNoPath,
48
+ address _sender,
49
+ address _recipient
50
+ ) public returns (uint256 amountOut) {
37
51
  // Transfer the tokens from the sender to this contract
38
52
  TransferHelper.safeTransferFrom(_customData.swapFrom, _sender, address(this), _customData.amount);
39
53
 
@@ -44,38 +58,125 @@ library HelperLibrary {
44
58
  // If a path is provided, execute a multi-hop swap
45
59
  IV3SwapRouter.ExactInputParams memory params = IV3SwapRouter.ExactInputParams({
46
60
  path: _customData.path,
47
- recipient: _sender,
61
+ recipient: _recipient,
48
62
  amountIn: _customData.amount,
49
63
  amountOutMinimum: _customData.minReturn
50
64
  });
51
- swapRouter.exactInput(params);
65
+ return swapRouter.exactInput(params);
52
66
  } else {
53
67
  // If no path is provided, execute a single-hop swap
54
68
  IV3SwapRouter.ExactInputSingleParams memory params = IV3SwapRouter.ExactInputSingleParams({
55
69
  tokenIn: _customData.swapFrom,
56
70
  tokenOut: outTokenIfNoPath,
57
71
  fee: 10000,
58
- recipient: _sender,
72
+ recipient: _recipient,
59
73
  amountIn: _customData.amount,
60
74
  amountOutMinimum: _customData.minReturn,
61
75
  sqrtPriceLimitX96: 0
62
76
  });
63
77
 
64
78
  // Execute the swap using `exactInputSingle`
65
- swapRouter.exactInputSingle(params);
79
+ return swapRouter.exactInputSingle(params);
66
80
  }
67
81
  }
68
82
 
69
83
  function getRealtimeStats(
70
84
  IGoodCollectiveSuperApp.Stats memory stats,
71
85
  ISuperToken superToken
72
- ) external view returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate) {
86
+ )
87
+ external
88
+ view
89
+ returns (
90
+ uint256 netIncome,
91
+ uint256 totalFees,
92
+ uint256 protocolFees,
93
+ uint256 managerFees,
94
+ int96 incomeFlowRate,
95
+ int96 feeRate,
96
+ int96 managerFeeRate
97
+ )
98
+ {
73
99
  incomeFlowRate = stats.lastIncomeRate;
74
100
  netIncome = stats.netIncome + uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
75
101
  feeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
76
- totalFees =
77
- stats.totalFees +
102
+ managerFeeRate = superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient);
103
+
104
+ protocolFees =
105
+ stats.protocolFees +
78
106
  uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
79
107
  (block.timestamp - stats.lastUpdate);
108
+ managerFees =
109
+ stats.managerFees +
110
+ uint96(superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient)) *
111
+ (block.timestamp - stats.lastUpdate);
112
+ totalFees = protocolFees + managerFees;
113
+ }
114
+
115
+ // this should be called before any flow rate changes
116
+ function updateStats(
117
+ IGoodCollectiveSuperApp.Stats storage stats,
118
+ ISuperToken superToken,
119
+ IRegistry registry,
120
+ uint32 managerFeeBps,
121
+ uint256 _amount
122
+ ) external {
123
+ uint feeBps;
124
+ if (address(registry) != address(0)) {
125
+ feeBps = registry.feeBps();
126
+ }
127
+ //use last rate before the current possible rate update
128
+ stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
129
+ if (stats.lastFeeRecipient != address(0)) {
130
+ //fees sent to last recipient, the flowRate to recipient still wasnt updated.
131
+ stats.protocolFees +=
132
+ uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
133
+ (block.timestamp - stats.lastUpdate);
134
+ }
135
+ if (stats.lastManagerFeeRecipient != address(0)) {
136
+ //fees sent to last recipient, the flowRate to recipient still wasnt updated.
137
+ stats.managerFees +=
138
+ uint96(superToken.getFlowRate(address(this), stats.lastManagerFeeRecipient)) *
139
+ (block.timestamp - stats.lastUpdate);
140
+ }
141
+
142
+ if (_amount > 0) {
143
+ stats.netIncome += (_amount * (10000 - feeBps - managerFeeBps)) / 10000;
144
+ stats.protocolFees += (_amount * feeBps) / 10000;
145
+ stats.managerFees += (_amount * managerFeeBps) / 10000;
146
+ }
147
+ stats.totalFees = stats.managerFees + stats.protocolFees;
148
+ stats.lastUpdate = block.timestamp;
149
+ }
150
+
151
+ function takeFeeFlow(
152
+ CFAv1Library.InitData storage cfaV1,
153
+ ISuperToken superToken,
154
+ address prevRecipient,
155
+ address recipient,
156
+ uint32 feeBps,
157
+ int96 _diffRate,
158
+ bytes memory _ctx
159
+ ) public returns (bytes memory newCtx) {
160
+ newCtx = _ctx;
161
+ if (address(recipient) == address(0)) return newCtx;
162
+ int96 curFeeRate = superToken.getFlowRate(address(this), prevRecipient);
163
+ bool newRecipient;
164
+ if (recipient != prevRecipient) {
165
+ newRecipient = true;
166
+ if (prevRecipient != address(0)) {
167
+ //delete old recipient flow
168
+ if (curFeeRate > 0) newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), prevRecipient, superToken); //passing in the ctx which is sent to the callback here
169
+ }
170
+ }
171
+ if (recipient == address(0)) return newCtx;
172
+
173
+ int96 newFeeRate = curFeeRate + (_diffRate * int32(feeBps)) / 10000;
174
+ if (newRecipient == false && curFeeRate > 0) {
175
+ if (newFeeRate <= 0) {
176
+ newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here
177
+ } else {
178
+ newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
179
+ }
180
+ } else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
80
181
  }
81
182
  }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@gooddollar/goodcollective-contracts",
3
3
  "packageManager": "yarn@3.2.1",
4
- "version": "1.1.0",
4
+ "version": "1.3.0",
5
5
  "license": "MIT",
6
6
  "types": "./typechain-types/index.ts",
7
7
  "files": [
8
8
  "contracts",
9
+ "artifacts/contracts",
9
10
  "typechain-types",
10
11
  "releases"
11
12
  ],
@@ -63,9 +64,9 @@
63
64
  "test": "npx hardhat test",
64
65
  "test:coverage": "npx hardhat coverage",
65
66
  "deploy": "hardhat deploy --export-all ./releases/deployment.json",
66
- "prepublish": "yarn version patch && yarn compile && git add package.json && git commit -m \"version bump\"",
67
+ "bump": "yarn version patch && yarn compile && git add package.json && git commit -m \"version bump\"",
67
68
  "publish": "yarn npm publish --access public",
68
69
  "test:setup": "yarn exec ./scripts/deployContracts.sh",
69
70
  "verify": "npx hardhat run scripts/verify.ts --network ${0}"
70
71
  }
71
- }
72
+ }