@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
package/README.md CHANGED
@@ -1 +1,14 @@
1
- # contracts
1
+ # Contracts
2
+
3
+ ## Structs
4
+
5
+ ### Pool Settings
6
+
7
+ - `nftType`: 1, // when you update the settings with setPoolSettings, `nftType` should always match what has been set when creating the pool
8
+ - `uniquenessValidator`: `ethers.constants.AddressZero`,
9
+ - `rewardPerEvent`: `[100, 300]`,
10
+ - `validEvents`: `[1, 2]`, // to be defined
11
+ - `manager`: `<address of the owner/creator of the pool>`,
12
+ - `membersValidator`: `ethers.constants.AddressZero`, // used to only accept certain members (address zero for anyone can join)
13
+ - `rewardToken`: `'0x62B8B11039FcfE5aB0C56E502b1C372A3d2a9c7A'`, // what token will a steward/member receive. currently only supports production G$'s
14
+ - `allowRewardOverride`: `false`,
@@ -11,7 +11,7 @@ import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
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
 
14
- import "hardhat/console.sol";
14
+ // import "hardhat/console.sol";
15
15
 
16
16
  contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
17
17
  error NOT_PROJECT_OWNER();
@@ -98,18 +98,20 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
98
98
  string memory _projectId,
99
99
  string memory _ipfs,
100
100
  DirectPaymentsPool.PoolSettings memory _settings,
101
- DirectPaymentsPool.SafetyLimits memory _limits
101
+ DirectPaymentsPool.SafetyLimits memory _limits,
102
+ uint32 _managerFeeBps
102
103
  ) external onlyProjectOwnerOrNon(_projectId) returns (DirectPaymentsPool pool) {
103
- return _createPool(_projectId, _ipfs, _settings, _limits, true);
104
+ return _createPool(_projectId, _ipfs, _settings, _limits, _managerFeeBps, true);
104
105
  }
105
106
 
106
107
  function createPool(
107
108
  string memory _projectId,
108
109
  string memory _ipfs,
109
110
  DirectPaymentsPool.PoolSettings memory _settings,
110
- DirectPaymentsPool.SafetyLimits memory _limits
111
+ DirectPaymentsPool.SafetyLimits memory _limits,
112
+ uint32 _managerFeeBps
111
113
  ) external onlyProjectOwnerOrNon(_projectId) returns (DirectPaymentsPool pool) {
112
- return _createPool(_projectId, _ipfs, _settings, _limits, false);
114
+ return _createPool(_projectId, _ipfs, _settings, _limits, _managerFeeBps, false);
113
115
  }
114
116
 
