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