@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.
- package/contracts/DirectPayments/DirectPaymentsFactory.sol +20 -5
- package/contracts/DirectPayments/DirectPaymentsPool.sol +12 -5
- package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +10 -4
- package/contracts/UBI/MultiClaimModule.sol +78 -0
- package/contracts/UBI/UBIPool.sol +388 -0
- package/contracts/UBI/UBIPoolFactory.sol +159 -0
- package/contracts/utils/HelperLibrary.sol +4 -6
- package/contracts/utils/SwapRouterMock.sol +3 -3
- package/package.json +4 -3
- package/releases/deployment.json +3941 -30
- package/typechain-types/@gooddollar/goodprotocol/contracts/token/index.ts +0 -2
- package/typechain-types/@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol/ISuperGoodDollar.ts +30 -30
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.ts +122 -0
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.ts +30 -30
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.ts +7 -2
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance.ts +57 -8
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETH.ts +2905 -0
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETHCustom.ts +170 -0
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/index.ts +5 -0
- package/typechain-types/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts +2 -0
- package/typechain-types/@uniswap/index.ts +2 -0
- package/typechain-types/@uniswap/swap-router-contracts/contracts/index.ts +5 -0
- package/typechain-types/@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.ts +357 -0
- package/typechain-types/@uniswap/swap-router-contracts/contracts/interfaces/index.ts +4 -0
- package/typechain-types/@uniswap/swap-router-contracts/index.ts +5 -0
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +104 -0
- package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/IIdentityV2.ts +6 -8
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp.ts +1000 -0
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/IRegistry.ts +102 -0
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.sol/index.ts +5 -0
- package/typechain-types/contracts/GoodCollective/index.ts +2 -1
- package/typechain-types/contracts/UBI/MultiClaimModule.sol/IClaimable.ts +92 -0
- package/typechain-types/contracts/UBI/MultiClaimModule.sol/IModule.ts +196 -0
- package/typechain-types/contracts/UBI/MultiClaimModule.sol/MultiClaimModule.ts +242 -0
- package/typechain-types/contracts/UBI/MultiClaimModule.sol/index.ts +6 -0
- package/typechain-types/contracts/UBI/UBIPool.sol/IIdentityV2.ts +103 -0
- package/typechain-types/contracts/UBI/UBIPool.sol/IMembersValidator.ts +125 -0
- package/typechain-types/contracts/UBI/UBIPool.sol/UBIPool.ts +2322 -0
- package/typechain-types/contracts/UBI/UBIPool.sol/index.ts +6 -0
- package/typechain-types/contracts/UBI/UBIPoolFactory.ts +1279 -0
- package/typechain-types/contracts/UBI/index.ts +8 -0
- package/typechain-types/contracts/index.ts +2 -0
- package/typechain-types/contracts/utils/SwapRouterMock.ts +15 -21
- package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/index.ts +0 -1
- package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol/ISuperGoodDollar__factory.ts +6 -6
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1__factory.ts +78 -0
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken__factory.ts +6 -6
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluidGovernance__factory.ts +23 -5
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid__factory.ts +5 -0
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETHCustom__factory.ts +59 -0
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/ISETH__factory.ts +1869 -0
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/ISETH.sol/index.ts +5 -0
- package/typechain-types/factories/@superfluid-finance/ethereum-contracts/contracts/interfaces/tokens/index.ts +1 -0
- package/typechain-types/factories/@uniswap/index.ts +1 -0
- package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/index.ts +4 -0
- package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter__factory.ts +243 -0
- package/typechain-types/factories/@uniswap/swap-router-contracts/contracts/interfaces/index.ts +4 -0
- package/typechain-types/factories/@uniswap/swap-router-contracts/index.ts +4 -0
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +62 -1
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +4 -4
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/IIdentityV2__factory.ts +1 -1
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/GoodCollectiveSuperApp__factory.ts +728 -0
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/IRegistry__factory.ts +52 -0
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp.sol/index.ts +5 -0
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +1 -1
- package/typechain-types/factories/contracts/GoodCollective/index.ts +1 -1
- package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IClaimable__factory.ts +33 -0
- package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/IModule__factory.ts +84 -0
- package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/MultiClaimModule__factory.ts +150 -0
- package/typechain-types/factories/contracts/UBI/MultiClaimModule.sol/index.ts +6 -0
- package/typechain-types/factories/contracts/UBI/UBIPool.sol/IIdentityV2__factory.ts +45 -0
- package/typechain-types/factories/contracts/UBI/UBIPool.sol/IMembersValidator__factory.ts +60 -0
- package/typechain-types/factories/contracts/UBI/UBIPool.sol/UBIPool__factory.ts +1835 -0
- package/typechain-types/factories/contracts/UBI/UBIPool.sol/index.ts +6 -0
- package/typechain-types/factories/contracts/UBI/UBIPoolFactory__factory.ts +954 -0
- package/typechain-types/factories/contracts/UBI/index.ts +6 -0
- package/typechain-types/factories/contracts/index.ts +1 -0
- package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +1 -1
- package/typechain-types/factories/contracts/utils/SwapRouterMock__factory.ts +3 -13
- package/typechain-types/hardhat.d.ts +99 -9
- 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
|
|
60
|
-
|
|
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
|
|
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,
|
|
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 (
|
|
124
|
-
return
|
|
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
|
-
|
|
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/
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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
|
+
}
|