115
117
  function _createPool(
@@ -117,17 +119,15 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
117
119
  string memory _ipfs,
118
120
  DirectPaymentsPool.PoolSettings memory _settings,
119
121
  DirectPaymentsPool.SafetyLimits memory _limits,
122
+ uint32 _managerFeeBps,
120
123
  bool useBeacon
121
124
  ) internal returns (DirectPaymentsPool pool) {
122
125
  //TODO: add check if msg.sender is whitelisted
123
126
 
124
127
  _settings.nftType = nextNftType;
125
- bytes memory initCall = abi.encodeWithSelector(
126
- DirectPaymentsPool.initialize.selector,
127
- nft,
128
- _settings,
129
- _limits,
130
- address(this)
128
+ bytes memory initCall = abi.encodeCall(
129
+ DirectPaymentsPool.initialize,
130
+ (nft, _settings, _limits, _managerFeeBps, this)
131
131
  );
132
132
 
133
133
  if (useBeacon) {
@@ -136,9 +136,7 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
136
136
  pool = DirectPaymentsPool(address(new ERC1967Proxy(impl.implementation(), initCall)));
137
137
  }
138
138
 
139
- nft.grantRole(nft.getManagerRole(nextNftType), _settings.manager);
140
139
  nft.grantRole(nft.getManagerRole(nextNftType), address(pool));
141
- pool.grantRole(pool.MINTER_ROLE(), _settings.manager);
142
140
 
143
141
  //access control to project is determinted by the first pool access control rules
144
142
  if (address(projectIdToControlPool[keccak256(bytes(_projectId))]) == address(0))
@@ -146,6 +144,7 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
146
144
  registry[address(pool)].ipfs = _ipfs;
147
145
  registry[address(pool)].projectId = _projectId;
148
146
 
147
+ pool.grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
149
148
  pool.renounceRole(DEFAULT_ADMIN_ROLE, address(this));
150
149
  pools.push(address(pool));
151
150
 
@@ -174,7 +173,16 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
174
173
  feeRecipient = _feeRecipient;
175
174
  }
176
175
 
177
- function addMember(address account) external onlyPool {
178
- memberPools[account].push(msg.sender);
176
+ function addMember(address member) external onlyPool {
177
+ memberPools[member].push(msg.sender);
178
+ }
179
+
180
+ function removeMember(address member) external onlyPool {
181
+ for (uint i = 0; i < memberPools[member].length; i++) {
182
+ if (memberPools[member][i] == msg.sender) {
183
+ memberPools[member][i] = memberPools[member][memberPools[member].length - 1];
184
+ memberPools[member].pop();
185
+ }
186
+ }
179
187
  }
180
188
  }
@@ -0,0 +1,54 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity >=0.8.0;
3
+
4
+ import "./DirectPaymentsPool.sol";
5
+
6
+ library DirectPayemntsLibrary {
7
+ function _updateMemberLimits(
8
+ DirectPaymentsPool.LimitsData storage memberStats,
9
+ uint128 reward,
10
+ uint64 curMonth
11
+ ) internal {
12
+ if (memberStats.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
13
+ {
14
+ memberStats.daily = reward;
15
+ } else {
16
+ memberStats.daily += reward;
17
+ }
18
+
19
+ if (memberStats.lastMonth < curMonth) //month switched
20
+ {
21
+ memberStats.monthly = reward;
22
+ } else {
23
+ memberStats.monthly += reward;
24
+ }
25
+
26
+ memberStats.total += reward;
27
+ memberStats.lastReward = uint64(block.timestamp);
28
+ memberStats.lastMonth = curMonth;
29
+ }
30
+
31
+ function _updateGlobalLimits(
32
+ DirectPaymentsPool.LimitsData storage globalLimits,
33
+ uint128 reward,
34
+ uint64 curMonth
35
+ ) internal {
36
+ if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
37
+ {
38
+ globalLimits.daily = reward;
39
+ } else {
40
+ globalLimits.daily += reward;
41
+ }
42
+
43
+ if (globalLimits.lastMonth < curMonth) //month switched
44
+ {
45
+ globalLimits.monthly = reward;
46
+ } else {
47
+ globalLimits.monthly += reward;
48
+ }
49
+
50
+ globalLimits.total += reward;
51
+ globalLimits.lastReward = uint64(block.timestamp);
52
+ globalLimits.lastMonth = curMonth;
53
+ }
54
+ }
@@ -9,6 +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
13
  import "../GoodCollective/GoodCollectiveSuperApp.sol";
13
14
 
14
15
  interface IMembersValidator {
@@ -111,6 +112,8 @@ contract DirectPaymentsPool is
111
112
  LimitsData public globalLimits;
112
113
  DirectPaymentsFactory public registry;
113
114
 
115
+ uint32 public managerFeeBps;
116
+
114
117
  /// @custom:oz-upgrades-unsafe-allow constructor
115
118
  constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
116
119
 
@@ -124,6 +127,10 @@ contract DirectPaymentsPool is
124
127
  return IRegistry(address(registry));
125
128
  }
126
129
 
130
+ function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) {
131
+ return (settings.manager, managerFeeBps);
132
+ }
133
+
127
134
  /**
128
135
  * @dev Initializes the contract with the given settings and limits.
129
136
  * @param _nft The ProvableNFT contract address.
@@ -134,14 +141,18 @@ contract DirectPaymentsPool is
134
141
  ProvableNFT _nft,
135
142
  PoolSettings memory _settings,
136
143
  SafetyLimits memory _limits,
144
+ uint32 _managerFeeBps,
137
145
  DirectPaymentsFactory _registry
138
146
  ) external initializer {
139
147
  registry = _registry;
140
148
  settings = _settings;
141
149
  limits = _limits;
142
150
  nft = _nft;
143
- _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
144
- _setupRole(DEFAULT_ADMIN_ROLE, _settings.manager);
151
+ managerFeeBps = _managerFeeBps;
152
+ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // when using factory this gives factory role which then set role to the real msg.sender
153
+ _setupRole(MANAGER_ROLE, _settings.manager);
154
+ _setupRole(MINTER_ROLE, _settings.manager);
155
+
145
156
  setSuperToken(ISuperToken(address(settings.rewardToken)));
146
157
  }
147
158
 
@@ -257,24 +268,7 @@ contract DirectPaymentsPool is
257
268
  return false;
258
269
  }
259
270
 
260
- uint64 curMonth = _month();
261
- if (memberLimits[member].lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
262
- {
263
- memberLimits[member].daily = reward;
264
- } else {
265
- memberLimits[member].daily += reward;
266
- }
267
-
268
- if (memberLimits[member].lastMonth < curMonth) //month switched
269
- {
270
- memberLimits[member].monthly = reward;
271
- } else {
272
- memberLimits[member].monthly += reward;
273
- }
274
-
275
- memberLimits[member].total += reward;
276
- memberLimits[member].lastReward = uint64(block.timestamp);
277
- memberLimits[member].lastMonth = curMonth;
271
+ DirectPayemntsLibrary._updateMemberLimits(memberLimits[member], reward, _month());
278
272
 
279
273
  if (
280
274
  memberLimits[member].daily > limits.maxMemberPerDay ||
@@ -289,25 +283,7 @@ contract DirectPaymentsPool is
289
283
  * @param reward The amount of rewards to enforce and update limits for.
290
284
  */
291
285
  function _enforceAndUpdateGlobalLimits(uint128 reward) internal {
292
- uint64 curMonth = _month();
293
-
294
- if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
295
- {
296
- globalLimits.daily = reward;
297
- } else {
298
- globalLimits.daily += reward;
299
- }
300
-
301
- if (globalLimits.lastMonth < curMonth) //month switched
302
- {
303
- globalLimits.monthly = reward;
304
- } else {
305
- globalLimits.monthly += reward;
306
- }
307
-
308
- globalLimits.total += reward;
309
- globalLimits.lastReward = uint64(block.timestamp);
310
- globalLimits.lastMonth = curMonth;
286
+ DirectPayemntsLibrary._updateGlobalLimits(globalLimits, reward, _month());
311
287
 
312
288
  if (globalLimits.monthly > limits.maxTotalPerMonth) revert OVER_GLOBAL_LIMITS();
313
289
  }
@@ -352,6 +328,13 @@ contract DirectPaymentsPool is
352
328
  super._grantRole(role, account);
353
329
  }
