-
Notifications
You must be signed in to change notification settings - Fork 173
feat: add MsgMaxBorrow #1690
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
feat: add MsgMaxBorrow #1690
Changes from 10 commits
7afb2b6
c833cfe
9e2ebd2
13c5876
76a87e5
e39614e
5705340
5e5237e
38fda0e
472c032
864f7e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1248,6 +1248,138 @@ func (s *IntegrationTestSuite) TestMsgBorrow() { | |
| } | ||
| } | ||
|
|
||
| func (s *IntegrationTestSuite) TestMsgMaxBorrow() { | ||
| type testCase struct { | ||
| msg string | ||
| addr sdk.AccAddress | ||
| coin sdk.Coin | ||
| err error | ||
| } | ||
|
|
||
| app, ctx, srv, require := s.app, s.ctx, s.msgSrvr, s.Require() | ||
|
|
||
| // create and fund a supplier which supplies 100 UMEE and 100 ATOM | ||
| supplier := s.newAccount(coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) | ||
| s.supply(supplier, coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) | ||
|
|
||
| // create a borrower which supplies and collateralizes 100 ATOM | ||
| borrower := s.newAccount(coin(atomDenom, 100_000000)) | ||
| s.supply(borrower, coin(atomDenom, 100_000000)) | ||
| s.collateralize(borrower, coin("u/"+atomDenom, 100_000000)) | ||
|
|
||
| // create an additional supplier (DUMP, PUMP tokens) | ||
| surplus := s.newAccount(coin(dumpDenom, 100_000000), coin(pumpDenom, 100_000000)) | ||
| s.supply(surplus, coin(pumpDenom, 100_000000)) | ||
| s.supply(surplus, coin(dumpDenom, 100_000000)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code should be reused, rather than copied over. |
||
|
|
||
| // this will be a DUMP (historic price 1.00, current price 0.50) borrower | ||
| // using PUMP (historic price 1.00, current price 2.00) collateral | ||
| dumpborrower := s.newAccount(coin(pumpDenom, 100_000000)) | ||
| s.supply(dumpborrower, coin(pumpDenom, 100_000000)) | ||
| s.collateralize(dumpborrower, coin("u/"+pumpDenom, 100_000000)) | ||
| // collateral value is $200 (current) or $100 (historic) | ||
| // collateral weights are always 0.25 in testing | ||
|
|
||
| // this will be a PUMP (historic price 1.00, current price 2.00) borrower | ||
| // using DUMP (historic price 1.00, current price 0.50) collateral | ||
| pumpborrower := s.newAccount(coin(dumpDenom, 100_000000)) | ||
| s.supply(pumpborrower, coin(dumpDenom, 100_000000)) | ||
| s.collateralize(pumpborrower, coin("u/"+dumpDenom, 100_000000)) | ||
| // collateral value is $50 (current) or $100 (historic) | ||
| // collateral weights are always 0.25 in testing | ||
|
|
||
| tcs := []testCase{ | ||
| { | ||
| "uToken", | ||
| borrower, | ||
| coin("u/"+umeeDenom, 0), | ||
| types.ErrUToken, | ||
| }, | ||
| { | ||
| "unregistered token", | ||
| borrower, | ||
| coin("abcd", 0), | ||
| types.ErrNotRegisteredToken, | ||
| }, | ||
| { | ||
| "zero collateral", | ||
| supplier, | ||
| coin(atomDenom, 0), | ||
| types.ErrMaxBorrowZero, | ||
| }, | ||
| { | ||
| "atom borrow", | ||
| borrower, | ||
| coin(atomDenom, 25_000000), | ||
| nil, | ||
| }, | ||
| { | ||
| "already borrowed max", | ||
| borrower, | ||
| coin(atomDenom, 0), | ||
| types.ErrMaxBorrowZero, | ||
| }, | ||
| { | ||
| "dump borrower", | ||
| dumpborrower, | ||
| coin(dumpDenom, 25_000000), | ||
| nil, | ||
| }, | ||
| { | ||
| "pump borrower", | ||
| pumpborrower, | ||
| coin(pumpDenom, 6_250000), | ||
| nil, | ||
| }, | ||
adamewozniak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| for _, tc := range tcs { | ||
| msg := &types.MsgMaxBorrow{ | ||
| Borrower: tc.addr.String(), | ||
| Denom: tc.coin.Denom, | ||
| } | ||
adamewozniak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if tc.err != nil { | ||
| _, err := srv.MaxBorrow(ctx, msg) | ||
| require.ErrorIs(err, tc.err, tc.msg) | ||
| } else { | ||
| // initial state | ||
| iBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) | ||
| iCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) | ||
| iUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) | ||
| iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) | ||
| iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) | ||
|
|
||
| // verify the output of borrow function | ||
| resp, err := srv.MaxBorrow(ctx, msg) | ||
| require.NoError(err, tc.msg) | ||
| require.Equal(&types.MsgMaxBorrowResponse{ | ||
| Borrowed: tc.coin, | ||
| }, resp, tc.msg) | ||
|
|
||
| // final state | ||
| fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) | ||
| fCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) | ||
| fUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) | ||
| fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) | ||
| fBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) | ||
|
|
||
| // verify token balance is increased by expected amount | ||
| require.Equal(iBalance.Add(tc.coin), fBalance, tc.msg, "balances") | ||
| // verify uToken collateral unchanged | ||
| require.Equal(iCollateral, fCollateral, tc.msg, "collateral") | ||
| // verify uToken supply is unchanged | ||
| require.Equal(iUTokenSupply, fUTokenSupply, tc.msg, "uToken supply") | ||
| // verify uToken exchange rate is unchanged | ||
| require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") | ||
| // verify borrowed coins increased by expected amount | ||
| require.Equal(iBorrowed.Add(tc.coin), fBorrowed, "borrowed coins") | ||
|
|
||
| // check all available invariants | ||
| s.checkInvariants(tc.msg) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func (s *IntegrationTestSuite) TestMsgRepay() { | ||
| type testCase struct { | ||
| msg string | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,7 @@ var ( | |
| ErrLiquidationIneligible = sdkerrors.Register(ModuleName, 403, "borrower not eligible for liquidation") | ||
| ErrMaxWithdrawZero = sdkerrors.Register(ModuleName, 404, "max withdraw amount was zero") | ||
| ErrNoHistoricMedians = sdkerrors.Register(ModuleName, 405, "insufficient historic medians available") | ||
| ErrMaxBorrowZero = sdkerrors.Register(ModuleName, 406, "max borrow amount was zero") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this is an error? If there is nothing more to borrow, we just return zero, rather error. Imaging smart contract just want to automatically set max borrow. It should handle zero value rather than dealing with failed transaction.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any supply / withdraw / borrow / etc that is a No-Op currently fails rather then succeeding with consumed gas. This follows suit
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If transaction fails, then the whole provided fee for gas is consumed anyway. So, it only makes doing composability more difficult. |
||
|
|
||
| // 5XX = Market Conditions | ||
| ErrLendingPoolInsufficient = sdkerrors.Register(ModuleName, 500, "lending pool insufficient") | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.