Dog
), an auction is started promptly to sell the transferred collateral for DAI in an attempt to cancel out the debt now assigned to the protocol.who
(which conforms to a particular interface), and calldata
to supply to it, and the auction contract will automatically invoke whatever logic is in the external contract.redo
), but changes to the configurable parameters of a price decrease calculator (or even the switching out of one calculator for another) by governance can also increase the price. It is recommended that bidders (or bidding UIs that abstract this detail away from the user) choose the maximum acceptable price (max
) argument to take
carefully to ensure a desirable outcome if the price should unexpectedly increase.tail
governance parameter)cusp
governance parameter).buf
parameter). This process will repeat until all collateral has been sold or the whole debt has been collected (unless the auction is canceled via yank
, e.g. during Emergency Shutdown); contrast this behavior with the current auctions, which reset until at least one bid is received.clipperCallee
pattern, then they need not store DAI or collateral on that account. This means a keeper need only hold enough ETH to execute transactions that can orchestrate the Clipper.take
call, sending collateral to a contract that returns DAI to the msg.sender
to pay for the collateral all in one transaction. The contract implementing the clipperCallee
interface can send any remaining collateral or DAI beyond owe
to a cold wallet address inaccessible to the keeper. NOTE: If the keeper chooses to behave as an EOA address, then the DAI and collateral would be exposed just as in LIQ-1.2
unless special care is taken to create a proxy contract.file
)Abacus/LinearDecrease
-- tau [seconds]Abacus/StairstepExponentialDecrease
-- cut [ray]step
multiplicative factor. cut = 0.99 * RAY
specifies a 1% drop every step
seconds.Abacus/StairstepExponentialDecrease
-- step [seconds]Abacus/ExponentialDecrease
-- cut [ray]cut = 0.99 * RAY
specifies a 1% drop every second.Clipper
-- buf [ray]buf = 1.2 * RAY
(20% above), then the initial price of that auction will be 1,200.Clipper
-- calc [address]Abacus
interface. Some examples of price functions are found in abaci.sol
file.Clipper
-- chip [wad]tab
to suck
from vow
to incentivize keepers when liquidating a vault or resetting an already existing auction. chip = 0.02 * WAD
is 2%.Clipper
-- cusp [ray]tail
, this parameter determines when an auction needs to be reset. E.g. if the initial price of an auction (top
) is set to 1,200 and cusp = 0.6 * RAY
(60% of the starting price), then the auction will need to be reset when reaching just below the price of 720.Clipper
-- dog [address]Clipper
-- spotter [address]Clipper
-- tail [seconds]cusp
, this parameter determines when an auction needs to be reset. E.g. if tail
is 1800 seconds, then if an auction is not complete after 30 minutes have elapsed, it will need to be reset.Clipper
-- tip [rad]suck
from vow
to incentivize keepers when liquidating a vault or resetting an already existing auction. tip = 100 * RAD
is 100 DAI.Clipper
-- vow [address]Dog
-- Hole [rad]Hole = 10,000,000 * RAD
is 10M DAI.Dog
-- ilk.chop [wad]ilk
). E.g. if there is a vault ready to be liquidated that has a debt of 1,000 DAI and chop = 1.13 * WAD
(13% above), then the max amount to be raised by the auction will be 1,130 DAI.Dog
-- ilk.clip [address]clip
per collateral (ilk
).Dog
-- ilk.hole [rad]ilk
). hole = 10,000,000 * RAD
is 10M DAI.Dog
-- vow [address]Dog.bark
) takes three caller supplied arguments:ilk
: the collateral ilkurn
: the Vault to be liquidatedkpr
: the address where DAI incentives will be sentDog.bark
performs several actions:urn
if it's undercollateralized andilk
's Clipper
vow
's bad debt accumulatorHole
with the Dirt
accumulatorilk.hole
with the ilk.dirt
accumulatorClipper.kick()
Bark()
eventCat
) and the Liquidation 2.0 version (the Dog
), an auction is started promptly to sell the transferred collateral for DAI in an attempt to cancel out the debt now assigned to the protocol. This makes the behavior of the new contract very similar to that of the old one, but there are some important differences, explained below.Cat.bite
) transfers a fixed amount of debt (the dunk
) from the affected Vault, along with a proportional amount of the Vault's collateral to the protocol. For example, if 50% of a Vault's debt is taken by the protocol, then half of its collateral is taken as well. If the Vault's debt is less than the dunk
, then all debt and collateral is taken. In 2.0, all debt is taken when the liquidation function (Dog.bark
) is called, and no analogue of the dunk
parameter exists. The reasoning behind this change is that because the new auctions allow partial purchases of collateral, the liquidity available to a participant no longer limits their ability to participate in auctions, so instead the total number of auctions should be minimized. Just to emphasize, there is no longer a minimum DAI liquidity requirement for the sale of collateral on a per-participant basis.LIQ-1.2
this is called the box
, and in LIQ-2.0
we call this the Hole
. Whenever the maximum possible addition to this value is less than the amount of debt+fees that would otherwise be sent to auction, a partial liquidation is performed so as not to exceed this amount. In the current system there is only a global limit; in 2.0, in addition to the global limit, there is also a per-collateral limit. Similar to how there is an ilk.line
for a collateral's debt ceiling and a Line
for the overall system debt ceiling, there is now an ilk.hole
to correspond with the Hole
. This ensures that typical market depth against DAI can be taken into account on a per-collateral basis by those determining risk parameters. The Dirt
accumulator tracks the total DAI needed to cover the debt and penalty fees of all active auctions, and must be less than Hole
for a call to bark
to succeed. For each collateral type, ilk.dirt
tracks the total DAI needed to cover the debt and penalty fees of a all active auctions for that ilk
, and must be less than ilk.hole
for a call to bark
(on a Vault of that ilk
) to succeed.Clipper.kick
) takes four caller supplied arguments:tab
: the target DAI to raise from the auction (debt
+ stability fees
+ liquidation penalty
) [rad]lot
: the amount of collateral available for purchase [wad]usr
: the address to return any excess collateral tokpr
: the address where DAI incentives will be sentClipper.kick
performs several checks and actions:Dog
or governance may call Clipper.kick
)id
to the new auctionid
into a list tracking active auctionstab
)lot
)tab
when Clipper.yank
is called by End.snip
top
)kpr
Kick()
eventbuf
parameter). Note that the current OSM price is between one and two hours delayed relative to the actual market price. A keeper doesn't make a call to Clipper.kick
directly, but rather makes a call to Dog.bark
which in turn calls Clipper.kick
.mat
(collateralization ratio) and chop
(liquidation penalty) in its setting, but also to allow for unique market conditions that might only apply to one or a few collateral types.Dirt
is close to Hole
or when ilk.dirt
is close to ilk.hole
for some collateral type. An attacker would purchase a small amount of collateral from a running auction, freeing up a small amount of room relative to either Hole
or ilk.hole
, then liquidate a small portion of an existing Vault to collect the reward, and repeat the process. This can be done over and over in the same transaction, and the activation of EIP 2929 (scheduled for the Berlin hard fork at the time of writing) will significantly reduce the associated gas costs (due to warm storage reads and writes). Note that since the attacker does not need to create their own Vaults, the size of the liquidation penalty in relation to the incentive value is not a deterrent; only gas costs matter to the attacker. The fact that the Dog prevents partial liquidations that remove less than ilk.dust
debt from a Vault helps to mitigate this scenario--so long as the liquidation penalty exceeds the total reward for this minimal liquidation size, and the liquidation penalty is reliably collected by the resulting auction (which may not hold under conditions of market or network perturbation), the system should not on balance accrue bad debt.LIQ-1.2
, Liquidations 2.0 comes with a four-stage circuit breaker built into the Clipper
contract. The stages are:0
): This means the breaker is not tripped and the protocol can liquidate new Vaults as well as service old liquidations.1
): This means no new liquidations (Clipper.kick
).2
): This means no new liquidations (Clipper.kick
), and auctions that have reached either a price or time endpoint cannot be reset (Clipper.redo
).3
): This means no new liquidations (Clipper.kick
), no takes (Clipper.take
), and no resets (Clipper.redo
).LIQ-1.2
, the circuit breaker will be available through a ClipperMom
contract giving governance the ability to bypass the GSM
delay for circuit breaker actions.Clipper.take
) takes five caller supplied arguments:id
: the numerical id of the auction to bid onamt
: the maximum amount of collateral to buy (amt
) — a purchase behaves like a limit order [wad]max
: the maximum acceptable price in DAI per unit collateral (max
) [ray]who
: address that will receive the collateral (who
)data
: an arbitrary bytestring (if provided, the address who
, if it is neither the Dog
nor Vat
address stored by the Clipper, is called as a contract via an interface described below, to which this data is passed) [bytes]Clipper.take
performs several initial checks:lot
) and the caller's specified quantity (amt
)—the resulting value is the slice
. This value is then multiplied by the current price of the auction to compute the DAI owed in exchange (owe
). If owe
exceeds the DAI collection target of the auction (tab
), then owe
is adjusted to be equal to tab
, and slice
is set to tab / price
(i.e. the auction will not sell more collateral than is needed to cover debt+fees from the liquidated Vault).slice
and owe
. If the remaining tab
in such a case would be less than chost
(a value stored by the contract, asynchronously set to ilk.dust * ilk.chop / WAD
by the upchost()
function), then: 1) If the overall DAI target is less than chost
, the function reverts. Callers should be aware of this possibility and account for it when necessary. 2) Otherwise, owe
is set to tab - chost
, and slice
is set to (tab - chost) / price
.take
or yank
to avoid cluttering the list of active auctions each Clipper
maintains, which could impair the performance of DEX and aggregator integrations (and possibly off-chain bots too, depending on how they are written).who
address provided by the caller. If the caller provided a bytestring with greater than zero length, an external call is made to the who
address, assuming it exposes a function, follow Solidity conventions, with the following signature:clipperCallee
must return DAI to. The second argument is DAI owed (as a 45 decimal digit fixed-precision integer, or rad
), the third argument is the collateral being purchased (as an 18 decimal digit fixed-precision integer, or wad
), regardless of the precision of the external token contract, and the last argument is identical to what bytestring the caller originally supplied to the purchase function. As mentioned earlier, a locking mechanism prevents reentry into the purchase function during this external call, and the Clipper.redo
call, for security reasons. Example implementations of the ClipperCallee
interface can be found in the exchange-callees repository.msg.sender
to the protocol.owe
, and the tab
(DAI collection target) and lot
(collateral for sale) of the auction are adjusted as well. If all collateral has been drained from an auction, all its data is cleared and it is removed from the active auctions list. If collateral remains, but the DAI collection target has been reached, the same is done and excess collateral is returned to the liquidated Vault.tau
is tail
.calc
), with an ETH-A
OSM price of 200 DAI, a buf
of 20%, a tail (tau)
of 21600 seconds, a tab
of *60,000 DAI with a lot
of 347.32
ETH. There are two bidders, Alice and Bob. Alice calls take
first and is willing to give *50,000 DAI in return for 256.41 ETH collateral, a price of 195 DAI per ETH. The price of ETH continues to fall over time, and Bob picks up the remaining 90.91 ETH for 10,000 DAI, a price of 110 DAI per ETH. If more than tail
seconds have elapsed since the start of the auction, or if the price has fallen to less than cusp
percent of top
, then Clipper.take
would revert if called, and the auction would need to be reset with a Clipper.redo
call.redo()
Clipper.redo
) takes two caller supplied arguments:id
: the current auction idkpr
: the address where DAI incentives will be sentClipper.redo
performs several checks and actions:tic
to reset the auction start timetop
with the current OSM price and buf
percentvat.suck
s DAI to the kpr
as an incentive if eligibleRedo()
eventClipper.redo
the auction, resetting it's expiration and setting the starting price to match the current oracle price + buf. The redo includes the same Dai incentive to the keeper as the Clipper.kick
, which is based on the flat fee plus the governance-defined percentage of collateral. There is one exception to this incentive. If the tab
or lot * price
remaining is under the Clipper.chost
limit, then there will be no incentive paid to redo
the auction. This is to help prevent incentive farming attacks where no keepers bid on dusty lots, and Clipper.redo
is called repeatedly. If auctions in this state build up, governance may choose to pay a keeper to clean them up.auth
function called yank
in Clipper
contract. This function requires that the auction exists and executes the following actions:dog.digs
in order to decrease its Dirt
and ilk.dirt
values by the remaining auction tab
msg.sender
Yank()
eventEnd
module, which takes care of system shutdown.End
module will be upgraded together with the auction contracts as a new function End.snip
is required.End.snip
is responsible for calling Clipper.yank
for any running auction when shutdown is triggered. It will receive the collateral from Clipper.yank
and will send it back to the vault owner together with the remaining debt to recover. One consideration to note is that the debt that the vault owner will receive includes the liquidation penalty part already charged.Dog
InterfaceETH-A
).1
if the system is active.chop
value of a given ilk
. chop
has wad precision.urn
for a particular ilk
. kpr
is the address where the keeper incentive is sent, allowing keepers to have liquidation rewards sent to a different address than the caller.Clipper
. The rad
argument has rad precision.Dog
and sets live
to 0.Clipper
InterfaceVat.ilk.dust
and Dog.ilk.chop
. This value is used as a heuristic for whether a partial purchase will leave an auction with too little collateral and whether incentives should be given out when redo
is called.pos
in the list of active auctions.0
for all functions allowed, 1
if kick
cannot be called, 2
if kick
and redo
cannot be called, and 3
if kick
, redo
and take
cannot be called.Dog
. tab
is rad-precion, lot
is wad-precision.list()
to get the unsorted array of auctions. Get the number of active auctions with count()
. The function active(uint256 pos)
(see above) can be used to access individual entries without needing to call list()
.chost
value stored in the contract to equal the product of Vat.ilk.dust
and Dog.ilk.chop
(the precision of the final value is rad). This allows take
and redo
to obtain chost
with a single SLOAD
(reading dust
from the Vat
requires 5 SLOAD
operations due to how the API is structured, and reading chop
is an additional SLOAD
).ilk.dust
amount. When this happens, it's usually because gas has become so expensive it impacts the efficiency of liquidations. That is, the cost of calling Dog.bark()
, Clipper.take()
, or Clipper.redo()
may exceed the collateral offered. Incentives may be used as a remedy to this potential issue and possibly a way for the protocol to keep ilk.dust
lower; however, governance must take care when increasing the ilk
s dust
, tip
, or chip
not to incentivize the creation of many Vaults to farm this incentive. An example of an exploit is as follows:dust
by 1500 DAI at the same time they scale ilk.tip
to subsidize auctions.chop
to shard (fork
) their existing Vault into many Vaults or create many new Vaults.poke()
s the OSM when next price is going down, then calls Dog.bark()
on all their Vaults to collect the incentive.ilk.tip
and ilk.chip
so as not to create this perverse incentive.LIQ-1.2
there is limited front-running risk as it requires capital to participate in auctions; however, in liquidations 2.0 if a keeper chooses to participate with no capital, there is substantial front-running risk from generalized front-running bots. The easier it is to replace the from
address of the transaction with one's own, the greater the risk. To mitigate this risk keepers are encouraged to used authorized proxy contracts to interact with liquidations 2.0 and provide some amount of their own capital when bidding. More aggressive gas prices may also work. Unfortunately, we found no great way to prevent generalized front-running that preserves single-block composability.Clipper.kick
and Clipper.redo
consult the OSM for the collateral price, we are vulnerable to an oracle attack that can only be mitigated by the oracle delay, Dog.Hole
, and ilk.hole
. We must rely on the number of guards in place to prevent price manipulation and oracle attacks. The fact that the price is delayed by one hour, however, prseents a risk of its own: since the price is out-of-date relative to the market, it may be either too high or too low to allow for efficient settlement given other parameters like 'buf' and the price decrease function. The consequences of either case are effectively covered by the sections on the risk of the price decreasing either too quickly or too slowly.Dog.Hole
and ilk.hole
can be set much higher in liquidations 2.0, there are still risks to setting this too high. A value for Dog.Hole
that's set too high could result in far too much DAI demand, breaking the peg high. This is somewhat mitigated by the PSM and stablecoin collateral types, but should still factor in to how this parameter is set. An ilk.hole
that is set too high, may have the additional result of causing a downward spiral as the liquidations push the asset price lower. In addition, if there is an oracle attack, this parameter can be thought of as our maximum exposure.Dog.Hole
or ilk.hole
too low, we run the risk of not being able to liquidate enough collateral at once. This could lead to a buildup of undercollateralized positions in the system, eventually causing the accrual of bad debt.tail
, cusp
, or the price decrease function or any of its parameters, can be changed at any time by governance and will affect the behavior of running auctions. Integrators should take this possibility into account, reasoning through how sudden changes in parameters would impact their bidding strategies. Governance should endeavor to change parameters infrequently and if possible, only when there are not any auctions that will be affected.Dog
InvariantsClipper
InvariantsClipper
, even though it only makes sense for the Dog
to do so. Governance (or a new module) could create a violation of this property by calling kick
without transferring appropriate collateral to the Clipper
. Thus this is actually a pseudo-invariant that only holds if callers of kick
respect the requirement to send collateral to the Clipper
, but it should hold for the live system if deployment and authorization is done correctly. It should hold if only active auctions are summed over as well.Dog
and Clipper