@gooddollar/goodcollective-contracts 1.0.2 → 1.0.4
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 +33 -4
- package/contracts/DirectPayments/DirectPaymentsPool.sol +16 -9
- package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +164 -29
- package/package.json +1 -1
- package/releases/deployment.json +1669 -4538
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +142 -4
- package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool.ts +277 -91
- package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/INameService.ts +102 -0
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.ts +165 -8
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +144 -2
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +233 -38
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/INameService__factory.ts +45 -0
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +148 -4
- package/typechain-types/@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable.ts +0 -193
- package/typechain-types/contracts/Lock.ts +0 -148
- package/typechain-types/factories/@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20PermitUpgradeable__factory.ts +0 -105
- package/typechain-types/factories/contracts/Lock__factory.ts +0 -129
|
@@ -31,9 +31,11 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
|
|
|
31
31
|
mapping(address => PoolRegistry) public registry;
|
|
32
32
|
mapping(bytes32 => DirectPaymentsPool) public projectIdToControlPool;
|
|
33
33
|
|
|
34
|
+
address public feeRecipient;
|
|
35
|
+
uint32 public feeBps;
|
|
36
|
+
|
|
34
37
|
modifier onlyProjectOwnerOrNon(string memory projectId) {
|
|
35
38
|
DirectPaymentsPool controlPool = projectIdToControlPool[keccak256(bytes(projectId))];
|
|
36
|
-
console.log("control:%s sender:%s %s", address(controlPool), msg.sender);
|
|
37
39
|
// console.log("result %s", controlPool.hasRole(controlPool.DEFAULT_ADMIN_ROLE(), msg.sender));
|
|
38
40
|
if (address(controlPool) != address(0)) {
|
|
39
41
|
if (controlPool.hasRole(controlPool.DEFAULT_ADMIN_ROLE(), msg.sender) == false) {
|
|
@@ -55,16 +57,32 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
|
|
|
55
57
|
|
|
56
58
|
function _authorizeUpgrade(address _impl) internal virtual override onlyRole(DEFAULT_ADMIN_ROLE) {}
|
|
57
59
|
|
|
58
|
-
function initialize(
|
|
60
|
+
function initialize(
|
|
61
|
+
address _owner,
|
|
62
|
+
address _dpimpl,
|
|
63
|
+
address _nftimpl,
|
|
64
|
+
address _feeRecipient,
|
|
65
|
+
uint32 _feeBps
|
|
66
|
+
) external initializer {
|
|
59
67
|
nextNftType = 1;
|
|
60
68
|
impl = _dpimpl;
|
|
61
|
-
bytes memory initCall = abi.encodeWithSelector(ProvableNFT.initialize.selector, "
|
|
69
|
+
bytes memory initCall = abi.encodeWithSelector(ProvableNFT.initialize.selector, "GoodCollective NFT", "GC-NFT");
|
|
62
70
|
nft = ProvableNFT(address(new ERC1967Proxy(_nftimpl, initCall)));
|
|
71
|
+
feeRecipient = _feeRecipient;
|
|
72
|
+
feeBps = _feeBps;
|
|
63
73
|
|
|
64
74
|
nft.grantRole(DEFAULT_ADMIN_ROLE, _owner);
|
|
65
75
|
_setupRole(DEFAULT_ADMIN_ROLE, _owner);
|
|
66
76
|
}
|
|
67
77
|
|
|
78
|
+
//TODO: implement a pool that's auto upgradeable using beacon method
|
|
79
|
+
function createBeaconPool(
|
|
80
|
+
string memory _projectId,
|
|
81
|
+
string memory _ipfs,
|
|
82
|
+
DirectPaymentsPool.PoolSettings memory _settings,
|
|
83
|
+
DirectPaymentsPool.SafetyLimits memory _limits
|
|
84
|
+
) external onlyProjectOwnerOrNon(_projectId) returns (DirectPaymentsPool pool) {}
|
|
85
|
+
|
|
68
86
|
function createPool(
|
|
69
87
|
string memory _projectId,
|
|
70
88
|
string memory _ipfs,
|
|
@@ -74,7 +92,13 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
|
|
|
74
92
|
//TODO: add check if msg.sender is whitelisted
|
|
75
93
|
|
|
76
94
|
_settings.nftType = nextNftType;
|
|
77
|
-
bytes memory initCall = abi.encodeWithSelector(
|
|
95
|
+
bytes memory initCall = abi.encodeWithSelector(
|
|
96
|
+
DirectPaymentsPool.initialize.selector,
|
|
97
|
+
nft,
|
|
98
|
+
_settings,
|
|
99
|
+
_limits,
|
|
100
|
+
address(this)
|
|
101
|
+
);
|
|
78
102
|
pool = DirectPaymentsPool(address(new ERC1967Proxy(impl, initCall)));
|
|
79
103
|
|
|
80
104
|
nft.grantRole(nft.getManagerRole(nextNftType), _settings.manager);
|
|
@@ -105,4 +129,9 @@ contract DirectPaymentsFactory is AccessControlUpgradeable, UUPSUpgradeable {
|
|
|
105
129
|
impl = _impl;
|
|
106
130
|
emit UpdatedImpl(_impl);
|
|
107
131
|
}
|
|
132
|
+
|
|
133
|
+
function setFeeInfo(address _feeRecipient, uint32 _feeBps) external onlyRole(DEFAULT_ADMIN_ROLE) {
|
|
134
|
+
feeBps = _feeBps;
|
|
135
|
+
feeRecipient = _feeRecipient;
|
|
136
|
+
}
|
|
108
137
|
}
|
|
@@ -55,7 +55,8 @@ contract DirectPaymentsPool is
|
|
|
55
55
|
event PoolLimitsChanged(SafetyLimits limits);
|
|
56
56
|
event MemberAdded(address member);
|
|
57
57
|
event MemberRemoved(address member);
|
|
58
|
-
event
|
|
58
|
+
event EventRewardClaimed(uint256 indexed tokenId, ProvableNFT.EventData eventData);
|
|
59
|
+
event NFTClaimed(uint256 indexed tokenId, uint256 totalRewards);
|
|
59
60
|
|
|
60
61
|
// Define functions
|
|
61
62
|
struct PoolSettings {
|
|
@@ -64,7 +65,7 @@ contract DirectPaymentsPool is
|
|
|
64
65
|
uint128[] rewardPerEvent;
|
|
65
66
|
address manager;
|
|
66
67
|
IMembersValidator membersValidator;
|
|
67
|
-
IIdentityV2
|
|
68
|
+
IIdentityV2 uniquenessValidator;
|
|
68
69
|
IERC20Upgradeable rewardToken;
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -90,7 +91,7 @@ contract DirectPaymentsPool is
|
|
|
90
91
|
mapping(address => bool) public members;
|
|
91
92
|
mapping(address => LimitsData) public memberLimits;
|
|
92
93
|
LimitsData public globalLimits;
|
|
93
|
-
|
|
94
|
+
DirectPaymentsFactory public registry;
|
|
94
95
|
|
|
95
96
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
96
97
|
constructor(ISuperfluid _host, ISwapRouter _swapRouter) GoodCollectiveSuperApp(_host, _swapRouter) {}
|
|
@@ -101,6 +102,10 @@ contract DirectPaymentsPool is
|
|
|
101
102
|
*/
|
|
102
103
|
function _authorizeUpgrade(address impl) internal virtual override {}
|
|
103
104
|
|
|
105
|
+
function getRegistry() public view override returns (DirectPaymentsFactory) {
|
|
106
|
+
return DirectPaymentsFactory(registry);
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
/**
|
|
105
110
|
* @dev Initializes the contract with the given settings and limits.
|
|
106
111
|
* @param _nft The ProvableNFT contract address.
|
|
@@ -110,9 +115,10 @@ contract DirectPaymentsPool is
|
|
|
110
115
|
function initialize(
|
|
111
116
|
ProvableNFT _nft,
|
|
112
117
|
PoolSettings memory _settings,
|
|
113
|
-
SafetyLimits memory _limits
|
|
118
|
+
SafetyLimits memory _limits,
|
|
119
|
+
DirectPaymentsFactory _registry
|
|
114
120
|
) external initializer {
|
|
115
|
-
|
|
121
|
+
registry = _registry;
|
|
116
122
|
settings = _settings;
|
|
117
123
|
limits = _limits;
|
|
118
124
|
nft = _nft;
|
|
@@ -123,7 +129,7 @@ contract DirectPaymentsPool is
|
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
function upgradeToLatest(bytes memory data) external payable virtual onlyProxy {
|
|
126
|
-
address impl = DirectPaymentsFactory(
|
|
132
|
+
address impl = DirectPaymentsFactory(registry).impl();
|
|
127
133
|
_authorizeUpgrade(impl);
|
|
128
134
|
_upgradeToAndCallUUPS(impl, data, false);
|
|
129
135
|
}
|
|
@@ -171,10 +177,11 @@ contract DirectPaymentsPool is
|
|
|
171
177
|
if (totalRewards > rewardsBalance) revert NO_BALANCE();
|
|
172
178
|
rewardsBalance -= totalRewards;
|
|
173
179
|
_sendReward(_data.events[i].contributers, uint128(reward * _data.events[i].quantity));
|
|
180
|
+
emit EventRewardClaimed(_nftId, _data.events[i]);
|
|
174
181
|
}
|
|
175
182
|
}
|
|
176
183
|
|
|
177
|
-
emit
|
|
184
|
+
emit NFTClaimed(_nftId, totalRewards);
|
|
178
185
|
}
|
|
179
186
|
|
|
180
187
|
/**
|
|
@@ -279,8 +286,8 @@ contract DirectPaymentsPool is
|
|
|
279
286
|
*/
|
|
280
287
|
|
|
281
288
|
function addMember(address member, bytes memory extraData) external {
|
|
282
|
-
if (address(settings.
|
|
283
|
-
address rootAddress = settings.
|
|
289
|
+
if (address(settings.uniquenessValidator) != address(0)) {
|
|
290
|
+
address rootAddress = settings.uniquenessValidator.getWhitelistedRoot(member);
|
|
284
291
|
if (rootAddress == address(0)) revert NOT_WHITELISTED(member);
|
|
285
292
|
}
|
|
286
293
|
|
|
@@ -6,21 +6,45 @@ import { SuperAppBaseFlow } from "./SuperAppBaseFlow.sol";
|
|
|
6
6
|
import { ISuperfluid, ISuperToken, SuperAppDefinitions } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
|
|
7
7
|
import { ISuperGoodDollar } from "@gooddollar/goodprotocol/contracts/token/superfluid/ISuperGoodDollar.sol";
|
|
8
8
|
import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
|
|
9
|
+
import { CFAv1Library, IConstantFlowAgreementV1 } from "@superfluid-finance/ethereum-contracts/contracts/apps/CFAv1Library.sol";
|
|
9
10
|
|
|
10
11
|
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
|
|
11
12
|
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
|
|
12
13
|
|
|
14
|
+
import "../DirectPayments/DirectPaymentsFactory.sol";
|
|
15
|
+
|
|
13
16
|
// import "hardhat/console.sol";
|
|
14
17
|
|
|
15
18
|
abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
19
|
+
int96 public constant MIN_FLOW_RATE = 386e9;
|
|
20
|
+
|
|
16
21
|
using SuperTokenV1Library for ISuperToken;
|
|
22
|
+
using CFAv1Library for CFAv1Library.InitData;
|
|
17
23
|
|
|
18
24
|
error ZERO_ADDRESS();
|
|
19
25
|
error ZERO_AMOUNT();
|
|
20
26
|
error UNSUPPORTED_TOKEN();
|
|
21
27
|
error ONLY_HOST_OR_SENDER(address);
|
|
28
|
+
error FEE_FLOW_FAILED(int96 curFeeRate, int96 newFeeRate);
|
|
29
|
+
error MIN_FLOWRATE(int96 flowRate);
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @dev Emitted when a supporter's contribution or flow rate is updated
|
|
33
|
+
* @param supporter The address of the supporter
|
|
34
|
+
* @param previousContribution The previous total contribution amount
|
|
35
|
+
* @param contribution The new total contribution amount
|
|
36
|
+
* @param previousFlowRate The previous flow rate if isFlowUpdate otherwise 0
|
|
37
|
+
* @param flowRate The new flow rate
|
|
38
|
+
* @param isFlowUpdate True if the update was a flow rate update, false if it was a single contribution update
|
|
39
|
+
*/
|
|
40
|
+
event SupporterUpdated(
|
|
41
|
+
address indexed supporter,
|
|
42
|
+
uint256 previousContribution,
|
|
43
|
+
uint256 contribution,
|
|
44
|
+
int96 previousFlowRate,
|
|
45
|
+
int96 flowRate,
|
|
46
|
+
bool isFlowUpdate
|
|
47
|
+
);
|
|
24
48
|
|
|
25
49
|
//TODO: ask about "view" for beforeagreement functions
|
|
26
50
|
// ask about "receiver" can it be different then app?
|
|
@@ -40,7 +64,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
40
64
|
address swapFrom;
|
|
41
65
|
uint256 amount;
|
|
42
66
|
uint256 minReturn;
|
|
43
|
-
uint256
|
|
67
|
+
uint256 deadline;
|
|
44
68
|
bytes path;
|
|
45
69
|
}
|
|
46
70
|
|
|
@@ -50,11 +74,24 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
50
74
|
uint128 lastUpdated;
|
|
51
75
|
}
|
|
52
76
|
|
|
77
|
+
struct Stats {
|
|
78
|
+
uint256 netIncome; //without fees
|
|
79
|
+
uint256 totalFees;
|
|
80
|
+
uint256 lastUpdate;
|
|
81
|
+
address lastFeeRecipient;
|
|
82
|
+
int96 lastIncomeRate;
|
|
83
|
+
}
|
|
84
|
+
|
|
53
85
|
ISuperToken public superToken;
|
|
54
86
|
|
|
55
87
|
mapping(address => SupporterData) public supporters;
|
|
56
88
|
|
|
57
|
-
|
|
89
|
+
//initialize cfaV1 variable
|
|
90
|
+
CFAv1Library.InitData public cfaV1;
|
|
91
|
+
|
|
92
|
+
Stats public stats;
|
|
93
|
+
|
|
94
|
+
uint256[48] private _reserved;
|
|
58
95
|
|
|
59
96
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
60
97
|
constructor(ISuperfluid _host, ISwapRouter _swapRouter) SuperAppBaseFlow(_host) {
|
|
@@ -62,6 +99,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
62
99
|
swapRouter = _swapRouter;
|
|
63
100
|
}
|
|
64
101
|
|
|
102
|
+
function getRegistry() public view virtual returns (DirectPaymentsFactory);
|
|
103
|
+
|
|
65
104
|
/**
|
|
66
105
|
* @dev Sets the address of the super token and registers the app with the host
|
|
67
106
|
* @param _superToken The address of the super token contract
|
|
@@ -77,6 +116,14 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
77
116
|
|
|
78
117
|
// Register the app with the host
|
|
79
118
|
host.registerApp(callBackDefinitions);
|
|
119
|
+
|
|
120
|
+
//initialize InitData struct, and set equal to cfaV1
|
|
121
|
+
cfaV1 = CFAv1Library.InitData(
|
|
122
|
+
host,
|
|
123
|
+
IConstantFlowAgreementV1(
|
|
124
|
+
address(host.getAgreementClass(keccak256("org.superfluid-finance.agreements.ConstantFlowAgreement.v1")))
|
|
125
|
+
)
|
|
126
|
+
);
|
|
80
127
|
}
|
|
81
128
|
|
|
82
129
|
function isAcceptedSuperToken(ISuperToken _superToken) public view override returns (bool) {
|
|
@@ -89,6 +136,20 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
89
136
|
return supporter.contribution + uint96(supporter.flowRate) * (block.timestamp - supporter.lastUpdated);
|
|
90
137
|
}
|
|
91
138
|
|
|
139
|
+
function getRealtimeStats()
|
|
140
|
+
public
|
|
141
|
+
view
|
|
142
|
+
returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate)
|
|
143
|
+
{
|
|
144
|
+
incomeFlowRate = stats.lastIncomeRate;
|
|
145
|
+
netIncome = stats.netIncome + uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
|
|
146
|
+
feeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
|
|
147
|
+
totalFees =
|
|
148
|
+
stats.totalFees +
|
|
149
|
+
uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
|
|
150
|
+
(block.timestamp - stats.lastUpdate);
|
|
151
|
+
}
|
|
152
|
+
|
|
92
153
|
/**
|
|
93
154
|
* @dev This function is called when a token transfer occurs
|
|
94
155
|
* @param _sender The address of the sender
|
|
@@ -100,16 +161,16 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
100
161
|
if (_amount == 0) revert ZERO_AMOUNT();
|
|
101
162
|
|
|
102
163
|
// Update the contribution amount for the sender in the supporters mapping
|
|
103
|
-
_updateSupporter(_sender,
|
|
164
|
+
_updateSupporter(_sender, int256(_amount), 0, "");
|
|
104
165
|
|
|
105
166
|
return true;
|
|
106
167
|
}
|
|
107
168
|
|
|
108
169
|
/**
|
|
109
|
-
* @dev
|
|
170
|
+
* @dev allow single contribution. user needs to approve tokens first. can be used in superfluid batch actions.
|
|
110
171
|
* @param _sender The address of the sender who is contributing tokens.
|
|
111
172
|
* @param _amount The amount of tokens being contributed.
|
|
112
|
-
* @param _ctx The context of the transaction for superfluid
|
|
173
|
+
* @param _ctx The context of the transaction for superfluid in case this was used in superfluid batch. otherwise can be empty.
|
|
113
174
|
* @return Returns the context of the transaction.
|
|
114
175
|
*/
|
|
115
176
|
function support(
|
|
@@ -123,7 +184,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
123
184
|
TransferHelper.safeTransferFrom(address(superToken), _sender, address(this), _amount);
|
|
124
185
|
|
|
125
186
|
// Update the contribution amount for the sender in the supporters mapping
|
|
126
|
-
_updateSupporter(_sender,
|
|
187
|
+
_updateSupporter(_sender, int256(_amount), 0, ""); //we pass empty ctx since this is not a flow but a single donation
|
|
127
188
|
|
|
128
189
|
return _ctx;
|
|
129
190
|
}
|
|
@@ -151,7 +212,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
151
212
|
ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
|
|
152
213
|
path: _customData.path,
|
|
153
214
|
recipient: _sender,
|
|
154
|
-
deadline: _customData.
|
|
215
|
+
deadline: _customData.deadline,
|
|
155
216
|
amountIn: _customData.amount,
|
|
156
217
|
amountOutMinimum: _customData.minReturn
|
|
157
218
|
});
|
|
@@ -163,7 +224,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
163
224
|
tokenOut: address(superToken),
|
|
164
225
|
fee: 10000,
|
|
165
226
|
recipient: _sender,
|
|
166
|
-
deadline: _customData.
|
|
227
|
+
deadline: _customData.deadline,
|
|
167
228
|
amountIn: _customData.amount,
|
|
168
229
|
amountOutMinimum: _customData.minReturn,
|
|
169
230
|
sqrtPriceLimitX96: 0
|
|
@@ -189,10 +250,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
189
250
|
bytes calldata _ctx
|
|
190
251
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
191
252
|
// Update the supporter's information
|
|
192
|
-
_updateSupporter(_sender, 0, 0);
|
|
193
|
-
|
|
194
|
-
// Return the context of the transaction
|
|
195
|
-
return _ctx;
|
|
253
|
+
return _updateSupporter(_sender, 0, 0, _ctx);
|
|
196
254
|
}
|
|
197
255
|
|
|
198
256
|
/**
|
|
@@ -211,10 +269,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
211
269
|
bytes calldata _ctx
|
|
212
270
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
213
271
|
// Update the supporter's information
|
|
214
|
-
_updateSupporter(_sender, _previousFlowRate, _lastUpdated);
|
|
215
|
-
|
|
216
|
-
// Return the context of the transaction
|
|
217
|
-
return _ctx;
|
|
272
|
+
return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
|
|
218
273
|
}
|
|
219
274
|
|
|
220
275
|
/**
|
|
@@ -234,27 +289,107 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
234
289
|
bytes calldata _ctx
|
|
235
290
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
236
291
|
// Update the supporter's information
|
|
237
|
-
_updateSupporter(_sender, _previousFlowRate, _lastUpdated);
|
|
238
|
-
|
|
239
|
-
// Return the context of the transaction
|
|
240
|
-
return _ctx;
|
|
292
|
+
return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
|
|
241
293
|
}
|
|
242
294
|
|
|
243
295
|
/**
|
|
244
296
|
* @dev Updates the information for a supporter
|
|
245
297
|
* @param _supporter The address of the supporter
|
|
246
|
-
* @param
|
|
298
|
+
* @param _previousFlowRateOrAmount The previous flow rate of the stream or single donation amount
|
|
247
299
|
* @param _lastUpdated The timestamp of the last update to the stream
|
|
300
|
+
* @param _ctx flow context
|
|
248
301
|
*/
|
|
249
|
-
function _updateSupporter(
|
|
302
|
+
function _updateSupporter(
|
|
303
|
+
address _supporter,
|
|
304
|
+
int256 _previousFlowRateOrAmount,
|
|
305
|
+
uint256 _lastUpdated,
|
|
306
|
+
bytes memory _ctx
|
|
307
|
+
) internal returns (bytes memory newCtx) {
|
|
308
|
+
newCtx = _ctx;
|
|
309
|
+
bool _isFlow = _ctx.length > 0;
|
|
310
|
+
_updateStats(_isFlow ? 0 : uint256(_previousFlowRateOrAmount));
|
|
250
311
|
// Get the current flow rate for the supporter
|
|
251
|
-
|
|
312
|
+
int96 flowRate = superToken.getFlowRate(_supporter, address(this));
|
|
313
|
+
uint256 prevContribution = supporters[_supporter].contribution;
|
|
314
|
+
if (_isFlow) {
|
|
315
|
+
//enforce minimal flow rate
|
|
316
|
+
if (flowRate > 0 && flowRate < MIN_FLOW_RATE) revert MIN_FLOWRATE(flowRate);
|
|
317
|
+
// Update the supporter's information
|
|
318
|
+
supporters[_supporter].lastUpdated = uint128(block.timestamp);
|
|
319
|
+
supporters[_supporter].flowRate = flowRate;
|
|
320
|
+
supporters[_supporter].contribution +=
|
|
321
|
+
uint96(int96(_previousFlowRateOrAmount)) *
|
|
322
|
+
(block.timestamp - _lastUpdated);
|
|
323
|
+
newCtx = _takeFeeFlow(flowRate - int96(_previousFlowRateOrAmount), _ctx);
|
|
324
|
+
// we update the last rate after we do all changes to our own flows
|
|
325
|
+
stats.lastIncomeRate = superToken.getNetFlowRate(address(this));
|
|
326
|
+
} else {
|
|
327
|
+
supporters[_supporter].contribution += uint256(_previousFlowRateOrAmount);
|
|
328
|
+
_takeFeeSingle(uint256(_previousFlowRateOrAmount));
|
|
329
|
+
}
|
|
252
330
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
331
|
+
emit SupporterUpdated(
|
|
332
|
+
_supporter,
|
|
333
|
+
prevContribution,
|
|
334
|
+
supporters[_supporter].contribution,
|
|
335
|
+
_isFlow ? int96(int256(_previousFlowRateOrAmount)) : int96(0),
|
|
336
|
+
flowRate,
|
|
337
|
+
_isFlow
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// this should be called before any flow rate changes
|
|
342
|
+
function _updateStats(uint256 _amount) internal {
|
|
343
|
+
//use last rate before the current possible rate update
|
|
344
|
+
stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
|
|
345
|
+
uint feeBps;
|
|
346
|
+
if (address(getRegistry()) != address(0)) {
|
|
347
|
+
feeBps = getRegistry().feeBps();
|
|
348
|
+
//fees sent to last recipient, the flowRate to recipient still wasnt updated.
|
|
349
|
+
stats.totalFees +=
|
|
350
|
+
uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
|
|
351
|
+
(block.timestamp - stats.lastUpdate);
|
|
352
|
+
}
|
|
353
|
+
if (_amount > 0) {
|
|
354
|
+
stats.netIncome += (_amount * (10000 - feeBps)) / 10000;
|
|
355
|
+
stats.totalFees += (_amount * feeBps) / 10000;
|
|
356
|
+
}
|
|
357
|
+
stats.lastUpdate = block.timestamp;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function _takeFeeFlow(int96 _diffRate, bytes memory _ctx) internal returns (bytes memory newCtx) {
|
|
361
|
+
newCtx = _ctx;
|
|
362
|
+
if (address(getRegistry()) == address(0)) return newCtx;
|
|
363
|
+
address recipient = getRegistry().feeRecipient();
|
|
364
|
+
int96 curFeeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
|
|
365
|
+
bool newRecipient;
|
|
366
|
+
if (recipient != stats.lastFeeRecipient) {
|
|
367
|
+
newRecipient = true;
|
|
368
|
+
if (stats.lastFeeRecipient != address(0)) {
|
|
369
|
+
//delete old recipient flow
|
|
370
|
+
if (curFeeRate > 0)
|
|
371
|
+
newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), stats.lastFeeRecipient, superToken); //passing in the ctx which is sent to the callback here
|
|
372
|
+
}
|
|
373
|
+
stats.lastFeeRecipient = recipient;
|
|
374
|
+
}
|
|
375
|
+
if (recipient == address(0)) return newCtx;
|
|
376
|
+
|
|
377
|
+
int96 feeRateChange = (_diffRate * int32(getRegistry().feeBps())) / 10000;
|
|
378
|
+
int96 newFeeRate = curFeeRate + feeRateChange;
|
|
379
|
+
if (newFeeRate <= 0 && newRecipient == false) {
|
|
380
|
+
newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here
|
|
381
|
+
} else if (curFeeRate > 0 && newRecipient == false) {
|
|
382
|
+
newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
|
|
383
|
+
} else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function _takeFeeSingle(uint256 _amount) internal {
|
|
387
|
+
if (address(getRegistry()) == address(0)) return;
|
|
388
|
+
address recipient = getRegistry().feeRecipient();
|
|
389
|
+
if (recipient == address(0)) return;
|
|
390
|
+
|
|
391
|
+
uint256 fee = (_amount * getRegistry().feeBps()) / 10000;
|
|
392
|
+
TransferHelper.safeTransfer(address(superToken), recipient, fee);
|
|
258
393
|
}
|
|
259
394
|
|
|
260
395
|
modifier onlyHostOrSender(address _sender) {
|