Skip to content

Commit

Permalink
feat: flash liquidation proto (#2102)
Browse files Browse the repository at this point in the history
* save progress pre-refactor

* godoc--

* proto comment update

* cleanup PR = empty function

* make proto-all

* rename to FastLiquidate

* reoute returns empty string

* changelog

* lint
  • Loading branch information
toteki committed Jun 22, 2023
1 parent 28314ad commit 11e720a
Show file tree
Hide file tree
Showing 7 changed files with 781 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Features

- [2192](https://github.com/umee-network/umee/pull/2102) Add `MsgFastLiquidate` to `x/leverage`
- [2085](https://github.com/umee-network/umee/pull/2085) Add `inspect` query to leverage module, which msut be enabled on a node by running with `-l` liquidator query flag.
- [1952](https://github.com/umee-network/umee/pull/1952) Add `x/incentive` module.
- [2015](https://github.com/umee-network/umee/pull/2015), [2050](https://github.com/umee-network/umee/pull/2050) Add `x/ugov` module.
Expand Down
35 changes: 35 additions & 0 deletions proto/umee/leverage/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ service Msg {
// of the target's collateral.
rpc Liquidate(MsgLiquidate) returns (MsgLiquidateResponse);

// FastLiquidate allows a user to repay a different user's borrowed coins in exchange for some
// of the target's collateral. For flash liquidations, the tokens to repay are borrowed instead of
// being taken from the liquidator's wallet, and the reward is immediately collateralized. Borrow
// limit checks for the liquidator are deferred until after the reward is collateralized, allowing
// this initial borrow to exceed the liquidator's borrow limit as long as it is healthy by the end
// of the transaction. Repay amount is calculated automatically, so the liquidator only specifies
// repay and reward token denoms.
rpc FastLiquidate(MsgFastLiquidate) returns (MsgFastLiquidateResponse);

// SupplyCollateral combines the Supply and Collateralize actions.
rpc SupplyCollateral(MsgSupplyCollateral) returns (MsgSupplyCollateralResponse);

Expand Down Expand Up @@ -140,6 +149,22 @@ message MsgLiquidate {
string reward_denom = 4;
}

// MsgFastLiquidate is the request structure for the FastLiquidate RPC.
message MsgFastLiquidate {
// Liquidator is the account address performing a liquidation and the signer
// of the message.
string liquidator = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// Borrower is the account whose borrow is being repaid, and collateral consumed,
// by the liquidation. It does not sign the message.
string borrower = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// RepayDenom is the base token that the liquidator will borrow and repay on behalf of
// the borrower.
string repay_denom = 3;
// RewardDenom is the uToken denom that the liquidator will receive as a liquidation reward
// and immediately collateralize.
string reward_denom = 4;
}

// MsgSupplyCollateral represents a user's request to supply and collateralize assets to the module.
message MsgSupplyCollateral {
// Supplier is the account address supplying assets and the signer of the message.
Expand Down Expand Up @@ -201,6 +226,16 @@ message MsgLiquidateResponse {
cosmos.base.v1beta1.Coin reward = 3 [(gogoproto.nullable) = false];
}

// MsgFastLiquidateResponse defines the Msg/FastLiquidate response type.
message MsgFastLiquidateResponse {
// Repaid is the amount of base tokens that the liquidator borrowed and repaid
// to the module on behalf of the borrower.
cosmos.base.v1beta1.Coin repaid = 1 [(gogoproto.nullable) = false];
// Reward is the amount of collateral that the liquidator gained
// as reward for the liquidation.
cosmos.base.v1beta1.Coin reward = 2 [(gogoproto.nullable) = false];
}

// MsgSupplyCollateralResponse defines the Msg/SupplyCollateral response type.
message MsgSupplyCollateralResponse {
// Collateralized is the amount of uTokens collateralized.
Expand Down
7 changes: 7 additions & 0 deletions x/leverage/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,10 @@ func (k Keeper) Liquidate(
}
return tokenRepay, uTokenLiquidate, uTokenLiquidate, nil
}

// FastLiquidate
func (k Keeper) FastLiquidate(
_ sdk.Context, _, _ sdk.AccAddress, _, _ string,
) (repaid sdk.Coin, reward sdk.Coin, err error) {
return sdk.Coin{}, sdk.Coin{}, err
}
45 changes: 45 additions & 0 deletions x/leverage/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,51 @@ func (s msgServer) Liquidate(
}, nil
}

func (s msgServer) FastLiquidate(
goCtx context.Context,
msg *types.MsgFastLiquidate,
) (*types.MsgFastLiquidateResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

liquidator, err := sdk.AccAddressFromBech32(msg.Liquidator)
if err != nil {
return nil, err
}
borrower, err := sdk.AccAddressFromBech32(msg.Borrower)
if err != nil {
return nil, err
}

repaid, reward, err := s.keeper.FastLiquidate(ctx, liquidator, borrower, msg.RepayDenom, msg.RewardDenom)
if err != nil {
return nil, err
}

// Fail here if liquidator ends up over their borrow limit under current or historic prices
// Tolerates missing collateral prices if the rest of the liquidator's collateral can cover all borrows
err = s.keeper.assertBorrowerHealth(ctx, liquidator)
if err != nil {
return nil, err
}

s.keeper.Logger(ctx).Debug(
"unhealthy borrower fast-liquidated",
"liquidator", msg.Liquidator,
"borrower", msg.Borrower,
"repaid", repaid.String(),
"reward", reward.String(),
)
sdkutil.Emit(&ctx, &types.EventLiquidate{
Liquidator: msg.Liquidator,
Borrower: msg.Borrower,
Liquidated: reward,
})
return &types.MsgFastLiquidateResponse{
Repaid: repaid,
Reward: reward,
}, nil
}

// GovUpdateRegistry updates existing tokens with new settings
// or adds the new tokens to registry.
func (s msgServer) GovUpdateRegistry(
Expand Down
33 changes: 33 additions & 0 deletions x/leverage/types/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,39 @@ func (msg *MsgLiquidate) GetSignBytes() []byte {
return sdk.MustSortJSON(bz)
}

func NewMsgFastLiquidate(liquidator, borrower sdk.AccAddress, repayDenom, rewardDenom string) *MsgFastLiquidate {
return &MsgFastLiquidate{
Liquidator: liquidator.String(),
Borrower: borrower.String(),
RepayDenom: repayDenom,
RewardDenom: rewardDenom,
}
}

func (msg MsgFastLiquidate) Route() string { return sdk.MsgTypeURL(&msg) }
func (msg MsgFastLiquidate) Type() string { return sdk.MsgTypeURL(&msg) }

func (msg *MsgFastLiquidate) ValidateBasic() error {
if err := validateSenderAndDenom(msg.Borrower, msg.RewardDenom); err != nil {
return err
}
if err := sdk.ValidateDenom(msg.RepayDenom); err != nil {
return err
}
_, err := sdk.AccAddressFromBech32(msg.Liquidator)
return err
}

func (msg *MsgFastLiquidate) GetSigners() []sdk.AccAddress {
return checkers.Signers(msg.Liquidator)
}

// GetSignBytes get the bytes for the message signer to sign on
func (msg *MsgFastLiquidate) GetSignBytes() []byte {
bz := ModuleCdc.MustMarshalJSON(msg)
return sdk.MustSortJSON(bz)
}

func validateSenderAndAsset(sender string, asset *sdk.Coin) error {
_, err := sdk.AccAddressFromBech32(sender)
if err != nil {
Expand Down
Loading

0 comments on commit 11e720a

Please sign in to comment.