@gooddollar/goodcollective-contracts 1.3.0 → 1.3.1

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 (40) hide show
  1. package/contracts/DirectPayments/DirectPaymentsFactory.sol +19 -5
  2. package/contracts/DirectPayments/DirectPaymentsLibrary.sol +168 -14
  3. package/contracts/DirectPayments/DirectPaymentsPool.sol +9 -116
  4. package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +7 -6
  5. package/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +4 -0
  6. package/contracts/GoodCollective/SuperAppBaseFlow.sol +6 -0
  7. package/contracts/UBI/UBIPoolFactory.sol +19 -3
  8. package/contracts/utils/HelperLibrary.sol +10 -0
  9. package/package.json +3 -3
  10. package/releases/deployment.json +7417 -30724
  11. package/typechain-types/contracts/DirectPayments/DirectPaymentsLibrary.ts +146 -0
  12. package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool.ts +53 -11
  13. package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/IMembersValidator.ts +6 -8
  14. package/typechain-types/contracts/DirectPayments/index.ts +1 -0
  15. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.ts +40 -0
  16. package/typechain-types/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp.ts +22 -1
  17. package/typechain-types/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/IRegistry.ts +38 -1
  18. package/typechain-types/contracts/GoodCollective/SuperAppBaseFlow.sol/IRegisterSuperapp.ts +152 -0
  19. package/typechain-types/contracts/GoodCollective/SuperAppBaseFlow.sol/SuperAppBaseFlow.ts +523 -0
  20. package/typechain-types/contracts/GoodCollective/SuperAppBaseFlow.sol/index.ts +5 -0
  21. package/typechain-types/contracts/GoodCollective/index.ts +2 -1
  22. package/typechain-types/contracts/UBI/UBIPool.ts +40 -0
  23. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +6 -1
  24. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsLibrary__factory.ts +169 -0
  25. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +30 -2
  26. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/IMembersValidator__factory.ts +1 -1
  27. package/typechain-types/factories/contracts/DirectPayments/index.ts +1 -0
  28. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +18 -0
  29. package/typechain-types/factories/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp__factory.ts +13 -0
  30. package/typechain-types/factories/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/IRegistry__factory.ts +24 -0
  31. package/typechain-types/factories/contracts/GoodCollective/SuperAppBaseFlow.sol/IRegisterSuperapp__factory.ts +62 -0
  32. package/typechain-types/factories/contracts/GoodCollective/SuperAppBaseFlow.sol/SuperAppBaseFlow__factory.ts +335 -0
  33. package/typechain-types/factories/contracts/GoodCollective/SuperAppBaseFlow.sol/index.ts +5 -0
  34. package/typechain-types/factories/contracts/GoodCollective/index.ts +1 -1
  35. package/typechain-types/factories/contracts/UBI/UBIPoolFactory__factory.ts +6 -1
  36. package/typechain-types/factories/contracts/UBI/UBIPool__factory.ts +19 -1
  37. package/typechain-types/factories/contracts/test/HelperLibraryTest__factory.ts +1 -1
  38. package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +11 -1
  39. package/typechain-types/hardhat.d.ts +18 -0
  40. package/typechain-types/index.ts +6 -2
@@ -4,10 +4,10 @@ pragma solidity >=0.8.0;
4
4
  // import the DirectPayments contract
5
5
  import "./DirectPaymentsPool.sol";
6
6
  import "./ProvableNFT.sol";
7
+ import "../GoodCollective/SuperAppBaseFlow.sol";
7
8
  import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
8
9
  import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
9
10
  import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
10
-
11
11
  import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
12
12
  import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
13
13
 
@@ -15,6 +15,7 @@ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils
15
15
 
16
16
  contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
17
17
  error NOT_PROJECT_OWNER();
18
+ error NOT_PROJECT_MANAGER();
18
19
  error NOT_POOL();
19
20
 
