/
Witch.sol
164 lines (144 loc) · 5.83 KB
/
Witch.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: BUSL-1.1
pragma solidity >=0.8.13;
import "@yield-protocol/utils-v2/src/utils/Cast.sol";
import "./WitchBase.sol";
/// @title The Witch is a DataTypes.Auction/Liquidation Engine for the Yield protocol
/// @notice The Witch grabs under-collateralised vaults, replacing the owner by itself. Then it sells
/// the vault collateral in exchange for underlying to pay its debt. The amount of collateral
/// given increases over time, until it offers to sell all the collateral for underlying to pay
/// all the debt. The auction is held open at the final price indefinitely.
/// @dev After the debt is settled, the Witch returns the vault to its original owner.
contract Witch is WitchBase {
using Cast for uint256;
constructor(ICauldron cauldron_, ILadle ladle_)
WitchBase(cauldron_, ladle_)
{}
// ======================================================================
// = Auction management functions =
// ======================================================================
/// @dev Put an under-collateralised vault up for liquidation
/// @param vaultId Id of the vault to liquidate
/// @param to Receiver of the auctioneer reward
/// @return auction_ Info associated to the auction itself
/// @return vault Vault that's being auctioned
/// @return series Series for the vault that's being auctioned
function auction(bytes12 vaultId, address to)
public
virtual
beforeAshes
returns (
DataTypes.Auction memory auction_,
DataTypes.Vault memory vault,
DataTypes.Series memory series
)
{
vault = cauldron.vaults(vaultId);
series = cauldron.series(vault.seriesId);
DataTypes.Line memory line;
(auction_, line) = _calcAuctionParameters(
vaultId,
series.baseId,
vault.ilkId,
vault.seriesId,
vault.owner,
to
);
vault = cauldron.give(vaultId, address(this));
emit Auctioned(
vaultId,
auction_,
line.duration,
line.collateralProportion
);
}
// ======================================================================
// = Bidding functions =
// ======================================================================
/// @notice Returns debt that could be paid given the maxBaseIn
function _debtFromBase(DataTypes.Auction memory auction_, uint128 maxBaseIn)
internal
virtual
override
returns (uint256 artIn)
{
artIn = cauldron.debtFromBase(auction_.seriesId, maxBaseIn);
}
/// @notice Returns base that could be paid given the artIn
function _debtToBase(DataTypes.Auction memory auction_, uint128 artIn)
internal
virtual
override
returns (uint256 baseIn)
{
baseIn = cauldron.debtToBase(auction_.seriesId, artIn);
}
/// @notice If too much fyToken are offered, only the necessary amount are taken.
/// @dev Pay up to `maxArtIn` debt from a vault in liquidation using fyToken, getting at least `minInkOut` collateral.
/// @param vaultId Id of the vault to buy
/// @param to Receiver for the collateral bought
/// @param maxArtIn Maximum amount of fyToken that will be paid
/// @param minInkOut Minimum amount of collateral that must be received
/// @return liquidatorCut Amount paid to `to`.
/// @return auctioneerCut Amount paid to an address specified by whomever started the auction. 0 if it's the same as the `to` address
/// @return artIn Amount of fyToken taken
function payFYToken(
bytes12 vaultId,
address to,
uint128 minInkOut,
uint128 maxArtIn
)
external
returns (
uint256 liquidatorCut,
uint256 auctioneerCut,
uint128 artIn
)
{
DataTypes.Auction memory auction_ = _auction(vaultId);
// If offering too much fyToken, take only the necessary.
artIn = maxArtIn > auction_.art ? auction_.art : maxArtIn;
// Calculate the collateral to be sold
(liquidatorCut, auctioneerCut) = _calcPayout(auction_, to, artIn);
if (liquidatorCut < minInkOut) {
revert NotEnoughBought(minInkOut, liquidatorCut);
}
// Update Cauldron and local auction data
_updateAccounting(
vaultId,
auction_,
(liquidatorCut + auctioneerCut).u128(),
artIn
);
// Move the assets
(liquidatorCut, auctioneerCut) = _payInk(
auction_.ilkId,
auction_.auctioneer,
to,
liquidatorCut,
auctioneerCut
);
if (artIn != 0) {
// Burn fyToken from liquidator
cauldron.series(auction_.seriesId).fyToken.burn(msg.sender, artIn);
}
_collateralBought(vaultId, to, liquidatorCut + auctioneerCut, artIn);
}
// ======================================================================
// = Quoting functions =
// ======================================================================
function _getVaultDetailsAndDebt(bytes12 vaultId)
internal
view
override
returns (VaultBalanceDebtData memory details)
{
DataTypes.Vault memory vault = cauldron.vaults(vaultId);
DataTypes.Series memory series = cauldron.series(vault.seriesId);
details.ilkId = vault.ilkId;
details.baseId = series.baseId;
details.seriesId = vault.seriesId;
details.owner = vault.owner;
details.balances = cauldron.balances(vaultId);
details.debt = cauldron.debt(series.baseId, vault.ilkId);
}
}