-
Notifications
You must be signed in to change notification settings - Fork 16
/
LiquidateWithReplacement.sol
164 lines (141 loc) · 6.98 KB
/
LiquidateWithReplacement.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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {Math} from "@src/libraries/Math.sol";
import {PERCENT} from "@src/libraries/Math.sol";
import {CreditPosition, DebtPosition, LoanLibrary, LoanStatus} from "@src/libraries/LoanLibrary.sol";
import {BorrowOffer, OfferLibrary} from "@src/libraries/OfferLibrary.sol";
import {VariablePoolBorrowRateParams} from "@src/libraries/YieldCurveLibrary.sol";
import {State} from "@src/SizeStorage.sol";
import {Liquidate, LiquidateParams} from "@src/libraries/actions/Liquidate.sol";
import {Errors} from "@src/libraries/Errors.sol";
import {Events} from "@src/libraries/Events.sol";
struct LiquidateWithReplacementParams {
// The debt position ID to liquidate
uint256 debtPositionId;
// The borrower
address borrower;
// The minimum profit in collateral tokens expected by the liquidator
uint256 minimumCollateralProfit;
// The deadline for the transaction
uint256 deadline;
// The minimum APR for the loan
uint256 minAPR;
}
/// @title LiquidateWithReplacement
/// @custom:security-contact security@size.credit
/// @author Size (https://size.credit/)
/// @notice Contains the logic for liquidating a debt position with a replacement borrower
library LiquidateWithReplacement {
using LoanLibrary for CreditPosition;
using OfferLibrary for BorrowOffer;
using LoanLibrary for State;
using Liquidate for State;
using LoanLibrary for DebtPosition;
/// @notice Validates the input parameters for liquidating a debt position with a replacement borrower
/// @param state The state
/// @param params The input parameters for liquidating a debt position with a replacement borrower
function validateLiquidateWithReplacement(State storage state, LiquidateWithReplacementParams calldata params)
external
view
{
DebtPosition storage debtPosition = state.getDebtPosition(params.debtPositionId);
BorrowOffer storage borrowOffer = state.data.users[params.borrower].borrowOffer;
// validate liquidate
state.validateLiquidate(
LiquidateParams({
debtPositionId: params.debtPositionId,
minimumCollateralProfit: params.minimumCollateralProfit
})
);
// validate debtPositionId
if (state.getLoanStatus(params.debtPositionId) != LoanStatus.ACTIVE) {
revert Errors.LOAN_NOT_ACTIVE(params.debtPositionId);
}
uint256 tenor = debtPosition.dueDate - block.timestamp;
if (tenor < state.riskConfig.minTenor || tenor > state.riskConfig.maxTenor) {
revert Errors.TENOR_OUT_OF_RANGE(tenor, state.riskConfig.minTenor, state.riskConfig.maxTenor);
}
// validate borrower
if (borrowOffer.isNull()) {
revert Errors.INVALID_BORROW_OFFER(params.borrower);
}
// validate deadline
if (params.deadline < block.timestamp) {
revert Errors.PAST_DEADLINE(params.deadline);
}
// validate minAPR
uint256 apr = borrowOffer.getAPRByTenor(
VariablePoolBorrowRateParams({
variablePoolBorrowRate: state.oracle.variablePoolBorrowRate,
variablePoolBorrowRateUpdatedAt: state.oracle.variablePoolBorrowRateUpdatedAt,
variablePoolBorrowRateStaleRateInterval: state.oracle.variablePoolBorrowRateStaleRateInterval
}),
tenor
);
if (apr < params.minAPR) {
revert Errors.APR_LOWER_THAN_MIN_APR(apr, params.minAPR);
}
}
/// @notice Validates the minimum profit in collateral tokens expected by the liquidator
/// @param state The state
/// @param params The input parameters for liquidating a debt position with a replacement borrower
/// @param liquidatorProfitCollateralToken The profit in collateral tokens expected by the liquidator
function validateMinimumCollateralProfit(
State storage state,
LiquidateWithReplacementParams calldata params,
uint256 liquidatorProfitCollateralToken
) external pure {
Liquidate.validateMinimumCollateralProfit(
state,
LiquidateParams({
debtPositionId: params.debtPositionId,
minimumCollateralProfit: params.minimumCollateralProfit
}),
liquidatorProfitCollateralToken
);
}
/// @notice Executes the liquidation of a debt position with a replacement borrower
/// @param state The state
/// @param params The input parameters for liquidating a debt position with a replacement borrower
/// @return issuanceValue The issuance value
/// @return liquidatorProfitCollateralToken The profit in collateral tokens expected by the liquidator
/// @return liquidatorProfitBorrowToken The profit in borrow tokens expected by the liquidator
function executeLiquidateWithReplacement(State storage state, LiquidateWithReplacementParams calldata params)
external
returns (uint256 issuanceValue, uint256 liquidatorProfitCollateralToken, uint256 liquidatorProfitBorrowToken)
{
emit Events.LiquidateWithReplacement(params.debtPositionId, params.borrower, params.minimumCollateralProfit);
DebtPosition storage debtPosition = state.getDebtPosition(params.debtPositionId);
DebtPosition memory debtPositionCopy = debtPosition;
BorrowOffer storage borrowOffer = state.data.users[params.borrower].borrowOffer;
uint256 tenor = debtPositionCopy.dueDate - block.timestamp;
liquidatorProfitCollateralToken = state.executeLiquidate(
LiquidateParams({
debtPositionId: params.debtPositionId,
minimumCollateralProfit: params.minimumCollateralProfit
})
);
uint256 ratePerTenor = borrowOffer.getRatePerTenor(
VariablePoolBorrowRateParams({
variablePoolBorrowRate: state.oracle.variablePoolBorrowRate,
variablePoolBorrowRateUpdatedAt: state.oracle.variablePoolBorrowRateUpdatedAt,
variablePoolBorrowRateStaleRateInterval: state.oracle.variablePoolBorrowRateStaleRateInterval
}),
tenor
);
issuanceValue = Math.mulDivDown(debtPositionCopy.futureValue, PERCENT, PERCENT + ratePerTenor);
liquidatorProfitBorrowToken = debtPositionCopy.futureValue - issuanceValue;
debtPosition.borrower = params.borrower;
debtPosition.futureValue = debtPositionCopy.futureValue;
debtPosition.liquidityIndexAtRepayment = 0;
emit Events.UpdateDebtPosition(
params.debtPositionId,
debtPosition.borrower,
debtPosition.futureValue,
debtPosition.liquidityIndexAtRepayment
);
state.data.debtToken.mint(params.borrower, debtPosition.futureValue);
state.data.borrowAToken.transferFrom(address(this), params.borrower, issuanceValue);
state.data.borrowAToken.transferFrom(address(this), state.feeConfig.feeRecipient, liquidatorProfitBorrowToken);
}
}