-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTraderPoolProposal.sol
272 lines (219 loc) · 8.71 KB
/
TraderPoolProposal.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC1155/extensions/ERC1155SupplyUpgradeable.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@dlsl/dev-modules/contracts-registry/AbstractDependant.sol";
import "@dlsl/dev-modules/libs/decimals/DecimalsConverter.sol";
import "../interfaces/core/IPriceFeed.sol";
import "../interfaces/trader/ITraderPoolProposal.sol";
import "../interfaces/trader/ITraderPoolMemberHook.sol";
import "../interfaces/core/IContractsRegistry.sol";
import "../libs/math/MathHelper.sol";
import "./TraderPool.sol";
abstract contract TraderPoolProposal is
ITraderPoolProposal,
ERC1155SupplyUpgradeable,
AbstractDependant
{
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
using MathHelper for uint256;
using DecimalsConverter for uint256;
using Math for uint256;
ParentTraderPoolInfo internal _parentTraderPoolInfo;
IPriceFeed public override priceFeed;
uint256 public proposalsTotalNum;
uint256 public override totalLockedLP;
uint256 public override investedBase;
mapping(uint256 => EnumerableSet.AddressSet) internal _investors; // proposal id => investors
mapping(address => EnumerableSet.UintSet) internal _activeInvestments; // user => proposals
mapping(address => mapping(uint256 => uint256)) internal _baseBalances; // user => proposal id => base invested
mapping(address => mapping(uint256 => uint256)) internal _lpBalances; // user => proposal id => LP invested
mapping(address => uint256) public override totalLPBalances; // user => LP invested
event ProposalRestrictionsChanged(uint256 proposalId, address sender);
event ProposalJoined(uint256 proposalId, address investor);
event ProposalLeft(uint256 proposalId, address investor);
event ProposalInvested(
uint256 proposalId,
address user,
uint256 investedLP,
uint256 investedBase,
uint256 receivedLP2
);
event ProposalDivested(
uint256 proposalId,
address user,
uint256 divestedLP2,
uint256 receivedLP,
uint256 receivedBase
);
modifier onlyParentTraderPool() {
_onlyParentTraderPool();
_;
}
modifier onlyTraderAdmin() {
_onlyTraderAdmin();
_;
}
modifier onlyBABTHolder() {
_onlyBABTHolder();
_;
}
function __TraderPoolProposal_init(
ParentTraderPoolInfo calldata parentTraderPoolInfo
) public onlyInitializing {
__ERC1155Supply_init();
_parentTraderPoolInfo = parentTraderPoolInfo;
IERC20(parentTraderPoolInfo.baseToken).safeApprove(
parentTraderPoolInfo.parentPoolAddress,
MAX_UINT
);
}
function setDependencies(address contractsRegistry) external override dependant {
IContractsRegistry registry = IContractsRegistry(contractsRegistry);
priceFeed = IPriceFeed(registry.getPriceFeedContract());
}
function getBaseToken() external view override returns (address) {
return _parentTraderPoolInfo.baseToken;
}
function getInvestedBaseInUSD() external view override returns (uint256 investedBaseUSD) {
(investedBaseUSD, ) = priceFeed.getNormalizedPriceOutUSD(
_parentTraderPoolInfo.baseToken,
investedBase
);
}
function getTotalActiveInvestments(address user) external view override returns (uint256) {
return _activeInvestments[user].length();
}
function _transferAndMintLP(
uint256 proposalId,
address to,
uint256 lpInvestment,
uint256 baseInvestment
) internal {
IERC20(_parentTraderPoolInfo.baseToken).safeTransferFrom(
_parentTraderPoolInfo.parentPoolAddress,
address(this),
baseInvestment.from18(_parentTraderPoolInfo.baseTokenDecimals)
);
uint256 baseInProposal = _baseInProposal(proposalId);
uint256 toMint = baseInvestment;
if (baseInProposal > 0) {
toMint = toMint.ratio(totalSupply(proposalId), baseInProposal);
}
totalLockedLP += lpInvestment;
investedBase += baseInvestment;
_updateTo(to, proposalId, toMint, lpInvestment, baseInvestment);
_mint(to, proposalId, toMint, "");
}
function _updateFromData(
address user,
uint256 proposalId,
uint256 lp2Amount
) internal returns (uint256 lpTransfer, uint256 baseTransfer) {
uint256 baseBalance = _baseBalances[user][proposalId];
uint256 lpBalance = _lpBalances[user][proposalId];
baseTransfer = baseBalance.ratio(lp2Amount, balanceOf(user, proposalId)).min(baseBalance);
lpTransfer = lpBalance.ratio(lp2Amount, balanceOf(user, proposalId)).min(lpBalance);
_baseBalances[user][proposalId] -= baseTransfer;
_lpBalances[user][proposalId] -= lpTransfer;
totalLPBalances[user] -= lpTransfer;
}
function _updateToData(
address user,
uint256 proposalId,
uint256 lpAmount,
uint256 baseAmount
) internal {
_activeInvestments[user].add(proposalId);
_baseBalances[user][proposalId] += baseAmount;
_lpBalances[user][proposalId] += lpAmount;
totalLPBalances[user] += lpAmount;
}
function _checkLeave(address user, uint256 proposalId, uint256 lp2Amount) internal {
if (balanceOf(user, proposalId) == lp2Amount) {
_activeInvestments[user].remove(proposalId);
if (user != _parentTraderPoolInfo.trader) {
_investors[proposalId].remove(user);
}
if (_activeInvestments[user].length() == 0) {
ITraderPoolMemberHook(_parentTraderPoolInfo.parentPoolAddress).checkLeave(user);
}
emit ProposalLeft(proposalId, user);
}
}
function _checkJoin(address user, uint256 proposalId) internal {
if (balanceOf(user, proposalId) == 0) {
if (user != _parentTraderPoolInfo.trader) {
_investors[proposalId].add(user);
}
ITraderPoolMemberHook(_parentTraderPoolInfo.parentPoolAddress).checkJoin(user);
emit ProposalJoined(proposalId, user);
}
}
function _updateFrom(
address user,
uint256 proposalId,
uint256 lp2Amount,
bool isTransfer
) internal virtual returns (uint256 lpTransfer, uint256 baseTransfer) {
(lpTransfer, baseTransfer) = _updateFromData(user, proposalId, lp2Amount);
if (isTransfer) {
emit ProposalDivested(proposalId, user, lp2Amount, lpTransfer, baseTransfer);
}
_checkLeave(user, proposalId, lp2Amount);
}
function _updateTo(
address user,
uint256 proposalId,
uint256 lp2Amount,
uint256 lpAmount,
uint256 baseAmount
) internal virtual {
_checkJoin(user, proposalId);
_updateToData(user, proposalId, lpAmount, baseAmount);
emit ProposalInvested(proposalId, user, lpAmount, baseAmount, lp2Amount);
}
function _beforeTokenTransfer(
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) internal override {
super._beforeTokenTransfer(operator, from, to, ids, amounts, data);
for (uint256 i = 0; i < amounts.length; i++) {
require(amounts[i] > 0, "TPP: 0 transfer");
if (from != address(0) && to != address(0) && to != from) {
(uint256 lpTransfer, uint256 baseTransfer) = _updateFrom(
from,
ids[i],
amounts[i],
true
);
_updateTo(to, ids[i], amounts[i], lpTransfer, baseTransfer);
}
}
}
function _baseInProposal(uint256 proposalId) internal view virtual returns (uint256);
function _onlyParentTraderPool() internal view {
require(msg.sender == _parentTraderPoolInfo.parentPoolAddress, "TPP: not a ParentPool");
}
function _onlyTraderAdmin() internal view {
require(
TraderPool(_parentTraderPoolInfo.parentPoolAddress).isTraderAdmin(msg.sender),
"TPP: not a trader admin"
);
}
function _onlyBABTHolder() internal view {
require(
TraderPool(_parentTraderPoolInfo.parentPoolAddress).isBABTHolder(msg.sender),
"TPP: not BABT holder"
);
}
}