@gooddollar/goodprotocol 1.0.8 → 1.0.9-beta.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 (99) hide show
  1. package/artifacts/contracts/DAOStackInterfaces.sol/Avatar.dbg.json +1 -1
  2. package/artifacts/contracts/DAOStackInterfaces.sol/Controller.dbg.json +1 -1
  3. package/artifacts/contracts/DAOStackInterfaces.sol/GlobalConstraintInterface.dbg.json +1 -1
  4. package/artifacts/contracts/DAOStackInterfaces.sol/IntVoteInterface.dbg.json +1 -1
  5. package/artifacts/contracts/DAOStackInterfaces.sol/ReputationInterface.dbg.json +1 -1
  6. package/artifacts/contracts/DAOStackInterfaces.sol/SchemeRegistrar.dbg.json +1 -1
  7. package/artifacts/contracts/Interfaces.sol/AggregatorV3Interface.dbg.json +1 -1
  8. package/artifacts/contracts/Interfaces.sol/ERC20.dbg.json +1 -1
  9. package/artifacts/contracts/Interfaces.sol/IAaveIncentivesController.dbg.json +1 -1
  10. package/artifacts/contracts/Interfaces.sol/IAdminWallet.dbg.json +1 -1
  11. package/artifacts/contracts/Interfaces.sol/IDonationStaking.dbg.json +1 -1
  12. package/artifacts/contracts/Interfaces.sol/IERC2917.dbg.json +1 -1
  13. package/artifacts/contracts/Interfaces.sol/IFirstClaimPool.dbg.json +1 -1
  14. package/artifacts/contracts/Interfaces.sol/IGoodDollar.dbg.json +1 -1
  15. package/artifacts/contracts/Interfaces.sol/IGoodStaking.dbg.json +1 -1
  16. package/artifacts/contracts/Interfaces.sol/IHasRouter.dbg.json +1 -1
  17. package/artifacts/contracts/Interfaces.sol/IIdentity.dbg.json +1 -1
  18. package/artifacts/contracts/Interfaces.sol/ILendingPool.dbg.json +1 -1
  19. package/artifacts/contracts/Interfaces.sol/INameService.dbg.json +1 -1
  20. package/artifacts/contracts/Interfaces.sol/IUBIScheme.dbg.json +1 -1
  21. package/artifacts/contracts/Interfaces.sol/ProxyAdmin.dbg.json +1 -1
  22. package/artifacts/contracts/Interfaces.sol/Reserve.dbg.json +1 -1
  23. package/artifacts/contracts/Interfaces.sol/Staking.dbg.json +1 -1
  24. package/artifacts/contracts/Interfaces.sol/Uniswap.dbg.json +1 -1
  25. package/artifacts/contracts/Interfaces.sol/UniswapFactory.dbg.json +1 -1
  26. package/artifacts/contracts/Interfaces.sol/UniswapPair.dbg.json +1 -1
  27. package/artifacts/contracts/Interfaces.sol/cERC20.dbg.json +1 -1
  28. package/artifacts/contracts/governance/ClaimersDistribution.sol/ClaimersDistribution.dbg.json +1 -1
  29. package/artifacts/contracts/governance/CompoundVotingMachine.sol/CompoundVotingMachine.dbg.json +1 -1
  30. package/artifacts/contracts/governance/GReputation.sol/GReputation.dbg.json +1 -1
  31. package/artifacts/contracts/governance/GovarnanceStaking.sol/GovernanceStaking.dbg.json +1 -1
  32. package/artifacts/contracts/governance/MultiBaseGovernanceShareField.sol/MultiBaseGovernanceShareField.dbg.json +1 -1
  33. package/artifacts/contracts/governance/Reputation.sol/Reputation.dbg.json +1 -1
  34. package/artifacts/contracts/governance/StakersDistribution.sol/StakersDistribution.dbg.json +1 -1
  35. package/artifacts/contracts/mocks/AaveMock.sol/AaveMock.dbg.json +1 -1
  36. package/artifacts/contracts/mocks/DAIMock.sol/DAIMock.dbg.json +1 -1
  37. package/artifacts/contracts/mocks/DecimalsMock.sol/DecimalsMock.dbg.json +1 -1
  38. package/artifacts/contracts/mocks/GoodCompoundStakingTest.sol/GoodCompoundStakingTest.dbg.json +1 -1
  39. package/artifacts/contracts/mocks/GoodFundManagerTest.sol/GoodFundManagerTest.dbg.json +1 -1
  40. package/artifacts/contracts/mocks/IncentiveControllerMock.sol/IncentiveControllerMock.dbg.json +1 -1
  41. package/artifacts/contracts/mocks/LendingPoolMock.sol/LendingPoolMock.dbg.json +1 -1
  42. package/artifacts/contracts/mocks/OverMintTester.sol/OverMintTester.dbg.json +1 -1
  43. package/artifacts/contracts/mocks/OverMintTesterRegularStake.sol/OverMintTesterRegularStake.dbg.json +1 -1
  44. package/artifacts/contracts/mocks/SixteenDecimalsTokenMock.sol/SixteenDecimalsTokenMock.dbg.json +1 -1
  45. package/artifacts/contracts/mocks/SwapHelperTest.sol/SwapHelperTest.dbg.json +1 -1
  46. package/artifacts/contracts/mocks/TwentyDecimalsTokenMock.sol/TwentyDecimalsTokenMock.dbg.json +1 -1
  47. package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock.dbg.json +1 -1
  48. package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock2.dbg.json +1 -1
  49. package/artifacts/contracts/mocks/UsdcMock.sol/USDCMock.dbg.json +1 -1
  50. package/artifacts/contracts/mocks/cBATMock.sol/cBATMock.dbg.json +1 -1
  51. package/artifacts/contracts/mocks/cDAILowWorthMock.sol/cDAILowWorthMock.dbg.json +1 -1
  52. package/artifacts/contracts/mocks/cDAIMock.sol/cDAIMock.dbg.json +1 -1
  53. package/artifacts/contracts/mocks/cDAINonMintableMock.sol/cDAINonMintableMock.dbg.json +1 -1
  54. package/artifacts/contracts/mocks/cDecimalsMock.sol/cDecimalsMock.dbg.json +1 -1
  55. package/artifacts/contracts/mocks/cSDTMock.sol/cSDTMock.dbg.json +1 -1
  56. package/artifacts/contracts/mocks/cUSDCMock.sol/cUSDCMock.dbg.json +1 -1
  57. package/artifacts/contracts/reserve/ExchangeHelper.sol/ExchangeHelper.dbg.json +1 -1
  58. package/artifacts/contracts/reserve/GoodMarketMaker.sol/GoodMarketMaker.dbg.json +1 -1
  59. package/artifacts/contracts/reserve/GoodReserveCDai.sol/ContributionCalc.dbg.json +1 -1
  60. package/artifacts/contracts/reserve/GoodReserveCDai.sol/GoodReserveCDai.dbg.json +1 -1
  61. package/artifacts/contracts/staking/BaseShareField.sol/BaseShareField.dbg.json +1 -1
  62. package/artifacts/contracts/staking/BaseShareFieldV2.sol/BaseShareFieldV2.dbg.json +4 -0
  63. package/artifacts/contracts/staking/BaseShareFieldV2.sol/BaseShareFieldV2.json +256 -0
  64. package/artifacts/contracts/staking/DonationsStaking.sol/DonationsStaking.dbg.json +1 -1
  65. package/artifacts/contracts/staking/GoodFundManager.sol/GoodFundManager.dbg.json +1 -1
  66. package/artifacts/contracts/staking/SimpleStaking.sol/SimpleStaking.dbg.json +1 -1
  67. package/artifacts/contracts/staking/SimpleStakingV2.sol/SimpleStakingV2.dbg.json +4 -0
  68. package/artifacts/contracts/staking/SimpleStakingV2.sol/SimpleStakingV2.json +1033 -0
  69. package/artifacts/contracts/staking/UniswapV2SwapHelper.sol/UniswapV2SwapHelper.dbg.json +1 -1
  70. package/artifacts/contracts/staking/aave/AaveStakingFactory.sol/AaveStakingFactory.dbg.json +1 -1
  71. package/artifacts/contracts/staking/aave/AaveStakingFactoryV2.sol/AaveStakingFactoryV2.dbg.json +4 -0
  72. package/artifacts/contracts/staking/aave/AaveStakingFactoryV2.sol/AaveStakingFactoryV2.json +148 -0
  73. package/artifacts/contracts/staking/aave/GoodAaveStaking.sol/GoodAaveStaking.dbg.json +1 -1
  74. package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.dbg.json +4 -0
  75. package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.json +1224 -0
  76. package/artifacts/contracts/staking/compound/CompoundStakingFactory.sol/CompoundStakingFactory.dbg.json +1 -1
  77. package/artifacts/contracts/staking/compound/GoodCompoundStaking.sol/GoodCompoundStaking.dbg.json +1 -1
  78. package/artifacts/contracts/ubi/UBIScheme.sol/UBIScheme.dbg.json +1 -1
  79. package/artifacts/contracts/unaudited-foundation/FuseFaucet.sol/FuseFaucet.dbg.json +1 -1
  80. package/artifacts/contracts/unaudited-foundation/InvitesV1.sol/InvitesV1.dbg.json +1 -1
  81. package/artifacts/contracts/utils/BancorFormula.sol/BancorFormula.dbg.json +1 -1
  82. package/artifacts/contracts/utils/DAOContract.sol/DAOContract.dbg.json +1 -1
  83. package/artifacts/contracts/utils/DAOUpgradeableContract.sol/DAOUpgradeableContract.dbg.json +1 -1
  84. package/artifacts/contracts/utils/DSMath.sol/DSMath.dbg.json +1 -1
  85. package/artifacts/contracts/utils/DataTypes.sol/DataTypes.dbg.json +1 -1
  86. package/artifacts/contracts/utils/NameService.sol/NameService.dbg.json +1 -1
  87. package/artifacts/contracts/utils/ProtocolUpgrade.sol/OldMarketMaker.dbg.json +1 -1
  88. package/artifacts/contracts/utils/ProtocolUpgrade.sol/ProtocolUpgrade.dbg.json +1 -1
  89. package/artifacts/contracts/utils/ProtocolUpgradeFuse.sol/ProtocolUpgradeFuse.dbg.json +1 -1
  90. package/artifacts/contracts/utils/ReputationTestHelper.sol/ReputationTestHelper.dbg.json +1 -1
  91. package/contracts/staking/BaseShareFieldV2.sol +319 -0
  92. package/contracts/staking/SimpleStakingV2.sol +522 -0
  93. package/contracts/staking/aave/AaveStakingFactoryV2.sol +93 -0
  94. package/contracts/staking/aave/GoodAaveStakingV2.sol +263 -0
  95. package/package.json +1 -1
  96. package/test/helpers.ts +5 -1
  97. package/test/staking/GoodAaveStakingFactoryV2.test.ts +122 -0
  98. package/test/staking/UsdcAaveStaking.test.ts +5 -1
  99. package/test/staking/UsdcAaveStakingV2.test.ts +291 -0
