New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
R4R: Move liquidator functions to cdp module #280
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good, left some comments on the mathy parts.
x/cdp/keeper/auctions.go
Outdated
if balance.LT(netAmount) { | ||
err = k.supplyKeeper.BurnCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, balance))) | ||
if err != nil { | ||
panic(err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return error instead of panic? Then it's up to the caller to decide whether to panic or do something else.
x/cdp/keeper/auctions.go
Outdated
remainingDebt := k.GetTotalDebt(ctx, types.LiquidatorMacc) | ||
params := k.GetParams(ctx) | ||
if remainingDebt.GTE(params.DebtAuctionThreshold) { | ||
_, err := k.auctionKeeper.StartDebtAuction(ctx, types.LiquidatorMacc, sdk.NewCoin("usdx", sdk.NewInt(1)), sdk.NewCoin(k.GetGovDenom(ctx), remainingDebt.Mul(sdk.NewInt(100))), sdk.NewCoin(k.GetDebtDenom(ctx), remainingDebt)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the bid arg here should be remainingDebt
as its a reverse auction and that is the amount you want to raise.
Also might be neater to move the 100x multiplier for the lot to a constant
x/cdp/keeper/auctions.go
Outdated
} | ||
|
||
// HandleSurplusAndDebtAuctions nets the surplus and debt balances and then creates surplus or debt auctions if the remaining balance is above the auction threshold parameter | ||
func (k Keeper) HandleSurplusAndDebtAuctions(ctx sdk.Context) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RunSurplusAndDebtAuctions
? 'Handle' is a bit vague
x/cdp/keeper/auctions.go
Outdated
for _, dp := range k.GetParams(ctx).DebtParams { | ||
for surplusToBurn.GT(sdk.ZeroInt()) { | ||
balance := k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc).GetCoins().AmountOf(dp.Denom) | ||
if balance.LT(netAmount) { | ||
err = k.supplyKeeper.BurnCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, balance))) | ||
if err != nil { | ||
panic(err) | ||
} | ||
surplusToBurn = surplusToBurn.Sub(balance) | ||
} else { | ||
err = k.supplyKeeper.BurnCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(dp.Denom, surplusToBurn))) | ||
if err != nil { | ||
panic(err) | ||
} | ||
surplusToBurn = sdk.ZeroInt() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this can get caught in a loop on the first dp (if it's balance is < netAmount)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! The outer and inner loops should be switched.
x/cdp/keeper/auctions.go
Outdated
auctionSize := k.getAuctionSize(ctx, deposits[0].Amount[0].Denom) | ||
partialAuctionDeposits := partialDeposits{} | ||
totalCollateral := deposits.SumCollateral() | ||
for deposits.SumCollateral().GTE(auctionSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If total deposits are < auctionSize then no auction will be started, but the cdp will still have been seized
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, should be checking for greater than zero.
x/cdp/keeper/auctions.go
Outdated
for i, dep := range deposits { | ||
// create auctions from individual deposits that are larger than the auction size | ||
k.CreateAuctionsFromDeposit(ctx, &dep, &debt, &totalCollateral, auctionSize, bidDenom) | ||
if !dep.Amount[0].Amount.IsZero() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found these loops a bit hard to understand, comments might help explain why things are happening
x/cdp/keeper/auctions.go
Outdated
auctionSize := k.getAuctionSize(ctx, deposits[0].Amount[0].Denom) | ||
partialAuctionDeposits := partialDeposits{} | ||
totalCollateral := deposits.SumCollateral() | ||
for deposits.SumCollateral().GTE(auctionSize) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace deposits.SumCollateral()
with totalCollateral
type partialDeposit struct { | ||
Depositor sdk.AccAddress | ||
Amount sdk.Coins | ||
DebtShare sdk.Int | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth discussing if partialDeposit
should be defined in a separate file or moved to cdp/types
.
partialAuctionDeposits := partialDeposits{} | ||
totalCollateral := deposits.SumCollateral() | ||
for deposits.SumCollateral().GTE(auctionSize) { | ||
for i, dep := range deposits { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Placeholder variables for denom, amount
x/cdp/keeper/auctions.go
Outdated
} | ||
|
||
// CreateAuctionsFromDeposit creates auctions from the input deposit until there is less than auctionSize left on the deposit | ||
func (k Keeper) CreateAuctionsFromDeposit(ctx sdk.Context, dep *types.Deposit, debt *sdk.Int, totalCollateral *sdk.Int, auctionSize sdk.Int, principalDenom string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method would be simpler if it used local variables to keep track of the data instead of updating the state via ref in each iteration of the loop. Return local variables and use them to update the state back in the primary function.
func (k Keeper) getLiquidationPenalty(ctx sdk.Context, denom string) sdk.Dec { | ||
cp, found := k.GetCollateral(ctx, denom) | ||
if !found { | ||
panic(fmt.Sprintf("collateral not found: %s", denom)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will panic if collateral type type 'random' is removed in between CDP deposit of 'random' and liquidation of the CDP.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will file an issue on this topic. It's worth documenting and thinking about how we want to handle it, but for now, it would be unlikely for governance to accept a proposal to remove a collateral that still had active cdps.
func (k Keeper) getAuctionSize(ctx sdk.Context, denom string) sdk.Int { | ||
cp, found := k.GetCollateral(ctx, denom) | ||
if !found { | ||
panic(fmt.Sprintf("collateral not found: %s", denom)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One minor change requested, but approving the PR as it can be addressed later.
x/cdp/keeper/auctions.go
Outdated
collateralAmount := dep.Amount[0].Amount | ||
collateralDenom := dep.Amount[0].Denom |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can move these up a couple of lines and replace dep.Amount[0].Denom
, dep.Amount[0].Amount
on line 59 with the local variables.
nit: In |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like there to be more tests of the mathy bits before testnet. But this could be a subsequent PR.
x/cdp/keeper/auctions.go
Outdated
if netAmount.GT(sdk.ZeroInt()) { | ||
surplusToBurn := netAmount | ||
err := k.supplyKeeper.BurnCoins(ctx, types.LiquidatorMacc, sdk.NewCoins(sdk.NewCoin(k.GetDebtDenom(ctx), netAmount))) | ||
if err != nil { | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This if could be
if netAmount.IsZero() {
return nil
}
surplusToBurn := ...
It would remove a level of indentation from the subsequent section.
x/cdp/keeper/auctions.go
Outdated
for surplusToBurn.GT(sdk.ZeroInt()) { | ||
for _, dp := range k.GetParams(ctx).DebtParams { | ||
balance := k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc).GetCoins().AmountOf(dp.Denom) | ||
if balance.LT(netAmount) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this beif balance.LT(surplusToBurn) {
?
netAmount
doesn't change in this loop.
x/cdp/keeper/auctions.go
Outdated
if dep.Amount.IsZero() { | ||
// remove the deposit from the slice if it is empty | ||
deposits = append(deposits[:i], deposits[i+1:]...) | ||
i-- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my editor is saying i--
is an ineffective assignment. Maye although it's updated here, the next for loop iteration overwrites it?
Could also be a false positive
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
* wip: tpyes and keeper methods * wip: iterators * wip: types and keeper methods * wip: add msgs * wip: client methods * wip: rebase develop * wip: types tests * wip: keeper tests, small fixes * wip: add cdp tests * wip: deposit tests * wip: keeper tests * wip: tests and module methods * feat: error when fetching expired price * feat: conversion factor for external assets * feat: debt floor for new cdps * feat: save deposits on export genesis * feat: ensure messages implement msg * feat: index deposits by status * fix: stray comment * wip: address review comments * address review comments * wip: move liquidation to cdp module * wip: handle liquidations directly * wip: use new auction interface * feat: auction collateral in cdp begin block * feat: update param validation * feat: surplus and debt auctions * address review comments * address review comments * fix: auction multiple deposits * clean up netting function
Features:
CollateralAuctions are created immediately when the cdp is seized
Implemented netting of surplus and debt
Implemented starting of surplus/debt auctions
Added gov denom to genesis state
Added params for surplus and debt auction thresholds