From 89b63a3cba0a7eb92081dafc178bf8a63445da54 Mon Sep 17 00:00:00 2001 From: Kevin Davis Date: Mon, 11 May 2020 18:35:16 -0400 Subject: [PATCH] fix: simplify collateral auction logic --- x/cdp/keeper/auctions.go | 172 ++++++++----------------------------- x/cdp/keeper/seize.go | 2 - x/cdp/keeper/seize_test.go | 5 -- 3 files changed, 34 insertions(+), 145 deletions(-) diff --git a/x/cdp/keeper/auctions.go b/x/cdp/keeper/auctions.go index 5b739aee..b6f55069 100644 --- a/x/cdp/keeper/auctions.go +++ b/x/cdp/keeper/auctions.go @@ -1,8 +1,6 @@ package keeper import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/kava-labs/kava/x/cdp/types" @@ -13,100 +11,15 @@ const ( dump = 100 ) -type partialDeposit struct { - Depositor sdk.AccAddress - Amount sdk.Coin - DebtShare sdk.Int -} - -func newPartialDeposit(depositor sdk.AccAddress, amount sdk.Coin, ds sdk.Int) partialDeposit { - return partialDeposit{ - Depositor: depositor, - Amount: amount, - DebtShare: ds, - } -} - -type partialDeposits []partialDeposit - -func (pd partialDeposits) SumCollateral() (sum sdk.Int) { - sum = sdk.ZeroInt() - for _, d := range pd { - sum = sum.Add(d.Amount.Amount) - } - return -} - -func (pd partialDeposits) SumDebt() (sum sdk.Int) { - sum = sdk.ZeroInt() - for _, d := range pd { - sum = sum.Add(d.DebtShare) - } - return -} - // AuctionCollateral creates auctions from the input deposits which attempt to raise the corresponding amount of debt func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt sdk.Int, bidDenom string) error { - auctionSize := k.getAuctionSize(ctx, deposits[0].Amount.Denom) - partialAuctionDeposits := partialDeposits{} - totalCollateral := deposits.SumCollateral() - for totalCollateral.GT(sdk.ZeroInt()) { - for i, dep := range deposits { - if dep.Amount.IsZero() { - continue - } - collateralAmount := dep.Amount.Amount - collateralDenom := dep.Amount.Denom - // create auctions from individual deposits that are larger than the auction size - debtChange, collateralChange, err := k.CreateAuctionsFromDeposit(ctx, dep, debt, totalCollateral, auctionSize, bidDenom) - if err != nil { - return err - } - fmt.Printf("AuctionCollateral:createFromD:loop%d:liquidator:%s\n", i, k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc)) - debt = debt.Sub(debtChange) - totalCollateral = totalCollateral.Sub(collateralChange) - dep.Amount = sdk.NewCoin(collateralDenom, collateralAmount.Sub(collateralChange)) - collateralAmount = collateralAmount.Sub(collateralChange) - // if there is leftover collateral that is less than a lot - if !dep.Amount.IsZero() { - // figure out how much debt this deposit accounts for - // (depositCollateral / totalCollateral) * totalDebtFromCDP - debtCoveredByDeposit := collateralAmount.Mul(debt).Quo(totalCollateral) // integer division (ie rounds down) - // if adding this deposit to the other partial deposits is less than a lot - if (partialAuctionDeposits.SumCollateral().Add(collateralAmount)).LT(auctionSize) { - // append the deposit to the partial deposits and zero out the deposit - pd := newPartialDeposit(dep.Depositor, dep.Amount, debtCoveredByDeposit) - partialAuctionDeposits = append(partialAuctionDeposits, pd) - dep.Amount = sdk.NewCoin(collateralDenom, sdk.ZeroInt()) - } else { - // if the sum of partial deposits now makes a lot - partialCollateral := sdk.NewCoin(collateralDenom, auctionSize.Sub(partialAuctionDeposits.SumCollateral())) - partialAmount := partialCollateral.Amount - partialDebt := partialAmount.Mul(debtCoveredByDeposit).Quo(collateralAmount) // integer division (ie rounds down) - // create a partial deposit from the deposit - partialDep := newPartialDeposit(dep.Depositor, partialCollateral, partialDebt) - // append it to the partial deposits - partialAuctionDeposits = append(partialAuctionDeposits, partialDep) - // create an auction from the partial deposits - debtChange, collateralChange, err := k.CreateAuctionFromPartialDeposits(ctx, partialAuctionDeposits, debt, totalCollateral, auctionSize, bidDenom) - if err != nil { - return err - } - fmt.Printf("AuctionCollateral:createFromPD:loop%d:liquidator:%s\n", i, k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc)) - debt = debt.Sub(debtChange) - totalCollateral = totalCollateral.Sub(collateralChange) - // reset partial deposits and update the deposit amount - partialAuctionDeposits = partialDeposits{} - dep.Amount = sdk.NewCoin(collateralDenom, collateralAmount.Sub(partialAmount)) - } - } - deposits[i] = dep - totalCollateral = deposits.SumCollateral() - } - } - if partialAuctionDeposits.SumCollateral().GT(sdk.ZeroInt()) { - _, _, err := k.CreateAuctionFromPartialDeposits(ctx, partialAuctionDeposits, debt, totalCollateral, partialAuctionDeposits.SumCollateral(), bidDenom) + auctionSize := k.getAuctionSize(ctx, deposits[0].Amount.Denom) + totalCollateral := deposits.SumCollateral() + for _, deposit := range deposits { + + debtCoveredByDeposit := (sdk.NewDecFromInt(deposit.Amount.Amount).Quo(sdk.NewDecFromInt(totalCollateral))).Mul(sdk.NewDecFromInt(debt)).RoundInt() + err := k.CreateAuctionsFromDeposit(ctx, deposit.Amount, deposit.Depositor, debtCoveredByDeposit, auctionSize, bidDenom) if err != nil { return err } @@ -114,54 +27,37 @@ func (k Keeper) AuctionCollateral(ctx sdk.Context, deposits types.Deposits, debt return nil } -// CreateAuctionsFromDeposit creates auctions from the input deposit until there is less than auctionSize left on the deposit +// CreateAuctionsFromDeposit creates auctions from the input deposit func (k Keeper) CreateAuctionsFromDeposit( - ctx sdk.Context, dep types.Deposit, debt sdk.Int, totalCollateral sdk.Int, auctionSize sdk.Int, - principalDenom string) (debtChange sdk.Int, collateralChange sdk.Int, err error) { - debtChange = sdk.ZeroInt() - collateralChange = sdk.ZeroInt() - depositAmount := dep.Amount.Amount - depositDenom := dep.Amount.Denom - for depositAmount.GTE(auctionSize) { - // figure out how much debt is covered by one lots worth of collateral - depositDebtAmount := (sdk.NewDecFromInt(auctionSize).Quo(sdk.NewDecFromInt(totalCollateral))).Mul(sdk.NewDecFromInt(debt)).RoundInt() - penalty := k.ApplyLiquidationPenalty(ctx, depositDenom, depositDebtAmount) - // start an auction for one lot, attempting to raise depositDebtAmount plus the liquidation penalty - _, err := k.auctionKeeper.StartCollateralAuction( - ctx, types.LiquidatorMacc, sdk.NewCoin(depositDenom, auctionSize), sdk.NewCoin(principalDenom, depositDebtAmount.Add(penalty)), []sdk.AccAddress{dep.Depositor}, - []sdk.Int{auctionSize}, sdk.NewCoin(k.GetDebtDenom(ctx), depositDebtAmount)) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroInt(), err + ctx sdk.Context, collateral sdk.Coin, returnAddr sdk.AccAddress, debt, auctionSize sdk.Int, + principalDenom string) (err error) { + + amountToAuction := sdk.NewInt(collateral.Amount.Int64()) + totalCollateralAmount := sdk.NewInt(collateral.Amount.Int64()) + remainingDebt := sdk.NewInt(debt.Int64()) + for amountToAuction.GT(sdk.ZeroInt()) { + for amountToAuction.GT(auctionSize) { + debtCoveredByAuction := (sdk.NewDecFromInt(auctionSize).Quo(sdk.NewDecFromInt(totalCollateralAmount))).Mul(sdk.NewDecFromInt(debt)).RoundInt() + penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, debtCoveredByAuction) + _, err := k.auctionKeeper.StartCollateralAuction( + ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, auctionSize), sdk.NewCoin(principalDenom, debtCoveredByAuction.Add(penalty)), []sdk.AccAddress{returnAddr}, + []sdk.Int{auctionSize}, sdk.NewCoin(k.GetDebtDenom(ctx), debtCoveredByAuction)) + if err != nil { + return err + } + amountToAuction = amountToAuction.Sub(auctionSize) + remainingDebt = remainingDebt.Sub(debtCoveredByAuction) } - depositAmount = depositAmount.Sub(auctionSize) - totalCollateral = totalCollateral.Sub(auctionSize) - debt = debt.Sub(depositDebtAmount) - // subtract one lot's worth of debt from the total debt covered by this deposit - debtChange = debtChange.Add(depositDebtAmount) - collateralChange = collateralChange.Add(auctionSize) - + penalty := k.ApplyLiquidationPenalty(ctx, collateral.Denom, remainingDebt) + _, err := k.auctionKeeper.StartCollateralAuction( + ctx, types.LiquidatorMacc, sdk.NewCoin(collateral.Denom, amountToAuction), sdk.NewCoin(principalDenom, remainingDebt.Add(penalty)), []sdk.AccAddress{returnAddr}, + []sdk.Int{amountToAuction}, sdk.NewCoin(k.GetDebtDenom(ctx), remainingDebt)) + if err != nil { + return err + } + amountToAuction = sdk.ZeroInt() } - return debtChange, collateralChange, nil -} - -// CreateAuctionFromPartialDeposits creates an auction from the input partial deposits -func (k Keeper) CreateAuctionFromPartialDeposits(ctx sdk.Context, partialDeps partialDeposits, debt sdk.Int, collateral sdk.Int, auctionSize sdk.Int, bidDenom string) (debtChange, collateralChange sdk.Int, err error) { - - returnAddrs := []sdk.AccAddress{} - returnWeights := []sdk.Int{} - depositDenom := partialDeps[0].Amount.Denom - for _, pd := range partialDeps { - returnAddrs = append(returnAddrs, pd.Depositor) - returnWeights = append(returnWeights, pd.DebtShare) - } - penalty := k.ApplyLiquidationPenalty(ctx, depositDenom, partialDeps.SumDebt()) - _, err = k.auctionKeeper.StartCollateralAuction(ctx, types.LiquidatorMacc, sdk.NewCoin(partialDeps[0].Amount.Denom, auctionSize), sdk.NewCoin(bidDenom, partialDeps.SumDebt().Add(penalty)), returnAddrs, returnWeights, sdk.NewCoin(k.GetDebtDenom(ctx), partialDeps.SumDebt())) - if err != nil { - return sdk.ZeroInt(), sdk.ZeroInt(), err - } - debtChange = partialDeps.SumDebt() - collateralChange = partialDeps.SumCollateral() - return debtChange, collateralChange, nil + return nil } // NetSurplusAndDebt burns surplus and debt coins equal to the minimum of surplus and debt balances held by the liquidator module account diff --git a/x/cdp/keeper/seize.go b/x/cdp/keeper/seize.go index 8d0112cc..b10c25ce 100644 --- a/x/cdp/keeper/seize.go +++ b/x/cdp/keeper/seize.go @@ -32,7 +32,6 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error { if err != nil { return err } - fmt.Printf("SeizeCollateral:liquidator: %s\n", k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc)) // liquidate deposits and send collateral from cdp to liquidator for _, dep := range deposits { @@ -50,7 +49,6 @@ func (k Keeper) SeizeCollateral(ctx sdk.Context, cdp types.CDP) error { } k.DeleteDeposit(ctx, dep.CdpID, dep.Depositor) } - fmt.Printf("SeizeCollateral:liquidator: %s\n", k.supplyKeeper.GetModuleAccount(ctx, types.LiquidatorMacc)) err = k.AuctionCollateral(ctx, deposits, debt, cdp.Principal.Denom) if err != nil { return err diff --git a/x/cdp/keeper/seize_test.go b/x/cdp/keeper/seize_test.go index b76f1e6c..45b707b9 100644 --- a/x/cdp/keeper/seize_test.go +++ b/x/cdp/keeper/seize_test.go @@ -2,7 +2,6 @@ package keeper_test import ( "errors" - "fmt" "math/rand" "testing" "time" @@ -167,10 +166,6 @@ func (suite *SeizeTestSuite) TestSeizeCollateralMultiDeposit() { p := cdp.Principal.Amount cl := cdp.Collateral.Amount tpb := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx") - fmt.Printf("%s\n", cdp) - fmt.Printf("%s\n", deposits) - fmt.Printf("cdpaccount: %s\n", suite.app.GetSupplyKeeper().GetModuleAccount(suite.ctx, types.ModuleName)) - fmt.Printf("liquidator: %s\n", suite.app.GetSupplyKeeper().GetModuleAccount(suite.ctx, types.LiquidatorMacc)) err = suite.keeper.SeizeCollateral(suite.ctx, cdp) suite.NoError(err) tpa := suite.keeper.GetTotalPrincipal(suite.ctx, "xrp", "usdx")