20
21
  event PoolCreated(
@@ -60,9 +61,9 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
60
61
  _;
61
62
  }
62
63
 
63
- modifier onlyPoolOwner(DirectPaymentsPool pool) {
64
- if (pool.hasRole(pool.DEFAULT_ADMIN_ROLE(), msg.sender) == false) {
65
- revert NOT_PROJECT_OWNER();
64
+ modifier onlyPoolManager(DirectPaymentsPool pool) {
65
+ if (pool.hasRole(pool.MANAGER_ROLE(), msg.sender) == false) {
66
+ revert NOT_PROJECT_MANAGER();
66
67
  }
67
68
 
68
69
  _;
@@ -136,6 +137,19 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
136
137
  pool = DirectPaymentsPool(address(new ERC1967Proxy(impl.implementation(), initCall)));
137
138
  }
138
139
 
140
+ // Register the app with the host
141
+ if (pool.host().isApp(pool) == false) {
142
+ try
143
+ IRegisterSuperapp(address(pool.host())).registerApp(address(pool), SuperAppDefinitions.APP_LEVEL_FINAL)
144
+ {} catch {
145
+ //fallback for older versions of superfluid used in unit tests
146
+ IRegisterSuperapp(address(pool.host())).registerAppByFactory(
147
+ address(pool),
148
+ SuperAppDefinitions.APP_LEVEL_FINAL
149
+ );
150
+ }
151
+ }
152
+
139
153
  nft.grantRole(nft.getManagerRole(nextNftType), address(pool));
140
154
 
141
155
  //access control to project is determinted by the first pool access control rules
@@ -153,7 +167,7 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
153
167
  nextNftType++;
154
168
  }
155
169
 
156
- function changePoolDetails(DirectPaymentsPool _pool, string memory _ipfs) external onlyPoolOwner(_pool) {
170
+ function changePoolDetails(DirectPaymentsPool _pool, string memory _ipfs) external onlyPoolManager(_pool) {
157
171
  registry[address(_pool)].ipfs = _ipfs;
158
172
  emit PoolDetailsChanged(address(_pool), _ipfs);
159
173
  }
@@ -1,14 +1,30 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity >=0.8.0;
3
3
 
4
+ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
5
+ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
6
+
4
7
  import "./DirectPaymentsPool.sol";
8
+ import "./ProvableNFT.sol";
5
9
 
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 {
12
28
  if (memberStats.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
13
29
  {
14
30
  memberStats.daily = reward;
@@ -16,7 +32,7 @@ library DirectPayemntsLibrary {
16
32
  memberStats.daily += reward;
17
33
  }
18
34
 
19
- if (memberStats.lastMonth < curMonth) //month switched
35
+ if (memberStats.lastMonth < _month()) //month switched
20
36
  {
21
37
  memberStats.monthly = reward;
22
38
  } else {
@@ -25,14 +41,20 @@ library DirectPayemntsLibrary {
25
41
 
26
42
  memberStats.total += reward;
27
43
  memberStats.lastReward = uint64(block.timestamp);
28
- memberStats.lastMonth = curMonth;
44
+ memberStats.lastMonth = _month();
29
45
  }
30
46
 
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(
32
54
  DirectPaymentsPool.LimitsData storage globalLimits,
33
- uint128 reward,
34
- uint64 curMonth
35
- ) internal {
55
+ DirectPaymentsPool.SafetyLimits memory limits,
56
+ uint128 reward
57
+ ) public {
36
58
  if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
37
59
  {
38
60
  globalLimits.daily = reward;
@@ -40,7 +62,7 @@ library DirectPayemntsLibrary {
40
62
  globalLimits.daily += reward;
41
63
  }
42
64
 
43
- if (globalLimits.lastMonth < curMonth) //month switched
65
+ if (globalLimits.lastMonth < _month()) //month switched
44
66
  {
45
67
  globalLimits.monthly = reward;
46
68
  } else {
@@ -49,6 +71,138 @@ library DirectPayemntsLibrary {
49
71
 
50
72
  globalLimits.total += reward;
51
73
  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));
53
207
  }
54
208
  }
@@ -9,7 +9,7 @@ import { IERC721ReceiverUpgradeable } from "@openzeppelin/contracts-upgradeable/
9
9
 
10
10
  import { ProvableNFT } from "./ProvableNFT.sol";
11
11
  import { DirectPaymentsFactory } from "./DirectPaymentsFactory.sol";
12
- import { DirectPayemntsLibrary } from "./DirectPaymentsLibrary.sol";
12
+ import { DirectPaymentsLibrary } from "./DirectPaymentsLibrary.sol";
13
13
  import "../GoodCollective/GoodCollectiveSuperApp.sol";
14
14
 
15
15
  interface IMembersValidator {
@@ -18,7 +18,7 @@ interface IMembersValidator {
18
18
  address operator,
19
19
  address member,
20
20
  bytes memory extraData
21
- ) external returns (bool);
21
+ ) external view returns (bool);
22
22
  }
23
23
 
24
24
  interface IIdentityV2 {
@@ -74,7 +74,7 @@ contract DirectPaymentsPool is
74
74
  uint256 rewardPerContributer
75
75
  );
76
76
  event NFTClaimed(uint256 indexed tokenId, uint256 totalRewards);
77
- event NOT_MEMBER_OR_WHITELISTED(address contributer);
77
+ event NOT_MEMBER_OR_WHITELISTED_OR_LIMITS(address contributer);
78
78
 
79
79
  // Define functions
80
80
  struct PoolSettings {
@@ -181,127 +181,20 @@ contract DirectPaymentsPool is
181
181
  function claim(uint256 _nftId, ProvableNFT.NFTData memory _data) public {
182
182
  nft.proveNFTData(_nftId, _data);
183
183
  if (claimedNfts[_nftId]) revert ALREADY_CLAIMED(_nftId);
184
-
184
+ claimedNfts[_nftId] = true;
185
185
  // TODO: should pool own the NFTs?
186
186
  // if (settings.collectNfts && nft.ownerOf(_nftId) != address(this)) revert NFT_MISSING(_nftId);
187
187
 
188
- _claim(_nftId, _data);
189
- }
190
-
191
- /**
192
- * @dev Claims rewards for the specified NFT ID.
193
- * @param _nftId The ID of the NFT to claim rewards for.
194
- * @param _data The NFTData struct containing data about the NFT.
195
- */
196
- function _claim(uint256 _nftId, ProvableNFT.NFTData memory _data) internal {
197
- claimedNfts[_nftId] = true;
198
- uint totalRewards;
199
- uint rewardsBalance = settings.rewardToken.balanceOf(address(this));
200
-
201
- bool allowRewardOverride = settings.allowRewardOverride;
188
+ // Loop through the events in the NFT data and add members
202
189
  for (uint256 i = 0; i < _data.events.length; i++) {
203
- uint reward = (
204
- allowRewardOverride && _data.events[i].rewardOverride > 0
205
- ? _data.events[i].rewardOverride
206
- : _eventReward(_data.events[i].subtype)
207
- ) * _data.events[i].quantity;
208
- if (reward > 0) {
209
- totalRewards += reward;
210
- if (totalRewards > rewardsBalance) revert NO_BALANCE();
211
- rewardsBalance -= totalRewards;
212
- _sendReward(_data.events[i].contributers, uint128(reward));
213
- emit EventRewardClaimed(
214
- _nftId,
215
- _data.events[i].subtype,
216
- _data.events[i].timestamp,
217
- _data.events[i].quantity,
218
- _data.events[i].eventUri,
219
- _data.events[i].contributers,
220
- uint128(reward / _data.events[i].contributers.length)
221
- );
222
- }
223
- }
224
-
225
- emit NFTClaimed(_nftId, totalRewards);
226
- }
227
-
228
- /**
229
- * @dev Returns the reward amount for the specified event type.
230
- * @param _eventType The type of the event to get the reward for.
231
- * @return reward amount for the specified event type.
232
- */
233
- function _eventReward(uint16 _eventType) internal view returns (uint128 reward) {
234
- for (uint i = 0; i < settings.validEvents.length; i++) {
235
- if (_eventType == settings.validEvents[i]) return settings.rewardPerEvent[i];
236
- }
237
- return 0;
238
- }
239
-
240
- /**
241
- * @dev Sends rewards to the specified recipients.
242
- * @param recipients The addresses of the recipients to send rewards to.
243
- * @param reward The total amount of rewards to send.
244
- */
245
- function _sendReward(address[] memory recipients, uint128 reward) internal {
246
- uint128 perReward = uint128(reward / recipients.length);
247
- uint128 totalSent;
248
- for (uint i = 0; i < recipients.length; i++) {
249
- bool valid = _enforceAndUpdateMemberLimits(recipients[i], perReward);
250
- if (valid) {
251
- settings.rewardToken.safeTransfer(recipients[i], perReward);
252
- totalSent += perReward;
253
- } else {
254
- emit NOT_MEMBER_OR_WHITELISTED(recipients[i]);
190
+ for (uint j = 0; j < _data.events[i].contributers.length; j++) {
191
+ //dont revert on non valid members, just dont reward them (their reward is lost)
192
+ _addMember(_data.events[i].contributers[j], "");
255
193
  }
256
194
  }
257
- _enforceAndUpdateGlobalLimits(totalSent);
195
+ DirectPaymentsLibrary._claim(globalLimits, limits, memberLimits, settings, _nftId, _data);
258
196
  }
259
197
 
260
- /**
261
- * @dev Enforces and updates the reward limits for the specified member.
262
- * @param member The address of the member to enforce and update limits for.
263
- * @param reward The amount of rewards to enforce and update limits for.
264
- */
265
- function _enforceAndUpdateMemberLimits(address member, uint128 reward) internal returns (bool) {
266
- //dont revert on non valid members, just dont reward them (their reward is lost)
267
- if (_addMember(member, "") == false) {
268
- return false;
269
- }
270
-
271
- DirectPayemntsLibrary._updateMemberLimits(memberLimits[member], reward, _month());
272
-
273
- if (
274
- memberLimits[member].daily > limits.maxMemberPerDay ||
275
- memberLimits[member].monthly > limits.maxMemberPerMonth
276
- ) revert OVER_MEMBER_LIMITS(member);
277
-
278
- return true;
279
- }
280
-
281
- /**
282
- * @dev Enforces and updates the global reward limits.
283
- * @param reward The amount of rewards to enforce and update limits for.
284
- */
285
- function _enforceAndUpdateGlobalLimits(uint128 reward) internal {
286
- DirectPayemntsLibrary._updateGlobalLimits(globalLimits, reward, _month());
287
-
288
- if (globalLimits.monthly > limits.maxTotalPerMonth) revert OVER_GLOBAL_LIMITS();
289
- }
290
-
291
- /**
292
- * @dev Returns the current month.
293
- * @return month current month as a uint64 value.
294
- */
295
- function _month() internal view returns (uint64 month) {
296
- return uint64(block.timestamp / (60 * 60 * 24 * 30));
297
- }
298
-
299
- /**
300
- * @dev Adds a member to the contract.
301
- * @param member The address of the member to add.
302
- * @param extraData Additional data to validate the member.
303
- */
304
-
305
198
  function _addMember(address member, bytes memory extraData) internal returns (bool isMember) {
306
199
  if (hasRole(MEMBER_ROLE, member)) return true;
307
200
 
@@ -3,7 +3,7 @@
3
3
  pragma solidity >=0.8.0;
4
4
 
5
5
  import { SuperAppBaseFlow } from "./SuperAppBaseFlow.sol";
6
- import { ISuperfluid, ISuperToken, SuperAppDefinitions } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
6
+ import { ISuperfluid, ISuperToken, SuperAppDefinitions, ISuperApp } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
7
7
  import { ISuperGoodDollar } from "@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol";
8
8
  import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
9
9
  import { CFAv1Library, IConstantFlowAgreementV1 } from "@superfluid-finance/ethereum-contracts/contracts/apps/CFAv1Library.sol";
@@ -90,11 +90,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
90
90
  // Set the super token address
91
91
  superToken = _superToken;
92
92
 
93
- // Define the callback definitions for the app
94
- uint256 callBackDefinitions = SuperAppDefinitions.APP_LEVEL_FINAL;
95
-
96
- // Register the app with the host
97
- host.registerApp(callBackDefinitions);
93
+ // // try to register the app with the host, required for backward compatability with unit tests
94
+ if (host.isApp(this) == false) try host.registerApp(SuperAppDefinitions.APP_LEVEL_FINAL) {} catch {}
98
95
 
99
96
  //initialize InitData struct, and set equal to cfaV1
100
97
  cfaV1 = CFAv1Library.InitData(
@@ -366,6 +363,10 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
366
363
  TransferHelper.safeTransfer(address(superToken), recipient, fee);
367
364
  }
368
365
 
366
+ function recoverFunds(address _recipient, uint256 amount) external {
367
+ HelperLibrary.recoverFunds(superToken, _recipient, amount);
368
+ }
369
+
369
370
  /**
370
371
  * for methods that can be called via superfluid batch or directly
371
372
  */
@@ -5,6 +5,8 @@ interface IRegistry {
5
5
  function feeRecipient() external view returns (address);
6
6
 
7
7
  function feeBps() external view returns (uint32);
8
+
9
+ function hasRole(bytes32 role, address account) external view returns (bool);
8
10
  }
9
11
 
10
12
  interface IGoodCollectiveSuperApp {
@@ -21,4 +23,6 @@ interface IGoodCollectiveSuperApp {
21
23
  }
22
24
 
23
25
  function getAdminFee() external view returns (address admin, uint32 feeBps);
26
+
27
+ function getRegistry() external view returns (IRegistry);
24
28
  }
@@ -6,6 +6,12 @@ import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/cont
6
6
 
7
7
  // import "hardhat/console.sol";
8
8
 
9
+ interface IRegisterSuperapp {
10
+ function registerApp(address app, uint256 configWord) external;
11
+
12
+ function registerAppByFactory(address app, uint256 configWord) external;
13
+ }
14
+
9
15
  abstract contract SuperAppBaseFlow is ISuperApp {
10
16
  using SuperTokenV1Library for ISuperToken;
11
17
 
@@ -11,11 +11,14 @@ import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/ac
11
11
  import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
12
12
 
13
13
  import "../Interfaces.sol";
14
+ import "../GoodCollective/GoodCollectiveSuperApp.sol";
15
+ import "../GoodCollective/SuperAppBaseFlow.sol";
14
16
 
15
17
  // import "hardhat/console.sol";
16
18
 
17
19
  contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
18
20
  error NOT_PROJECT_OWNER();
21
+ error NOT_PROJECT_MANAGER();
19
22
  error NOT_POOL();
20
23
 
21
24
  event PoolCreated(
@@ -58,8 +61,8 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
58
61
  _;
59
62
  }
60
63
 
61
- modifier onlyPoolOwner(UBIPool pool) {
62
- if (pool.hasRole(pool.DEFAULT_ADMIN_ROLE(), msg.sender) == false) {
64
+ modifier onlyPoolManager(UBIPool pool) {
65
+ if (pool.hasRole(pool.MANAGER_ROLE(), msg.sender) == false) {
63
66
  revert NOT_PROJECT_OWNER();
64
67
  }
65
68
 
@@ -121,6 +124,19 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
121
124
  pool = UBIPool(address(new ERC1967Proxy(impl.implementation(), initCall)));
122
125
  }
123
126
 
127
+ // Register the app with the host
128
+ if (pool.host().isApp(pool) == false) {
129
+ try
130
+ IRegisterSuperapp(address(pool.host())).registerApp(address(pool), SuperAppDefinitions.APP_LEVEL_FINAL)
131
+ {} catch {
132
+ //fallback for older versions of superfluid used in unit tests
133
+ IRegisterSuperapp(address(pool.host())).registerAppByFactory(
134
+ address(pool),
135
+ SuperAppDefinitions.APP_LEVEL_FINAL
136
+ );
137
+ }
138
+ }
139
+
124
140
  //access control to project is determinted by the first pool access control rules
125
141
  if (address(projectIdToControlPool[keccak256(bytes(_projectId))]) == address(0))
126
142
  projectIdToControlPool[keccak256(bytes(_projectId))] = pool;
@@ -134,7 +150,7 @@ contract UBIPoolFactory is AccessControlUpgradeable, UUPSUpgradeable {
134
150
  emit PoolCreated(address(pool), _projectId, _ipfs, _settings, _limits);
135
151
  }
136
152
 
137
- function changePoolDetails(UBIPool _pool, string memory _ipfs) external onlyPoolOwner(_pool) {
153
+ function changePoolDetails(UBIPool _pool, string memory _ipfs) external onlyPoolManager(_pool) {
138
154
  registry[address(_pool)].ipfs = _ipfs;
139
155
  emit PoolDetailsChanged(address(_pool), _ipfs);
140
156
  }
@@ -16,6 +16,9 @@ library HelperLibrary {
16
16
  using SuperTokenV1Library for ISuperToken;
17
17
  using CFAv1Library for CFAv1Library.InitData;
18
18
 
19
+ error ZERO_AMOUNT();
20
+ error ZERO_ADDRESS();
21
+
19
22
  /**
20
23
  * @dev A struct containing information about a token swap
21
24
  * @param swapFrom The address of the token being swapped
@@ -179,4 +182,11 @@ library HelperLibrary {
179
182
  }
180
183
  } else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
181
184
  }
185
+
186
+ function recoverFunds(ISuperToken superToken, address recipient, uint256 amount) external {
187
+ require(IGoodCollectiveSuperApp(address(this)).getRegistry().hasRole(0x00, msg.sender), "not owner");
188
+ if (amount == 0) revert ZERO_AMOUNT();
189
+ if (recipient == address(0)) revert ZERO_ADDRESS();
190
+ superToken.transfer(recipient, amount);
191
+ }
182
192
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gooddollar/goodcollective-contracts",
3
3
  "packageManager": "yarn@3.2.1",
4
- "version": "1.3.0",
4
+ "version": "1.3.1",
5
5
  "license": "MIT",
6
6
  "types": "./typechain-types/index.ts",
7
7
  "files": [
@@ -35,11 +35,11 @@
35
35
  "dotenv": "^16.3.1",
36
36
  "ethereum-waffle": "^3.0.0",
37
37
  "graphql": "^16.7.1",
38
- "hardhat": "^2.17.1",
38
+ "hardhat": "^2.26.2",
39
39
  "hardhat-abi-exporter": "^2.10.1",
40
40
  "hardhat-celo": "^0.0.4",
41
41
  "hardhat-contract-sizer": "^2.10.0",
42
- "hardhat-deploy": "^0.12.4",
42
+ "hardhat-deploy": "^1.0.4",
43
43
  "hardhat-gas-reporter": "^1.0.8",
44
44
  "prettier": "^2.8.8",
45
45
  "prettier-plugin-solidity": "^1.1.3",