@gooddollar/goodcollective-contracts 1.0.5-beta.7a2c704 → 1.1.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 (81) hide show
  1. package/contracts/DirectPayments/DirectPaymentsFactory.sol +20 -5
  2. package/contracts/DirectPayments/DirectPaymentsPool.sol +12 -5
  3. package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +10 -4
  4. package/contracts/UBI/MultiClaimModule.sol +78 -0
  5. package/contracts/UBI/UBIPool.sol +388 -0
  6. package/contracts/UBI/UBIPoolFactory.sol +159 -0
  7. package/contracts/utils/HelperLibrary.sol +4 -6
  8. package/contracts/utils/SwapRouterMock.sol +3 -3
  9. package/package.json +4 -3
  10. package/releases/deployment.json +3941 -30
  11. package/typechain-types/@gooddollar/goodprotocol/contracts/token/index.ts +0 -2
  12. package/typechain-types/@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol/ISuperGoodDollar.ts +30 -30
  13. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.ts +122 -0
  14. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.ts +30 -30
  15. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.ts +7 -2
  16. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance.ts +57 -8
  17. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETH.ts +2905 -0
  18. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETHCustom.ts +170 -0
  19. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/index.ts +5 -0
  20. package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts +2 -0
  21. package/typechain-types/@uniswap/index.ts +2 -0
  22. package/typechain-types/@uniswap/swap-router-contracts/contracts/index.ts +5 -0
  23. package/typechain-types/@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.ts +357 -0
  24. package/typechain-types/@uniswap/swap-router-contracts/contracts/interfaces/index.ts +4 -0
  25. package/typechain-types/@uniswap/swap-router-contracts/index.ts +5 -0
  26. package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +104 -0
  27. package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/IIdentityV2.ts +6 -8
  28. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp.ts +1000 -0
  29. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/IRegistry.ts +102 -0
  30. package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/index.ts +5 -0
  31. package/typechain-types/contracts/GoodCollective/index.ts +2 -1
  32. package/typechain-types/contracts/UBI/MultiClaimModule.sol/IClaimable.ts +92 -0
  33. package/typechain-types/contracts/UBI/MultiClaimModule.sol/IModule.ts +196 -0
  34. package/typechain-types/contracts/UBI/MultiClaimModule.sol/MultiClaimModule.ts +242 -0
  35. package/typechain-types/contracts/UBI/MultiClaimModule.sol/index.ts +6 -0
  36. package/typechain-types/contracts/UBI/UBIPool.sol/IIdentityV2.ts +103 -0
  37. package/typechain-types/contracts/UBI/UBIPool.sol/IMembersValidator.ts +125 -0
  38. package/typechain-types/contracts/UBI/UBIPool.sol/UBIPool.ts +2322 -0
  39. package/typechain-types/contracts/UBI/UBIPool.sol/index.ts +6 -0
  40. package/typechain-types/contracts/UBI/UBIPoolFactory.ts +1279 -0
  41. package/typechain-types/contracts/UBI/index.ts +8 -0
  42. package/typechain-types/contracts/index.ts +2 -0
  43. package/typechain-types/contracts/utils/SwapRouterMock.ts +15 -21
  44. package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/index.ts +0 -1
  45. package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol/ISuperGoodDollar__factory.ts +6 -6
  46. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1__factory.ts +78 -0
  47. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken__factory.ts +6 -6
  48. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance__factory.ts +23 -5
  49. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid__factory.ts +5 -0
  50. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETHCustom__factory.ts +59 -0
  51. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETH__factory.ts +1869 -0
  52. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/index.ts +5 -0
  53. package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts +1 -0
  54. package/typechain-types/factories/@uniswap/index.ts +1 -0
  55. package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/index.ts +4 -0
  56. package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter__factory.ts +243 -0
  57. package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/interfaces/index.ts +4 -0
  58. package/typechain-types/factories/@uniswap/swap-router-contracts/index.ts +4 -0
  59. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +62 -1
  60. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +4 -4
  61. package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/IIdentityV2__factory.ts +1 -1
  62. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp__factory.ts +728 -0
  63. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/IRegistry__factory.ts +52 -0
  64. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/index.ts +5 -0
  65. package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +1 -1
  66. package/typechain-types/factories/contracts/GoodCollective/index.ts +1 -1
  67. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IClaimable__factory.ts +33 -0
  68. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IModule__factory.ts +84 -0
  69. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/MultiClaimModule__factory.ts +150 -0
  70. package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/index.ts +6 -0
  71. package/typechain-types/factories/contracts/UBI/UBIPool.sol/IIdentityV2__factory.ts +45 -0
  72. package/typechain-types/factories/contracts/UBI/UBIPool.sol/IMembersValidator__factory.ts +60 -0
  73. package/typechain-types/factories/contracts/UBI/UBIPool.sol/UBIPool__factory.ts +1835 -0
  74. package/typechain-types/factories/contracts/UBI/UBIPool.sol/index.ts +6 -0
  75. package/typechain-types/factories/contracts/UBI/UBIPoolFactory__factory.ts +954 -0
  76. package/typechain-types/factories/contracts/UBI/index.ts +6 -0
  77. package/typechain-types/factories/contracts/index.ts +1 -0
  78. package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +1 -1
  79. package/typechain-types/factories/contracts/utils/SwapRouterMock__factory.ts +3 -13
  80. package/typechain-types/hardhat.d.ts +99 -9
  81. package/typechain-types/index.ts +22 -4
