@gooddollar/goodcollective-contracts 1.0.3 → 1.0.5-beta.7a2c704
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 +70 -13
- package/contracts/DirectPayments/DirectPaymentsPool.sol +102 -40
- package/contracts/DirectPayments/ProvableNFT.sol +17 -11
- package/contracts/GoodCollective/GoodCollectiveSuperApp.sol +155 -77
- package/contracts/GoodCollective/IGoodCollectiveSuperApp.sol +12 -0
- package/contracts/utils/HelperLibrary.sol +83 -0
- package/package.json +13 -7
- package/releases/deployment.json +12022 -1689
- package/typechain-types/@gooddollar/goodprotocol/contracts/token/IFeesFormula.ts +115 -0
- package/typechain-types/@gooddollar/goodprotocol/contracts/token/index.ts +1 -0
- package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +176 -0
- package/typechain-types/@openzeppelin/contracts/access/index.ts +4 -0
- package/typechain-types/@openzeppelin/contracts/index.ts +2 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/BeaconProxy.ts +115 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.ts +247 -0
- package/typechain-types/@openzeppelin/contracts/proxy/beacon/index.ts +2 -0
- package/typechain-types/contracts/DirectPayments/DirectPaymentsFactory.ts +168 -14
- package/typechain-types/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool.ts +419 -186
- package/typechain-types/contracts/DirectPayments/ProvableNFT.ts +32 -3
- package/typechain-types/contracts/GoodCollective/GoodCollectiveSuperApp.ts +170 -13
- package/typechain-types/contracts/utils/HelperLibrary.ts +154 -0
- package/typechain-types/contracts/utils/index.ts +1 -0
- package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/IFeesFormula__factory.ts +60 -0
- package/typechain-types/factories/@gooddollar/goodprotocol/contracts/token/index.ts +1 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +78 -0
- package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +4 -0
- package/typechain-types/factories/@openzeppelin/contracts/index.ts +1 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/BeaconProxy__factory.ts +143 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon__factory.ts +170 -0
- package/typechain-types/factories/@openzeppelin/contracts/proxy/beacon/index.ts +2 -0
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsFactory__factory.ts +226 -3
- package/typechain-types/factories/contracts/DirectPayments/DirectPaymentsPool.sol/DirectPaymentsPool__factory.ts +473 -95
- package/typechain-types/factories/contracts/DirectPayments/ProvableNFT__factory.ts +56 -1
- package/typechain-types/factories/contracts/GoodCollective/GoodCollectiveSuperApp__factory.ts +148 -4
- package/typechain-types/factories/contracts/utils/HelperLibrary__factory.ts +129 -0
- package/typechain-types/factories/contracts/utils/index.ts +1 -0
- package/typechain-types/hardhat.d.ts +45 -0
- package/typechain-types/index.ts +8 -0
|
@@ -6,44 +6,53 @@ 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
|
+
import "../utils/HelperLibrary.sol";
|
|
16
|
+
|
|
13
17
|
// import "hardhat/console.sol";
|
|
14
18
|
|
|
15
19
|
abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
20
|
+
int96 public constant MIN_FLOW_RATE = 386e9;
|
|
21
|
+
|
|
16
22
|
using SuperTokenV1Library for ISuperToken;
|
|
23
|
+
using CFAv1Library for CFAv1Library.InitData;
|
|
17
24
|
|
|
18
25
|
error ZERO_ADDRESS();
|
|
19
26
|
error ZERO_AMOUNT();
|
|
20
27
|
error UNSUPPORTED_TOKEN();
|
|
21
28
|
error ONLY_HOST_OR_SENDER(address);
|
|
29
|
+
error FEE_FLOW_FAILED(int96 curFeeRate, int96 newFeeRate);
|
|
30
|
+
error MIN_FLOWRATE(int96 flowRate);
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @dev Emitted when a supporter's contribution or flow rate is updated
|
|
34
|
+
* @param supporter The address of the supporter
|
|
35
|
+
* @param previousContribution The previous total contribution amount
|
|
36
|
+
* @param contribution The new total contribution amount
|
|
37
|
+
* @param previousFlowRate The previous flow rate if isFlowUpdate otherwise 0
|
|
38
|
+
* @param flowRate The new flow rate
|
|
39
|
+
* @param isFlowUpdate True if the update was a flow rate update, false if it was a single contribution update
|
|
40
|
+
*/
|
|
41
|
+
event SupporterUpdated(
|
|
42
|
+
address indexed supporter,
|
|
43
|
+
uint256 previousContribution,
|
|
44
|
+
uint256 contribution,
|
|
45
|
+
int96 previousFlowRate,
|
|
46
|
+
int96 flowRate,
|
|
47
|
+
bool isFlowUpdate
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
//TODO:
|
|
26
51
|
// ask about "receiver" can it be different then app?
|
|
27
52
|
|
|
28
53
|
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
|
|
29
54
|
ISwapRouter public immutable swapRouter;
|
|
30
55
|
|
|
31
|
-
/**
|
|
32
|
-
* @dev A struct containing information about a token swap
|
|
33
|
-
* @param swapFrom The address of the token being swapped
|
|
34
|
-
* @param amount The amount of tokens being swapped
|
|
35
|
-
* @param minReturn The minimum amount of tokens to be received in the swap
|
|
36
|
-
* @param timestamp The deadline for the swap to occur
|
|
37
|
-
* @param path The path of tokens to take in a uniswap v3 multi-hop swap, encoded as bytes
|
|
38
|
-
*/
|
|
39
|
-
struct SwapData {
|
|
40
|
-
address swapFrom;
|
|
41
|
-
uint256 amount;
|
|
42
|
-
uint256 minReturn;
|
|
43
|
-
uint256 deadline;
|
|
44
|
-
bytes path;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
56
|
struct SupporterData {
|
|
48
57
|
uint256 contribution;
|
|
49
58
|
int96 flowRate;
|
|
@@ -54,7 +63,12 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
54
63
|
|
|
55
64
|
mapping(address => SupporterData) public supporters;
|
|
56
65
|
|
|
57
|
-
|
|
66
|
+
//initialize cfaV1 variable
|
|
67
|
+
CFAv1Library.InitData public cfaV1;
|
|
68
|
+
|
|
69
|
+
IGoodCollectiveSuperApp.Stats public stats;
|
|
70
|
+
|
|
71
|
+
uint256[48] private _reserved;
|
|
58
72
|
|
|
59
73
|
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
60
74
|
constructor(ISuperfluid _host, ISwapRouter _swapRouter) SuperAppBaseFlow(_host) {
|
|
@@ -62,6 +76,8 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
62
76
|
swapRouter = _swapRouter;
|
|
63
77
|
}
|
|
64
78
|
|
|
79
|
+
function getRegistry() public view virtual returns (DirectPaymentsFactory);
|
|
80
|
+
|
|
65
81
|
/**
|
|
66
82
|
* @dev Sets the address of the super token and registers the app with the host
|
|
67
83
|
* @param _superToken The address of the super token contract
|
|
@@ -77,6 +93,14 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
77
93
|
|
|
78
94
|
// Register the app with the host
|
|
79
95
|
host.registerApp(callBackDefinitions);
|
|
96
|
+
|
|
97
|
+
//initialize InitData struct, and set equal to cfaV1
|
|
98
|
+
cfaV1 = CFAv1Library.InitData(
|
|
99
|
+
host,
|
|
100
|
+
IConstantFlowAgreementV1(
|
|
101
|
+
address(host.getAgreementClass(keccak256("org.superfluid-finance.agreements.ConstantFlowAgreement.v1")))
|
|
102
|
+
)
|
|
103
|
+
);
|
|
80
104
|
}
|
|
81
105
|
|
|
82
106
|
function isAcceptedSuperToken(ISuperToken _superToken) public view override returns (bool) {
|
|
@@ -89,6 +113,14 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
89
113
|
return supporter.contribution + uint96(supporter.flowRate) * (block.timestamp - supporter.lastUpdated);
|
|
90
114
|
}
|
|
91
115
|
|
|
116
|
+
function getRealtimeStats()
|
|
117
|
+
public
|
|
118
|
+
view
|
|
119
|
+
returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate)
|
|
120
|
+
{
|
|
121
|
+
return HelperLibrary.getRealtimeStats(stats, superToken);
|
|
122
|
+
}
|
|
123
|
+
|
|
92
124
|
/**
|
|
93
125
|
* @dev This function is called when a token transfer occurs
|
|
94
126
|
* @param _sender The address of the sender
|
|
@@ -100,16 +132,16 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
100
132
|
if (_amount == 0) revert ZERO_AMOUNT();
|
|
101
133
|
|
|
102
134
|
// Update the contribution amount for the sender in the supporters mapping
|
|
103
|
-
_updateSupporter(_sender,
|
|
135
|
+
_updateSupporter(_sender, int256(_amount), 0, "");
|
|
104
136
|
|
|
105
137
|
return true;
|
|
106
138
|
}
|
|
107
139
|
|
|
108
140
|
/**
|
|
109
|
-
* @dev
|
|
141
|
+
* @dev allow single contribution. user needs to approve tokens first. can be used in superfluid batch actions.
|
|
110
142
|
* @param _sender The address of the sender who is contributing tokens.
|
|
111
143
|
* @param _amount The amount of tokens being contributed.
|
|
112
|
-
* @param _ctx The context of the transaction for superfluid
|
|
144
|
+
* @param _ctx The context of the transaction for superfluid in case this was used in superfluid batch. otherwise can be empty.
|
|
113
145
|
* @return Returns the context of the transaction.
|
|
114
146
|
*/
|
|
115
147
|
function support(
|
|
@@ -123,7 +155,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
123
155
|
TransferHelper.safeTransferFrom(address(superToken), _sender, address(this), _amount);
|
|
124
156
|
|
|
125
157
|
// Update the contribution amount for the sender in the supporters mapping
|
|
126
|
-
_updateSupporter(_sender,
|
|
158
|
+
_updateSupporter(_sender, int256(_amount), 0, ""); //we pass empty ctx since this is not a flow but a single donation
|
|
127
159
|
|
|
128
160
|
return _ctx;
|
|
129
161
|
}
|
|
@@ -136,42 +168,11 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
136
168
|
* @return Returns the context of the transaction
|
|
137
169
|
*/
|
|
138
170
|
function handleSwap(
|
|
139
|
-
SwapData memory _customData,
|
|
171
|
+
HelperLibrary.SwapData memory _customData,
|
|
140
172
|
address _sender,
|
|
141
173
|
bytes memory _ctx
|
|
142
174
|
) external onlyHostOrSender(_sender) returns (bytes memory) {
|
|
143
|
-
|
|
144
|
-
TransferHelper.safeTransferFrom(_customData.swapFrom, _sender, address(this), _customData.amount);
|
|
145
|
-
|
|
146
|
-
// Approve the router to spend the tokens
|
|
147
|
-
TransferHelper.safeApprove(_customData.swapFrom, address(swapRouter), _customData.amount);
|
|
148
|
-
|
|
149
|
-
if (_customData.path.length > 0) {
|
|
150
|
-
// If a path is provided, execute a multi-hop swap
|
|
151
|
-
ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
|
|
152
|
-
path: _customData.path,
|
|
153
|
-
recipient: _sender,
|
|
154
|
-
deadline: _customData.deadline,
|
|
155
|
-
amountIn: _customData.amount,
|
|
156
|
-
amountOutMinimum: _customData.minReturn
|
|
157
|
-
});
|
|
158
|
-
swapRouter.exactInput(params);
|
|
159
|
-
} else {
|
|
160
|
-
// If no path is provided, execute a single-hop swap
|
|
161
|
-
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
162
|
-
tokenIn: _customData.swapFrom,
|
|
163
|
-
tokenOut: address(superToken),
|
|
164
|
-
fee: 10000,
|
|
165
|
-
recipient: _sender,
|
|
166
|
-
deadline: _customData.deadline,
|
|
167
|
-
amountIn: _customData.amount,
|
|
168
|
-
amountOutMinimum: _customData.minReturn,
|
|
169
|
-
sqrtPriceLimitX96: 0
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// Execute the swap using `exactInputSingle`
|
|
173
|
-
swapRouter.exactInputSingle(params);
|
|
174
|
-
}
|
|
175
|
+
HelperLibrary.handleSwap(swapRouter, _customData, address(superToken), _sender);
|
|
175
176
|
|
|
176
177
|
// Return the context of the transaction
|
|
177
178
|
return _ctx;
|
|
@@ -189,10 +190,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
189
190
|
bytes calldata _ctx
|
|
190
191
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
191
192
|
// Update the supporter's information
|
|
192
|
-
_updateSupporter(_sender, 0, 0);
|
|
193
|
-
|
|
194
|
-
// Return the context of the transaction
|
|
195
|
-
return _ctx;
|
|
193
|
+
return _updateSupporter(_sender, 0, 0, _ctx);
|
|
196
194
|
}
|
|
197
195
|
|
|
198
196
|
/**
|
|
@@ -211,10 +209,7 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
211
209
|
bytes calldata _ctx
|
|
212
210
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
213
211
|
// Update the supporter's information
|
|
214
|
-
_updateSupporter(_sender, _previousFlowRate, _lastUpdated);
|
|
215
|
-
|
|
216
|
-
// Return the context of the transaction
|
|
217
|
-
return _ctx;
|
|
212
|
+
return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
|
|
218
213
|
}
|
|
219
214
|
|
|
220
215
|
/**
|
|
@@ -234,29 +229,112 @@ abstract contract GoodCollectiveSuperApp is SuperAppBaseFlow {
|
|
|
234
229
|
bytes calldata _ctx
|
|
235
230
|
) internal virtual override returns (bytes memory /*newCtx*/) {
|
|
236
231
|
// Update the supporter's information
|
|
237
|
-
_updateSupporter(_sender, _previousFlowRate, _lastUpdated);
|
|
238
|
-
|
|
239
|
-
// Return the context of the transaction
|
|
240
|
-
return _ctx;
|
|
232
|
+
return _updateSupporter(_sender, _previousFlowRate, _lastUpdated, _ctx);
|
|
241
233
|
}
|
|
242
234
|
|
|
243
235
|
/**
|
|
244
236
|
* @dev Updates the information for a supporter
|
|
245
237
|
* @param _supporter The address of the supporter
|
|
246
|
-
* @param
|
|
238
|
+
* @param _previousFlowRateOrAmount The previous flow rate of the stream or single donation amount
|
|
247
239
|
* @param _lastUpdated The timestamp of the last update to the stream
|
|
240
|
+
* @param _ctx flow context
|
|
248
241
|
*/
|
|
249
|
-
function _updateSupporter(
|
|
242
|
+
function _updateSupporter(
|
|
243
|
+
address _supporter,
|
|
244
|
+
int256 _previousFlowRateOrAmount,
|
|
245
|
+
uint256 _lastUpdated,
|
|
246
|
+
bytes memory _ctx
|
|
247
|
+
) internal returns (bytes memory newCtx) {
|
|
248
|
+
newCtx = _ctx;
|
|
249
|
+
bool _isFlow = _ctx.length > 0;
|
|
250
|
+
_updateStats(_isFlow ? 0 : uint256(_previousFlowRateOrAmount));
|
|
250
251
|
// Get the current flow rate for the supporter
|
|
251
|
-
|
|
252
|
+
int96 flowRate = superToken.getFlowRate(_supporter, address(this));
|
|
253
|
+
uint256 prevContribution = supporters[_supporter].contribution;
|
|
254
|
+
if (_isFlow) {
|
|
255
|
+
//enforce minimal flow rate
|
|
256
|
+
if (flowRate > 0 && flowRate < MIN_FLOW_RATE) revert MIN_FLOWRATE(flowRate);
|
|
257
|
+
// Update the supporter's information
|
|
258
|
+
supporters[_supporter].lastUpdated = uint128(block.timestamp);
|
|
259
|
+
supporters[_supporter].flowRate = flowRate;
|
|
260
|
+
supporters[_supporter].contribution +=
|
|
261
|
+
uint96(int96(_previousFlowRateOrAmount)) *
|
|
262
|
+
(block.timestamp - _lastUpdated);
|
|
263
|
+
newCtx = _takeFeeFlow(flowRate - int96(_previousFlowRateOrAmount), _ctx);
|
|
264
|
+
// we update the last rate after we do all changes to our own flows
|
|
265
|
+
stats.lastIncomeRate = superToken.getNetFlowRate(address(this));
|
|
266
|
+
} else {
|
|
267
|
+
supporters[_supporter].contribution += uint256(_previousFlowRateOrAmount);
|
|
268
|
+
_takeFeeSingle(uint256(_previousFlowRateOrAmount));
|
|
269
|
+
}
|
|
252
270
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
271
|
+
emit SupporterUpdated(
|
|
272
|
+
_supporter,
|
|
273
|
+
prevContribution,
|
|
274
|
+
supporters[_supporter].contribution,
|
|
275
|
+
_isFlow ? int96(int256(_previousFlowRateOrAmount)) : int96(0),
|
|
276
|
+
flowRate,
|
|
277
|
+
_isFlow
|
|
278
|
+
);
|
|
258
279
|
}
|
|
259
280
|
|
|
281
|
+
// this should be called before any flow rate changes
|
|
282
|
+
function _updateStats(uint256 _amount) internal {
|
|
283
|
+
//use last rate before the current possible rate update
|
|
284
|
+
stats.netIncome += uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
|
|
285
|
+
uint feeBps;
|
|
286
|
+
if (address(getRegistry()) != address(0)) {
|
|
287
|
+
feeBps = getRegistry().feeBps();
|
|
288
|
+
//fees sent to last recipient, the flowRate to recipient still wasnt updated.
|
|
289
|
+
stats.totalFees +=
|
|
290
|
+
uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
|
|
291
|
+
(block.timestamp - stats.lastUpdate);
|
|
292
|
+
}
|
|
293
|
+
if (_amount > 0) {
|
|
294
|
+
stats.netIncome += (_amount * (10000 - feeBps)) / 10000;
|
|
295
|
+
stats.totalFees += (_amount * feeBps) / 10000;
|
|
296
|
+
}
|
|
297
|
+
stats.lastUpdate = block.timestamp;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function _takeFeeFlow(int96 _diffRate, bytes memory _ctx) internal returns (bytes memory newCtx) {
|
|
301
|
+
newCtx = _ctx;
|
|
302
|
+
if (address(getRegistry()) == address(0)) return newCtx;
|
|
303
|
+
address recipient = getRegistry().feeRecipient();
|
|
304
|
+
int96 curFeeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
|
|
305
|
+
bool newRecipient;
|
|
306
|
+
if (recipient != stats.lastFeeRecipient) {
|
|
307
|
+
newRecipient = true;
|
|
308
|
+
if (stats.lastFeeRecipient != address(0)) {
|
|
309
|
+
//delete old recipient flow
|
|
310
|
+
if (curFeeRate > 0)
|
|
311
|
+
newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), stats.lastFeeRecipient, superToken); //passing in the ctx which is sent to the callback here
|
|
312
|
+
}
|
|
313
|
+
stats.lastFeeRecipient = recipient;
|
|
314
|
+
}
|
|
315
|
+
if (recipient == address(0)) return newCtx;
|
|
316
|
+
|
|
317
|
+
int96 feeRateChange = (_diffRate * int32(getRegistry().feeBps())) / 10000;
|
|
318
|
+
int96 newFeeRate = curFeeRate + feeRateChange;
|
|
319
|
+
if (newFeeRate <= 0 && newRecipient == false) {
|
|
320
|
+
newCtx = cfaV1.deleteFlowWithCtx(newCtx, address(this), recipient, superToken); //passing in the ctx which is sent to the callback here
|
|
321
|
+
} else if (curFeeRate > 0 && newRecipient == false) {
|
|
322
|
+
newCtx = cfaV1.updateFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
|
|
323
|
+
} else if (newFeeRate > 0) newCtx = cfaV1.createFlowWithCtx(newCtx, recipient, superToken, newFeeRate); //passing in the ctx which is sent to the callback here
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function _takeFeeSingle(uint256 _amount) internal {
|
|
327
|
+
if (address(getRegistry()) == address(0)) return;
|
|
328
|
+
address recipient = getRegistry().feeRecipient();
|
|
329
|
+
if (recipient == address(0)) return;
|
|
330
|
+
|
|
331
|
+
uint256 fee = (_amount * getRegistry().feeBps()) / 10000;
|
|
332
|
+
TransferHelper.safeTransfer(address(superToken), recipient, fee);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* for methods that can be called via superfluid batch or directly
|
|
337
|
+
*/
|
|
260
338
|
modifier onlyHostOrSender(address _sender) {
|
|
261
339
|
if (msg.sender != _sender && msg.sender != address(host)) revert ONLY_HOST_OR_SENDER(msg.sender);
|
|
262
340
|
_;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity >=0.8.0;
|
|
3
|
+
|
|
4
|
+
interface IGoodCollectiveSuperApp {
|
|
5
|
+
struct Stats {
|
|
6
|
+
uint256 netIncome; //without fees
|
|
7
|
+
uint256 totalFees;
|
|
8
|
+
uint256 lastUpdate;
|
|
9
|
+
address lastFeeRecipient;
|
|
10
|
+
int96 lastIncomeRate;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
|
|
3
|
+
pragma solidity >=0.8.0;
|
|
4
|
+
|
|
5
|
+
import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
|
|
6
|
+
import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
|
|
7
|
+
import { ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
|
|
8
|
+
import { SuperTokenV1Library } from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol";
|
|
9
|
+
|
|
10
|
+
import "../GoodCollective/IGoodCollectiveSuperApp.sol";
|
|
11
|
+
|
|
12
|
+
library HelperLibrary {
|
|
13
|
+
using SuperTokenV1Library for ISuperToken;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @dev A struct containing information about a token swap
|
|
17
|
+
* @param swapFrom The address of the token being swapped
|
|
18
|
+
* @param amount The amount of tokens being swapped
|
|
19
|
+
* @param minReturn The minimum amount of tokens to be received in the swap
|
|
20
|
+
* @param timestamp The deadline for the swap to occur
|
|
21
|
+
* @param path The path of tokens to take in a uniswap v3 multi-hop swap, encoded as bytes
|
|
22
|
+
*/
|
|
23
|
+
struct SwapData {
|
|
24
|
+
address swapFrom;
|
|
25
|
+
uint256 amount;
|
|
26
|
+
uint256 minReturn;
|
|
27
|
+
uint256 deadline;
|
|
28
|
+
bytes path;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handleSwap(
|
|
32
|
+
ISwapRouter swapRouter,
|
|
33
|
+
SwapData memory _customData,
|
|
34
|
+
address outTokenIfNoPath,
|
|
35
|
+
address _sender
|
|
36
|
+
) external {
|
|
37
|
+
// Transfer the tokens from the sender to this contract
|
|
38
|
+
TransferHelper.safeTransferFrom(_customData.swapFrom, _sender, address(this), _customData.amount);
|
|
39
|
+
|
|
40
|
+
// Approve the router to spend the tokens
|
|
41
|
+
TransferHelper.safeApprove(_customData.swapFrom, address(swapRouter), _customData.amount);
|
|
42
|
+
|
|
43
|
+
if (_customData.path.length > 0) {
|
|
44
|
+
// If a path is provided, execute a multi-hop swap
|
|
45
|
+
ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({
|
|
46
|
+
path: _customData.path,
|
|
47
|
+
recipient: _sender,
|
|
48
|
+
deadline: _customData.deadline,
|
|
49
|
+
amountIn: _customData.amount,
|
|
50
|
+
amountOutMinimum: _customData.minReturn
|
|
51
|
+
});
|
|
52
|
+
swapRouter.exactInput(params);
|
|
53
|
+
} else {
|
|
54
|
+
// If no path is provided, execute a single-hop swap
|
|
55
|
+
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
56
|
+
tokenIn: _customData.swapFrom,
|
|
57
|
+
tokenOut: outTokenIfNoPath,
|
|
58
|
+
fee: 10000,
|
|
59
|
+
recipient: _sender,
|
|
60
|
+
deadline: _customData.deadline,
|
|
61
|
+
amountIn: _customData.amount,
|
|
62
|
+
amountOutMinimum: _customData.minReturn,
|
|
63
|
+
sqrtPriceLimitX96: 0
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Execute the swap using `exactInputSingle`
|
|
67
|
+
swapRouter.exactInputSingle(params);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getRealtimeStats(
|
|
72
|
+
IGoodCollectiveSuperApp.Stats memory stats,
|
|
73
|
+
ISuperToken superToken
|
|
74
|
+
) external view returns (uint256 netIncome, uint256 totalFees, int96 incomeFlowRate, int96 feeRate) {
|
|
75
|
+
incomeFlowRate = stats.lastIncomeRate;
|
|
76
|
+
netIncome = stats.netIncome + uint96(stats.lastIncomeRate) * (block.timestamp - stats.lastUpdate);
|
|
77
|
+
feeRate = superToken.getFlowRate(address(this), stats.lastFeeRecipient);
|
|
78
|
+
totalFees =
|
|
79
|
+
stats.totalFees +
|
|
80
|
+
uint96(superToken.getFlowRate(address(this), stats.lastFeeRecipient)) *
|
|
81
|
+
(block.timestamp - stats.lastUpdate);
|
|
82
|
+
}
|
|
83
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddollar/goodcollective-contracts",
|
|
3
3
|
"packageManager": "yarn@3.2.1",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.5-beta.7a2c704",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"types": "./typechain-types/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
"releases"
|
|
11
11
|
],
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@gooddollar/goodprotocol": "
|
|
13
|
+
"@gooddollar/goodprotocol": "^2.0.15",
|
|
14
14
|
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
|
|
15
15
|
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
|
16
16
|
"@nomicfoundation/hardhat-toolbox": "2.*",
|
|
17
|
-
"@nomicfoundation/hardhat-verify": "^1.
|
|
17
|
+
"@nomicfoundation/hardhat-verify": "^1.1.1",
|
|
18
18
|
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
|
19
19
|
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
|
20
20
|
"@openzeppelin/contracts": "^4.9.2",
|
|
@@ -26,13 +26,16 @@
|
|
|
26
26
|
"@types/chai": "^4.2.0",
|
|
27
27
|
"@types/mocha": ">=9.1.0",
|
|
28
28
|
"@types/prettier": "^2",
|
|
29
|
+
"@types/sinon-chai": "^3.2.3",
|
|
29
30
|
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
|
30
31
|
"@uniswap/v3-periphery": "^1.4.3",
|
|
31
32
|
"chai": "^4.2.0",
|
|
32
33
|
"dotenv": "^16.3.1",
|
|
34
|
+
"ethereum-waffle": "^3.0.0",
|
|
33
35
|
"graphql": "^16.7.1",
|
|
34
|
-
"hardhat": "2.
|
|
36
|
+
"hardhat": "^2.17.1",
|
|
35
37
|
"hardhat-abi-exporter": "^2.10.1",
|
|
38
|
+
"hardhat-celo": "^0.0.4",
|
|
36
39
|
"hardhat-contract-sizer": "^2.10.0",
|
|
37
40
|
"hardhat-deploy": "^0.11.31",
|
|
38
41
|
"hardhat-gas-reporter": "^1.0.8",
|
|
@@ -44,6 +47,7 @@
|
|
|
44
47
|
"typescript": "^5.1.3"
|
|
45
48
|
},
|
|
46
49
|
"scripts": {
|
|
50
|
+
"build": "yarn compile",
|
|
47
51
|
"compile": "npx hardhat compile",
|
|
48
52
|
"clean": "npx hardhat clean",
|
|
49
53
|
"solhint": "solhint ./contracts/**/*.sol",
|
|
@@ -58,7 +62,9 @@
|
|
|
58
62
|
"test": "npx hardhat test",
|
|
59
63
|
"test:coverage": "npx hardhat coverage",
|
|
60
64
|
"deploy": "hardhat deploy --export-all ./releases/deployment.json",
|
|
61
|
-
"prepublish": "yarn version patch && yarn compile",
|
|
62
|
-
"publish": "yarn npm publish --access public"
|
|
65
|
+
"prepublish": "yarn version patch && yarn compile && git add package.json && git commit -m \"version bump\"",
|
|
66
|
+
"publish": "yarn npm publish --access public",
|
|
67
|
+
"test:setup": "yarn exec ./scripts/deployContracts.sh",
|
|
68
|
+
"verify": "npx hardhat run scripts/verify.ts --network ${0}"
|
|
63
69
|
}
|
|
64
|
-
}
|
|
70
|
+
}
|