@gooddollar/goodcollective-contracts 1.2.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.
- package/contracts/DirectPayments/DirectPaymentsFactory.sol +12 -7
- package/contracts/DirectPayments/DirectPaymentsLibrary.sol +54 -0
- package/contracts/DirectPayments/DirectPaymentsPool.sol +13 -38
- package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +65 -13
- package/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +6 -0
- package/contracts/Interfaces.sol +25 -0
- package/contracts/UBI/UBIPool.sol +76 -75
- package/contracts/UBI/UBIPoolFactory.sol +15 -10
- package/contracts/utils/HelperLibrary.sol +56 -23
- package/package.json +1 -1
- package/releases/deployment.json +4836 -3746
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +16 -4
- package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool.ts +138 -9
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.ts +101 -6
- package/typechain-types/contracts/{DirectPayments/DirectPaymentsFactory.sol/IRegistry.ts → GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp.ts} +18 -27
- package/typechain-types/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/index.ts +1 -0
- package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IIdentityV2.ts +1 -1
- package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IMembersValidator.ts +1 -1
- package/typechain-types/contracts/{UBI/UBIPool.sol → Interfaces.sol}/index.ts +0 -1
- package/typechain-types/contracts/UBI/{UBIPool.sol/UBIPool.ts → UBIPool.ts} +239 -47
- package/typechain-types/contracts/UBI/UBIPoolFactory.ts +58 -34
- package/typechain-types/contracts/UBI/index.ts +1 -2
- package/typechain-types/contracts/index.ts +2 -0
- package/typechain-types/contracts/utils/HelperLibrary.ts +46 -4
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +11 -1
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +72 -1
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +48 -0
- package/typechain-types/factories/contracts/{DirectPayments/DirectPaymentsFactory.sol/IRegistry__factory.ts → GoodCollective/IGoodCollectiveSuperApp.sol/IGoodCollectiveSuperApp__factory.ts} +19 -23
- package/typechain-types/factories/contracts/GoodCollective/IGoodCollectiveSuperApp.sol/index.ts +1 -0
- package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IIdentityV2__factory.ts +1 -1
- package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/IMembersValidator__factory.ts +1 -1
- package/typechain-types/factories/contracts/{UBI/UBIPool.sol → Interfaces.sol}/index.ts +0 -1
- package/typechain-types/factories/contracts/UBI/UBIPoolFactory__factory.ts +51 -7
- package/typechain-types/factories/contracts/UBI/UBIPool__factory.ts +2052 -0
- package/typechain-types/factories/contracts/UBI/index.ts +1 -1
- package/typechain-types/factories/contracts/index.ts +1 -0
- package/typechain-types/factories/contracts/test/HelperLibraryTest__factory.ts +1 -1
- package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +31 -1
- package/typechain-types/hardhat.d.ts +18 -9
- package/typechain-types/index.ts +4 -2
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.sol/DirectPaymentsFactory.ts +0 -1327
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.sol/index.ts +0 -5
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory.sol/DirectPaymentsFactory__factory.ts +0 -1004
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory.sol/index.ts +0 -5
- package/typechain-types/factories/contracts/UBI/UBIPool.sol/UBIPool__factory.ts +0 -1916
|
@@ -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,12 +119,16 @@ 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.encodeCall(
|
|
128
|
+
bytes memory initCall = abi.encodeCall(
|
|
129
|
+
DirectPaymentsPool.initialize,
|
|
130
|
+
(nft, _settings, _limits, _managerFeeBps, this)
|
|
131
|
+
);
|
|
126
132
|
|
|
127
133
|
if (useBeacon) {
|
|
128
134
|
pool = DirectPaymentsPool(address(new BeaconProxy(address(impl), initCall)));
|
|
@@ -130,7 +136,6 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
|
|
|
130
136
|
pool = DirectPaymentsPool(address(new ERC1967Proxy(impl.implementation(), initCall)));
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
nft.grantRole(nft.getManagerRole(nextNftType), _settings.manager);
|
|
134
139
|
nft.grantRole(nft.getManagerRole(nextNftType), address(pool));
|
|
135
140
|
|
|
136
141
|
//access control to project is determinted by the first pool access control rules
|
|
@@ -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,12 +141,14 @@ 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;
|
|
151
|
+
managerFeeBps = _managerFeeBps;
|
|
143
152
|
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // when using factory this gives factory role which then set role to the real msg.sender
|
|
144
153
|
_setupRole(MANAGER_ROLE, _settings.manager);
|
|
145
154
|
_setupRole(MINTER_ROLE, _settings.manager);
|
|
@@ -259,24 +268,7 @@ contract DirectPaymentsPool is
|
|
|
259
268
|
return false;
|
|
260
269
|
}
|
|
261
270
|
|
|
262
|
-
|
|
263
|
-
if (memberLimits[member].lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
|
|
264
|
-
{
|
|
265
|
-
memberLimits[member].daily = reward;
|
|
266
|
-
} else {
|
|
267
|
-
memberLimits[member].daily += reward;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (memberLimits[member].lastMonth < curMonth) //month switched
|
|
271
|
-
{
|
|
272
|
-
memberLimits[member].monthly = reward;
|
|
273
|
-
} else {
|
|
274
|
-
memberLimits[member].monthly += reward;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
memberLimits[member].total += reward;
|
|
278
|
-
memberLimits[member].lastReward = uint64(block.timestamp);
|
|
279
|
-
memberLimits[member].lastMonth = curMonth;
|
|
271
|
+
DirectPayemntsLibrary._updateMemberLimits(memberLimits[member], reward, _month());
|
|
280
272
|
|
|
281
273
|
if (
|
|
282
274
|
memberLimits[member].daily > limits.maxMemberPerDay ||
|
|
@@ -291,25 +283,7 @@ contract DirectPaymentsPool is
|
|
|
291
283
|
* @param reward The amount of rewards to enforce and update limits for.
|
|
292
284
|
*/
|
|
293
285
|
function _enforceAndUpdateGlobalLimits(uint128 reward) internal {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (globalLimits.lastReward + 60 * 60 * 24 < block.timestamp) //more than a day passed since last reward
|
|
297
|
-
{
|
|
298
|
-
globalLimits.daily = reward;
|
|
299
|
-
} else {
|
|
300
|
-
globalLimits.daily += reward;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (globalLimits.lastMonth < curMonth) //month switched
|
|
304
|
-
{
|
|
305
|
-
globalLimits.monthly = reward;
|
|
306
|
-
} else {
|
|
307
|
-
globalLimits.monthly += reward;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
globalLimits.total += reward;
|
|
311
|
-
globalLimits.lastReward = uint64(block.timestamp);
|
|
312
|
-
globalLimits.lastMonth = curMonth;
|
|
286
|
+
DirectPayemntsLibrary._updateGlobalLimits(globalLimits, reward, _month());
|
|
313
287
|
|
|
314
288
|
if (globalLimits.monthly > limits.maxTotalPerMonth) revert OVER_GLOBAL_LIMITS();
|
|
315
289
|
}
|
|
@@ -409,7 +383,8 @@ contract DirectPaymentsPool is
|
|
|
409
383
|
* @dev Sets the settings for the pool.
|
|
410
384
|
* @param _settings The new pool settings.
|
|
411
385
|
*/
|
|
412
|
-
function setPoolSettings(PoolSettings memory _settings) public onlyRole(MANAGER_ROLE) {
|
|
386
|
+
function setPoolSettings(PoolSettings memory _settings, uint32 _managerFeeBps) public onlyRole(MANAGER_ROLE) {
|
|
387
|
+
managerFeeBps = _managerFeeBps;
|
|
413
388
|
if (_settings.nftType != settings.nftType) revert NFTTYPE_CHANGED();
|
|
414
389
|
if (_settings.manager == address(0)) revert EMPTY_MANAGER();
|
|
415
390
|
|
|
@@ -14,6 +14,8 @@ import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol";
|
|
|
14
14
|
import "../DirectPayments/DirectPaymentsFactory.sol";
|
|
15
15
|
import "../utils/HelperLibrary.sol";
|
|
16
16
|
|
|
17
|
+
// import "hardhat/console.sol";
|
|
18
|
+
|
|
17
19
|
abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
18
20
|
int96 public constant MIN_FLOW_RATE = 386e9;
|
|
19
21
|
|
|
@@ -66,7 +68,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
66
68
|
|
|
67
69
|
IGoodCollectiveSuperApp.Stats public stats;
|
|
68
70
|
|
|
69
|
-
uint256[
|
|
71
|
+
uint256[45] private _reserved;
|
|
70
72
|
|
|
71
73
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
72
74
|
constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) SuperAppBaseFlow(_host) {
|
|
@@ -76,6 +78,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
76
78
|
|
|
77
79
|
function getRegistry() public view virtual returns (IRegistry);
|
|
78
80
|
|
|
81
|
+
function getManagerFee() public view virtual returns (address admin, uint32 feeBps);
|
|
82
|
+
|
|
79
83
|
/**
|
|
80
84
|
* @dev Sets the address of the super token and registers the app with the host
|
|
81
85
|
* @param _superToken The address of the super token contract
|
|
@@ -114,7 +118,15 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
114
118
|
function getRealtimeStats()
|
|
115
119
|
public
|
|
116
120
|
view
|
|
117
|
-
returns (
|
|
121
|
+
returns (
|
|
122
|
+
uint256 netIncome,
|
|
123
|
+
uint256 totalFees,
|
|
124
|
+
uint256 protocolFees,
|
|
125
|
+
uint256 managerFees,
|
|
126
|
+
int96 incomeFlowRate,
|
|
127
|
+
int96 feeRate,
|
|
128
|
+
int96 managerFeeRate
|
|
129
|
+
)
|
|
118
130
|
{
|
|
119
131
|
return HelperLibrary.getRealtimeStats(stats, superToken);
|
|
120
132
|
}
|
|
@@ -249,6 +261,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
249
261
|
bytes calldata _ctx
|
|
250
262
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
251
263
|
// Update the supporter's information
|
|
264
|
+
|
|
252
265
|
return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
|
|
253
266
|
}
|
|
254
267
|
|
|
@@ -267,10 +280,18 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
267
280
|
) internal returns (bytes memory newCtx) {
|
|
268
281
|
newCtx = _ctx;
|
|
269
282
|
bool _isFlow = _ctx.length > 0;
|
|
270
|
-
|
|
283
|
+
(address feeRecipient, uint32 feeBps) = getManagerFee();
|
|
284
|
+
HelperLibrary.updateStats(
|
|
285
|
+
stats,
|
|
286
|
+
superToken,
|
|
287
|
+
getRegistry(),
|
|
288
|
+
feeBps,
|
|
289
|
+
_isFlow ? 0 : uint256(_previousFlowRateOrAmount)
|
|
290
|
+
);
|
|
271
291
|
// Get the current flow rate for the supporter
|
|
272
292
|
int96 flowRate = superToken.getFlowRate(_supporter, address(this));
|
|
273
293
|
uint256 prevContribution = supporters[_supporter].contribution;
|
|
294
|
+
|
|
274
295
|
if (_isFlow) {
|
|
275
296
|
//enforce minimal flow rate
|
|
276
297
|
if (flowRate > 0 && flowRate < MIN_FLOW_RATE) revert MIN_FLOWRATE(flowRate);
|
|
@@ -278,21 +299,54 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
278
299
|
supporters[_supporter].lastUpdated = uint128(block.timestamp);
|
|
279
300
|
supporters[_supporter].flowRate = flowRate;
|
|
280
301
|
supporters[_supporter].contribution +=
|
|
281
|
-
|
|
302
|
+
uint256(_previousFlowRateOrAmount) *
|
|
282
303
|
(block.timestamp - _lastUpdated);
|
|
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
|
+
|
|
283
326
|
newCtx = HelperLibrary.takeFeeFlow(
|
|
284
327
|
cfaV1,
|
|
285
|
-
stats,
|
|
286
328
|
superToken,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
|
290
334
|
);
|
|
335
|
+
|
|
336
|
+
stats.lastManagerFeeRecipient = feeRecipient;
|
|
337
|
+
// console.log("admin fee stream ok");
|
|
291
338
|
// we update the last rate after we do all changes to our own flows
|
|
292
339
|
stats.lastIncomeRate = superToken.getNetFlowRate(address(this));
|
|
293
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
|
+
|
|
294
349
|
supporters[_supporter].contribution += uint256(_previousFlowRateOrAmount);
|
|
295
|
-
_takeFeeSingle(uint256(_previousFlowRateOrAmount));
|
|
296
350
|
}
|
|
297
351
|
|
|
298
352
|
emit SupporterUpdated(
|
|
@@ -305,12 +359,10 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
305
359
|
);
|
|
306
360
|
}
|
|
307
361
|
|
|
308
|
-
function _takeFeeSingle(uint256 _amount) internal {
|
|
309
|
-
if (address(getRegistry()) == address(0)) return;
|
|
310
|
-
address recipient = getRegistry().feeRecipient();
|
|
362
|
+
function _takeFeeSingle(address recipient, uint32 feeBps, uint256 _amount) internal {
|
|
311
363
|
if (recipient == address(0)) return;
|
|
312
364
|
|
|
313
|
-
uint256 fee = (_amount *
|
|
365
|
+
uint256 fee = (_amount * feeBps) / 10000;
|
|
314
366
|
TransferHelper.safeTransfer(address(superToken), recipient, fee);
|
|
315
367
|
}
|
|
316
368
|
|
|
@@ -14,5 +14,11 @@ interface IGoodCollectiveSuperApp {
|
|
|
14
14
|
uint256 lastUpdate;
|
|
15
15
|
address lastFeeRecipient;
|
|
16
16
|
int96 lastIncomeRate;
|
|
17
|
+
address lastManagerFeeRecipient;
|
|
18
|
+
uint256 protocolFees;
|
|
19
|
+
uint256 managerFees;
|
|
20
|
+
// adding fields MUST update GoodCollectiveSuperApp storage layout
|
|
17
21
|
}
|
|
22
|
+
|
|
23
|
+
function getAdminFee() external view returns (address admin, uint32 feeBps);
|
|
18
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
|
+
}
|
|
@@ -9,19 +9,7 @@ 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;
|
|
@@ -33,7 +21,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
33
21
|
error ALREADY_CLAIMED(address whitelistedRoot);
|
|
34
22
|
error INVALID_0_VALUE();
|
|
35
23
|
error EMPTY_MANAGER();
|
|
36
|
-
error
|
|
24
|
+
error MAX_MEMBERS_REACHED();
|
|
25
|
+
error MAX_PERIOD_CLAIMERS_REACHED(uint256 claimers);
|
|
37
26
|
|
|
38
27
|
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
|
|
39
28
|
bytes32 public constant MEMBER_ROLE = keccak256("MEMBER_ROLE");
|
|
@@ -53,13 +42,6 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
53
42
|
event UBICycleCalculated(uint256 day, uint256 pool, uint256 cycleLength, uint256 dailyUBIPool);
|
|
54
43
|
|
|
55
44
|
event UBIClaimed(address indexed whitelistedRoot, address indexed claimer, uint256 amount);
|
|
56
|
-
// Define functions
|
|
57
|
-
struct PoolSettings {
|
|
58
|
-
address manager;
|
|
59
|
-
IMembersValidator membersValidator;
|
|
60
|
-
IIdentityV2 uniquenessValidator;
|
|
61
|
-
IERC20Upgradeable rewardToken;
|
|
62
|
-
}
|
|
63
45
|
|
|
64
46
|
struct UBISettings {
|
|
65
47
|
//number of days of each UBI pool cycle
|
|
@@ -70,8 +52,10 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
70
52
|
uint32 minActiveUsers;
|
|
71
53
|
// can you trigger claim for someone else
|
|
72
54
|
bool claimForEnabled;
|
|
55
|
+
// max daily claim amount
|
|
73
56
|
uint maxClaimAmount;
|
|
74
|
-
|
|
57
|
+
// max number of members in a pool
|
|
58
|
+
uint32 maxMembers;
|
|
75
59
|
bool onlyMembers;
|
|
76
60
|
}
|
|
77
61
|
|
|
@@ -90,7 +74,16 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
90
74
|
uint256 periodClaimers;
|
|
91
75
|
uint256 periodDistributed;
|
|
92
76
|
mapping(address => uint256) lastClaimed;
|
|
93
|
-
uint32
|
|
77
|
+
uint32 membersCount;
|
|
78
|
+
}
|
|
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;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
89
|
PoolSettings public settings;
|
|
@@ -98,6 +91,7 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
98
91
|
PoolStatus public status;
|
|
99
92
|
|
|
100
93
|
UBIPoolFactory public registry;
|
|
94
|
+
ExtendedSettings public extendedSettings;
|
|
101
95
|
|
|
102
96
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
103
97
|
constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
|
|
@@ -112,6 +106,10 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
112
106
|
return IRegistry(address(registry));
|
|
113
107
|
}
|
|
114
108
|
|
|
109
|
+
function getManagerFee() public view override returns (address feeRecipient, uint32 feeBps) {
|
|
110
|
+
return (settings.manager, extendedSettings.managerFeeBps);
|
|
111
|
+
}
|
|
112
|
+
|
|
115
113
|
/**
|
|
116
114
|
* @dev Initializes the contract with the given settings and limits.
|
|
117
115
|
* @param _settings The PoolSettings struct containing pool settings.
|
|
@@ -120,11 +118,13 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
120
118
|
function initialize(
|
|
121
119
|
PoolSettings memory _settings,
|
|
122
120
|
UBISettings memory _ubiSettings,
|
|
121
|
+
ExtendedSettings memory _extendedSettings,
|
|
123
122
|
UBIPoolFactory _registry
|
|
124
123
|
) external initializer {
|
|
125
124
|
registry = _registry;
|
|
126
125
|
settings = _settings;
|
|
127
126
|
ubiSettings = _ubiSettings;
|
|
127
|
+
extendedSettings = _extendedSettings;
|
|
128
128
|
_verifyPoolSettings(_settings);
|
|
129
129
|
_verifyUBISettings(_ubiSettings);
|
|
130
130
|
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender); // when using factory this gives factory role which then set role to the real msg.sender
|
|
@@ -148,36 +148,22 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
148
148
|
* the daily balance is determined by dividing current pool by the cycle length
|
|
149
149
|
* @return The amount of GoodDollar the user can claim
|
|
150
150
|
*/
|
|
151
|
-
function distributionFormula()
|
|
151
|
+
function distributionFormula() public returns (uint256) {
|
|
152
152
|
// once every claim cycle
|
|
153
153
|
uint256 currentDay = getCurrentDay();
|
|
154
154
|
if (currentDay >= status.currentDay + ubiSettings.claimPeriodDays) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
//start early cycle if daily pool size is +%5 previous pool or not enough until end of cycle
|
|
159
|
-
uint256 nextDailyPool = currentBalance / cycleLength;
|
|
160
|
-
bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
|
|
161
|
-
(currentDayInCycle() <= cycleLength &&
|
|
162
|
-
currentBalance < (status.dailyCyclePool * (cycleLength - currentDayInCycle())));
|
|
163
|
-
|
|
164
|
-
if (
|
|
165
|
-
currentDayInCycle() >= status.currentCycleLength || shouldStartEarlyCycle
|
|
166
|
-
) //start of cycle or first time
|
|
167
|
-
{
|
|
155
|
+
(uint256 nextDailyPool, uint256 nextDailyUbi, bool newCycle) = _calcNextDailyUBI();
|
|
156
|
+
if (newCycle) {
|
|
157
|
+
uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
|
|
168
158
|
status.dailyCyclePool = nextDailyPool;
|
|
169
|
-
status.currentCycleLength =
|
|
159
|
+
status.currentCycleLength = ubiSettings.cycleLengthDays;
|
|
170
160
|
status.startOfCycle = currentDay;
|
|
171
|
-
emit UBICycleCalculated(currentDay, currentBalance,
|
|
161
|
+
emit UBICycleCalculated(currentDay, currentBalance, ubiSettings.cycleLengthDays, nextDailyPool);
|
|
172
162
|
}
|
|
173
163
|
|
|
174
|
-
|
|
175
|
-
status.dailyUbi =
|
|
176
|
-
|
|
177
|
-
status.dailyCyclePool / max((prevPeriodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
|
|
178
|
-
);
|
|
179
|
-
//update minActiveUsers as claimers grow
|
|
180
|
-
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;
|
|
181
167
|
|
|
182
168
|
emit UBICalculated(
|
|
183
169
|
currentDay,
|
|
@@ -193,6 +179,32 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
193
179
|
return status.dailyUbi;
|
|
194
180
|
}
|
|
195
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
|
+
|
|
196
208
|
/**
|
|
197
209
|
* @dev returns the day count since start of current cycle
|
|
198
210
|
*/
|
|
@@ -231,12 +243,15 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
231
243
|
if (whitelistedRoot == address(0)) revert NOT_WHITELISTED(claimer);
|
|
232
244
|
|
|
233
245
|
// if open for anyone but has limits, we add the first claimers as members to handle the max claimers
|
|
234
|
-
if ((ubiSettings.
|
|
246
|
+
if ((ubiSettings.maxMembers > 0 && ubiSettings.onlyMembers == false)) _grantRole(MEMBER_ROLE, claimer);
|
|
235
247
|
|
|
236
248
|
// check membership if has claimers limits or limited to members only
|
|
237
|
-
if ((ubiSettings.
|
|
249
|
+
if ((ubiSettings.maxMembers > 0 || ubiSettings.onlyMembers) && hasRole(MEMBER_ROLE, claimer) == false)
|
|
238
250
|
revert NOT_MEMBER(claimer);
|
|
239
251
|
|
|
252
|
+
if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers)
|
|
253
|
+
revert MAX_PERIOD_CLAIMERS_REACHED(status.periodClaimers);
|
|
254
|
+
|
|
240
255
|
// calculats the formula up today ie on day 0 there are no active users, on day 1 any user
|
|
241
256
|
// (new or active) will trigger the calculation with the active users count of the day before
|
|
242
257
|
// and so on. the new or inactive users that will become active today, will not take into account
|
|
@@ -287,17 +302,17 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
287
302
|
|
|
288
303
|
function _grantRole(bytes32 role, address account) internal virtual override {
|
|
289
304
|
if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account) == false) {
|
|
290
|
-
if (ubiSettings.
|
|
291
|
-
revert
|
|
305
|
+
if (ubiSettings.maxMembers > 0 && status.membersCount > ubiSettings.maxMembers)
|
|
306
|
+
revert MAX_MEMBERS_REACHED();
|
|
292
307
|
registry.addMember(account);
|
|
293
|
-
status.
|
|
308
|
+
status.membersCount += 1;
|
|
294
309
|
}
|
|
295
310
|
super._grantRole(role, account);
|
|
296
311
|
}
|
|
297
312
|
|
|
298
313
|
function _revokeRole(bytes32 role, address account) internal virtual override {
|
|
299
314
|
if (role == MEMBER_ROLE && hasRole(MEMBER_ROLE, account)) {
|
|
300
|
-
status.
|
|
315
|
+
status.membersCount -= 1;
|
|
301
316
|
registry.removeMember(account);
|
|
302
317
|
}
|
|
303
318
|
super._revokeRole(role, account);
|
|
@@ -307,9 +322,13 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
307
322
|
* @dev Sets the safety limits for the pool.
|
|
308
323
|
* @param _ubiSettings The new safety limits.
|
|
309
324
|
*/
|
|
310
|
-
function setUBISettings(
|
|
325
|
+
function setUBISettings(
|
|
326
|
+
UBISettings memory _ubiSettings,
|
|
327
|
+
ExtendedSettings memory _extendedSettings
|
|
328
|
+
) public onlyRole(MANAGER_ROLE) {
|
|
311
329
|
_verifyUBISettings(_ubiSettings);
|
|
312
330
|
ubiSettings = _ubiSettings;
|
|
331
|
+
extendedSettings = _extendedSettings;
|
|
313
332
|
emit UBISettingsChanged(_ubiSettings);
|
|
314
333
|
}
|
|
315
334
|
|
|
@@ -345,28 +364,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
345
364
|
) revert INVALID_0_VALUE();
|
|
346
365
|
}
|
|
347
366
|
|
|
348
|
-
function estimateNextDailyUBI() public view returns (uint256) {
|
|
349
|
-
|
|
350
|
-
//start early cycle if we can increase the daily UBI pool
|
|
351
|
-
uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays;
|
|
352
|
-
bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
|
|
353
|
-
(currentDayInCycle() <= status.currentCycleLength &&
|
|
354
|
-
currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle())));
|
|
355
|
-
|
|
356
|
-
uint256 _dailyCyclePool = status.dailyCyclePool;
|
|
357
|
-
uint256 _dailyUbi;
|
|
358
|
-
if (
|
|
359
|
-
(currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle
|
|
360
|
-
) //start of cycle or first time
|
|
361
|
-
{
|
|
362
|
-
_dailyCyclePool = currentBalance / ubiSettings.cycleLengthDays;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
_dailyUbi = min(
|
|
366
|
-
ubiSettings.maxClaimAmount,
|
|
367
|
-
_dailyCyclePool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
|
|
368
|
-
);
|
|
369
|
-
return _dailyUbi;
|
|
367
|
+
function estimateNextDailyUBI() public view returns (uint256 nextDailyUbi) {
|
|
368
|
+
(, nextDailyUbi, ) = _calcNextDailyUBI();
|
|
370
369
|
}
|
|
371
370
|
|
|
372
371
|
function checkEntitlement() public view returns (uint256) {
|
|
@@ -384,6 +383,8 @@ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgrad
|
|
|
384
383
|
// current day has already been updated which means
|
|
385
384
|
// that the dailyUbi has been updated
|
|
386
385
|
if (status.currentDay == getCurrentDay() && status.dailyUbi > 0) {
|
|
386
|
+
if (extendedSettings.maxPeriodClaimers > 0 && status.periodClaimers >= extendedSettings.maxPeriodClaimers)
|
|
387
|
+
return 0;
|
|
387
388
|
return hasClaimed(_member) ? 0 : status.dailyUbi;
|
|
388
389
|
}
|
|
389
390
|
return estimateNextDailyUBI();
|