@@ -15,6 +15,7 @@ import "hardhat/console.sol";
15
15
 
16
16
  contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
17
17
  error NOT_PROJECT_OWNER();
18
+ error NOT_POOL();
18
19
 
19
20
  event PoolCreated(
20
21
  address indexed pool,
@@ -45,6 +46,9 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
45
46
  address public feeRecipient;
46
47
  uint32 public feeBps;
47
48
 
49
+ mapping(address => address[]) public memberPools;
50
+ address[] public pools;
51
+
48
52
  modifier onlyProjectOwnerOrNon(string memory projectId) {
49
53
  DirectPaymentsPool controlPool = projectIdToControlPool[keccak256(bytes(projectId))];
50
54
  // console.log("result %s", controlPool.hasRole(controlPool.DEFAULT_ADMIN_ROLE(), msg.sender));
@@ -56,16 +60,21 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
56
60
  _;
57
61
  }
58
62
 
59
- modifier onlyProjectOwnerByPool(DirectPaymentsPool pool) {
60
- string memory projectId = registry[address(pool)].projectId;
61
- DirectPaymentsPool controlPool = projectIdToControlPool[keccak256(bytes(projectId))];
62
- if (controlPool.hasRole(controlPool.DEFAULT_ADMIN_ROLE(), msg.sender) == false) {
63
+ modifier onlyPoolOwner(DirectPaymentsPool pool) {
64
+ if (pool.hasRole(pool.DEFAULT_ADMIN_ROLE(), msg.sender) == false) {
63
65
  revert NOT_PROJECT_OWNER();
64
66
  }
65
67
 
66
68
  _;
67
69
  }
68
70
 
71
+ modifier onlyPool() {
72
+ if (bytes(registry[msg.sender].projectId).length == 0) {
73
+ revert NOT_POOL();
74
+ }
75
+ _;
76
+ }
77
+
69
78
  function _authorizeUpgrade(address _impl) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
70
79
 
71
80
  function initialize(
@@ -138,12 +147,14 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
138
147
  registry[address(pool)].projectId = _projectId;
139
148
 
140
149
  pool.renounceRole(DEFAULT_ADMIN_ROLE, address(this));
150
+ pools.push(address(pool));
151
+
141
152
  emit PoolCreated(address(pool), _projectId, _ipfs, nextNftType, _settings, _limits);
142
153
 
143
154
  nextNftType++;
144
155
  }
145
156
 
146
- function changePoolDetails(DirectPaymentsPool _pool, string memory _ipfs) external onlyProjectOwnerByPool(_pool) {
157
+ function changePoolDetails(DirectPaymentsPool _pool, string memory _ipfs) external onlyPoolOwner(_pool) {
147
158
  registry[address(_pool)].ipfs = _ipfs;
148
159
  emit PoolDetailsChanged(address(_pool), _ipfs);
149
160
  }
@@ -162,4 +173,8 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
162
173
  feeBps = _feeBps;
163
174
  feeRecipient = _feeRecipient;
164
175
  }
176
+
177
+ function addMember(address account) external onlyPool {
178
+ memberPools[account].push(msg.sender);
179
+ }
165
180
  }
@@ -21,7 +21,7 @@ interface IMembersValidator {
21
21
  }
22
22
 
23
23
  interface IIdentityV2 {
24
- function getWhitelistedRoot(address member) external returns (address);
24
+ function getWhitelistedRoot(address member) external view returns (address);
25
25
  }
26
26
 
27
27
  /**
@@ -112,7 +112,7 @@ contract DirectPaymentsPool is
112
112
  DirectPaymentsFactory public registry;
113
113
 
114
114
  /// @custom:oz-upgrades-unsafe-allow constructor
115
- constructor(ISuperfluid _host, ISwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
115
+ constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
116
116
 
117
117
  /**
118
118
  * @dev Authorizes an upgrade for the implementation contract.
@@ -120,8 +120,8 @@ contract DirectPaymentsPool is
120
120
  */
121
121
  function _authorizeUpgrade(address impl) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
122
122
 
123
- function getRegistry() public view override returns (DirectPaymentsFactory) {
124
- return DirectPaymentsFactory(registry);
123
+ function getRegistry() public view override returns (IRegistry) {
124
+ return IRegistry(address(registry));
125
125
  }
126
126
 
127
127
  /**
@@ -341,10 +341,17 @@ contract DirectPaymentsPool is
341
341
  }
342
342
  }
343
343
 
344
- _setupRole(MEMBER_ROLE, member);
344
+ _grantRole(MEMBER_ROLE, member);
345
345
  return true;
346
346
  }
347
347
 
348
+ function _grantRole(bytes32 role, address account) internal virtual override {
349
+ if (role == MEMBER_ROLE) {
350
+ registry.addMember(account);
351
+ }
352
+ super._grantRole(role, account);
353
+ }
354
+
348
355
  function mintNFT(address _to, ProvableNFT.NFTData memory _nftData, bool withClaim) external onlyRole(MINTER_ROLE) {
349
356
  uint nftId = nft.mintPermissioned(_to, _nftData, true, "");
350
357
  if (withClaim) {
@@ -9,13 +9,19 @@ import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/cont
9
9
  import { CFAv1Library, IConstantFlowAgreementV1 } from "@superfluid-finance/ethereum-contracts/contracts/apps/CFAv1Library.sol";
10
10
 
11
11
  import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
12
- import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
12
+ import "@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol";
13
13
 
14
14
  import "../DirectPayments/DirectPaymentsFactory.sol";
15
15
  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
+
19
25
  abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
20
26
  int96 public constant MIN_FLOW_RATE = 386e9;
21
27
 
@@ -51,7 +57,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
51
57
  // ask about "receiver" can it be different then app?
52
58
 
53
59
  /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
54
- ISwapRouter public immutable swapRouter;
60
+ IV3SwapRouter public immutable swapRouter;
55
61
 
56
62
  struct SupporterData {
57
63
  uint256 contribution;
@@ -71,12 +77,12 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
71
77
  uint256[48] private _reserved;
72
78
 
73
79
  /// @custom:oz-upgrades-unsafe-allow constructor
74
- constructor(ISuperfluid _host, ISwapRouter _swapRouter) SuperAppBaseFlow(_host) {
80
+ constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) SuperAppBaseFlow(_host) {
75
81
  if (address(_host) == address(0)) revert ZERO_ADDRESS();
76
82
  swapRouter = _swapRouter;
77
83
  }
78
84
 
79
- function getRegistry() public view virtual returns (DirectPaymentsFactory);
85
+ function getRegistry() public view virtual returns (IRegistry);
80
86
 
81
87
  /**
82
88
  * @dev Sets the address of the super token and registers the app with the host
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
5
+
6
+ // Interface for ERC-7579 IModule (minimal implementation)
7
+ interface IModule {
8
+ /**
9
+ * @dev This function is called by the smart account during installation of the module
10
+ * @param data arbitrary data that may be required on the module during `onInstall` initialization
11
+ *
12
+ * MUST revert on error (i.e. if module is already enabled)
13
+ */
14
+ function onInstall(bytes calldata data) external;
15
+
16
+ /**
17
+ * @dev This function is called by the smart account during uninstallation of the module
18
+ * @param data arbitrary data that may be required on the module during `onUninstall` de-initialization
19
+ *
20
+ * MUST revert on error
21
+ */
22
+ function onUninstall(bytes calldata data) external;
23
+
24
+ /**
25
+ * @dev Returns boolean value if module is a certain type
26
+ * @param typeID the module type ID according the ERC-7579 spec
27
+ *
28
+ * MUST return true if the module is of the given type and false otherwise
29
+ */
30
+ function isModuleType(uint256 typeID) external view returns (bool);
31
+
32
+ /**
33
+ * @dev Returns bit-encoded integer of the different typeIds of the module
34
+ *
35
+ * MUST return all the bit-encoded typeIds of the module
36
+ */
37
+ function getModuleTypes() external view returns (uint256);
38
+ }
39
+
40
+ interface IClaimable {
41
+ function claim() external;
42
+ }
43
+
44
+ contract MultiClaimModule is IModule {
45
+ // Function to claim tokens from multiple contracts
46
+ function multiClaim(
47
+ address[] calldata contracts,
48
+ IERC20Upgradeable token,
49
+ address recipient
50
+ ) external returns (bool[] memory success) {
51
+ // Loop through each contract address
52
+ uint256 tokenBalance = token.balanceOf(address(this));
53
+ success = new bool[](contracts.length);
54
+ for (uint i = 0; i < contracts.length; i++) {
55
+ try IClaimable(contracts[i]).claim() {
56
+ success[i] = true;
57
+ } catch {}
58
+ }
59
+
60
+ // Transfer any token balance after claims to recipient
61
+ uint256 tokenBalanceDiff = token.balanceOf(address(this)) - tokenBalance;
62
+ if (tokenBalanceDiff > 0) {
63
+ token.transfer(recipient, tokenBalanceDiff);
64
+ }
65
+ }
66
+
67
+ function onInstall(bytes calldata data) external override {}
68
+
69
+ function onUninstall(bytes calldata data) external override {}
70
+
71
+ function isModuleType(uint256 typeID) external view override returns (bool) {
72
+ return true;
73
+ }
74
+
75
+ function getModuleTypes() public view override returns (uint256) {
76
+ return 2;
77
+ }
78
+ }
@@ -0,0 +1,388 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity >=0.8;
3
+ import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
4
+ import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5
+ import { IERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
6
+ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
7
+ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
8
+ import { IERC721ReceiverUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol";
9
+
10
+ import "../GoodCollective/GoodCollectiveSuperApp.sol";
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
+ }
25
+
26
+ contract UBIPool is AccessControlUpgradeable, GoodCollectiveSuperApp, UUPSUpgradeable {
27
+ using SafeERC20Upgradeable for IERC20Upgradeable;
28
+
29
+ error CLAIMFOR_DISABLED();
30
+ error NOT_MEMBER(address claimer);
31
+ error NOT_WHITELISTED(address claimer);
32
+ error ALREADY_CLAIMED(address claimer);
33
+ error INVALID_0_VALUE();
34
+ error EMPTY_MANAGER();
35
+ error MAX_MEMBERS_REACHED();
36
+
37
+ bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
38
+ bytes32 public constant MEMBER_ROLE = keccak256("MEMBER_ROLE");
39
+
40
+ event PoolSettingsChanged(PoolSettings settings);
41
+ event UBISettingsChanged(UBISettings settings);
42
+ // Emits when daily ubi is calculated
43
+ event UBICalculated(
44
+ uint256 day,
45
+ uint256 dailyUbi,
46
+ uint256 blockNumber,
47
+ uint256 periodClaimers,
48
+ uint256 periodDistributed
49
+ );
50
+
51
+ //Emits whenever a new multi day cycle starts
52
+ event UBICycleCalculated(uint256 day, uint256 pool, uint256 cycleLength, uint256 dailyUBIPool);
53
+
54
+ 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
+
63
+ struct UBISettings {
64
+ //number of days of each UBI pool cycle
65
+ uint32 cycleLengthDays;
66
+ //how often can someone claim their UBI
67
+ uint32 claimPeriodDays;
68
+ //minimum amount of users to divide the pool for, renamed from defaultDailyUbi
69
+ uint32 minActiveUsers;
70
+ // can you trigger claim for someone else
71
+ bool claimForEnabled;
72
+ uint maxClaimAmount;
73
+ uint32 maxMembers;
74
+ }
75
+
76
+ struct PoolStatus {
77
+ //current day of distribution
78
+ uint256 currentDay;
79
+ // Result of distribution formula
80
+ // calculated each day
81
+ uint256 dailyUbi;
82
+ //the amount of G$ UBI pool for each day in the cycle to be divided by active users
83
+ uint256 dailyCyclePool;
84
+ //timestamp of current cycle start
85
+ uint256 startOfCycle;
86
+ //should be 0 for starters so distributionFormula detects new cycle on first day claim
87
+ uint256 currentCycleLength;
88
+ uint256 periodClaimers;
89
+ uint256 periodDistributed;
90
+ mapping(address => uint256) lastClaimed;
91
+ uint32 membersCount;
92
+ }
93
+
94
+ PoolSettings public settings;
95
+ UBISettings public ubiSettings;
96
+ PoolStatus public status;
97
+
98
+ UBIPoolFactory public registry;
99
+
100
+ /// @custom:oz-upgrades-unsafe-allow constructor
101
+ constructor(ISuperfluid _host, IV3SwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
102
+
103
+ /**
104
+ * @dev Authorizes an upgrade for the implementation contract.
105
+ * @param impl The address of the new implementation contract.
106
+ */
107
+ function _authorizeUpgrade(address impl) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
108
+
109
+ function getRegistry() public view override returns (IRegistry) {
110
+ return IRegistry(address(registry));
111
+ }
112
+
113
+ /**
114
+ * @dev Initializes the contract with the given settings and limits.
115
+ * @param _settings The PoolSettings struct containing pool settings.
116
+ * @param _ubiSettings The UBISettings struct containing safety limits.
117
+ */
118
+ function initialize(
119
+ PoolSettings memory _settings,
120
+ UBISettings memory _ubiSettings,
121
+ UBIPoolFactory _registry
122
+ ) external initializer {
123
+ registry = _registry;
124
+ settings = _settings;
125
+ ubiSettings = _ubiSettings;
126
+ _verifyUBISettings(_ubiSettings);
127
+ _setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
128
+ _setupRole(MANAGER_ROLE, _settings.manager);
129
+ setSuperToken(ISuperToken(address(settings.rewardToken)));
130
+ }
131
+
132
+ function upgradeToLatest(bytes memory data) external payable virtual {
133
+ address impl = address(UBIPoolFactory(address(registry)).impl());
134
+ _authorizeUpgrade(impl);
135
+ _upgradeToAndCallUUPS(impl, data, false);
136
+ }
137
+
138
+ function getCurrentDay() public view returns (uint256) {
139
+ return (block.timestamp - 12 hours) / (1 days); //make day start at 12:00pm
140
+ }
141
+
142
+ /**
143
+ * @dev The claim calculation formula. Divide the daily pool with
144
+ * the sum of the active users.
145
+ * the daily balance is determined by dividing current pool by the cycle length
146
+ * @return The amount of GoodDollar the user can claim
147
+ */
148
+ function distributionFormula() internal returns (uint256) {
149
+ // once every claim cycle
150
+ uint256 currentDay = getCurrentDay();
151
+ if (currentDay > status.currentDay + ubiSettings.claimPeriodDays) {
152
+ status.currentDay = currentDay;
153
+ uint32 cycleLength = ubiSettings.cycleLengthDays;
154
+ uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
155
+ //start early cycle if daily pool size is +%5 previous pool or not enough until end of cycle
156
+ uint256 nextDailyPool = currentBalance / cycleLength;
157
+ bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
158
+ currentBalance < (status.dailyCyclePool * (cycleLength - currentDayInCycle()));
159
+
160
+ if (
161
+ currentDayInCycle() >= status.currentCycleLength || shouldStartEarlyCycle
162
+ ) //start of cycle or first time
163
+ {
164
+ status.dailyCyclePool = nextDailyPool;
165
+ status.currentCycleLength = cycleLength;
166
+ status.startOfCycle = currentDay;
167
+ emit UBICycleCalculated(currentDay, currentBalance, cycleLength, nextDailyPool);
168
+ }
169
+
170
+ uint256 prevPeriodClaimers = status.periodClaimers;
171
+ status.dailyUbi = min(
172
+ ubiSettings.maxClaimAmount,
173
+ status.dailyCyclePool / max((prevPeriodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
174
+ );
175
+ //update minActiveUsers as claimers grow
176
+ ubiSettings.minActiveUsers = uint32(max(prevPeriodClaimers / 2, ubiSettings.minActiveUsers));
177
+
178
+ emit UBICalculated(
179
+ currentDay,
180
+ status.dailyUbi,
181
+ block.number,
182
+ status.periodClaimers,
183
+ status.periodDistributed
184
+ );
185
+ status.periodClaimers = 0;
186
+ status.periodDistributed = 0;
187
+ }
188
+
189
+ return status.dailyUbi;
190
+ }
191
+
192
+ /**
193
+ * @dev returns the day count since start of current cycle
194
+ */
195
+ function currentDayInCycle() public view returns (uint256) {
196
+ return getCurrentDay() - status.startOfCycle;
197
+ }
198
+
199
+ function max(uint256 a, uint256 b) private pure returns (uint256) {
200
+ return a >= b ? a : b;
201
+ }
202
+
203
+ function min(uint256 a, uint256 b) private pure returns (uint256) {
204
+ return a < b ? a : b;
205
+ }
206
+
207
+ /**
208
+ * @dev Claims UBI
209
+ */
210
+ function claim() external {
211
+ _claim(msg.sender, true);
212
+ }
213
+
214
+ function claimFor(address claimer, bool sendToWhitelistedRoot) external {
215
+ if (!ubiSettings.claimForEnabled) revert CLAIMFOR_DISABLED();
216
+
217
+ _claim(claimer, sendToWhitelistedRoot);
218
+ }
219
+
220
+ /**
221
+ * @dev Claims UBI
222
+ * @param claimer the address of the claimer.
223
+ */
224
+
225
+ function _claim(address claimer, bool sendToWhitelistedRoot) internal {
226
+ address whitelistedRoot = IIdentityV2(settings.uniquenessValidator).getWhitelistedRoot(claimer);
227
+ if (whitelistedRoot == address(0)) revert NOT_WHITELISTED(claimer);
228
+ if (address(settings.membersValidator) != address(0) && hasRole(MEMBER_ROLE, claimer) == false)
229
+ revert NOT_MEMBER(claimer);
230
+
231
+ // calculats the formula up today ie on day 0 there are no active users, on day 1 any user
232
+ // (new or active) will trigger the calculation with the active users count of the day before
233
+ // and so on. the new or inactive users that will become active today, will not take into account
234
+ // within the calculation.
235
+ uint256 dailyUbi = distributionFormula();
236
+
237
+ // active user which has not claimed today yet, ie user last claimed < today
238
+ if (status.lastClaimed[whitelistedRoot] == status.currentDay) revert ALREADY_CLAIMED(whitelistedRoot);
239
+
240
+ status.lastClaimed[whitelistedRoot] = status.currentDay;
241
+ status.periodClaimers += 1;
242
+ status.periodDistributed += dailyUbi;
243
+
244
+ settings.rewardToken.safeTransfer(sendToWhitelistedRoot ? whitelistedRoot : claimer, dailyUbi);
245
+
246
+ emit UBIClaimed(whitelistedRoot, claimer, dailyUbi);
247
+ }
248
+
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
+ /**
262
+ * @dev Adds a member to the contract.
263
+ * @param member The address of the member to add.
264
+ * @param extraData Additional data to validate the member.
265
+ */
266
+
267
+ function addMember(address member, bytes memory extraData) external returns (bool isMember) {
268
+ if (hasRole(MEMBER_ROLE, member)) return true;
269
+
270
+ if (address(settings.uniquenessValidator) != address(0)) {
271
+ address rootAddress = settings.uniquenessValidator.getWhitelistedRoot(member);
272
+ if (rootAddress == address(0)) revert NOT_WHITELISTED(member);
273
+ }
274
+
275
+ // if no members validator then anyone can join the pool
276
+ if (address(settings.membersValidator) != address(0)) {
277
+ if (settings.membersValidator.isMemberValid(address(this), msg.sender, member, extraData) == false) {
278
+ return false;
279
+ }
280
+ }
281
+
282
+ _grantRole(MEMBER_ROLE, member);
283
+ return true;
284
+ }
285
+
286
+ function _grantRole(bytes32 role, address account) internal virtual override {
287
+ if (role == MEMBER_ROLE) {
288
+ registry.addMember(account);
289
+ if (ubiSettings.maxMembers > 0 && status.membersCount > ubiSettings.maxMembers)
290
+ revert MAX_MEMBERS_REACHED();
291
+ status.membersCount += 1;
292
+ }
293
+ super._grantRole(role, account);
294
+ }
295
+
296
+ function _revokeRole(bytes32 role, address account) internal virtual override {
297
+ if (role == MEMBER_ROLE) {
298
+ status.membersCount -= 1;
299
+ }
300
+ super._revokeRole(role, account);
301
+ }
302
+
303
+ /**
304
+ * @dev Sets the safety limits for the pool.
305
+ * @param _ubiSettings The new safety limits.
306
+ */
307
+ function setUBISettings(UBISettings memory _ubiSettings) public onlyRole(MANAGER_ROLE) {
308
+ _verifyUBISettings(_ubiSettings);
309
+ ubiSettings = _ubiSettings;
310
+ emit UBISettingsChanged(_ubiSettings);
311
+ }
312
+
313
+ function _verifyUBISettings(UBISettings memory _ubiSettings) internal pure {
314
+ if (
315
+ _ubiSettings.claimPeriodDays == 0 ||
316
+ _ubiSettings.cycleLengthDays == 0 ||
317
+ _ubiSettings.minActiveUsers == 0 ||
318
+ _ubiSettings.maxClaimAmount == 0
319
+ ) revert INVALID_0_VALUE();
320
+ }
321
+
322
+ /**
323
+ * @dev Sets the settings for the pool.
324
+ * @param _settings The new pool settings.
325
+ */
326
+ function setPoolSettings(PoolSettings memory _settings) public onlyRole(MANAGER_ROLE) {
327
+ if (_settings.manager == address(0)) revert EMPTY_MANAGER();
328
+
329
+ if (_settings.manager != settings.manager) {
330
+ _revokeRole(MANAGER_ROLE, settings.manager);
331
+ _setupRole(MANAGER_ROLE, _settings.manager);
332
+ }
333
+ settings = _settings;
334
+ emit PoolSettingsChanged(_settings);
335
+ }
336
+
337
+ function estimateNextDailyUBI() public view returns (uint256) {
338
+ uint256 currentBalance = settings.rewardToken.balanceOf(address(this));
339
+ //start early cycle if we can increase the daily UBI pool
340
+ uint256 nextDailyPool = currentBalance / ubiSettings.cycleLengthDays;
341
+ bool shouldStartEarlyCycle = nextDailyPool > (status.dailyCyclePool * 105) / 100 ||
342
+ currentBalance < (status.dailyCyclePool * (status.currentCycleLength - currentDayInCycle()));
343
+
344
+ uint256 _dailyCyclePool = status.dailyCyclePool;
345
+ uint256 _dailyUbi;
346
+ if (
347
+ (currentDayInCycle() + 1) >= status.currentCycleLength || shouldStartEarlyCycle
348
+ ) //start of cycle or first time
349
+ {
350
+ _dailyCyclePool = currentBalance / ubiSettings.cycleLengthDays;
351
+ }
352
+
353
+ _dailyUbi = min(
354
+ ubiSettings.maxClaimAmount,
355
+ _dailyCyclePool / max((status.periodClaimers * 10500) / 10000, ubiSettings.minActiveUsers)
356
+ );
357
+ return _dailyUbi;
358
+ }
359
+
360
+ function checkEntitlement() public view returns (uint256) {
361
+ return checkEntitlement(msg.sender);
362
+ }
363
+
364
+ /**
365
+ * @dev Checks the amount which the sender address is eligible to claim for,
366
+ * regardless if they have been whitelisted or not. In case the user is
367
+ * active, then the current day must be equal to the actual day, i.e. claim
368
+ * or fish has already been executed today.
369
+ * @return The amount of GD tokens the address can claim.
370
+ */
371
+ function checkEntitlement(address _member) public view returns (uint256) {
372
+ // current day has already been updated which means
373
+ // that the dailyUbi has been updated
374
+ if (status.currentDay == getCurrentDay() && status.dailyUbi > 0) {
375
+ return hasClaimed(_member) ? 0 : status.dailyUbi;
376
+ }
377
+ return estimateNextDailyUBI();
378
+ }
379
+
380
+ function hasClaimed(address _member) public view returns (bool) {
381
+ address whitelistedRoot = IIdentityV2(settings.uniquenessValidator).getWhitelistedRoot(_member);
382
+ return status.lastClaimed[whitelistedRoot] == status.currentDay;
383
+ }
384
+
385
+ function nextClaimTime() public view returns (uint256) {
386
+ return getCurrentDay() * (1 days) - (12 hours);
387
+ }
388
+ }