Skip to content

Commit

Permalink
fix!: restrict MsgWithdraw to only accept uToken inputs (#1023)
Browse files Browse the repository at this point in the history
* change Withdraw to only accept uToken

* fix test

* rename amount -> coin

Co-authored-by: Adam Wozniak <[email protected]>
  • Loading branch information
toteki and adamewozniak authored Jun 21, 2022
1 parent 30bca26 commit f556e52
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 42 deletions.
5 changes: 2 additions & 3 deletions proto/umee/leverage/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ message MsgLendAsset {
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false];
}

// MsgWithdrawAsset represents a lender's request to withdraw a previously
// loaned base asset type from the module. Amount can either be exact uTokens to
// withdraw or equivalent base assets.
// MsgWithdrawAsset represents a lender's request to withdraw lent assets.
// Amount must be a uToken.
message MsgWithdrawAsset {
string lender = 1;
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.nullable) = false];
Expand Down
2 changes: 1 addition & 1 deletion x/leverage/client/tests/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ func (s *IntegrationTestSuite) TestLeverageScenario() {
cli.GetCmdWithdrawAsset(),
[]string{
val.Address.String(),
"1000uumee",
"1000u/uumee",
},
nil,
}
Expand Down
52 changes: 17 additions & 35 deletions x/leverage/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,33 +108,15 @@ func (k Keeper) LendAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, loan sdk.C
}

// WithdrawAsset attempts to deposit uTokens into the leverage module in exchange
// for the original tokens loaned. Accepts either a uToken amount to withdraw or
// an equivalent base token amount to be converted automatically via exchange rate.
// If the token or uToken denom is invalid or account balance insufficient for either
// lender or module, we return an error.
func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, withdrawal sdk.Coin) error {
var (
uToken sdk.Coin
err error
)

if k.IsAcceptedToken(ctx, withdrawal.Denom) {
// Automatically convert base token input to equivalent uTokens
uToken, err = k.ExchangeToken(ctx, withdrawal)
if err != nil {
return err
}
} else {
// Otherwise use original input
uToken = withdrawal
}

if !k.IsAcceptedUToken(ctx, uToken.Denom) {
return sdkerrors.Wrap(types.ErrInvalidAsset, uToken.String())
// for the original tokens loaned. Accepts a uToken amount to exchange for base tokens.
// If the uToken denom is invalid or account or module balance insufficient, returns error.
func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, coin sdk.Coin) error {
if !k.IsAcceptedUToken(ctx, coin.Denom) {
return sdkerrors.Wrap(types.ErrInvalidAsset, coin.String())
}

// calculate base asset amount to withdraw
token, err := k.ExchangeUToken(ctx, uToken)
token, err := k.ExchangeUToken(ctx, coin)
if err != nil {
return err
}
Expand All @@ -147,12 +129,12 @@ func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, withdr
}

// Withdraw will first attempt to use any uTokens in the lender's wallet
amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, lenderAddr).AmountOf(uToken.Denom), uToken.Amount)
amountFromWallet := sdk.MinInt(k.bankKeeper.SpendableCoins(ctx, lenderAddr).AmountOf(coin.Denom), coin.Amount)
// Any additional uTokens must come from the lender's collateral
amountFromCollateral := uToken.Amount.Sub(amountFromWallet)
amountFromCollateral := coin.Amount.Sub(amountFromWallet)

if amountFromCollateral.IsPositive() {
if k.GetCollateralSetting(ctx, lenderAddr, uToken.Denom) {
if k.GetCollateralSetting(ctx, lenderAddr, coin.Denom) {
// Calculate current borrowed value
borrowed := k.GetBorrowerBorrows(ctx, lenderAddr)
borrowedValue, err := k.TotalTokenValue(ctx, borrowed)
Expand All @@ -162,12 +144,12 @@ func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, withdr

// Check for sufficient collateral
collateral := k.GetBorrowerCollateral(ctx, lenderAddr)
if collateral.AmountOf(uToken.Denom).LT(amountFromCollateral) {
return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String())
if collateral.AmountOf(coin.Denom).LT(amountFromCollateral) {
return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String())
}

// Calculate what borrow limit will be AFTER this withdrawal
collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(uToken.Denom, amountFromCollateral))
collateralToWithdraw := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromCollateral))
newBorrowLimit, err := k.CalculateBorrowLimit(ctx, collateral.Sub(collateralToWithdraw))
if err != nil {
return err
Expand All @@ -180,18 +162,18 @@ func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, withdr
}

// reduce the lender's collateral by amountFromCollateral
newCollateral := sdk.NewCoin(uToken.Denom, collateral.AmountOf(uToken.Denom).Sub(amountFromCollateral))
newCollateral := sdk.NewCoin(coin.Denom, collateral.AmountOf(coin.Denom).Sub(amountFromCollateral))
if err = k.setCollateralAmount(ctx, lenderAddr, newCollateral); err != nil {
return err
}
} else {
// If collateral was needed despite being disabled, wallet balance must have been insufficient
return sdkerrors.Wrap(types.ErrInsufficientBalance, uToken.String())
return sdkerrors.Wrap(types.ErrInsufficientBalance, coin.String())
}
}

// transfer amountFromWallet uTokens to the module account
uTokens := sdk.NewCoins(sdk.NewCoin(uToken.Denom, amountFromWallet))
uTokens := sdk.NewCoins(sdk.NewCoin(coin.Denom, amountFromWallet))
if err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, lenderAddr, types.ModuleName, uTokens); err != nil {
return err
}
Expand All @@ -203,10 +185,10 @@ func (k Keeper) WithdrawAsset(ctx sdk.Context, lenderAddr sdk.AccAddress, withdr
}

// burn the uTokens and set the new total uToken supply
if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(uToken)); err != nil {
if err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(coin)); err != nil {
return err
}
if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, uToken.Denom).Sub(uToken)); err != nil {
if err = k.setUTokenSupply(ctx, k.GetUTokenSupply(ctx, coin.Denom).Sub(coin)); err != nil {
return err
}

Expand Down
5 changes: 2 additions & 3 deletions x/leverage/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit f556e52

Please sign in to comment.