@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.
- package/artifacts/contracts/DAOStackInterfaces.sol/Avatar.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/Controller.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/GlobalConstraintInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/IntVoteInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/ReputationInterface.dbg.json +1 -1
- package/artifacts/contracts/DAOStackInterfaces.sol/SchemeRegistrar.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/AggregatorV3Interface.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ERC20.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IAaveIncentivesController.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IAdminWallet.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IDonationStaking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IERC2917.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IFirstClaimPool.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IGoodDollar.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IGoodStaking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IHasRouter.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IIdentity.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ILendingPool.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/INameService.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/IUBIScheme.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/ProxyAdmin.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Reserve.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Staking.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/Uniswap.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/UniswapFactory.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/UniswapPair.dbg.json +1 -1
- package/artifacts/contracts/Interfaces.sol/cERC20.dbg.json +1 -1
- package/artifacts/contracts/governance/ClaimersDistribution.sol/ClaimersDistribution.dbg.json +1 -1
- package/artifacts/contracts/governance/CompoundVotingMachine.sol/CompoundVotingMachine.dbg.json +1 -1
- package/artifacts/contracts/governance/GReputation.sol/GReputation.dbg.json +1 -1
- package/artifacts/contracts/governance/GovarnanceStaking.sol/GovernanceStaking.dbg.json +1 -1
- package/artifacts/contracts/governance/MultiBaseGovernanceShareField.sol/MultiBaseGovernanceShareField.dbg.json +1 -1
- package/artifacts/contracts/governance/Reputation.sol/Reputation.dbg.json +1 -1
- package/artifacts/contracts/governance/StakersDistribution.sol/StakersDistribution.dbg.json +1 -1
- package/artifacts/contracts/mocks/AaveMock.sol/AaveMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/DAIMock.sol/DAIMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/DecimalsMock.sol/DecimalsMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/GoodCompoundStakingTest.sol/GoodCompoundStakingTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/GoodFundManagerTest.sol/GoodFundManagerTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/IncentiveControllerMock.sol/IncentiveControllerMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/LendingPoolMock.sol/LendingPoolMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/OverMintTester.sol/OverMintTester.dbg.json +1 -1
- package/artifacts/contracts/mocks/OverMintTesterRegularStake.sol/OverMintTesterRegularStake.dbg.json +1 -1
- package/artifacts/contracts/mocks/SixteenDecimalsTokenMock.sol/SixteenDecimalsTokenMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/SwapHelperTest.sol/SwapHelperTest.dbg.json +1 -1
- package/artifacts/contracts/mocks/TwentyDecimalsTokenMock.sol/TwentyDecimalsTokenMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/UpgradableMocks.sol/UpgradableMock2.dbg.json +1 -1
- package/artifacts/contracts/mocks/UsdcMock.sol/USDCMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cBATMock.sol/cBATMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAILowWorthMock.sol/cDAILowWorthMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAIMock.sol/cDAIMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDAINonMintableMock.sol/cDAINonMintableMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cDecimalsMock.sol/cDecimalsMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cSDTMock.sol/cSDTMock.dbg.json +1 -1
- package/artifacts/contracts/mocks/cUSDCMock.sol/cUSDCMock.dbg.json +1 -1
- package/artifacts/contracts/reserve/ExchangeHelper.sol/ExchangeHelper.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodMarketMaker.sol/GoodMarketMaker.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodReserveCDai.sol/ContributionCalc.dbg.json +1 -1
- package/artifacts/contracts/reserve/GoodReserveCDai.sol/GoodReserveCDai.dbg.json +1 -1
- package/artifacts/contracts/staking/BaseShareField.sol/BaseShareField.dbg.json +1 -1
- package/artifacts/contracts/staking/BaseShareFieldV2.sol/BaseShareFieldV2.dbg.json +4 -0
- package/artifacts/contracts/staking/BaseShareFieldV2.sol/BaseShareFieldV2.json +256 -0
- package/artifacts/contracts/staking/DonationsStaking.sol/DonationsStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/GoodFundManager.sol/GoodFundManager.dbg.json +1 -1
- package/artifacts/contracts/staking/SimpleStaking.sol/SimpleStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/SimpleStakingV2.sol/SimpleStakingV2.dbg.json +4 -0
- package/artifacts/contracts/staking/SimpleStakingV2.sol/SimpleStakingV2.json +1033 -0
- package/artifacts/contracts/staking/UniswapV2SwapHelper.sol/UniswapV2SwapHelper.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/AaveStakingFactory.sol/AaveStakingFactory.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/AaveStakingFactoryV2.sol/AaveStakingFactoryV2.dbg.json +4 -0
- package/artifacts/contracts/staking/aave/AaveStakingFactoryV2.sol/AaveStakingFactoryV2.json +148 -0
- package/artifacts/contracts/staking/aave/GoodAaveStaking.sol/GoodAaveStaking.dbg.json +1 -1
- package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.dbg.json +4 -0
- package/artifacts/contracts/staking/aave/GoodAaveStakingV2.sol/GoodAaveStakingV2.json +1224 -0
- package/artifacts/contracts/staking/compound/CompoundStakingFactory.sol/CompoundStakingFactory.dbg.json +1 -1
- package/artifacts/contracts/staking/compound/GoodCompoundStaking.sol/GoodCompoundStaking.dbg.json +1 -1
- package/artifacts/contracts/ubi/UBIScheme.sol/UBIScheme.dbg.json +1 -1
- package/artifacts/contracts/unaudited-foundation/FuseFaucet.sol/FuseFaucet.dbg.json +1 -1
- package/artifacts/contracts/unaudited-foundation/InvitesV1.sol/InvitesV1.dbg.json +1 -1
- package/artifacts/contracts/utils/BancorFormula.sol/BancorFormula.dbg.json +1 -1
- package/artifacts/contracts/utils/DAOContract.sol/DAOContract.dbg.json +1 -1
- package/artifacts/contracts/utils/DAOUpgradeableContract.sol/DAOUpgradeableContract.dbg.json +1 -1
- package/artifacts/contracts/utils/DSMath.sol/DSMath.dbg.json +1 -1
- package/artifacts/contracts/utils/DataTypes.sol/DataTypes.dbg.json +1 -1
- package/artifacts/contracts/utils/NameService.sol/NameService.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgrade.sol/OldMarketMaker.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgrade.sol/ProtocolUpgrade.dbg.json +1 -1
- package/artifacts/contracts/utils/ProtocolUpgradeFuse.sol/ProtocolUpgradeFuse.dbg.json +1 -1
- package/artifacts/contracts/utils/ReputationTestHelper.sol/ReputationTestHelper.dbg.json +1 -1
- package/contracts/staking/BaseShareFieldV2.sol +319 -0
- package/contracts/staking/SimpleStakingV2.sol +522 -0
- package/contracts/staking/aave/AaveStakingFactoryV2.sol +93 -0
- package/contracts/staking/aave/GoodAaveStakingV2.sol +263 -0
- package/package.json +1 -1
- package/test/helpers.ts +5 -1
- package/test/staking/GoodAaveStakingFactoryV2.test.ts +122 -0
- package/test/staking/UsdcAaveStaking.test.ts +5 -1
- 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
|
+
}
|