@@ -0,0 +1,522 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity >=0.8.0;
4
+
5
+ import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
6
+ import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
7
+ import "../Interfaces.sol";
8
+ import "../DAOStackInterfaces.sol";
9
+ import "../utils/NameService.sol";
10
+ import "../utils/DAOContract.sol";
11
+ import "./GoodFundManager.sol";
12
+ import "./BaseShareFieldV2.sol";
13
+ import "../governance/StakersDistribution.sol";
14
+ import "./UniswapV2SwapHelper.sol";
15
+
16
+ /**
17
+ * @title Staking contract that donates earned interest to the DAO
18
+ * allowing stakers to deposit Tokens
19
+ * or withdraw their stake in Tokens
20
+ * the FundManager can request to receive the interest
21
+ */
22
+ abstract contract SimpleStakingV2 is
23
+ ERC20Upgradeable,
24
+ DAOContract,
25
+ BaseShareFieldV2,
26
+ ReentrancyGuardUpgradeable,
27
+ IHasRouter
28
+ {
29
+ // Token address
30
+ ERC20 public token;
31
+ // Interest Token address
32
+ ERC20 public iToken;
33
+
34
+ // emergency pause
35
+ bool public isPaused;
36
+
37
+ //max percentage of token/dai pool liquidity to swap to DAI when collecting interest out of 100000
38
+ uint24 public maxLiquidityPercentageSwap = 300; //0.3%
39
+
40
+ /**
41
+ * @dev Emitted when `staker` stake `value` tokens of `token`
42
+ */
43
+ event Staked(address indexed staker, address token, uint256 value);
44
+
45
+ /**
46
+ * @dev Emitted when `staker` withdraws their stake `value` tokens and contracts balance will
47
+ * be reduced to`remainingBalance`.
48
+ */
49
+ event StakeWithdraw(address indexed staker, address token, uint256 value);
50
+
51
+ /**
52
+ * @dev Emitted when fundmanager transfers intrest collected from defi protrocol.
53
+ * `recipient` will receive `intrestTokenValue` as intrest.
54
+ */
55
+ event InterestCollected(
56
+ address recipient,
57
+ uint256 iTokenGains, // interest accrued
58
+ uint256 tokenGains, // interest worth in underlying token value
59
+ uint256 actualTokenRedeemed, //actual token redeemed in uniswap (max 0.3% of liquidity) to DAI
60
+ uint256 actualRewardTokenEarned, //actual reward token earned
61
+ uint256 interestCollectedInDAI //actual dai sent to the reserve as interest from converting token and optionally reward token in uniswap
62
+ );
63
+
64
+ /**
65
+ * @dev Constructor
66
+ * @param _token The address of Token
67
+ * @param _iToken The address of Interest Token
68
+ * @param _ns The address of the INameService contract
69
+ * @param _tokenName The name of the staking token
70
+ * @param _tokenSymbol The symbol of the staking token
71
+ * @param _maxRewardThreshold the blocks that should pass to get 1x reward multiplier
72
+
73
+ */
74
+ function initialize(
75
+ address _token,
76
+ address _iToken,
77
+ INameService _ns,
78
+ string memory _tokenName,
79
+ string memory _tokenSymbol,
80
+ uint64 _maxRewardThreshold
81
+ ) public virtual initializer {
82
+ setDAO(_ns);
83
+ token = ERC20(_token);
84
+ iToken = ERC20(_iToken);
85
+ __ERC20_init(_tokenName, _tokenSymbol);
86
+ require(
87
+ token.decimals() <= 18,
88
+ "Token decimals should be less than 18 decimals"
89
+ );
90
+ tokenDecimalDifference = 18 - token.decimals();
91
+ maxMultiplierThreshold = _maxRewardThreshold;
92
+ }
93
+
94
+ function setMaxLiquidityPercentageSwap(uint24 _maxPercentage) public virtual {
95
+ _onlyAvatar();
96
+ maxLiquidityPercentageSwap = _maxPercentage;
97
+ }
98
+
99
+ /**
100
+ * @dev Calculates worth of given amount of iToken in Token
101
+ * @param _amount Amount of iToken to calculate worth in Token
102
+ * @return Worth of given amount of iToken in Token
103
+ */
104
+ function iTokenWorthInToken(uint256 _amount)
105
+ public
106
+ view
107
+ virtual
108
+ returns (uint256);
109
+
110
+ /**
111
+ * @dev Get gas cost for interest transfer so can be used in the calculation of collectable interest for particular gas amount
112
+ * @return returns hardcoded gas cost
113
+ */
114
+ function getGasCostForInterestTransfer()
115
+ external
116
+ view
117
+ virtual
118
+ returns (uint32);
119
+
120
+ /**
121
+ * @dev Returns decimal value for token.
122
+ */
123
+ function tokenDecimal() internal view virtual returns (uint256);
124
+
125
+ /**
126
+ * @dev Returns decimal value for intrest token.
127
+ */
128
+ function iTokenDecimal() internal view virtual returns (uint256);
129
+
130
+ /**
131
+ * @dev Redeem invested tokens from defi protocol.
132
+ * @param _amount tokens to be redeemed.
133
+ */
134
+ function redeem(uint256 _amount) internal virtual;
135
+
136
+ /**
137
+ * @dev Redeem invested underlying tokens from defi protocol and exchange into DAI
138
+ * @param _amount tokens to be redeemed
139
+ * @return amount of token swapped to dai, amount of reward token swapped to dai, total dai
140
+ */
141
+ function redeemUnderlyingToDAI(uint256 _amount, address _recipient)
142
+ internal
143
+ virtual
144
+ returns (
145
+ uint256,
146
+ uint256,
147
+ uint256
148
+ );
149
+
150
+ /**
151
+ * @dev Invests staked tokens to defi protocol.
152
+ * @param _amount tokens staked.
153
+ */
154
+ function mintInterestToken(uint256 _amount) internal virtual;
155
+
156
+ /**
157
+ * @dev Function that calculates current interest gains of this staking contract
158
+ * @param _returnTokenBalanceInUSD determine return token balance of staking contract in USD
159
+ * @param _returnTokenGainsInUSD determine return token gains of staking contract in USD
160
+ * @return return gains in itoken,Token and worth of total locked Tokens,token balance in USD (8 decimals),token Gains in USD (8 decimals)
161
+ */
162
+ function currentGains(
163
+ bool _returnTokenBalanceInUSD,
164
+ bool _returnTokenGainsInUSD
165
+ )
166
+ public
167
+ view
168
+ virtual
169
+ returns (
170
+ uint256,
171
+ uint256,
172
+ uint256,
173
+ uint256,
174
+ uint256
175
+ );
176
+
177
+ /**
178
+ * @dev Approve infinite tokens to defi protocols in order to save gas
179
+ */
180
+ function _approveTokens() internal virtual;
181
+
182
+ /**
183
+ * @dev Allows a staker to deposit Tokens. Notice that `approve` is
184
+ * needed to be executed before the execution of this method.
185
+ * Can be executed only when the contract is not paused.
186
+ * @param _amount The amount of Token or iToken to stake (it depends on _inInterestToken parameter)
187
+ * @param _donationPer The % of interest staker want to donate.
188
+ * @param _inInterestToken specificy if stake in iToken or Token
189
+ */
190
+ function stake(
191
+ uint256 _amount,
192
+ uint256 _donationPer,
193
+ bool _inInterestToken
194
+ ) external virtual nonReentrant {
195
+ require(isPaused == false, "Staking is paused");
196
+ require(
197
+ _donationPer == 0 || _donationPer == 100,
198
+ "Donation percentage should be 0 or 100"
199
+ );
200
+ require(_amount > 0, "You need to stake a positive token amount");
201
+ require(
202
+ (_inInterestToken ? iToken : token).transferFrom(
203
+ _msgSender(),
204
+ address(this),
205
+ _amount
206
+ ),
207
+ "transferFrom failed, make sure you approved token transfer"
208
+ );
209
+ _amount = _inInterestToken ? iTokenWorthInToken(_amount) : _amount;
210
+ if (_inInterestToken == false) {
211
+ mintInterestToken(_amount); //mint iToken
212
+ }
213
+ _mint(_msgSender(), _amount); // mint Staking token for staker
214
+ (
215
+ uint32 rewardsPerBlock,
216
+ uint64 blockStart,
217
+ uint64 blockEnd,
218
+
219
+ ) = GoodFundManager(nameService.getAddress("FUND_MANAGER"))
220
+ .rewardsForStakingContract(address(this));
221
+ _increaseProductivity(
222
+ _msgSender(),
223
+ _amount,
224
+ rewardsPerBlock,
225
+ blockStart,
226
+ blockEnd,
227
+ _donationPer
228
+ );
229
+
230
+ //notify GDAO distrbution for stakers
231
+ StakersDistribution sd = StakersDistribution(
232
+ nameService.getAddress("GDAO_STAKERS")
233
+ );
234
+ if (address(sd) != address(0)) {
235
+ uint256 stakeAmountInEighteenDecimals = token.decimals() == 18
236
+ ? _amount
237
+ : _amount * 10**(18 - token.decimals());
238
+ sd.userStaked(_msgSender(), stakeAmountInEighteenDecimals);
239
+ }
240
+
241
+ emit Staked(_msgSender(), address(token), _amount);
242
+ }
243
+
244
+ /**
245
+ * @dev Withdraws the sender staked Token.
246
+ * @param _amount Amount to withdraw in Token or iToken
247
+ * @param _inInterestToken if true _amount is in iToken and also returned in iToken other wise use Token
248
+ */
249
+ function withdrawStake(uint256 _amount, bool _inInterestToken)
250
+ external
251
+ virtual
252
+ nonReentrant
253
+ {
254
+ uint256 tokenWithdraw;
255
+
256
+ if (_inInterestToken) {
257
+ uint256 tokenWorth = iTokenWorthInToken(_amount);
258
+ require(
259
+ iToken.transfer(_msgSender(), _amount),
260
+ "withdraw transfer failed"
261
+ );
262
+ tokenWithdraw = _amount = tokenWorth;
263
+ } else {
264
+ tokenWithdraw = _amount;
265
+ redeem(tokenWithdraw);
266
+
267
+ //this is required for redeem precision loss
268
+ uint256 tokenActual = token.balanceOf(address(this));
269
+ if (tokenActual < tokenWithdraw) {
270
+ tokenWithdraw = tokenActual;
271
+ }
272
+ require(
273
+ token.transfer(_msgSender(), tokenWithdraw),
274
+ "withdraw transfer failed"
275
+ );
276
+ }
277
+
278
+ GoodFundManager fm = GoodFundManager(
279
+ nameService.getAddress("FUND_MANAGER")
280
+ );
281
+
282
+ //this will revert in case user doesnt have enough productivity to withdraw _amount, as productivity=staking tokens amount
283
+ _burn(msg.sender, _amount); // burn their staking tokens
284
+
285
+ (uint32 rewardsPerBlock, uint64 blockStart, uint64 blockEnd, ) = fm
286
+ .rewardsForStakingContract(address(this));
287
+
288
+ _decreaseProductivity(
289
+ _msgSender(),
290
+ _amount,
291
+ rewardsPerBlock,
292
+ blockStart,
293
+ blockEnd
294
+ );
295
+ fm.mintReward(nameService.getAddress("CDAI"), _msgSender()); // send rewards to user and use cDAI address since reserve in cDAI
296
+
297
+ //notify GDAO distrbution for stakers
298
+ StakersDistribution sd = StakersDistribution(
299
+ nameService.getAddress("GDAO_STAKERS")
300
+ );
301
+ if (address(sd) != address(0)) {
302
+ uint256 withdrawAmountInEighteenDecimals = token.decimals() == 18
303
+ ? _amount
304
+ : _amount * 10**(18 - token.decimals());
305
+ sd.userWithdraw(_msgSender(), withdrawAmountInEighteenDecimals);
306
+ }
307
+
308
+ emit StakeWithdraw(msg.sender, address(token), tokenWithdraw);
309
+ }
310
+
311
+ /**
312
+ * @dev withdraw staker G$ rewards + GDAO rewards
313
+ * withdrawing rewards resets the multiplier! so if user just want GDAO he should use claimReputation()
314
+ */
315
+ function withdrawRewards() external nonReentrant {
316
+ GoodFundManager(nameService.getAddress("FUND_MANAGER")).mintReward(
317
+ nameService.getAddress("CDAI"),
318
+ _msgSender()
319
+ ); // send rewards to user and use cDAI address since reserve in cDAI
320
+ claimReputation();
321
+ }
322
+
323
+ /**
324
+ * @dev withdraw staker GDAO rewards
325
+ */
326
+ function claimReputation() public {
327
+ //claim reputation rewards
328
+ StakersDistribution sd = StakersDistribution(
329
+ nameService.getAddress("GDAO_STAKERS")
330
+ );
331
+ if (address(sd) != address(0)) {
332
+ address[] memory contracts = new address[](1);
333
+ contracts[0] = (address(this));
334
+ sd.claimReputation(_msgSender(), contracts);
335
+ }
336
+ }
337
+
338
+ /**
339
+ * @dev notify stakersdistribution when user performs transfer operation
340
+ */
341
+ function _transfer(
342
+ address _from,
343
+ address _to,
344
+ uint256 _value
345
+ ) internal override {
346
+ super._transfer(_from, _to, _value);
347
+
348
+ StakersDistribution sd = StakersDistribution(
349
+ nameService.getAddress("GDAO_STAKERS")
350
+ );
351
+ (
352
+ uint32 rewardsPerBlock,
353
+ uint64 blockStart,
354
+ uint64 blockEnd,
355
+
356
+ ) = GoodFundManager(nameService.getAddress("FUND_MANAGER"))
357
+ .rewardsForStakingContract(address(this));
358
+
359
+ _decreaseProductivity(_from, _value, rewardsPerBlock, blockStart, blockEnd);
360
+
361
+ _increaseProductivity(
362
+ _to,
363
+ _value,
364
+ rewardsPerBlock,
365
+ blockStart,
366
+ blockEnd,
367
+ 0
368
+ );
369
+
370
+ if (address(sd) != address(0)) {
371
+ address[] memory contracts;
372
+ contracts[0] = (address(this));
373
+ sd.userWithdraw(_from, _value);
374
+ sd.userStaked(_to, _value);
375
+ }
376
+ }
377
+
378
+ // @dev To find difference in token's decimal and iToken's decimal
379
+ // @return difference in decimals.
380
+ // @return true if token's decimal is more than iToken's
381
+ function tokenDecimalPrecision() internal view returns (uint256, bool) {
382
+ uint256 _tokenDecimal = tokenDecimal();
383
+ uint256 _iTokenDecimal = iTokenDecimal();
384
+ uint256 decimalDifference = _tokenDecimal > _iTokenDecimal
385
+ ? _tokenDecimal - _iTokenDecimal
386
+ : _iTokenDecimal - _tokenDecimal;
387
+ return (decimalDifference, _tokenDecimal > _iTokenDecimal);
388
+ }
389
+
390
+ /**
391
+ * @dev Collects gained interest by fundmanager.
392
+ * @param _recipient The recipient of cDAI gains
393
+ * @return actualTokenRedeemed actualRewardTokenRedeemed actualDai collected interest from token,
394
+ * collected interest from reward token, total DAI received from swapping token+reward token
395
+ */
396
+ function collectUBIInterest(address _recipient)
397
+ public
398
+ virtual
399
+ returns (
400
+ uint256 actualTokenRedeemed,
401
+ uint256 actualRewardTokenRedeemed,
402
+ uint256 actualDai
403
+ )
404
+ {
405
+ _canMintRewards();
406
+ // otherwise fund manager has to wait for the next interval
407
+ require(
408
+ _recipient != address(this),
409
+ "Recipient cannot be the staking contract"
410
+ );
411
+
412
+ (uint256 iTokenGains, uint256 tokenGains, , , ) = currentGains(
413
+ false,
414
+ false
415
+ );
416
+
417
+ (
418
+ actualTokenRedeemed,
419
+ actualRewardTokenRedeemed,
420
+ actualDai
421
+ ) = redeemUnderlyingToDAI(iTokenGains, _recipient);
422
+
423
+ emit InterestCollected(
424
+ _recipient,
425
+ iTokenGains,
426
+ tokenGains,
427
+ actualTokenRedeemed,
428
+ actualRewardTokenRedeemed,
429
+ actualDai
430
+ );
431
+ }
432
+
433
+ /**
434
+ * @dev making the contract inactive
435
+ * NOTICE: this could theoretically result in future interest earned in cdai to remain locked
436
+ */
437
+ function pause(bool _isPaused) public {
438
+ _onlyAvatar();
439
+ isPaused = _isPaused;
440
+ }
441
+
442
+ /**
443
+ * @dev method to recover any stuck ERC20 tokens (ie compound COMP)
444
+ * @param _token the ERC20 token to recover
445
+ */
446
+ function recover(ERC20 _token) public {
447
+ _onlyAvatar();
448
+ uint256 toWithdraw = _token.balanceOf(address(this));
449
+
450
+ // recover left iToken(stakers token) only when all stakes have been withdrawn
451
+ if (address(_token) == address(iToken)) {
452
+ require(
453
+ totalProductivity == 0 && isPaused,
454
+ "can recover iToken only when stakes have been withdrawn"
455
+ );
456
+ }
457
+ require(
458
+ _token.transfer(address(avatar), toWithdraw),
459
+ "recover transfer failed"
460
+ );
461
+ }
462
+
463
+ /**
464
+ @dev function calculate Token price in USD
465
+ @param _oracle chainlink oracle usd/token oralce
466
+ @param _amount Amount of Token to calculate worth of it
467
+ @param _decimals decimals of Token
468
+ @return Returns worth of Tokens in USD
469
+ */
470
+ function getTokenValueInUSD(
471
+ address _oracle,
472
+ uint256 _amount,
473
+ uint256 _decimals
474
+ ) public view returns (uint256) {
475
+ AggregatorV3Interface tokenPriceOracle = AggregatorV3Interface(_oracle);
476
+ int256 tokenPriceinUSD = tokenPriceOracle.latestAnswer();
477
+ return (uint256(tokenPriceinUSD) * _amount) / (10**_decimals); // tokenPriceinUSD in 8 decimals and _amount is in Token's decimals so we divide it to Token's decimal at the end to reduce 8 decimals back
478
+ }
479
+
480
+ function _canMintRewards() internal view override {
481
+ require(
482
+ _msgSender() == nameService.getAddress("FUND_MANAGER"),
483
+ "Only FundManager can call this method"
484
+ );
485
+ }
486
+
487
+ function decimals() public view virtual override returns (uint8) {
488
+ return token.decimals();
489
+ }
490
+
491
+ /**
492
+ * @param _staker account to get rewards status for
493
+ * @return (minted, pending) in G$ 2 decimals
494
+ */
495
+ function getUserMintedAndPending(address _staker)
496
+ external
497
+ view
498
+ returns (uint256, uint256)
499
+ {
500
+ (
501
+ uint32 rewardsPerBlock,
502
+ uint64 blockStart,
503
+ uint64 blockEnd,
504
+
505
+ ) = GoodFundManager(nameService.getAddress("FUND_MANAGER"))
506
+ .rewardsForStakingContract(address(this));
507
+
508
+ uint256 pending = getUserPendingReward(
509
+ _staker,
510
+ rewardsPerBlock,
511
+ blockStart,
512
+ blockEnd
513
+ );
514
+
515
+ //divide by 1e16 to return in 2 decimals
516
+ return (users[_staker].rewardMinted / 1e16, pending / 1e16);
517
+ }
518
+
519
+ function getRouter() public view override returns (Uniswap) {
520
+ return Uniswap(nameService.getAddress("UNISWAP_ROUTER"));
521
+ }
522
+ }
@@ -0,0 +1,93 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity >=0.8.0;
4
+ import "@openzeppelin/contracts-upgradeable/proxy/ClonesUpgradeable.sol";
5
+ import "./GoodAaveStakingV2.sol";
6
+ import "../../Interfaces.sol";
7
+
8
+ /**
9
+ * @title Staking contract that donates earned interest to the DAO
10
+ * allowing stakers to deposit Token
11
+ * or withdraw their stake in Token
12
+ * the contracts buy cToken and can transfer the daily interest to the DAO
13
+ */
14
+ contract AaveStakingFactoryV2 {
15
+ using ClonesUpgradeable for address;
16
+
17
+ address public impl = address(new GoodAaveStakingV2());
18
+
19
+ event Deployed(address proxy, address token);
20
+
21
+ function clone(ERC20 token, bytes32 paramsHash)
22
+ public
23
+ returns (GoodAaveStakingV2)
24
+ {
25
+ address deployed = address(impl).cloneDeterministic(
26
+ keccak256(abi.encodePacked(address(token), paramsHash))
27
+ );
28
+ emit Deployed(deployed, address(token));
29
+ return GoodAaveStakingV2(deployed);
30
+ }
31
+
32
+ /**
33
+ @dev Function to clone Staking contract and initialize new one with new token
34
+ @param token Staking token to use in staking contract
35
+ @param _lendingPool address of the lending Pool of AAVE Protocol
36
+ @param _ns NameService that holds whole necessary addresses
37
+ @param _maxRewardThreshold Block numbers that need to pass in order to user would get their rewards with 1x multiplier instead of 0.5x
38
+ @param _tokenUsdOracle address of the TOKEN/USD oracle
39
+ @param _incentiveController Incentive Controller of AAVE protocol in order to claim rewards from AAVE
40
+ @param _aaveUSDOracle address of the AAVE/USD oracle
41
+ */
42
+ function cloneAndInit(
43
+ ERC20 token,
44
+ address _lendingPool,
45
+ INameService _ns,
46
+ uint64 _maxRewardThreshold,
47
+ address _tokenUsdOracle,
48
+ IAaveIncentivesController _incentiveController,
49
+ address _aaveUSDOracle,
50
+ address[] memory _tokenToDaiSwapPath
51
+ ) public {
52
+ GoodAaveStakingV2 deployed = clone(
53
+ token,
54
+ keccak256(
55
+ abi.encodePacked(
56
+ _lendingPool,
57
+ address(_ns),
58
+ _maxRewardThreshold,
59
+ _tokenUsdOracle,
60
+ address(_incentiveController),
61
+ _aaveUSDOracle
62
+ )
63
+ )
64
+ );
65
+
66
+ ERC20 iToken = ERC20(
67
+ ILendingPool(_lendingPool).getReserveData(address(token)).aTokenAddress
68
+ );
69
+ deployed.init(
70
+ address(token),
71
+ address(_lendingPool),
72
+ _ns,
73
+ string(abi.encodePacked("GoodAaveStakingV2 ", iToken.name())),
74
+ string(abi.encodePacked("g", iToken.symbol())),
75
+ _maxRewardThreshold,
76
+ _tokenUsdOracle,
77
+ _incentiveController,
78
+ _aaveUSDOracle,
79
+ _tokenToDaiSwapPath
80
+ );
81
+ }
82
+
83
+ function predictAddress(ERC20 token, bytes32 paramsHash)
84
+ public
85
+ view
86
+ returns (address)
87
+ {
88
+ return
89
+ address(impl).predictDeterministicAddress(
90
+ keccak256(abi.encodePacked(address(token), paramsHash))
91
+ );
92
+ }
93
+ }