-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathEntryPositionsManager.sol
289 lines (238 loc) · 10.5 KB
/
EntryPositionsManager.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
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.13;
import "./interfaces/IEntryPositionsManager.sol";
import "./PositionsManagerUtils.sol";
/// @title EntryPositionsManager.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice Morpho's entry points: supply and borrow.
contract EntryPositionsManager is IEntryPositionsManager, PositionsManagerUtils {
using ReserveConfiguration for DataTypes.ReserveConfigurationMap;
using HeapOrdering for HeapOrdering.HeapArray;
using PercentageMath for uint256;
using SafeTransferLib for ERC20;
using WadRayMath for uint256;
using Math for uint256;
/// EVENTS ///
/// @notice Emitted when a supply happens.
/// @param _from The address of the account sending funds.
/// @param _onBehalf The address of the account whose positions will be updated.
/// @param _poolToken The address of the market where assets are supplied into.
/// @param _amount The amount of assets supplied (in underlying).
/// @param _balanceOnPool The supply balance on pool after update.
/// @param _balanceInP2P The supply balance in peer-to-peer after update.
event Supplied(
address indexed _from,
address indexed _onBehalf,
address indexed _poolToken,
uint256 _amount,
uint256 _balanceOnPool,
uint256 _balanceInP2P
);
/// @notice Emitted when a borrow happens.
/// @param _borrower The address of the borrower.
/// @param _poolToken The address of the market where assets are borrowed.
/// @param _amount The amount of assets borrowed (in underlying).
/// @param _balanceOnPool The borrow balance on pool after update.
/// @param _balanceInP2P The borrow balance in peer-to-peer after update
event Borrowed(
address indexed _borrower,
address indexed _poolToken,
uint256 _amount,
uint256 _balanceOnPool,
uint256 _balanceInP2P
);
/// ERRORS ///
/// @notice Thrown when borrowing is impossible, because it is not enabled on pool for this specific market.
error BorrowingNotEnabled();
/// @notice Thrown when the user does not have enough collateral for the borrow.
error UnauthorisedBorrow();
/// @notice Thrown when someone tries to supply but the supply is paused.
error SupplyIsPaused();
/// @notice Thrown when someone tries to borrow but the borrow is paused.
error BorrowIsPaused();
/// STRUCTS ///
// Struct to avoid stack too deep.
struct SupplyVars {
uint256 remainingToSupply;
uint256 poolBorrowIndex;
uint256 toRepay;
}
/// LOGIC ///
/// @dev Implements supply logic.
/// @param _poolToken The address of the pool token the user wants to interact with.
/// @param _from The address of the account sending funds.
/// @param _onBehalf The address of the account whose positions will be updated.
/// @param _amount The amount of token (in underlying).
/// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
function supplyLogic(
address _poolToken,
address _from,
address _onBehalf,
uint256 _amount,
uint256 _maxGasForMatching
) external {
if (_onBehalf == address(0)) revert AddressIsZero();
if (_amount == 0) revert AmountIsZero();
Types.Market memory market = market[_poolToken];
ERC20 underlyingToken = ERC20(market.underlyingToken);
if (!market.isCreated) revert MarketNotCreated();
if (marketPauseStatus[_poolToken].isSupplyPaused) revert SupplyIsPaused();
_updateIndexes(_poolToken);
_setSupplying(_onBehalf, borrowMask[_poolToken], true);
underlyingToken.safeTransferFrom(_from, address(this), _amount);
Types.Delta storage delta = deltas[_poolToken];
SupplyVars memory vars;
vars.poolBorrowIndex = poolIndexes[_poolToken].poolBorrowIndex;
vars.remainingToSupply = _amount;
/// Peer-to-peer supply ///
// Match the peer-to-peer borrow delta.
if (delta.p2pBorrowDelta > 0 && !market.isP2PDisabled) {
uint256 matchedDelta = Math.min(
delta.p2pBorrowDelta.rayMul(vars.poolBorrowIndex),
vars.remainingToSupply
); // In underlying.
delta.p2pBorrowDelta = delta.p2pBorrowDelta.zeroFloorSub(
vars.remainingToSupply.rayDiv(vars.poolBorrowIndex)
);
vars.toRepay += matchedDelta;
vars.remainingToSupply -= matchedDelta;
emit P2PBorrowDeltaUpdated(_poolToken, delta.p2pBorrowDelta);
}
// Promote pool borrowers.
if (
vars.remainingToSupply > 0 &&
!market.isP2PDisabled &&
borrowersOnPool[_poolToken].getHead() != address(0)
) {
(uint256 matched, ) = _matchBorrowers(
_poolToken,
vars.remainingToSupply,
_maxGasForMatching
); // In underlying.
vars.toRepay += matched;
vars.remainingToSupply -= matched;
delta.p2pBorrowAmount += matched.rayDiv(p2pBorrowIndex[_poolToken]);
}
Types.SupplyBalance storage supplierSupplyBalance = supplyBalanceInOf[_poolToken][
_onBehalf
];
if (vars.toRepay > 0) {
uint256 toAddInP2P = vars.toRepay.rayDiv(p2pSupplyIndex[_poolToken]);
delta.p2pSupplyAmount += toAddInP2P;
supplierSupplyBalance.inP2P += toAddInP2P;
_repayToPool(underlyingToken, vars.toRepay); // Reverts on error.
emit P2PAmountsUpdated(_poolToken, delta.p2pSupplyAmount, delta.p2pBorrowAmount);
}
/// Pool supply ///
// Supply on pool.
if (vars.remainingToSupply > 0) {
supplierSupplyBalance.onPool += vars.remainingToSupply.rayDiv(
poolIndexes[_poolToken].poolSupplyIndex
); // In scaled balance.
_supplyToPool(underlyingToken, vars.remainingToSupply); // Reverts on error.
}
_updateSupplierInDS(_poolToken, _onBehalf);
emit Supplied(
_from,
_onBehalf,
_poolToken,
_amount,
supplierSupplyBalance.onPool,
supplierSupplyBalance.inP2P
);
}
/// @dev Implements borrow logic.
/// @param _poolToken The address of the market the user wants to interact with.
/// @param _amount The amount of token (in underlying).
/// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
function borrowLogic(
address _poolToken,
uint256 _amount,
uint256 _maxGasForMatching
) external {
if (_amount == 0) revert AmountIsZero();
Types.Market memory market = market[_poolToken];
if (!market.isCreated) revert MarketNotCreated();
if (marketPauseStatus[_poolToken].isBorrowPaused) revert BorrowIsPaused();
ERC20 underlyingToken = ERC20(market.underlyingToken);
if (!pool.getConfiguration(address(underlyingToken)).getBorrowingEnabled())
revert BorrowingNotEnabled();
_updateIndexes(_poolToken);
_setBorrowing(msg.sender, borrowMask[_poolToken], true);
if (!_borrowAllowed(msg.sender, _poolToken, _amount)) revert UnauthorisedBorrow();
uint256 remainingToBorrow = _amount;
uint256 toWithdraw;
Types.Delta storage delta = deltas[_poolToken];
uint256 poolSupplyIndex = poolIndexes[_poolToken].poolSupplyIndex;
/// Peer-to-peer borrow ///
// Match the peer-to-peer supply delta.
if (delta.p2pSupplyDelta > 0 && !market.isP2PDisabled) {
uint256 matchedDelta = Math.min(
delta.p2pSupplyDelta.rayMul(poolSupplyIndex),
remainingToBorrow
); // In underlying.
delta.p2pSupplyDelta = delta.p2pSupplyDelta.zeroFloorSub(
remainingToBorrow.rayDiv(poolSupplyIndex)
);
toWithdraw += matchedDelta;
remainingToBorrow -= matchedDelta;
emit P2PSupplyDeltaUpdated(_poolToken, delta.p2pSupplyDelta);
}
// Promote pool suppliers.
if (
remainingToBorrow > 0 &&
!market.isP2PDisabled &&
suppliersOnPool[_poolToken].getHead() != address(0)
) {
(uint256 matched, ) = _matchSuppliers(
_poolToken,
remainingToBorrow,
_maxGasForMatching
); // In underlying.
toWithdraw += matched;
remainingToBorrow -= matched;
delta.p2pSupplyAmount += matched.rayDiv(p2pSupplyIndex[_poolToken]);
}
Types.BorrowBalance storage borrowerBorrowBalance = borrowBalanceInOf[_poolToken][
msg.sender
];
if (toWithdraw > 0) {
uint256 toAddInP2P = toWithdraw.rayDiv(p2pBorrowIndex[_poolToken]); // In peer-to-peer unit.
delta.p2pBorrowAmount += toAddInP2P;
borrowerBorrowBalance.inP2P += toAddInP2P;
emit P2PAmountsUpdated(_poolToken, delta.p2pSupplyAmount, delta.p2pBorrowAmount);
_withdrawFromPool(underlyingToken, _poolToken, toWithdraw); // Reverts on error.
}
/// Pool borrow ///
// Borrow on pool.
if (remainingToBorrow > 0) {
borrowerBorrowBalance.onPool += remainingToBorrow.rayDiv(
poolIndexes[_poolToken].poolBorrowIndex
); // In adUnit.
_borrowFromPool(underlyingToken, remainingToBorrow);
}
_updateBorrowerInDS(_poolToken, msg.sender);
underlyingToken.safeTransfer(msg.sender, _amount);
emit Borrowed(
msg.sender,
_poolToken,
_amount,
borrowerBorrowBalance.onPool,
borrowerBorrowBalance.inP2P
);
}
/// @dev Checks whether the user can borrow or not.
/// @param _user The user to determine liquidity for.
/// @param _poolToken The market to hypothetically borrow in.
/// @param _borrowedAmount The amount of tokens to hypothetically borrow (in underlying).
/// @return Whether the borrow is allowed or not.
function _borrowAllowed(
address _user,
address _poolToken,
uint256 _borrowedAmount
) internal returns (bool) {
Types.LiquidityData memory values = _liquidityData(_user, _poolToken, 0, _borrowedAmount);
return values.debtEth <= values.borrowableEth;
}
}