354
330
 
331
+ function _revokeRole(bytes32 role, address account) internal virtual override {
332
+ if (role == MEMBER_ROLE) {
333
+ registry.removeMember(account);
334
+ }
335
+ super._revokeRole(role, account);
336
+ }
337
+
355
338
  function mintNFT(address _to, ProvableNFT.NFTData memory _nftData, bool withClaim) external onlyRole(MINTER_ROLE) {
356
339
  uint nftId = nft.mintPermissioned(_to, _nftData, true, "");
357
340
  if (withClaim) {
@@ -391,7 +374,7 @@ contract DirectPaymentsPool is
391
374
  * @dev Sets the safety limits for the pool.
392
375
  * @param _limits The new safety limits.
393
376
  */
394
- function setPoolLimits(SafetyLimits memory _limits) public onlyRole(DEFAULT_ADMIN_ROLE) {
377
+ function setPoolLimits(SafetyLimits memory _limits) public onlyRole(MANAGER_ROLE) {
395
378
  limits = _limits;
396
379
  emit PoolLimitsChanged(_limits);
397
380
  }
@@ -400,7 +383,8 @@ contract DirectPaymentsPool is
400
383
  * @dev Sets the settings for the pool.
401
384
  * @param _settings The new pool settings.
402
385
  */
403
- function setPoolSettings(PoolSettings memory _settings) public onlyRole(DEFAULT_ADMIN_ROLE) {
386
+ function setPoolSettings(PoolSettings memory _settings, uint32 _managerFeeBps) public onlyRole(MANAGER_ROLE) {
387
+ managerFeeBps = _managerFeeBps;
404
388
  if (_settings.nftType != settings.nftType) revert NFTTYPE_CHANGED();
405
389
  if (_settings.manager == address(0)) revert EMPTY_MANAGER();
406
390
 
@@ -16,12 +16,6 @@ import "../utils/HelperLibrary.sol";
16
16
 
17
17
  // import "hardhat/console.sol";
18
18
 
19
- interface IRegistry {
20
- function feeRecipient() external view returns (address);
21
-
22
- function feeBps() external view returns (uint32);
23
- }
24
-
25
19
  abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
26
20
  int96 public constant MIN_FLOW_RATE = 386e9;
27
21
 
@@ -74,7 +68,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
74
68
 
75
69
  IGoodCollectiveSuperApp.Stats public stats;
76
70
 
77
- uint256[48] private _reserved;
71
+ uint256[45] private _reserved;
78
72
 
79
73
  /// @custom:oz-upgrades-unsafe-allow constructor
80
74
  constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) SuperAppBaseFlow(_host) {
@@ -84,6 +78,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
84
78
 
85
79
  function getRegistry() public view virtual returns (IRegistry);
86
80
 
81
+ function getManagerFee() public view virtual returns (address admin, uint32 feeBps);
82
+
87
83
  /**
88
84
  * @dev Sets the address of the super token and registers the app with the host
89
85
  * @param _superToken The address of the super token contract
@@ -122,7 +118,15 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
122
118
  function getRealtimeStats()
123
119
  public
124
120
  view
125
- returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate)
121
+ returns (
122
+ uint256 netIncome,
123
+ uint256 totalFees,
124
+ uint256 protocolFees,
125
+ uint256 managerFees,
126
+ int96 incomeFlowRate,
127
+ int96 feeRate,
128
+ int96 managerFeeRate
129
+ )
126
130
  {
127
131
  return HelperLibrary.getRealtimeStats(stats, superToken);
128
132
  }
@@ -166,6 +170,28 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
166
170
  return _ctx;
167
171
  }
168
172
 
173
+ /**
174
+ * @dev allow single contribution. user needs to approve tokens first. can be used in superfluid batch actions.
175
+ * @param _sender The address of the sender who is contributing tokens.
176
+ * @param _customData The SwapData struct containing information about the swap
177
+ * @param _ctx The context of the transaction for superfluid in case this was used in superfluid batch. otherwise can be empty.
178
+ * @return Returns the context of the transaction.
179
+ */
180
+ function supportWithSwap(
181
+ address _sender,
182
+ HelperLibrary.SwapData memory _customData,
183
+ bytes memory _ctx
184
+ ) external onlyHostOrSender(_sender) returns (bytes memory) {
185
+ uint256 balance = superToken.balanceOf(address(this));
186
+ HelperLibrary.handleSwap(swapRouter, _customData, address(superToken), _sender, address(this));
187
+ uint256 amountReceived = superToken.balanceOf(address(this)) - balance;
188
+ if (amountReceived == 0) revert ZERO_AMOUNT();
189
+
190
+ // Update the contribution amount for the sender in the supporters mapping
191
+ _updateSupporter(_sender, int256(amountReceived), 0, ""); //we pass empty ctx since this is not a flow but a single donation
192
+ return _ctx;
193
+ }
194
+
169
195
  /**
170
196
  * @dev Handles the swap of tokens using the SwapData struct
171
197
  * @param _customData The SwapData struct containing information about the swap
@@ -235,6 +261,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
235
261
  bytes calldata _ctx
236
262
  ) internal virtual override returns (bytes memory /*newCtx*/) {
237
263
  // Update the supporter's information
264
+
238
265
  return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
239
266
  }
240
267
 
@@ -253,10 +280,18 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
253
280
  ) internal returns (bytes memory newCtx) {
254
281
  newCtx = _ctx;
255
282
  bool _isFlow = _ctx.length > 0;
256
- _updateStats(_isFlow ? 0 : uint256(_previousFlowRateOrAmount));
283
+ (address feeRecipient, uint32 feeBps) = getManagerFee();
284
+ HelperLibrary.updateStats(
285
+ stats,
286
+ superToken,
287
+ getRegistry(),
288
+ feeBps,
289
+ _isFlow ? 0 : uint256(_previousFlowRateOrAmount)
290
+ );
257
291
  // Get the current flow rate for the supporter
258
292
  int96 flowRate = superToken.getFlowRate(_supporter, address(this));
259
293
  uint256 prevContribution = supporters[_supporter].contribution;
294
+
260
295
  if (_isFlow) {
261
296
  //enforce minimal flow rate
262
297
  if (flowRate > 0 && flowRate < MIN_FLOW_RATE) revert MIN_FLOWRATE(flowRate);
@@ -264,14 +299,54 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
264
299
  supporters[_supporter].lastUpdated = uint128(block.timestamp);
265
300
  supporters[_supporter].flowRate = flowRate;
266
301
  supporters[_supporter].contribution +=
267
- uint96(int96(_previousFlowRateOrAmount)) *
302
+ uint256(_previousFlowRateOrAmount) *
268
303
  (block.timestamp - _lastUpdated);
269
- newCtx = _takeFeeFlow(flowRate - int96(_previousFlowRateOrAmount), _ctx);
304
+
305
+ // address feeRecipient;
306
+ // uint32 feeBps;
307
+ if (address(getRegistry()) != address(0)) {
308
+ feeRecipient = getRegistry().feeRecipient();
309
+ feeBps = getRegistry().feeBps();
310
+ // console.log("taking fees %s %s", feeRecipient, feeBps);
311
+
312
+ newCtx = HelperLibrary.takeFeeFlow(
313
+ cfaV1,
314
+ superToken,
315
+ stats.lastFeeRecipient,
316
+ feeRecipient,
317
+ feeBps,
318
+ flowRate - int96(_previousFlowRateOrAmount), // we use diff, because manager takes fee from many streams not just this one
319
+ newCtx
320
+ );
321
+ stats.lastFeeRecipient = feeRecipient;
322
+ }
323
+ // console.log("protocol fee stream ok");
324
+ (feeRecipient, feeBps) = getManagerFee();
325
+
326
+ newCtx = HelperLibrary.takeFeeFlow(
327
+ cfaV1,
328
+ superToken,
329
+ stats.lastManagerFeeRecipient,
330
+ feeRecipient,
331
+ feeBps,
332
+ flowRate - int96(_previousFlowRateOrAmount), // we use diff, because manager takes fee from many streams not just this one
333
+ newCtx
334
+ );
335
+
336
+ stats.lastManagerFeeRecipient = feeRecipient;
337
+ // console.log("admin fee stream ok");
270
338
  // we update the last rate after we do all changes to our own flows
271
339
  stats.lastIncomeRate = superToken.getNetFlowRate(address(this));
272
340
  } else {
341
+ if (address(getRegistry()) != address(0)) {
342
+ feeRecipient = getRegistry().feeRecipient();
343
+ feeBps = getRegistry().feeBps();
344
+ _takeFeeSingle(feeRecipient, feeBps, uint256(_previousFlowRateOrAmount));
345
+ }
346
+ (feeRecipient, feeBps) = getManagerFee();
347
+ _takeFeeSingle(feeRecipient, feeBps, uint256(_previousFlowRateOrAmount));
348
+
273
349
  supporters[_supporter].contribution += uint256(_previousFlowRateOrAmount);
274
- _takeFeeSingle(uint256(_previousFlowRateOrAmount));
275
350
  }
276
351
 
277
352
  emit SupporterUpdated(
@@ -284,57 +359,10 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
284
359
  );
285
360
  }
286
361
 
287
- // this should be called before any flow rate changes
288
- function _updateStats(uint256 _amount) internal {
289
- //use last rate before the current possible rate update
290
- stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
291
- uint feeBps;
292
- if (address(getRegistry()) != address(0)) {
293
- feeBps = getRegistry().feeBps();
294
- //fees sent to last recipient, the flowRate to recipient still wasnt updated.
295
- stats.totalFees +=
296
- uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
297
- (block.timestamp - stats.lastUpdate);
298
- }
299
- if (_amount > 0) {
300
- stats.netIncome += (_amount * (10000 - feeBps)) / 10000;
301
- stats.totalFees += (_amount * feeBps) / 10000;
302
- }
303
- stats.lastUpdate = block.timestamp;
304
- }
305
-
306
- function _takeFeeFlow(int96 _diffRate, bytes memory _ctx) internal returns (bytes memory newCtx) {
307
- newCtx = _ctx;
308
- if (address(getRegistry()) == address(0)) return newCtx;
309
- address recipient = getRegistry().feeRecipient();
310
- int96 curFeeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
311
- bool newRecipient;
312
- if (recipient != stats.lastFeeRecipient) {
313
- newRecipient = true;
314
- if (stats.lastFeeRecipient != address(0)) {
315
- //delete old recipient flow
316
- if (curFeeRate > 0)
317
- newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), stats.lastFeeRecipient, superToken); //passing in the ctx which is sent to the callback here
318
- }
319
- stats.lastFeeRecipient = recipient;
320
- }
321
- if (recipient == address(0)) return newCtx;
322
-
323
- int96 feeRateChange = (_diffRate * int32(getRegistry().feeBps())) / 10000;
324
- int96 newFeeRate = curFeeRate + feeRateChange;
325
- if (newFeeRate <= 0 && newRecipient == false) {
326
- newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here
327
- } else if (curFeeRate > 0 && newRecipient == false) {
328
- newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
329
- } else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
330
- }
331
-
332
- function _takeFeeSingle(uint256 _amount) internal {
333
- if (address(getRegistry()) == address(0)) return;
334
- address recipient = getRegistry().feeRecipient();
362
+ function _takeFeeSingle(address recipient, uint32 feeBps, uint256 _amount) internal {
335
363
  if (recipient == address(0)) return;
336
364
 
337
- uint256 fee = (_amount * getRegistry().feeBps()) / 10000;
365
+ uint256 fee = (_amount * feeBps) / 10000;
338
366
  TransferHelper.safeTransfer(address(superToken), recipient, fee);
339
367
  }
340
368
 
@@ -1,6 +1,12 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity >=0.8.0;
3
3
 
4
+ interface IRegistry {
5
+ function feeRecipient() external view returns (address);
6
+
7
+ function feeBps() external view returns (uint32);
8
+ }
9
+
4
10
  interface IGoodCollectiveSuperApp {
5
11
  struct Stats {
6
12
  uint256 netIncome; //without fees
@@ -8,5 +14,11 @@ interface IGoodCollectiveSuperApp {
8
14
  uint256 lastUpdate;
9
15
  address lastFeeRecipient;
10
16
  int96 lastIncomeRate;
17
+ address lastManagerFeeRecipient;
18
+ uint256 protocolFees;
19
+ uint256 managerFees;
20
+ // adding fields MUST update GoodCollectiveSuperApp storage layout
11
21
  }
22
+
23
+ function getAdminFee() external view returns (address admin, uint32 feeBps);
12
24
  }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity >=0.8.0;
4
+
5
+ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
6
+
7
+ interface IMembersValidator {
8
+ function isMemberValid(
9
+ address pool,
10
+ address operator,
11
+ address member,
12
+ bytes memory extraData
13
+ ) external returns (bool);
14
+ }
15
+
16
+ interface IIdentityV2 {
17
+ function getWhitelistedRoot(address member) external view returns (address);
18
+ }
19
+
20
+ struct PoolSettings {
21
+ address manager;
22
+ IMembersValidator membersValidator;
23
+ IIdentityV2 uniquenessValidator;
24
+ IERC20Upgradeable rewardToken;
25
+ }