From ca8df099691339345ba612d7057e58ad3250e9c3 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 25 Aug 2022 00:04:57 -0700 Subject: [PATCH 01/36] supply and withdraw tests --- x/leverage/fixtures/token.go | 5 + x/leverage/keeper/borrows_test.go | 2 +- x/leverage/keeper/keeper_test.go | 618 ++++++++++++----------------- x/leverage/keeper/oracle_test.go | 12 +- x/leverage/keeper/reserves_test.go | 75 ++++ x/leverage/keeper/suite_test.go | 273 +++++++++++++ x/leverage/types/errors.go | 1 + 7 files changed, 604 insertions(+), 382 deletions(-) create mode 100644 x/leverage/keeper/reserves_test.go create mode 100644 x/leverage/keeper/suite_test.go diff --git a/x/leverage/fixtures/token.go b/x/leverage/fixtures/token.go index 920345f6bd..36149fda1a 100644 --- a/x/leverage/fixtures/token.go +++ b/x/leverage/fixtures/token.go @@ -6,6 +6,11 @@ import ( "github.com/umee-network/umee/v2/x/leverage/types" ) +const ( + // AtomDenom is an ibc denom to be used as ATOM's BaseDenom during testing + AtomDenom = "ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D" +) + // Token returns a valid token func Token(base, symbol string) types.Token { return types.Token{ diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index fe96686b81..eb425219f0 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -212,7 +212,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { s.Require().Equal(expectedUmeeLimit, borrowLimit) // Create collateral atom uTokens (1k u/uatom) - atomCollatDenom := types.ToUTokenDenom(atomIBCDenom) + atomCollatDenom := types.ToUTokenDenom(atomDenom) atomCollateral := sdk.NewCoins(sdk.NewInt64Coin(atomCollatDenom, 1000000000)) // Manually compute borrow limit using collateral weight of 0.25 diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 05f453d6e8..340972bc87 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -1,240 +1,253 @@ package keeper_test import ( - "fmt" - "testing" - "time" - - sdkmath "cosmossdk.io/math" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - "github.com/stretchr/testify/suite" - tmrand "github.com/tendermint/tendermint/libs/rand" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" umeeapp "github.com/umee-network/umee/v2/app" - "github.com/umee-network/umee/v2/x/leverage" - "github.com/umee-network/umee/v2/x/leverage/fixtures" "github.com/umee-network/umee/v2/x/leverage/keeper" "github.com/umee-network/umee/v2/x/leverage/types" ) -const ( - initialPower = int64(10000000000) - atomIBCDenom = "ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D" - umeeDenom = umeeapp.BondDenom -) - -var ( - initTokens = sdk.TokensFromConsensusPower(initialPower, sdk.DefaultPowerReduction) - initCoins = sdk.NewCoins(sdk.NewCoin(umeeapp.BondDenom, initTokens)) -) - -// creates a test token with reasonable initial parameters -func newToken(base, symbol string) types.Token { - return fixtures.Token(base, symbol) -} - -type IntegrationTestSuite struct { - suite.Suite - - ctx sdk.Context - app *umeeapp.UmeeApp - tk keeper.TestKeeper - queryClient types.QueryClient - setupAccountCounter sdkmath.Int -} - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(IntegrationTestSuite)) -} - -func (s *IntegrationTestSuite) SetupTest() { - app := umeeapp.Setup(s.T(), false, 1) - ctx := app.BaseApp.NewContext(false, tmproto.Header{ - ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)), - Height: 1, - Time: time.Unix(0, 0), - }) - - umeeToken := newToken(umeeapp.BondDenom, "UMEE") - atomIBCToken := newToken(atomIBCDenom, "ATOM") - - // we only override the Leverage keeper so we can supply a custom mock oracle - k, tk := keeper.NewTestKeeper( - s.Require(), - app.AppCodec(), - app.GetKey(types.ModuleName), - app.GetSubspace(types.ModuleName), - app.BankKeeper, - newMockOracleKeeper(), - ) - - s.tk = tk - app.LeverageKeeper = k - app.LeverageKeeper = *app.LeverageKeeper.SetHooks(types.NewMultiHooks()) - - leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, atomIBCToken)) - - queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, keeper.NewQuerier(app.LeverageKeeper)) - - s.app = app - s.ctx = ctx - s.setupAccountCounter = sdkmath.ZeroInt() - s.queryClient = types.NewQueryClient(queryHelper) -} - -// setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, -// may also supply them to receive uTokens, and may also enable those uTokens as collateral and borrow tokens in the same denom. -func (s *IntegrationTestSuite) setupAccount(denom string, mintAmount, supplyAmount, borrowAmount int64, collateral bool) sdk.AccAddress { - // create a unique address - s.setupAccountCounter = s.setupAccountCounter.Add(sdk.OneInt()) - addrStr := fmt.Sprintf("%-20s", "addr"+s.setupAccountCounter.String()+"_______________") - addr := sdk.AccAddress([]byte(addrStr)) - - // register the account in AccountKeeper - acct := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) - s.app.AccountKeeper.SetAccount(s.ctx, acct) - - if mintAmount > 0 { - // mint and send mintAmount tokens to account - s.Require().NoError(s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), - )) - s.Require().NoError(s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, addr, - sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), - )) +func (s *IntegrationTestSuite) TestSupply() { + type testCase struct { + msg string + addr sdk.AccAddress + coin sdk.Coin + expectedUTokens sdk.Coin + expectErr bool } - if supplyAmount > 0 { - // account supplies supplyAmount tokens and receives uTokens - uTokens, err := s.app.LeverageKeeper.Supply(s.ctx, addr, sdk.NewInt64Coin(denom, supplyAmount)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(denom), supplyAmount), uTokens) + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a supplier with 100 UMEE and 100 ATOM + supplier := s.newAccount(coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) + + // create and modify a borrower to force the uToken exchange rate of ATOM from 1 to 1.5 + borrower := s.newAccount(coin(atomDenom, 100_000000)) + s.supply(borrower, coin(atomDenom, 100_000000)) + s.collateralize(borrower, coin("u/"+atomDenom, 100_000000)) + s.borrow(borrower, coin(atomDenom, 10_000000)) + s.tk.SetBorrow(ctx, borrower, coin(atomDenom, 60_000000)) + + tcs := []testCase{ + { + "unregistered denom", + supplier, + coin("abcd", 80_000000), + sdk.Coin{}, + true, + }, + { + "uToken", + supplier, + coin("u/"+umeeDenom, 80_000000), + sdk.Coin{}, + true, + }, + { + "insufficient balance", + supplier, + coin(umeeDenom, 120_000000), + sdk.Coin{}, + true, + }, + { + "valid supply", + supplier, + coin(umeeDenom, 80_000000), + coin("u/"+umeeDenom, 80_000000), + false, + }, + { + "high exchange rate", + supplier, + coin(atomDenom, 60_000000), + coin("u/"+atomDenom, 40_000000), + false, + }, } - if collateral { - // account enables associated uToken as collateral - collat, err := s.app.LeverageKeeper.ExchangeToken(s.ctx, sdk.NewInt64Coin(denom, supplyAmount)) - s.Require().NoError(err) - err = s.app.LeverageKeeper.Collateralize(s.ctx, addr, collat) - s.Require().NoError(err) + for _, tc := range tcs { + if tc.expectErr { + _, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) + require.Error(err) + } else { + uDenom := types.ToUTokenDenom(tc.coin.Denom) + + // initial state + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tc.coin.Denom) + iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) + + // verify the outputs of supply function + uToken, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) + require.NoError(err) + require.Equal(tc.expectedUTokens, uToken, tc.msg) + + // final state + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tc.coin.Denom) + fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) + + // verify token balance decreased by the expected amount + require.Equal(iBalance.Sub(tc.coin), fBalance, tc.msg, "token balance") + // verify uToken balance increased by the expected amount + require.Equal(iUTokens.Add(tc.expectedUTokens), fUTokens, tc.msg, "uToken balance") + // verify uToken supply increased by the expected amount + require.Equal(iUTokenSupply.Add(tc.expectedUTokens), fUTokenSupply, tc.msg, "uToken supply") + // verify uToken exchange rate is unchanged (sensitive to rounding) + require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") + } } - - if borrowAmount > 0 { - // account borrows borrowAmount tokens - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(denom, borrowAmount)) - s.Require().NoError(err) - } - - // return the account addresse - return addr } -func (s *IntegrationTestSuite) TestSupply_InvalidAsset() { - addr := sdk.AccAddress([]byte("addr________________")) - acc := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) - s.app.AccountKeeper.SetAccount(s.ctx, acc) - - // create coins of an unregistered base asset type "uabcd" - invalidCoin := sdk.NewInt64Coin("uabcd", 1000000000) // 1k abcd - invalidCoins := sdk.NewCoins(invalidCoin) - - // mint and send coins - s.Require().NoError(s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, invalidCoins)) - s.Require().NoError(s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, addr, invalidCoins)) - - // supplying should fail as we have not registered token "uabcd" - uTokens, err := s.app.LeverageKeeper.Supply(s.ctx, addr, invalidCoin) - s.Require().Error(err) - s.Require().Equal(sdk.Coin{}, uTokens) -} - -func (s *IntegrationTestSuite) TestSupply_Valid() { - app, ctx := s.app, s.ctx - - addr := sdk.AccAddress([]byte("addr____________1234")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - app.AccountKeeper.SetAccount(ctx, acc) - - // mint and send coins - s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, initCoins)) - s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, initCoins)) - - // supply asset - uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - uToken, err := s.app.LeverageKeeper.Supply(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000)) // 1k umee - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 1000000000), uToken) // 1k u/umee - - // verify the total supply of the minted uToken - supply := s.app.LeverageKeeper.GetUTokenSupply(ctx, uTokenDenom) - expected := sdk.NewInt64Coin(uTokenDenom, 1000000000) // 1k u/umee - s.Require().Equal(expected, supply) - - // verify the user's balances - tokenBalance := app.BankKeeper.GetBalance(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(initTokens.Sub(sdk.NewInt(1000000000)), tokenBalance.Amount) - - uTokenBalance := app.BankKeeper.GetBalance(ctx, addr, uTokenDenom) - s.Require().Equal(int64(1000000000), uTokenBalance.Amount.Int64()) -} - -func (s *IntegrationTestSuite) TestWithdraw_Valid() { - app, ctx := s.app, s.ctx - - addr := sdk.AccAddress([]byte("addr________________")) - acc := app.AccountKeeper.NewAccountWithAddress(ctx, addr) - app.AccountKeeper.SetAccount(ctx, acc) - - // mint and send coins - s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, initCoins)) - s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, initCoins)) - - // supply asset - _, err := s.app.LeverageKeeper.Supply(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000)) // 1k umee - s.Require().NoError(err) - - // verify the total supply of the minted uToken - uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - supply := s.app.LeverageKeeper.GetUTokenSupply(ctx, uTokenDenom) - expected := sdk.NewInt64Coin(uTokenDenom, 1000000000) // 1k u/umee - s.Require().Equal(expected, supply) - - // withdraw the total amount of assets supplied - uToken := expected - withdrawn, err := s.app.LeverageKeeper.Withdraw(ctx, addr, uToken) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000), withdrawn) // 1k umee - - // verify total supply of the uTokens - supply = s.app.LeverageKeeper.GetUTokenSupply(ctx, uTokenDenom) - s.Require().Equal(int64(0), supply.Amount.Int64()) - - // verify the user's balances - tokenBalance := app.BankKeeper.GetBalance(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(initTokens, tokenBalance.Amount) - - uTokenBalance := app.BankKeeper.GetBalance(ctx, addr, uTokenDenom) - s.Require().Equal(int64(0), uTokenBalance.Amount.Int64()) -} - -func (s *IntegrationTestSuite) TestSetReserves() { - // get initial reserves - amount := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) - s.Require().Equal(amount, sdk.ZeroInt()) +func (s *IntegrationTestSuite) TestWithdraw() { + type testCase struct { + msg string + addr sdk.AccAddress + uToken sdk.Coin + expectFromBalance int64 + expectFromCollateral int64 + expectedTokens sdk.Coin + expectErr bool + } - // artifically reserve 200 umee - err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().NoError(err) + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a supplier with 100 UMEE and 100 ATOM, then supply 100 UMEE and 50 ATOM + // also collateralize 75 of supplied UMEE + supplier := s.newAccount(coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) + s.supply(supplier, coin(umeeDenom, 100_000000)) + s.collateralize(supplier, coin("u/"+umeeDenom, 75_000000)) + s.supply(supplier, coin(atomDenom, 50_000000)) + + // create and modify a borrower to force the uToken exchange rate of ATOM from 1 to 1.2 + borrower := s.newAccount(coin(atomDenom, 100_000000)) + s.supply(borrower, coin(atomDenom, 100_000000)) + s.collateralize(borrower, coin("u/"+atomDenom, 100_000000)) + s.borrow(borrower, coin(atomDenom, 10_000000)) + s.tk.SetBorrow(ctx, borrower, coin(atomDenom, 40_000000)) + + tcs := []testCase{ + { + "unregistered denom", + supplier, + coin("abcd", 80_000000), + 0, + 0, + sdk.Coin{}, + true, + }, + { + "unregistered uToken", + supplier, + coin("u/abcd", 80_000000), + 0, + 0, + sdk.Coin{}, + true, + }, + { + "base token", + supplier, + coin(umeeDenom, 80_000000), + 0, + 0, + sdk.Coin{}, + true, + }, + { + "insufficient uTokens", + supplier, + coin("u/"+umeeDenom, 120_000000), + 0, + 0, + sdk.Coin{}, + true, + }, + { + "withdraw from balance", + supplier, + coin("u/"+umeeDenom, 10_000000), + 10_000000, + 0, + coin(umeeDenom, 10_000000), + false, + }, + { + "withdraw from collateral", + supplier, + coin("u/"+umeeDenom, 90_000000), + 15_000000, + 75_000000, + coin(umeeDenom, 90_000000), + false, + }, + { + "high exchange rate", + supplier, + coin("u/"+atomDenom, 50_000000), + 50_000000, + 0, + coin(atomDenom, 60_000000), + false, + }, + { + "borrow limit", + borrower, + coin("u/"+atomDenom, 50_000000), + 0, + 0, + sdk.Coin{}, + true, + }, + } - // get new reserves - amount = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) - s.Require().Equal(amount, sdk.NewInt(200000000)) + for _, tc := range tcs { + if tc.expectErr { + _, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) + require.Error(err) + } else { + tokenDenom := types.ToTokenDenom(tc.uToken.Denom) + + // initial state + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tokenDenom) + iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, tc.uToken.Denom) + iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, tc.uToken.Denom) + iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, tc.uToken.Denom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tokenDenom) + + // verify the outputs of withdraw function + token, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) + + require.NoError(err) + require.Equal(tc.expectedTokens, token, tc.msg) + + // final state + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tokenDenom) + fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, tc.uToken.Denom) + fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, tc.uToken.Denom) + fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, tc.uToken.Denom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tokenDenom) + + // verify token balance increased by the expected amount + require.Equal(iBalance.Add(tc.expectedTokens), fBalance, tc.msg, "token balance") + // verify uToken balance decreased by the expected amount + require.Equal(iUTokens.Amount.Int64()-tc.expectFromBalance, + fUTokens.Amount.Int64(), tc.msg, "uTokens from balance") + // verify uToken collateral decreased by the expected amount + require.Equal(iCollateral.Amount.Int64()-tc.expectFromCollateral, + fCollateral.Amount.Int64(), tc.msg, "uToken collateral") + // verify uToken supply decreased by the expected amount + require.Equal(iUTokenSupply.Sub(tc.uToken).Amount.Int64(), + fUTokenSupply.Amount.Int64(), tc.msg, "uToken supply") + // verify uToken exchange rate is unchanged (sensitive to rounding) + require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") + } + } } func (s *IntegrationTestSuite) TestGetToken() { @@ -257,71 +270,6 @@ func (s *IntegrationTestSuite) TestGetToken() { s.Require().NoError(t.AssertNotBlacklisted()) } -// initialize the common starting scenario from which borrow and repay tests stem: -// Umee and u/umee are registered assets; a "supplier" account has 9k umee and 1k u/umee; -// the leverage module has 1k umee in its lending pool (module account); and a "bum" -// account has been created with no assets. -func (s *IntegrationTestSuite) initBorrowScenario() (supplier, bum sdk.AccAddress) { - app, ctx := s.app, s.ctx - - // create an account and address which will represent a supplier - supplierAddr := sdk.AccAddress([]byte("addr______________01")) - supplierAcc := app.AccountKeeper.NewAccountWithAddress(ctx, supplierAddr) - app.AccountKeeper.SetAccount(ctx, supplierAcc) - - // create an account and address which will represent a user with no assets - bumAddr := sdk.AccAddress([]byte("addr______________02")) - bumAcc := app.AccountKeeper.NewAccountWithAddress(ctx, bumAddr) - app.AccountKeeper.SetAccount(ctx, bumAcc) - - // mint and send 10k umee to supplier - s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee - )) - s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, supplierAddr, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee, - )) - - // supplier supplies 1000 umee and receives 1k u/umee - supplyCoin := sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000) - _, err := s.app.LeverageKeeper.Supply(ctx, supplierAddr, supplyCoin) - s.Require().NoError(err) - - // supplier enables u/umee as collateral - collat, err := s.app.LeverageKeeper.ExchangeToken(ctx, supplyCoin) - s.Require().NoError(err) - err = s.app.LeverageKeeper.Collateralize(ctx, supplierAddr, collat) - s.Require().NoError(err) - - // return the account addresses - return supplierAddr, bumAddr -} - -// mintAndSupplyAtom mints a amount of atoms to an address -// account has been created with no assets. -func (s *IntegrationTestSuite) mintAndSupplyAtom(mintTo sdk.AccAddress, amountToMint, amountToSupply int64) { - app, ctx := s.app, s.ctx - - // mint and send atom to mint addr - s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(atomIBCDenom, amountToMint)), // amountToMint Atom - )) - s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, mintTo, - sdk.NewCoins(sdk.NewInt64Coin(atomIBCDenom, amountToMint)), // amountToMint Atom, - )) - - // user supplies amountToSupply atom and receives amountToSupply u/atom - supplyCoin := sdk.NewInt64Coin(atomIBCDenom, amountToSupply) - _, err := s.app.LeverageKeeper.Supply(ctx, mintTo, supplyCoin) - s.Require().NoError(err) - - // user enables u/atom as collateral - collat, err := s.app.LeverageKeeper.ExchangeToken(ctx, supplyCoin) - s.Require().NoError(err) - err = s.app.LeverageKeeper.Collateralize(ctx, mintTo, collat) - s.Require().NoError(err) -} - func (s *IntegrationTestSuite) TestBorrow_Invalid() { addr, _ := s.initBorrowScenario() @@ -549,60 +497,6 @@ func (s *IntegrationTestSuite) TestRepay_Overpay() { s.Require().Error(err) } -func (s *IntegrationTestSuite) TestRepayBadDebt() { - // Creating a supplier so module account has some uumee - _ = s.setupAccount(umeeDenom, 200000000, 200000000, 0, false) // 200 umee - - // Using an address with no assets - addr := s.setupAccount(umeeDenom, 0, 0, 0, false) - - // Create an uncollateralized debt position - badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee - err := s.tk.SetBorrow(s.ctx, addr, badDebt) - s.Require().NoError(err) - - // Manually mark the bad debt for repayment - s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr, umeeDenom, true)) - - // Manually set reserves to 60 umee - reserve := sdk.NewInt64Coin(umeeDenom, 60000000) - err = s.tk.SetReserveAmount(s.ctx, reserve) - s.Require().NoError(err) - - // Sweep all bad debts, which should repay 60 umee of the bad debt (partial repayment) - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) - - // Confirm that a debt of 40 umee remains - remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) - - // Confirm that reserves are exhausted - remainingReserve := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) - s.Require().Equal(sdk.ZeroInt(), remainingReserve) - - // Manually set reserves to 70 umee - reserve = sdk.NewInt64Coin(umeeDenom, 70000000) - err = s.tk.SetReserveAmount(s.ctx, reserve) - s.Require().NoError(err) - - // Sweep all bad debts, which should fully repay the bad debt this time - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) - - // Confirm that the debt is eliminated - remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) - - // Confirm that reserves are now at 30 umee - remainingReserve = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt(30000000), remainingReserve) - - // Sweep all bad debts - but there are none - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) -} - func (s *IntegrationTestSuite) TestDeriveExchangeRate() { // The init scenario is being used so module balance starts at 1000 umee // and the uToken supply starts at 1000 due to supplier account @@ -744,7 +638,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset s.Require().Equal([]sdk.AccAddress{addr}, targetAddress) // if it tries to borrow any other asset it should return an error - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomIBCDenom, 1)) + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 1)) s.Require().Error(err) } @@ -763,17 +657,16 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset s.Require().NoError(err) s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - mintAmountAtom := int64(100000000) // 100 atom - supplyAmountAtom := int64(50000000) // 50 atom - - // mints and send to user 100 atom and already + // mints and send to addr 100 atom and already // enable 50 u/atom as collateral. - s.mintAndSupplyAtom(addr, mintAmountAtom, supplyAmountAtom) + s.fundAccount(addr, coin(atomDenom, 100_000000)) + s.supply(addr, coin(atomDenom, 50_000000)) + s.collateralize(addr, coin("u/"+atomDenom, 50_000000)) // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomIBCDenom, 4000000)) // 4 atom + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom s.Require().NoError(err) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation @@ -784,7 +677,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) // Note: Setting atom collateral weight to 0.01 to make the user eligible for liquidation - atomIBCToken := newToken(atomIBCDenom, "ATOM") + atomIBCToken := newToken(atomDenom, "ATOM") atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") @@ -810,17 +703,16 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { s.Require().NoError(err) s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - mintAmountAtom := int64(100000000) // 100 atom - supplyAmountAtom := int64(50000000) // 50 atom - // mints and send to anotherSupplier 100 atom and already // enable 50 u/atom as collateral. - s.mintAndSupplyAtom(anotherSupplier, mintAmountAtom, supplyAmountAtom) + s.fundAccount(anotherSupplier, coin(atomDenom, 100_000000)) + s.supply(anotherSupplier, coin(atomDenom, 50_000000)) + s.collateralize(anotherSupplier, coin("u/"+atomDenom, 50_000000)) // anotherSupplier borrows 4 atom (max current allowed - 1) anotherSupplier amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, anotherSupplier, sdk.NewInt64Coin(atomIBCDenom, 4000000)) // 4 atom + err = s.app.LeverageKeeper.Borrow(s.ctx, anotherSupplier, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom s.Require().NoError(err) // Note: Setting umee liquidation threshold to 0.05 to make the supplier eligible for liquidation @@ -831,7 +723,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) // Note: Setting atom collateral weight to 0.01 to make the supplier eligible for liquidation - atomIBCToken := newToken(atomIBCDenom, "ATOM") + atomIBCToken := newToken(atomDenom, "ATOM") atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") @@ -897,30 +789,6 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { s.Require().False(broken) } -func (s *IntegrationTestSuite) TestWithdraw_InsufficientCollateral() { - // Create a supplier with 1 u/umee collateral by supplying 1 umee - supplierAddr := s.setupAccount(umeeapp.BondDenom, 1000000, 1000000, 0, true) - - // Create an additional supplier so lending pool has extra umee - _ = s.setupAccount(umeeapp.BondDenom, 1000000, 1000000, 0, true) - - // verify collateral amount and total supply of minted uTokens - uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, supplierAddr, uTokenDenom) - s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 1000000), collateral) // 1 u/umee - supply := s.app.LeverageKeeper.GetUTokenSupply(s.ctx, uTokenDenom) - s.Require().Equal(sdk.NewInt64Coin(uTokenDenom, 2000000), supply) // 2 u/umee - - // withdraw more collateral than available - uToken := collateral.Add(sdk.NewInt64Coin(uTokenDenom, 1)) - - withdrawn, err := s.app.LeverageKeeper.Withdraw(s.ctx, supplierAddr, uToken) - s.Require().EqualError(err, - "0 uToken balance + 1000000 from collateral is less than 1000001u/uumee to withdraw: insufficient balance", - ) - s.Require().Equal(sdk.Coin{}, withdrawn) -} - func (s *IntegrationTestSuite) TestTotalCollateral() { // Test zero collateral uDenom := types.ToUTokenDenom(umeeDenom) @@ -935,7 +803,7 @@ func (s *IntegrationTestSuite) TestTotalCollateral() { s.Require().Equal(sdk.NewInt(1000000000), collateral) } -func (s *IntegrationTestSuite) TestLiqudateBorrow() { +func (s *IntegrationTestSuite) TestLiqudate() { addr, _ := s.initBorrowScenario() // The "supplier" user from the init scenario is being used because it diff --git a/x/leverage/keeper/oracle_test.go b/x/leverage/keeper/oracle_test.go index e23ba1854e..9b4bf6c3ad 100644 --- a/x/leverage/keeper/oracle_test.go +++ b/x/leverage/keeper/oracle_test.go @@ -43,7 +43,7 @@ func (m *mockOracleKeeper) GetExchangeRateBase(ctx sdk.Context, denom string) (s func (m *mockOracleKeeper) Reset() { m.exchangeRates = map[string]sdk.Dec{ umeeapp.BondDenom: sdk.MustNewDecFromStr("4.21"), - atomIBCDenom: sdk.MustNewDecFromStr("39.38"), + atomDenom: sdk.MustNewDecFromStr("39.38"), } } @@ -52,7 +52,7 @@ func (s *IntegrationTestSuite) TestOracle_TokenPrice() { s.Require().NoError(err) s.Require().Equal(sdk.MustNewDecFromStr("0.00000421"), p) - p, err = s.app.LeverageKeeper.TokenPrice(s.ctx, atomIBCDenom) + p, err = s.app.LeverageKeeper.TokenPrice(s.ctx, atomDenom) s.Require().NoError(err) s.Require().Equal(sdk.MustNewDecFromStr("0.00003938"), p) @@ -78,7 +78,7 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { s.ctx, sdk.NewCoins( sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), - sdk.NewInt64Coin(atomIBCDenom, 4700000), + sdk.NewInt64Coin(atomDenom, 4700000), ), ) s.Require().NoError(err) @@ -89,7 +89,7 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { s.ctx, sdk.NewCoins( sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), - sdk.NewInt64Coin(atomIBCDenom, 4700000), + sdk.NewInt64Coin(atomDenom, 4700000), sdk.NewInt64Coin("foo", 4700000), ), ) @@ -98,12 +98,12 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { } func (s *IntegrationTestSuite) TestOracle_PriceRatio() { - r, err := s.app.LeverageKeeper.PriceRatio(s.ctx, umeeapp.BondDenom, atomIBCDenom) + r, err := s.app.LeverageKeeper.PriceRatio(s.ctx, umeeapp.BondDenom, atomDenom) s.Require().NoError(err) // $4.21 / $39.38 s.Require().Equal(sdk.MustNewDecFromStr("0.106907059421025901"), r) - _, err = s.app.LeverageKeeper.PriceRatio(s.ctx, "foo", atomIBCDenom) + _, err = s.app.LeverageKeeper.PriceRatio(s.ctx, "foo", atomDenom) s.Require().Error(err) _, err = s.app.LeverageKeeper.PriceRatio(s.ctx, umeeapp.BondDenom, "foo") diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go new file mode 100644 index 0000000000..47bc9b7f5c --- /dev/null +++ b/x/leverage/keeper/reserves_test.go @@ -0,0 +1,75 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + umeeapp "github.com/umee-network/umee/v2/app" +) + +func (s *IntegrationTestSuite) TestSetReserves() { + // get initial reserves + amount := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) + s.Require().Equal(amount, sdk.ZeroInt()) + + // artifically reserve 200 umee + err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + s.Require().NoError(err) + + // get new reserves + amount = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) + s.Require().Equal(amount, sdk.NewInt(200000000)) +} + +func (s *IntegrationTestSuite) TestRepayBadDebt() { + // Creating a supplier so module account has some uumee + _ = s.setupAccount(umeeDenom, 200000000, 200000000, 0, false) // 200 umee + + // Using an address with no assets + addr := s.setupAccount(umeeDenom, 0, 0, 0, false) + + // Create an uncollateralized debt position + badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee + err := s.tk.SetBorrow(s.ctx, addr, badDebt) + s.Require().NoError(err) + + // Manually mark the bad debt for repayment + s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr, umeeDenom, true)) + + // Manually set reserves to 60 umee + reserve := sdk.NewInt64Coin(umeeDenom, 60000000) + err = s.tk.SetReserveAmount(s.ctx, reserve) + s.Require().NoError(err) + + // Sweep all bad debts, which should repay 60 umee of the bad debt (partial repayment) + err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) + s.Require().NoError(err) + + // Confirm that a debt of 40 umee remains + remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) + s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) + + // Confirm that reserves are exhausted + remainingReserve := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) + s.Require().Equal(sdk.ZeroInt(), remainingReserve) + + // Manually set reserves to 70 umee + reserve = sdk.NewInt64Coin(umeeDenom, 70000000) + err = s.tk.SetReserveAmount(s.ctx, reserve) + s.Require().NoError(err) + + // Sweep all bad debts, which should fully repay the bad debt this time + err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) + s.Require().NoError(err) + + // Confirm that the debt is eliminated + remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) + s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) + + // Confirm that reserves are now at 30 umee + remainingReserve = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) + s.Require().Equal(sdk.NewInt(30000000), remainingReserve) + + // Sweep all bad debts - but there are none + err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) + s.Require().NoError(err) +} diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go new file mode 100644 index 0000000000..1567cb921a --- /dev/null +++ b/x/leverage/keeper/suite_test.go @@ -0,0 +1,273 @@ +package keeper_test + +import ( + "fmt" + "testing" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + "github.com/stretchr/testify/suite" + tmrand "github.com/tendermint/tendermint/libs/rand" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + + umeeapp "github.com/umee-network/umee/v2/app" + "github.com/umee-network/umee/v2/x/leverage" + "github.com/umee-network/umee/v2/x/leverage/fixtures" + "github.com/umee-network/umee/v2/x/leverage/keeper" + "github.com/umee-network/umee/v2/x/leverage/types" +) + +const ( + initialPower = int64(10000000000) + umeeDenom = umeeapp.BondDenom + atomDenom = fixtures.AtomDenom +) + +var ( + initTokens = sdk.TokensFromConsensusPower(initialPower, sdk.DefaultPowerReduction) + initCoins = sdk.NewCoins(sdk.NewCoin(umeeapp.BondDenom, initTokens)) +) + +type IntegrationTestSuite struct { + suite.Suite + + ctx sdk.Context + app *umeeapp.UmeeApp + tk keeper.TestKeeper + queryClient types.QueryClient + setupAccountCounter sdkmath.Int +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} + +func (s *IntegrationTestSuite) SetupTest() { + app := umeeapp.Setup(s.T(), false, 1) + ctx := app.BaseApp.NewContext(false, tmproto.Header{ + ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)), + Height: 1, + Time: time.Unix(0, 0), + }) + + umeeToken := newToken(umeeapp.BondDenom, "UMEE") + atomIBCToken := newToken(atomDenom, "ATOM") + + // we only override the Leverage keeper so we can supply a custom mock oracle + k, tk := keeper.NewTestKeeper( + s.Require(), + app.AppCodec(), + app.GetKey(types.ModuleName), + app.GetSubspace(types.ModuleName), + app.BankKeeper, + newMockOracleKeeper(), + ) + + s.tk = tk + app.LeverageKeeper = k + app.LeverageKeeper = *app.LeverageKeeper.SetHooks(types.NewMultiHooks()) + + leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) + s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) + s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, atomIBCToken)) + + queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, keeper.NewQuerier(app.LeverageKeeper)) + + s.app = app + s.ctx = ctx + s.setupAccountCounter = sdkmath.ZeroInt() + s.queryClient = types.NewQueryClient(queryHelper) +} + +// creates a test token with reasonable initial parameters +func newToken(base, symbol string) types.Token { + return fixtures.Token(base, symbol) +} + +// creates a coin with a given base denom and amount +func coin(denom string, amount int64) sdk.Coin { + return sdk.NewInt64Coin(denom, amount) +} + +// newAccount creates a new account for testing, and funds it with any input tokens. +func (s *IntegrationTestSuite) newAccount(funds ...sdk.Coin) sdk.AccAddress { + app, ctx := s.app, s.ctx + + // create a unique address + s.setupAccountCounter = s.setupAccountCounter.Add(sdk.OneInt()) + addrStr := fmt.Sprintf("%-20s", "addr"+s.setupAccountCounter.String()+"_______________") + addr := sdk.AccAddress([]byte(addrStr)) + + // register the account in AccountKeeper + acct := app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) + app.AccountKeeper.SetAccount(ctx, acct) + + s.fundAccount(addr, funds...) + + return addr +} + +// fundAccount mints and sends tokens to an account for testing. +func (s *IntegrationTestSuite) fundAccount(addr sdk.AccAddress, funds ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + coins := sdk.NewCoins(funds...) + if !coins.IsZero() { + // mint and send tokens to account + require.NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, coins)) + require.NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, coins)) + } +} + +// supply tokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range coins { + _, err := app.LeverageKeeper.Supply(ctx, addr, coin) + require.NoError(err) + } +} + +// withdraw uTokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range uTokens { + _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin) + require.NoError(err) + } +} + +// collateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range uTokens { + err := app.LeverageKeeper.Collateralize(ctx, addr, coin) + require.NoError(err) + } +} + +// decollateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range uTokens { + err := app.LeverageKeeper.Decollateralize(ctx, addr, coin) + require.NoError(err) + } +} + +// borrow tokens as an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range coins { + err := app.LeverageKeeper.Borrow(ctx, addr, coin) + require.NoError(err) + } +} + +// repay tokens as an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range coins { + repaid, err := app.LeverageKeeper.Repay(ctx, addr, coin) + require.NoError(err) + // ensure intended repayment amount was not reduced, as doing so would create a misleading test + require.Equal(repaid, coin) + } +} + +// setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, +// may also supply them to receive uTokens, and may also enable those uTokens as collateral and borrow tokens in the same denom. +func (s *IntegrationTestSuite) setupAccount(denom string, mintAmount, supplyAmount, borrowAmount int64, collateral bool) sdk.AccAddress { + // create a unique address + s.setupAccountCounter = s.setupAccountCounter.Add(sdk.OneInt()) + addrStr := fmt.Sprintf("%-20s", "addr"+s.setupAccountCounter.String()+"_______________") + addr := sdk.AccAddress([]byte(addrStr)) + + // register the account in AccountKeeper + acct := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) + s.app.AccountKeeper.SetAccount(s.ctx, acct) + + if mintAmount > 0 { + // mint and send mintAmount tokens to account + s.Require().NoError(s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), + )) + s.Require().NoError(s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, addr, + sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), + )) + } + + if supplyAmount > 0 { + // account supplies supplyAmount tokens and receives uTokens + uTokens, err := s.app.LeverageKeeper.Supply(s.ctx, addr, sdk.NewInt64Coin(denom, supplyAmount)) + s.Require().NoError(err) + s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(denom), supplyAmount), uTokens) + } + + if collateral { + // account enables associated uToken as collateral + collat, err := s.app.LeverageKeeper.ExchangeToken(s.ctx, sdk.NewInt64Coin(denom, supplyAmount)) + s.Require().NoError(err) + err = s.app.LeverageKeeper.Collateralize(s.ctx, addr, collat) + s.Require().NoError(err) + } + + if borrowAmount > 0 { + // account borrows borrowAmount tokens + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(denom, borrowAmount)) + s.Require().NoError(err) + } + + // return the account addresse + return addr +} + +// initialize the common starting scenario from which borrow and repay tests stem: +// Umee and u/umee are registered assets; a "supplier" account has 9k umee and 1k u/umee; +// the leverage module has 1k umee in its lending pool (module account); and a "bum" +// account has been created with no assets. +func (s *IntegrationTestSuite) initBorrowScenario() (supplier, bum sdk.AccAddress) { + app, ctx := s.app, s.ctx + + // create an account and address which will represent a supplier + supplierAddr := sdk.AccAddress([]byte("addr______________01")) + supplierAcc := app.AccountKeeper.NewAccountWithAddress(ctx, supplierAddr) + app.AccountKeeper.SetAccount(ctx, supplierAcc) + + // create an account and address which will represent a user with no assets + bumAddr := sdk.AccAddress([]byte("addr______________02")) + bumAcc := app.AccountKeeper.NewAccountWithAddress(ctx, bumAddr) + app.AccountKeeper.SetAccount(ctx, bumAcc) + + // mint and send 10k umee to supplier + s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, + sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee + )) + s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, supplierAddr, + sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee, + )) + + // supplier supplies 1000 umee and receives 1k u/umee + supplyCoin := sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000) + _, err := s.app.LeverageKeeper.Supply(ctx, supplierAddr, supplyCoin) + s.Require().NoError(err) + + // supplier enables u/umee as collateral + collat, err := s.app.LeverageKeeper.ExchangeToken(ctx, supplyCoin) + s.Require().NoError(err) + err = s.app.LeverageKeeper.Collateralize(ctx, supplierAddr, collat) + s.Require().NoError(err) + + // return the account addresses + return supplierAddr, bumAddr +} diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 357fb61401..ceb6160418 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -40,4 +40,5 @@ var ( ) ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom should be a uToken") ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom should not be a uToken") + ErrMaxSupply = sdkerrors.Register(ModuleName, 1129, "market total supply would exceed MaxSupply") ) From d17672ecccd91b7100d11ab73a17d137a7a5b846 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 25 Aug 2022 00:05:25 -0700 Subject: [PATCH 02/36] err-- --- x/leverage/types/errors.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index ceb6160418..357fb61401 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -40,5 +40,4 @@ var ( ) ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom should be a uToken") ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom should not be a uToken") - ErrMaxSupply = sdkerrors.Register(ModuleName, 1129, "market total supply would exceed MaxSupply") ) From 3f6e746dc5f4e1f4d22273ddb2b523648bf9be6e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Thu, 25 Aug 2022 00:14:14 -0700 Subject: [PATCH 03/36] make proto-all --- swagger/swagger.yaml | 119 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 101 insertions(+), 18 deletions(-) diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index 79a418d465..236b587e17 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -184,6 +184,60 @@ paths: type: string tags: - Query + /umee/leverage/v1/bad_debts: + get: + summary: >- + BadDebts queries a list of borrow positions that have been marked for + bad debt repayment. + operationId: BadDebts + responses: + '200': + description: A successful response. + schema: + type: object + properties: + targets: + type: array + items: + type: object + properties: + address: + type: string + denom: + type: string + description: >- + BadDebt is a bad debt instance used in the leverage module's + genesis state. + description: >- + Targets are borrow positions currently marked for bad debt + repayment. Each contains an Address and a Denom. + description: >- + QueryBadDebtsResponse defines the response structure for the + BedDebts gRPC service handler. + default: + description: An unexpected error response. + schema: + type: object + properties: + error: + type: string + code: + type: integer + format: int32 + message: + type: string + details: + type: array + items: + type: object + properties: + type_url: + type: string + value: + type: string + format: byte + tags: + - Query /umee/leverage/v1/liquidation_targets: get: summary: >- @@ -205,9 +259,7 @@ paths: liquidation. description: >- QueryLiquidationTargetsResponse defines the response structure for - the - - LiquidationTargets gRPC service handler. + the LiquidationTargets gRPC service handler. default: description: An unexpected error response. schema: @@ -475,13 +527,13 @@ paths: direct_liquidation_fee: type: string description: >- - Direct Liquidation Fee is the reduction in liquidation - incentive experienced + Direct Liquidation Fee is a reduction factor in + liquidation incentive - by liquidators who choose to receive base assets instead - of uTokens as + experienced by liquidators who choose to receive base + assets instead of - liquidation rewards. + uTokens as liquidation rewards. description: Params defines the parameters for the leverage module. description: >- QueryParamsResponse defines the response structure for the Params @@ -1368,6 +1420,16 @@ definitions: value: type: string format: byte + umee.leverage.v1.BadDebt: + type: object + properties: + address: + type: string + denom: + type: string + description: >- + BadDebt is a bad debt instance used in the leverage module's genesis + state. umee.leverage.v1.Params: type: object properties: @@ -1403,12 +1465,12 @@ definitions: direct_liquidation_fee: type: string description: >- - Direct Liquidation Fee is the reduction in liquidation incentive - experienced + Direct Liquidation Fee is a reduction factor in liquidation incentive - by liquidators who choose to receive base assets instead of uTokens as + experienced by liquidators who choose to receive base assets instead + of - liquidation rewards. + uTokens as liquidation rewards. description: Params defines the parameters for the leverage module. umee.leverage.v1.QueryAccountBalancesResponse: type: object @@ -1504,6 +1566,27 @@ definitions: description: >- QueryAccountSummaryResponse defines the response structure for the AccountSummary gRPC service handler. + umee.leverage.v1.QueryBadDebtsResponse: + type: object + properties: + targets: + type: array + items: + type: object + properties: + address: + type: string + denom: + type: string + description: >- + BadDebt is a bad debt instance used in the leverage module's genesis + state. + description: >- + Targets are borrow positions currently marked for bad debt repayment. + Each contains an Address and a Denom. + description: >- + QueryBadDebtsResponse defines the response structure for the BedDebts gRPC + service handler. umee.leverage.v1.QueryLiquidationTargetsResponse: type: object properties: @@ -1512,7 +1595,7 @@ definitions: items: type: string description: Targets are the addresses of borrowers eligible for liquidation. - description: |- + description: >- QueryLiquidationTargetsResponse defines the response structure for the LiquidationTargets gRPC service handler. umee.leverage.v1.QueryMarketSummaryResponse: @@ -1703,13 +1786,13 @@ definitions: direct_liquidation_fee: type: string description: >- - Direct Liquidation Fee is the reduction in liquidation incentive - experienced + Direct Liquidation Fee is a reduction factor in liquidation + incentive - by liquidators who choose to receive base assets instead of - uTokens as + experienced by liquidators who choose to receive base assets + instead of - liquidation rewards. + uTokens as liquidation rewards. description: Params defines the parameters for the leverage module. description: |- QueryParamsResponse defines the response structure for the Params gRPC From 18ad3f907c5f2da0e9ad002c44073f695b783e40 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:05:53 -0700 Subject: [PATCH 04/36] error improvements and fix cascading client test error DEVX --- app/app.go | 1 - x/leverage/client/tests/suite.go | 105 ++++++++++----------------- x/leverage/client/tests/tests.go | 32 ++++---- x/leverage/keeper/borrows_test.go | 2 +- x/leverage/keeper/collateral.go | 5 +- x/leverage/keeper/collateral_test.go | 2 +- x/leverage/keeper/exchange_rate.go | 16 ++-- x/leverage/keeper/keeper.go | 6 +- x/leverage/keeper/keeper_test.go | 52 ++++++------- x/leverage/keeper/suite_test.go | 11 +-- x/leverage/keeper/token.go | 6 +- x/leverage/keeper/validate.go | 15 +++- x/leverage/types/errors.go | 73 ++++++++++--------- x/leverage/types/token.go | 4 +- x/leverage/types/tx.go | 12 +-- 15 files changed, 163 insertions(+), 179 deletions(-) diff --git a/app/app.go b/app/app.go index e2e82fd042..1537830eab 100644 --- a/app/app.go +++ b/app/app.go @@ -703,7 +703,6 @@ func (app *UmeeApp) setAnteHandler(txConfig client.TxConfig) { OracleKeeper: app.OracleKeeper, }, ) - if err != nil { panic(err) } diff --git a/x/leverage/client/tests/suite.go b/x/leverage/client/tests/suite.go index edf3315bde..450140855a 100644 --- a/x/leverage/client/tests/suite.go +++ b/x/leverage/client/tests/suite.go @@ -17,8 +17,6 @@ import ( type IntegrationTestSuite struct { suite.Suite - abort bool // stop interdependent tests on the first error for clarity - cfg network.Config network *network.Network } @@ -47,27 +45,32 @@ func (s *IntegrationTestSuite) TearDownSuite() { // TestCases are queries and transactions that can be run, and return a boolean // which indicates to abort the test suite if true type TestCase interface { - Run(s *IntegrationTestSuite) bool + Run(s *IntegrationTestSuite) +} + +// runTestQuery +func (s *IntegrationTestSuite) runTestQueries(tqs ...testQuery) { + for _, t := range tqs { + t.Run(s) + } } // runTestCases runs test transactions or queries, stopping early if an error occurs -func (s *IntegrationTestSuite) runTestCases(tcs ...TestCase) { - for _, t := range tcs { - if !s.abort { - s.abort = t.Run(s) - } +func (s *IntegrationTestSuite) runTestTransactions(txs ...testTransaction) { + for _, t := range txs { + t.Run(s) } } type testTransaction struct { - name string + msg string command *cobra.Command args []string expectedErr *errors.Error } type testQuery struct { - name string + msg string command *cobra.Command args []string expectErr bool @@ -75,7 +78,7 @@ type testQuery struct { expectedResponse proto.Message } -func (t testTransaction) Run(s *IntegrationTestSuite) (abort bool) { +func (t testTransaction) Run(s *IntegrationTestSuite) { clientCtx := s.network.Validators[0].ClientCtx txFlags := []string{ @@ -87,36 +90,21 @@ func (t testTransaction) Run(s *IntegrationTestSuite) (abort bool) { t.args = append(t.args, txFlags...) - s.Run(t.name, func() { - out, err := clitestutil.ExecTestCLICmd(clientCtx, t.command, t.args) - s.Require().NoError(err) - if err != nil { - abort = true - } - - resp := &sdk.TxResponse{} - err = clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp) - s.Require().NoError(err, out.String()) - if err != nil { - abort = true - } - - if t.expectedErr == nil { - s.Require().Equal(0, int(resp.Code), "events %v", resp.Events) - if int(resp.Code) != 0 { - abort = true - } - } else { - s.Require().Equal(int(t.expectedErr.ABCICode()), int(resp.Code)) - if int(resp.Code) != int(t.expectedErr.ABCICode()) { - abort = true - } - } - }) - return abort + out, err := clitestutil.ExecTestCLICmd(clientCtx, t.command, t.args) + s.Require().NoError(err, t.msg) + + resp := &sdk.TxResponse{} + err = clientCtx.Codec.UnmarshalJSON(out.Bytes(), resp) + s.Require().NoError(err, t.msg) + + if t.expectedErr == nil { + s.Require().Equal(0, int(resp.Code), t.msg) + } else { + s.Require().Equal(int(t.expectedErr.ABCICode()), int(resp.Code), t.msg) + } } -func (t testQuery) Run(s *IntegrationTestSuite) (abort bool) { +func (t testQuery) Run(s *IntegrationTestSuite) { clientCtx := s.network.Validators[0].ClientCtx queryFlags := []string{ @@ -125,31 +113,16 @@ func (t testQuery) Run(s *IntegrationTestSuite) (abort bool) { t.args = append(t.args, queryFlags...) - s.Run(t.name, func() { - out, err := clitestutil.ExecTestCLICmd(clientCtx, t.command, t.args) - - if t.expectErr { - s.Require().Error(err) - if err == nil { - abort = true - } - } else { - s.Require().NoError(err) - if err != nil { - abort = true - } - - err = clientCtx.Codec.UnmarshalJSON(out.Bytes(), t.responseType) - s.Require().NoError(err, out.String()) - if err != nil { - abort = true - } - - s.Require().Equal(t.expectedResponse, t.responseType) - if !s.Assert().Equal(t.expectedResponse, t.responseType) { - abort = true - } - } - }) - return abort + out, err := clitestutil.ExecTestCLICmd(clientCtx, t.command, t.args) + + if t.expectErr { + s.Require().Error(err, t.msg) + } else { + s.Require().NoError(err, t.msg) + + err = clientCtx.Codec.UnmarshalJSON(out.Bytes(), t.responseType) + s.Require().NoError(err, t.msg) + + s.Require().Equal(t.expectedResponse, t.responseType) + } } diff --git a/x/leverage/client/tests/tests.go b/x/leverage/client/tests/tests.go index f6985149c3..23e9eb6229 100644 --- a/x/leverage/client/tests/tests.go +++ b/x/leverage/client/tests/tests.go @@ -9,8 +9,8 @@ import ( ) func (s *IntegrationTestSuite) TestInvalidQueries() { - invalidQueries := []TestCase{ - testQuery{ + invalidQueries := []testQuery{ + { "query market summary - invalid denom", cli.GetCmdQueryMarketSummary(), []string{ @@ -20,7 +20,7 @@ func (s *IntegrationTestSuite) TestInvalidQueries() { nil, nil, }, - testQuery{ + { "query account balances - invalid address", cli.GetCmdQueryAccountBalances(), []string{ @@ -30,7 +30,7 @@ func (s *IntegrationTestSuite) TestInvalidQueries() { nil, nil, }, - testQuery{ + { "query account summary - invalid address", cli.GetCmdQueryAccountSummary(), []string{ @@ -43,7 +43,7 @@ func (s *IntegrationTestSuite) TestInvalidQueries() { } // These queries do not require any borrower setup because they contain invalid arguments - s.runTestCases(invalidQueries...) + s.runTestQueries(invalidQueries...) } func (s *IntegrationTestSuite) TestLeverageScenario() { @@ -51,8 +51,8 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { oraclePrice := sdk.MustNewDecFromStr("0.00003421") - initialQueries := []TestCase{ - testQuery{ + initialQueries := []testQuery{ + { "query params", cli.GetCmdQueryParams(), []string{}, @@ -62,7 +62,7 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { Params: types.DefaultParams(), }, }, - testQuery{ + { "query registered tokens", cli.GetCmdQueryRegisteredTokens(), []string{}, @@ -94,7 +94,7 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { }, }, }, - testQuery{ + { "query market summary - zero supply", cli.GetCmdQueryMarketSummary(), []string{ @@ -202,8 +202,8 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { nil, } - nonzeroQueries := []TestCase{ - testQuery{ + nonzeroQueries := []testQuery{ + { "query account balances", cli.GetCmdQueryAccountBalances(), []string{ @@ -223,7 +223,7 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { ), }, }, - testQuery{ + { "query account summary", cli.GetCmdQueryAccountSummary(), []string{ @@ -250,20 +250,20 @@ func (s *IntegrationTestSuite) TestLeverageScenario() { } // These queries do not require any borrower setup - s.runTestCases(initialQueries...) + s.runTestQueries(initialQueries...) // These transactions will set up nonzero leverage positions and allow nonzero query results - s.runTestCases( + s.runTestTransactions( supply, addCollateral, borrow, ) // These queries run while the supplying and borrowing is active to produce nonzero output - s.runTestCases(nonzeroQueries...) + s.runTestQueries(nonzeroQueries...) // These transactions run after nonzero queries are finished - s.runTestCases( + s.runTestTransactions( liquidate, repay, removeCollateral, diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index eb425219f0..fdaca4b6ee 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -194,7 +194,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { // Unregistered asset invalidCoins := sdk.NewCoins(sdk.NewInt64Coin("abcd", 1000)) _, err = s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, invalidCoins) - s.Require().EqualError(err, "abcd: invalid asset") + s.Require().ErrorIs(err, types.ErrNotUToken) // Create collateral uTokens (1k u/umee) umeeCollatDenom := types.ToUTokenDenom(umeeDenom) diff --git a/x/leverage/keeper/collateral.go b/x/leverage/keeper/collateral.go index 6ebcf231c0..50b6af3347 100644 --- a/x/leverage/keeper/collateral.go +++ b/x/leverage/keeper/collateral.go @@ -3,7 +3,6 @@ package keeper import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/umee-network/umee/v2/x/leverage/types" ) @@ -64,8 +63,8 @@ func (k Keeper) GetCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress // stored value is cleared. A negative amount or invalid coin causes an error. // This function does not move coins to or from the module account. func (k Keeper) setCollateralAmount(ctx sdk.Context, borrowerAddr sdk.AccAddress, collateral sdk.Coin) error { - if !collateral.IsValid() { - return sdkerrors.Wrap(types.ErrInvalidAsset, collateral.String()) + if err := collateral.Validate(); err != nil { + return err } if borrowerAddr.Empty() { diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index fd48279084..7171d85f72 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -60,7 +60,7 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { // force invalid denom err = s.tk.SetCollateralAmount(s.ctx, addr, sdk.Coin{Denom: "", Amount: sdk.ZeroInt()}) - s.Require().EqualError(err, "0: invalid asset") + s.Require().ErrorContains(err, "invalid denom") // set u/umee collateral amount s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 10))) diff --git a/x/leverage/keeper/exchange_rate.go b/x/leverage/keeper/exchange_rate.go index b52840c3c9..411403ce95 100644 --- a/x/leverage/keeper/exchange_rate.go +++ b/x/leverage/keeper/exchange_rate.go @@ -10,13 +10,13 @@ import ( // ExchangeToken converts an sdk.Coin containing a base asset to its value as a // uToken. func (k Keeper) ExchangeToken(ctx sdk.Context, token sdk.Coin) (sdk.Coin, error) { - if !token.IsValid() { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, token.String()) + if err := token.Validate(); err != nil { + return sdk.Coin{}, err } uTokenDenom := types.ToUTokenDenom(token.Denom) if uTokenDenom == "" { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, token.Denom) + return sdk.Coin{}, sdkerrors.Wrap(types.ErrUToken, token.Denom) } exchangeRate := k.DeriveExchangeRate(ctx, token.Denom) @@ -28,13 +28,13 @@ func (k Keeper) ExchangeToken(ctx sdk.Context, token sdk.Coin) (sdk.Coin, error) // ExchangeUToken converts an sdk.Coin containing a uToken to its value in a base // token. func (k Keeper) ExchangeUToken(ctx sdk.Context, uToken sdk.Coin) (sdk.Coin, error) { - if !uToken.IsValid() { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, uToken.String()) + if err := uToken.Validate(); err != nil { + return sdk.Coin{}, err } tokenDenom := types.ToTokenDenom(uToken.Denom) if tokenDenom == "" { - return sdk.Coin{}, sdkerrors.Wrap(types.ErrInvalidAsset, uToken.Denom) + return sdk.Coin{}, sdkerrors.Wrap(types.ErrNotUToken, uToken.Denom) } exchangeRate := k.DeriveExchangeRate(ctx, tokenDenom) @@ -46,8 +46,8 @@ func (k Keeper) ExchangeUToken(ctx sdk.Context, uToken sdk.Coin) (sdk.Coin, erro // ExchangeUTokens converts an sdk.Coins containing uTokens to their values in base // tokens. func (k Keeper) ExchangeUTokens(ctx sdk.Context, uTokens sdk.Coins) (sdk.Coins, error) { - if !uTokens.IsValid() { - return sdk.Coins{}, sdkerrors.Wrap(types.ErrInvalidAsset, uTokens.String()) + if err := uTokens.Validate(); err != nil { + return sdk.Coins{}, err } tokens := sdk.Coins{} diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 9e9502df07..c94a35b880 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -263,7 +263,7 @@ func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk. // determine amount of selected denom currently owed owed := k.GetBorrow(ctx, borrowerAddr, payment.Denom) if owed.IsZero() { - return sdk.Coin{}, types.ErrInvalidRepayment.Wrapf("No %s borrowed ", payment.Denom) + return sdk.Coin{}, types.ErrDenomNotBorrowed.Wrap(payment.Denom) } // prevent overpaying @@ -299,7 +299,7 @@ func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, co // Detect where sufficient collateral exists to disable collateral := k.GetBorrowerCollateral(ctx, borrowerAddr) if collateral.AmountOf(coin.Denom).LT(coin.Amount) { - return types.ErrInsufficientBalance + return types.ErrInsufficientCollateral } // Determine what borrow limit would be AFTER disabling this denom as collateral @@ -368,7 +368,7 @@ func (k Keeper) Liquidate( if tokenRepay.IsZero() { // Zero repay amount returned from liquidation computation means the target was eligible for liquidation // but the proposed reward and repayment would have zero effect. - return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, types.ErrLiquidationInvalid + return sdk.Coin{}, sdk.Coin{}, sdk.Coin{}, types.ErrLiquidationRepayZero } // repay some of the borrower's debt using the liquidator's balance diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 340972bc87..78f676dbfa 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -2,6 +2,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" umeeapp "github.com/umee-network/umee/v2/app" @@ -15,7 +16,7 @@ func (s *IntegrationTestSuite) TestSupply() { addr sdk.AccAddress coin sdk.Coin expectedUTokens sdk.Coin - expectErr bool + err error } app, ctx, require := s.app, s.ctx, s.Require() @@ -36,42 +37,42 @@ func (s *IntegrationTestSuite) TestSupply() { supplier, coin("abcd", 80_000000), sdk.Coin{}, - true, + types.ErrNotRegisteredToken, }, { "uToken", supplier, coin("u/"+umeeDenom, 80_000000), sdk.Coin{}, - true, + types.ErrUToken, }, { "insufficient balance", supplier, coin(umeeDenom, 120_000000), sdk.Coin{}, - true, + sdkerrors.ErrInsufficientFunds, }, { "valid supply", supplier, coin(umeeDenom, 80_000000), coin("u/"+umeeDenom, 80_000000), - false, + nil, }, { "high exchange rate", supplier, coin(atomDenom, 60_000000), coin("u/"+atomDenom, 40_000000), - false, + nil, }, } for _, tc := range tcs { - if tc.expectErr { + if tc.err != nil { _, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) - require.Error(err) + require.ErrorContains(err, tc.err.Error(), tc.msg) } else { uDenom := types.ToUTokenDenom(tc.coin.Denom) @@ -112,7 +113,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { expectFromBalance int64 expectFromCollateral int64 expectedTokens sdk.Coin - expectErr bool + err error } app, ctx, require := s.app, s.ctx, s.Require() @@ -131,24 +132,19 @@ func (s *IntegrationTestSuite) TestWithdraw() { s.borrow(borrower, coin(atomDenom, 10_000000)) s.tk.SetBorrow(ctx, borrower, coin(atomDenom, 40_000000)) + // create an additional UMEE supplier + other := s.newAccount(coin(umeeDenom, 100_000000)) + s.supply(other, coin(umeeDenom, 100_000000)) + tcs := []testCase{ { - "unregistered denom", + "unregistered base token", supplier, coin("abcd", 80_000000), 0, 0, sdk.Coin{}, - true, - }, - { - "unregistered uToken", - supplier, - coin("u/abcd", 80_000000), - 0, - 0, - sdk.Coin{}, - true, + types.ErrNotUToken, }, { "base token", @@ -157,7 +153,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { 0, 0, sdk.Coin{}, - true, + types.ErrNotUToken, }, { "insufficient uTokens", @@ -166,7 +162,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { 0, 0, sdk.Coin{}, - true, + types.ErrInsufficientBalance, }, { "withdraw from balance", @@ -175,7 +171,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { 10_000000, 0, coin(umeeDenom, 10_000000), - false, + nil, }, { "withdraw from collateral", @@ -184,7 +180,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { 15_000000, 75_000000, coin(umeeDenom, 90_000000), - false, + nil, }, { "high exchange rate", @@ -193,7 +189,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { 50_000000, 0, coin(atomDenom, 60_000000), - false, + nil, }, { "borrow limit", @@ -202,14 +198,14 @@ func (s *IntegrationTestSuite) TestWithdraw() { 0, 0, sdk.Coin{}, - true, + types.ErrUndercollaterized, }, } for _, tc := range tcs { - if tc.expectErr { + if tc.err != nil { _, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) - require.Error(err) + require.ErrorIs(err, tc.err, tc.msg) } else { tokenDenom := types.ToTokenDenom(tc.uToken.Denom) diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 1567cb921a..8414c11591 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -26,11 +26,6 @@ const ( atomDenom = fixtures.AtomDenom ) -var ( - initTokens = sdk.TokensFromConsensusPower(initialPower, sdk.DefaultPowerReduction) - initCoins = sdk.NewCoins(sdk.NewCoin(umeeapp.BondDenom, initTokens)) -) - type IntegrationTestSuite struct { suite.Suite @@ -133,6 +128,7 @@ func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { } } +/* // withdraw uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -142,6 +138,7 @@ func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin require.NoError(err) } } +*/ // collateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { @@ -153,6 +150,7 @@ func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk } } +/* // decollateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -162,6 +160,7 @@ func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...s require.NoError(err) } } +*/ // borrow tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { @@ -173,6 +172,7 @@ func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { } } +/* // repay tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -184,6 +184,7 @@ func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { require.Equal(repaid, coin) } } +*/ // setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, // may also supply them to receive uTokens, and may also enable those uTokens as collateral and borrow tokens in the same denom. diff --git a/x/leverage/keeper/token.go b/x/leverage/keeper/token.go index 411c6a6837..9909371f35 100644 --- a/x/leverage/keeper/token.go +++ b/x/leverage/keeper/token.go @@ -1,8 +1,6 @@ package keeper import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -20,7 +18,7 @@ func (k Keeper) SetTokenSettings(ctx sdk.Context, token types.Token) error { bz, err := k.cdc.Marshal(&token) if err != nil { - panic(fmt.Errorf("failed to encode token settings: %w", err)) + return err } k.hooks.AfterTokenRegistered(ctx, token) @@ -42,7 +40,7 @@ func (k Keeper) GetTokenSettings(ctx sdk.Context, denom string) (types.Token, er token := types.Token{} bz := store.Get(tokenKey) if len(bz) == 0 { - return token, sdkerrors.Wrap(types.ErrInvalidAsset, denom) + return token, sdkerrors.Wrap(types.ErrNotRegisteredToken, denom) } err := k.cdc.Unmarshal(bz, &token) diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index 0693e06e71..e4644120fc 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -9,6 +9,9 @@ import ( // validateAcceptedDenom validates an sdk.Coin and ensures it is a registered Token // with Blacklisted == false func (k Keeper) validateAcceptedDenom(ctx sdk.Context, denom string) error { + if types.HasUTokenPrefix(denom) { + return types.ErrUToken.Wrap(denom) + } token, err := k.GetTokenSettings(ctx, denom) if err != nil { return err @@ -44,11 +47,14 @@ func (k Keeper) validateAcceptedUToken(ctx sdk.Context, coin sdk.Coin) error { } // validateSupply validates an sdk.Coin and ensures its Denom is a Token with EnableMsgSupply -func (k Keeper) validateSupply(ctx sdk.Context, loan sdk.Coin) error { - if err := loan.Validate(); err != nil { +func (k Keeper) validateSupply(ctx sdk.Context, coin sdk.Coin) error { + if err := coin.Validate(); err != nil { return err } - token, err := k.GetTokenSettings(ctx, loan.Denom) + if types.HasUTokenPrefix(coin.Denom) { + return types.ErrUToken.Wrap(coin.Denom) + } + token, err := k.GetTokenSettings(ctx, coin.Denom) if err != nil { return err } @@ -60,6 +66,9 @@ func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { if err := borrow.Validate(); err != nil { return err } + if types.HasUTokenPrefix(borrow.Denom) { + return types.ErrUToken.Wrap(borrow.Denom) + } token, err := k.GetTokenSettings(ctx, borrow.Denom) if err != nil { return err diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 357fb61401..406ecba3c8 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -7,37 +7,44 @@ import ( ) var ( - ErrInvalidAsset = sdkerrors.Register(ModuleName, 1100, "invalid asset") - ErrInsufficientBalance = sdkerrors.Register(ModuleName, 1101, "insufficient balance") - ErrUndercollaterized = sdkerrors.Register(ModuleName, 1102, "borrow positions are undercollaterized") - ErrLendingPoolInsufficient = sdkerrors.Register(ModuleName, 1103, "lending pool insufficient") - ErrInvalidRepayment = sdkerrors.Register(ModuleName, 1104, "invalid repayment") - ErrInvalidAddress = sdkerrors.Register(ModuleName, 1105, "invalid address") - ErrNegativeTotalBorrowed = sdkerrors.Register(ModuleName, 1106, "total borrowed was negative") - ErrInvalidUtilization = sdkerrors.Register(ModuleName, 1107, "invalid token utilization") - ErrLiquidationIneligible = sdkerrors.Register(ModuleName, 1108, "borrower not eligible for liquidation") - ErrBadValue = sdkerrors.Register(ModuleName, 1109, "bad USD value") - ErrLiquidatorBalanceZero = sdkerrors.Register(ModuleName, 1110, "liquidator base asset balance is zero") - ErrNegativeTimeElapsed = sdkerrors.Register(ModuleName, 1111, "negative time elapsed since last interest time") - ErrInvalidOraclePrice = sdkerrors.Register(ModuleName, 1112, "invalid oracle price") - ErrNegativeAPY = sdkerrors.Register(ModuleName, 1113, "negative APY") - ErrInvalidExchangeRate = sdkerrors.Register(ModuleName, 1114, "exchange rate less than one") - ErrInconsistentTotalBorrow = sdkerrors.Register(ModuleName, 1115, "total adjusted borrow inconsistency") - ErrInvalidInteresrScalar = sdkerrors.Register(ModuleName, 1116, "interest scalar less than one") - ErrEmptyAddress = sdkerrors.Register(ModuleName, 1117, "empty address") - ErrLiquidationRewardRatio = sdkerrors.Register(ModuleName, 1118, "requested liquidation reward not met") - ErrSupplyNotAllowed = sdkerrors.Register(ModuleName, 1119, "supplying of asset disabled") - ErrBorrowNotAllowed = sdkerrors.Register(ModuleName, 1120, "borrowing of asset disabled") - ErrBlacklisted = sdkerrors.Register(ModuleName, 1121, "base denom blacklisted") - ErrCollateralWeightZero = sdkerrors.Register(ModuleName, 1122, "token collateral weight is zero") - ErrLiquidationInvalid = sdkerrors.Register(ModuleName, 1123, "liquidation invalid") - ErrMaxSupplyUtilization = sdkerrors.Register(ModuleName, 1124, "market would exceed MaxSupplyUtilization") - ErrMinCollateralLiquidity = sdkerrors.Register(ModuleName, 1125, "market would fall below MinCollateralLiquidity") - ErrMaxCollateralShare = sdkerrors.Register( - ModuleName, - 1126, - "market total collateral would exceed MaxCollateralShare", - ) - ErrNotUToken = sdkerrors.Register(ModuleName, 1127, "denom should be a uToken") - ErrUToken = sdkerrors.Register(ModuleName, 1128, "denom should not be a uToken") + // 1XX = General Validation + ErrEmptyAddress = sdkerrors.Register(ModuleName, 100, "empty address") + ErrNilAsset = sdkerrors.Register(ModuleName, 101, "nil asset") + + // 2XX = Token Registry + ErrNotRegisteredToken = sdkerrors.Register(ModuleName, 200, "not a registered Token") + ErrNotRegisteredUToken = sdkerrors.Register(ModuleName, 201, "not a registered uToken") + ErrUToken = sdkerrors.Register(ModuleName, 202, "denom should not be a uToken") + ErrNotUToken = sdkerrors.Register(ModuleName, 203, "denom should be a uToken") + ErrSupplyNotAllowed = sdkerrors.Register(ModuleName, 204, "supplying of asset disabled") + ErrBorrowNotAllowed = sdkerrors.Register(ModuleName, 205, "borrowing of asset disabled") + ErrBlacklisted = sdkerrors.Register(ModuleName, 206, "base denom blacklisted") + ErrCollateralWeightZero = sdkerrors.Register(ModuleName, 207, "token collateral weight is zero") + + // 3XX = User Positions + ErrInsufficientBalance = sdkerrors.Register(ModuleName, 300, "insufficient balance") + ErrInsufficientCollateral = sdkerrors.Register(ModuleName, 301, "insufficient collateral") + ErrDenomNotBorrowed = sdkerrors.Register(ModuleName, 302, "denom not borrowed") + ErrLiquidationRepayZero = sdkerrors.Register(ModuleName, 303, "liquidation would repay zero tokens") + + // 4XX = Price Sensitive + ErrBadValue = sdkerrors.Register(ModuleName, 400, "bad USD value") + ErrInvalidOraclePrice = sdkerrors.Register(ModuleName, 401, "invalid oracle price") + ErrUndercollaterized = sdkerrors.Register(ModuleName, 402, "borrow positions are undercollaterized") + ErrLiquidationIneligible = sdkerrors.Register(ModuleName, 403, "borrower not eligible for liquidation") + + // 5XX = Market Conditions + ErrLendingPoolInsufficient = sdkerrors.Register(ModuleName, 500, "lending pool insufficient") + ErrMaxSupplyUtilization = sdkerrors.Register(ModuleName, 501, "market would exceed MaxSupplyUtilization") + ErrMinCollateralLiquidity = sdkerrors.Register(ModuleName, 502, "market would fall below MinCollateralLiquidity") + ErrMaxCollateralShare = sdkerrors.Register(ModuleName, 503, "market would exceed MaxCollateralShare") + + // 6XX = Internal Failsafes + ErrInvalidUtilization = sdkerrors.Register(ModuleName, 600, "invalid token utilization") + ErrNegativeTotalBorrowed = sdkerrors.Register(ModuleName, 601, "total borrowed was negative") + ErrNegativeAPY = sdkerrors.Register(ModuleName, 602, "negative APY") + ErrNegativeTimeElapsed = sdkerrors.Register(ModuleName, 603, "negative time elapsed since last interest time") + ErrInvalidExchangeRate = sdkerrors.Register(ModuleName, 604, "exchange rate less than one") + ErrInconsistentTotalBorrow = sdkerrors.Register(ModuleName, 605, "total adjusted borrow inconsistency") + ErrInvalidInteresrScalar = sdkerrors.Register(ModuleName, 606, "interest scalar less than one") ) diff --git a/x/leverage/types/token.go b/x/leverage/types/token.go index 6ac3ccc64a..e4ebb97c1f 100644 --- a/x/leverage/types/token.go +++ b/x/leverage/types/token.go @@ -50,7 +50,7 @@ func (t Token) Validate() error { } if HasUTokenPrefix(t.BaseDenom) { // prevent base asset denoms that start with "u/" - return sdkerrors.Wrap(ErrInvalidAsset, t.BaseDenom) + return ErrUToken.Wrap(t.BaseDenom) } if err := sdk.ValidateDenom(t.SymbolDenom); err != nil { @@ -58,7 +58,7 @@ func (t Token) Validate() error { } if HasUTokenPrefix(t.SymbolDenom) { // prevent symbol denoms that start with "u/" - return sdkerrors.Wrap(ErrInvalidAsset, t.SymbolDenom) + return ErrUToken.Wrap(t.SymbolDenom) } // Reserve factor and collateral weight range between 0 and 1, inclusive. diff --git a/x/leverage/types/tx.go b/x/leverage/types/tx.go index d911a37162..57449cffbf 100644 --- a/x/leverage/types/tx.go +++ b/x/leverage/types/tx.go @@ -2,7 +2,6 @@ package types import ( sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/umee-network/umee/v2/util/checkers" ) @@ -66,7 +65,7 @@ func (msg MsgCollateralize) Route() string { return ModuleName } func (msg MsgCollateralize) Type() string { return EventTypeCollateralize } func (msg *MsgCollateralize) ValidateBasic() error { - return validateSenderAndAsset(msg.Borrower, nil) + return validateSenderAndAsset(msg.Borrower, &msg.Asset) } func (msg *MsgCollateralize) GetSigners() []sdk.AccAddress { @@ -90,7 +89,7 @@ func (msg MsgDecollateralize) Route() string { return ModuleName } func (msg MsgDecollateralize) Type() string { return EventTypeDecollateralize } func (msg *MsgDecollateralize) ValidateBasic() error { - return validateSenderAndAsset(msg.Borrower, nil) + return validateSenderAndAsset(msg.Borrower, &msg.Asset) } func (msg *MsgDecollateralize) GetSigners() []sdk.AccAddress { @@ -189,8 +188,11 @@ func validateSenderAndAsset(sender string, asset *sdk.Coin) error { if err != nil { return err } - if asset != nil && !asset.IsValid() { - return sdkerrors.Wrap(ErrInvalidAsset, asset.String()) + if asset == nil { + return ErrNilAsset + } + if err := asset.Validate(); err != nil { + return err } return nil } From 01a0d9dde75ec4dbe30dfe2e5c400b9ef206a6ee Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:28:39 -0700 Subject: [PATCH 05/36] TestCollateralize --- x/leverage/keeper/keeper_test.go | 125 +++++++++++++++++++++++-------- x/leverage/keeper/suite_test.go | 14 ++-- x/leverage/types/errors.go | 13 ++-- 3 files changed, 105 insertions(+), 47 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 78f676dbfa..66ebf9fb5d 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -74,13 +74,14 @@ func (s *IntegrationTestSuite) TestSupply() { _, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) require.ErrorContains(err, tc.err.Error(), tc.msg) } else { + denom := tc.coin.Denom uDenom := types.ToUTokenDenom(tc.coin.Denom) // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tc.coin.Denom) + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) - iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) // verify the outputs of supply function uToken, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) @@ -88,10 +89,10 @@ func (s *IntegrationTestSuite) TestSupply() { require.Equal(tc.expectedUTokens, uToken, tc.msg) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tc.coin.Denom) + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) - fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) // verify token balance decreased by the expected amount require.Equal(iBalance.Sub(tc.coin), fBalance, tc.msg, "token balance") @@ -99,7 +100,7 @@ func (s *IntegrationTestSuite) TestSupply() { require.Equal(iUTokens.Add(tc.expectedUTokens), fUTokens, tc.msg, "uToken balance") // verify uToken supply increased by the expected amount require.Equal(iUTokenSupply.Add(tc.expectedUTokens), fUTokenSupply, tc.msg, "uToken supply") - // verify uToken exchange rate is unchanged (sensitive to rounding) + // verify uToken exchange rate is unchanged require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") } } @@ -207,14 +208,15 @@ func (s *IntegrationTestSuite) TestWithdraw() { _, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) require.ErrorIs(err, tc.err, tc.msg) } else { - tokenDenom := types.ToTokenDenom(tc.uToken.Denom) + denom := types.ToTokenDenom(tc.uToken.Denom) + uDenom := tc.uToken.Denom // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tokenDenom) - iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, tc.uToken.Denom) - iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, tc.uToken.Denom) - iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, tc.uToken.Denom) - iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tokenDenom) + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) + iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) // verify the outputs of withdraw function token, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) @@ -223,11 +225,11 @@ func (s *IntegrationTestSuite) TestWithdraw() { require.Equal(tc.expectedTokens, token, tc.msg) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, tokenDenom) - fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, tc.uToken.Denom) - fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, tc.uToken.Denom) - fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, tc.uToken.Denom) - fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tokenDenom) + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) + fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) // verify token balance increased by the expected amount require.Equal(iBalance.Add(tc.expectedTokens), fBalance, tc.msg, "token balance") @@ -240,30 +242,87 @@ func (s *IntegrationTestSuite) TestWithdraw() { // verify uToken supply decreased by the expected amount require.Equal(iUTokenSupply.Sub(tc.uToken).Amount.Int64(), fUTokenSupply.Amount.Int64(), tc.msg, "uToken supply") - // verify uToken exchange rate is unchanged (sensitive to rounding) + // verify uToken exchange rate is unchanged require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") } } } -func (s *IntegrationTestSuite) TestGetToken() { - uabc := newToken("uabc", "ABC") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, uabc)) +func (s *IntegrationTestSuite) TestCollateralize() { + type testCase struct { + msg string + addr sdk.AccAddress + uToken sdk.Coin + err error + } - t, err := s.app.LeverageKeeper.GetTokenSettings(s.ctx, "uabc") - s.Require().NoError(err) - s.Require().Equal(t.ReserveFactor, sdk.MustNewDecFromStr("0.2")) - s.Require().Equal(t.CollateralWeight, sdk.MustNewDecFromStr("0.25")) - s.Require().Equal(t.LiquidationThreshold, sdk.MustNewDecFromStr("0.25")) - s.Require().Equal(t.BaseBorrowRate, sdk.MustNewDecFromStr("0.02")) - s.Require().Equal(t.KinkBorrowRate, sdk.MustNewDecFromStr("0.22")) - s.Require().Equal(t.MaxBorrowRate, sdk.MustNewDecFromStr("1.52")) - s.Require().Equal(t.KinkUtilization, sdk.MustNewDecFromStr("0.8")) - s.Require().Equal(t.LiquidationIncentive, sdk.MustNewDecFromStr("0.1")) + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a supplier with 200 UMEE, then supply 100 UMEE + supplier := s.newAccount(coin(umeeDenom, 200_000000)) + s.supply(supplier, coin(umeeDenom, 100_000000)) + + tcs := []testCase{ + { + "base token", + supplier, + coin(umeeDenom, 80_000000), + types.ErrNotUToken, + }, + { + "unregistered uToken", + supplier, + coin("u/abcd", 80_000000), + types.ErrNotRegisteredToken, + }, + { + "valid collateralize", + supplier, + coin("u/"+umeeDenom, 80_000000), + nil, + }, + { + "insufficient balance", + supplier, + coin("u/"+umeeDenom, 40_000000), + sdkerrors.ErrInsufficientFunds, + }, + } + + for _, tc := range tcs { + if tc.err != nil { + err := app.LeverageKeeper.Collateralize(ctx, tc.addr, tc.uToken) + require.ErrorContains(err, tc.err.Error(), tc.msg) + } else { + denom := types.ToTokenDenom(tc.uToken.Denom) + uDenom := tc.uToken.Denom + + // initial state + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) - s.Require().NoError(t.AssertBorrowEnabled()) - s.Require().NoError(t.AssertSupplyEnabled()) - s.Require().NoError(t.AssertNotBlacklisted()) + // verify the output of collateralize function + err := app.LeverageKeeper.Collateralize(ctx, tc.addr, tc.uToken) + require.NoError(err) + + // final state + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + + // verify token balance is unchanged + require.Equal(iBalance, fBalance, tc.msg, "token balance") + // verify uToken balance decreased by the expected amount + require.Equal(iUTokens.Sub(tc.uToken), fUTokens, tc.msg, "uToken balance") + // 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") + } + } } func (s *IntegrationTestSuite) TestBorrow_Invalid() { diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 8414c11591..98dca08743 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -124,7 +124,7 @@ func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { for _, coin := range coins { _, err := app.LeverageKeeper.Supply(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "supply") } } @@ -135,7 +135,7 @@ func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin for _, coin := range uTokens { _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "withdraw") } } */ @@ -146,7 +146,7 @@ func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk for _, coin := range uTokens { err := app.LeverageKeeper.Collateralize(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "collateralize") } } @@ -157,7 +157,7 @@ func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...s for _, coin := range uTokens { err := app.LeverageKeeper.Decollateralize(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "decollateralize") } } */ @@ -168,7 +168,7 @@ func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { for _, coin := range coins { err := app.LeverageKeeper.Borrow(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "borrow") } } @@ -179,9 +179,9 @@ func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { for _, coin := range coins { repaid, err := app.LeverageKeeper.Repay(ctx, addr, coin) - require.NoError(err) + require.NoError(err, "repay") // ensure intended repayment amount was not reduced, as doing so would create a misleading test - require.Equal(repaid, coin) + require.Equal(repaid, coin, "repay") } } */ diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 406ecba3c8..e325f11c59 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -13,13 +13,12 @@ var ( // 2XX = Token Registry ErrNotRegisteredToken = sdkerrors.Register(ModuleName, 200, "not a registered Token") - ErrNotRegisteredUToken = sdkerrors.Register(ModuleName, 201, "not a registered uToken") - ErrUToken = sdkerrors.Register(ModuleName, 202, "denom should not be a uToken") - ErrNotUToken = sdkerrors.Register(ModuleName, 203, "denom should be a uToken") - ErrSupplyNotAllowed = sdkerrors.Register(ModuleName, 204, "supplying of asset disabled") - ErrBorrowNotAllowed = sdkerrors.Register(ModuleName, 205, "borrowing of asset disabled") - ErrBlacklisted = sdkerrors.Register(ModuleName, 206, "base denom blacklisted") - ErrCollateralWeightZero = sdkerrors.Register(ModuleName, 207, "token collateral weight is zero") + ErrUToken = sdkerrors.Register(ModuleName, 201, "denom should not be a uToken") + ErrNotUToken = sdkerrors.Register(ModuleName, 202, "denom should be a uToken") + ErrSupplyNotAllowed = sdkerrors.Register(ModuleName, 203, "supplying of Token disabled") + ErrBorrowNotAllowed = sdkerrors.Register(ModuleName, 204, "borrowing of Token disabled") + ErrBlacklisted = sdkerrors.Register(ModuleName, 205, "blacklisted Token") + ErrCollateralWeightZero = sdkerrors.Register(ModuleName, 206, "collateral weight of Token is zero") // 3XX = User Positions ErrInsufficientBalance = sdkerrors.Register(ModuleName, 300, "insufficient balance") From 55e024296efd93c32b6149f3d77e40d1855173d2 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:29:22 -0700 Subject: [PATCH 06/36] ++ --- x/leverage/keeper/token_test.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 x/leverage/keeper/token_test.go diff --git a/x/leverage/keeper/token_test.go b/x/leverage/keeper/token_test.go new file mode 100644 index 0000000000..49b56c8718 --- /dev/null +++ b/x/leverage/keeper/token_test.go @@ -0,0 +1,25 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (s *IntegrationTestSuite) TestGetToken() { + uabc := newToken("uabc", "ABC") + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, uabc)) + + t, err := s.app.LeverageKeeper.GetTokenSettings(s.ctx, "uabc") + s.Require().NoError(err) + s.Require().Equal(t.ReserveFactor, sdk.MustNewDecFromStr("0.2")) + s.Require().Equal(t.CollateralWeight, sdk.MustNewDecFromStr("0.25")) + s.Require().Equal(t.LiquidationThreshold, sdk.MustNewDecFromStr("0.25")) + s.Require().Equal(t.BaseBorrowRate, sdk.MustNewDecFromStr("0.02")) + s.Require().Equal(t.KinkBorrowRate, sdk.MustNewDecFromStr("0.22")) + s.Require().Equal(t.MaxBorrowRate, sdk.MustNewDecFromStr("1.52")) + s.Require().Equal(t.KinkUtilization, sdk.MustNewDecFromStr("0.8")) + s.Require().Equal(t.LiquidationIncentive, sdk.MustNewDecFromStr("0.1")) + + s.Require().NoError(t.AssertBorrowEnabled()) + s.Require().NoError(t.AssertSupplyEnabled()) + s.Require().NoError(t.AssertNotBlacklisted()) +} From 3ac25d8d5ea030634c119ddf7c08bd89e5bb9f19 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:42:15 -0700 Subject: [PATCH 07/36] TestDe+Collateralize --- x/leverage/keeper/keeper.go | 3 ++ x/leverage/keeper/keeper_test.go | 88 +++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index c94a35b880..680f3bea06 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -295,6 +295,9 @@ func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, co if err := coin.Validate(); err != nil { return err } + if !types.HasUTokenPrefix(coin.Denom) { + return types.ErrNotUToken.Wrap(coin.Denom) + } // Detect where sufficient collateral exists to disable collateral := k.GetBorrowerCollateral(ctx, borrowerAddr) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 66ebf9fb5d..7e90d532bf 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -72,7 +72,7 @@ func (s *IntegrationTestSuite) TestSupply() { for _, tc := range tcs { if tc.err != nil { _, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) - require.ErrorContains(err, tc.err.Error(), tc.msg) + require.ErrorIs(err, tc.err, tc.msg) } else { denom := tc.coin.Denom uDenom := types.ToUTokenDenom(tc.coin.Denom) @@ -80,6 +80,7 @@ func (s *IntegrationTestSuite) TestSupply() { // initial state iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) @@ -91,6 +92,7 @@ func (s *IntegrationTestSuite) TestSupply() { // final state fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) @@ -98,6 +100,8 @@ func (s *IntegrationTestSuite) TestSupply() { require.Equal(iBalance.Sub(tc.coin), fBalance, tc.msg, "token balance") // verify uToken balance increased by the expected amount require.Equal(iUTokens.Add(tc.expectedUTokens), fUTokens, tc.msg, "uToken balance") + // verify uToken collateral unchanged + require.Equal(iCollateral.Amount.Int64(), fCollateral.Amount.Int64(), tc.msg, "uToken collateral") // verify uToken supply increased by the expected amount require.Equal(iUTokenSupply.Add(tc.expectedUTokens), fUTokenSupply, tc.msg, "uToken supply") // verify uToken exchange rate is unchanged @@ -292,7 +296,7 @@ func (s *IntegrationTestSuite) TestCollateralize() { for _, tc := range tcs { if tc.err != nil { err := app.LeverageKeeper.Collateralize(ctx, tc.addr, tc.uToken) - require.ErrorContains(err, tc.err.Error(), tc.msg) + require.ErrorIs(err, tc.err, tc.msg) } else { denom := types.ToTokenDenom(tc.uToken.Denom) uDenom := tc.uToken.Denom @@ -300,6 +304,7 @@ func (s *IntegrationTestSuite) TestCollateralize() { // initial state iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) @@ -310,6 +315,7 @@ func (s *IntegrationTestSuite) TestCollateralize() { // final state fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) @@ -317,6 +323,84 @@ func (s *IntegrationTestSuite) TestCollateralize() { require.Equal(iBalance, fBalance, tc.msg, "token balance") // verify uToken balance decreased by the expected amount require.Equal(iUTokens.Sub(tc.uToken), fUTokens, tc.msg, "uToken balance") + // verify uToken collateral increased by the expected amount + require.Equal(iCollateral.Add(tc.uToken), fCollateral, tc.msg, "uToken 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") + } + } +} + +func (s *IntegrationTestSuite) TestDecollateralize() { + type testCase struct { + msg string + addr sdk.AccAddress + uToken sdk.Coin + err error + } + + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a supplier with 200 UMEE, then supply and collateralize 100 UMEE + supplier := s.newAccount(coin(umeeDenom, 200_000000)) + s.supply(supplier, coin(umeeDenom, 100_000000)) + s.collateralize(supplier, coin("u/"+umeeDenom, 100_000000)) + + tcs := []testCase{ + { + "base token", + supplier, + coin(umeeDenom, 80_000000), + types.ErrNotUToken, + }, + { + "valid decollateralize", + supplier, + coin("u/"+umeeDenom, 80_000000), + nil, + }, + { + "insufficient collateral", + supplier, + coin("u/"+umeeDenom, 40_000000), + types.ErrInsufficientCollateral, + }, + } + + for _, tc := range tcs { + if tc.err != nil { + err := app.LeverageKeeper.Decollateralize(ctx, tc.addr, tc.uToken) + require.ErrorIs(err, tc.err, tc.msg) + } else { + denom := types.ToTokenDenom(tc.uToken.Denom) + uDenom := tc.uToken.Denom + + // initial state + iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) + iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + + // verify the output of decollateralize function + err := app.LeverageKeeper.Decollateralize(ctx, tc.addr, tc.uToken) + require.NoError(err) + + // final state + fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) + fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) + fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) + fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + + // verify token balance is unchanged + require.Equal(iBalance, fBalance, tc.msg, "token balance") + // verify uToken balance increased by the expected amount + require.Equal(iUTokens.Add(tc.uToken), fUTokens, tc.msg, "uToken balance") + // verify uToken collateral decreased by the expected amount + require.Equal(iCollateral.Sub(tc.uToken), fCollateral, tc.msg, "uToken collateral") // verify uToken supply is unchanged require.Equal(iUTokenSupply, fUTokenSupply, tc.msg, "uToken supply") // verify uToken exchange rate is unchanged From fb2465d2fc5c37d766af345b3b5bc805baf6db9c Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 19:54:53 -0700 Subject: [PATCH 08/36] case++ --- x/leverage/keeper/keeper_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 7e90d532bf..ee9d0c7874 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -348,6 +348,12 @@ func (s *IntegrationTestSuite) TestDecollateralize() { s.supply(supplier, coin(umeeDenom, 100_000000)) s.collateralize(supplier, coin("u/"+umeeDenom, 100_000000)) + // create a borrower which supplies, collateralizes, then borrows ATOM + borrower := s.newAccount(coin(atomDenom, 100_000000)) + s.supply(borrower, coin(atomDenom, 100_000000)) + s.collateralize(borrower, coin("u/"+atomDenom, 100_000000)) + s.borrow(borrower, coin(atomDenom, 10_000000)) + tcs := []testCase{ { "base token", @@ -367,6 +373,12 @@ func (s *IntegrationTestSuite) TestDecollateralize() { coin("u/"+umeeDenom, 40_000000), types.ErrInsufficientCollateral, }, + { + "borrow limit", + borrower, + coin("u/"+atomDenom, 100_000000), + types.ErrUndercollaterized, + }, } for _, tc := range tcs { From 196a2dd6471368cddf86e2e4d26ae9aa7a6c6f12 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:01:23 -0700 Subject: [PATCH 09/36] borrow test + more cases --- x/leverage/keeper/keeper_test.go | 375 ++++++++++++++++++------------- x/leverage/keeper/suite_test.go | 13 +- 2 files changed, 225 insertions(+), 163 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index ee9d0c7874..67fb18a958 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -46,6 +46,13 @@ func (s *IntegrationTestSuite) TestSupply() { sdk.Coin{}, types.ErrUToken, }, + { + "no balance", + borrower, + coin(umeeDenom, 20_000000), + sdk.Coin{}, + sdkerrors.ErrInsufficientFunds, + }, { "insufficient balance", supplier, @@ -60,6 +67,13 @@ func (s *IntegrationTestSuite) TestSupply() { coin("u/"+umeeDenom, 80_000000), nil, }, + { + "additional supply", + supplier, + coin(umeeDenom, 20_000000), + coin("u/"+umeeDenom, 20_000000), + nil, + }, { "high exchange rate", supplier, @@ -75,14 +89,13 @@ func (s *IntegrationTestSuite) TestSupply() { require.ErrorIs(err, tc.err, tc.msg) } else { denom := tc.coin.Denom - uDenom := types.ToUTokenDenom(tc.coin.Denom) // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + iCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + iUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) // verify the outputs of supply function uToken, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) @@ -90,22 +103,22 @@ func (s *IntegrationTestSuite) TestSupply() { require.Equal(tc.expectedUTokens, uToken, tc.msg) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + fCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + fUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + fBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) - // verify token balance decreased by the expected amount - require.Equal(iBalance.Sub(tc.coin), fBalance, tc.msg, "token balance") - // verify uToken balance increased by the expected amount - require.Equal(iUTokens.Add(tc.expectedUTokens), fUTokens, tc.msg, "uToken balance") + // verify token balance decreased and uToken balance increased by the expected amounts + require.Equal(iBalance.Sub(tc.coin).Add(tc.expectedUTokens), fBalance, tc.msg, "token balance") // verify uToken collateral unchanged - require.Equal(iCollateral.Amount.Int64(), fCollateral.Amount.Int64(), tc.msg, "uToken collateral") + require.Equal(iCollateral, fCollateral, tc.msg, "uToken collateral") // verify uToken supply increased by the expected amount require.Equal(iUTokenSupply.Add(tc.expectedUTokens), fUTokenSupply, tc.msg, "uToken supply") // verify uToken exchange rate is unchanged require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") + // verify borrowed coins are unchanged + require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") } } } @@ -115,8 +128,8 @@ func (s *IntegrationTestSuite) TestWithdraw() { msg string addr sdk.AccAddress uToken sdk.Coin - expectFromBalance int64 - expectFromCollateral int64 + expectFromBalance sdk.Coins + expectFromCollateral sdk.Coins expectedTokens sdk.Coin err error } @@ -146,8 +159,8 @@ func (s *IntegrationTestSuite) TestWithdraw() { "unregistered base token", supplier, coin("abcd", 80_000000), - 0, - 0, + nil, + nil, sdk.Coin{}, types.ErrNotUToken, }, @@ -155,8 +168,8 @@ func (s *IntegrationTestSuite) TestWithdraw() { "base token", supplier, coin(umeeDenom, 80_000000), - 0, - 0, + nil, + nil, sdk.Coin{}, types.ErrNotUToken, }, @@ -164,8 +177,8 @@ func (s *IntegrationTestSuite) TestWithdraw() { "insufficient uTokens", supplier, coin("u/"+umeeDenom, 120_000000), - 0, - 0, + nil, + nil, sdk.Coin{}, types.ErrInsufficientBalance, }, @@ -173,26 +186,35 @@ func (s *IntegrationTestSuite) TestWithdraw() { "withdraw from balance", supplier, coin("u/"+umeeDenom, 10_000000), - 10_000000, - 0, + sdk.NewCoins(coin("u/"+umeeDenom, 10_000000)), + nil, coin(umeeDenom, 10_000000), nil, }, { - "withdraw from collateral", + "some from collateral", supplier, - coin("u/"+umeeDenom, 90_000000), - 15_000000, - 75_000000, - coin(umeeDenom, 90_000000), + coin("u/"+umeeDenom, 80_000000), + sdk.NewCoins(coin("u/"+umeeDenom, 15_000000)), + sdk.NewCoins(coin("u/"+umeeDenom, 65_000000)), + coin(umeeDenom, 80_000000), + nil, + }, + { + "only from collateral", + supplier, + coin("u/"+umeeDenom, 10_000000), + nil, + sdk.NewCoins(coin("u/"+umeeDenom, 10_000000)), + coin(umeeDenom, 10_000000), nil, }, { "high exchange rate", supplier, coin("u/"+atomDenom, 50_000000), - 50_000000, - 0, + sdk.NewCoins(coin("u/"+atomDenom, 50_000000)), + nil, coin(atomDenom, 60_000000), nil, }, @@ -200,8 +222,8 @@ func (s *IntegrationTestSuite) TestWithdraw() { "borrow limit", borrower, coin("u/"+atomDenom, 50_000000), - 0, - 0, + nil, + nil, sdk.Coin{}, types.ErrUndercollaterized, }, @@ -213,14 +235,13 @@ func (s *IntegrationTestSuite) TestWithdraw() { require.ErrorIs(err, tc.err, tc.msg) } else { denom := types.ToTokenDenom(tc.uToken.Denom) - uDenom := tc.uToken.Denom // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + iCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + iUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) // verify the outputs of withdraw function token, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) @@ -229,25 +250,23 @@ func (s *IntegrationTestSuite) TestWithdraw() { require.Equal(tc.expectedTokens, token, tc.msg) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + fCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + fUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + fBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) // verify token balance increased by the expected amount - require.Equal(iBalance.Add(tc.expectedTokens), fBalance, tc.msg, "token balance") - // verify uToken balance decreased by the expected amount - require.Equal(iUTokens.Amount.Int64()-tc.expectFromBalance, - fUTokens.Amount.Int64(), tc.msg, "uTokens from balance") + require.Equal(iBalance.Add(tc.expectedTokens).Sub(tc.expectFromBalance...), + fBalance, tc.msg, "token balance") // verify uToken collateral decreased by the expected amount - require.Equal(iCollateral.Amount.Int64()-tc.expectFromCollateral, - fCollateral.Amount.Int64(), tc.msg, "uToken collateral") + s.requireEqualCoins(iCollateral.Sub(tc.expectFromCollateral...), fCollateral, tc.msg, "uToken collateral") // verify uToken supply decreased by the expected amount - require.Equal(iUTokenSupply.Sub(tc.uToken).Amount.Int64(), - fUTokenSupply.Amount.Int64(), tc.msg, "uToken supply") + require.Equal(iUTokenSupply.Sub(tc.uToken), fUTokenSupply, tc.msg, "uToken supply") // verify uToken exchange rate is unchanged require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") + // verify borrowed coins are unchanged + require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") } } } @@ -279,12 +298,24 @@ func (s *IntegrationTestSuite) TestCollateralize() { coin("u/abcd", 80_000000), types.ErrNotRegisteredToken, }, + { + "wrong balance", + supplier, + coin("u/"+atomDenom, 10_000000), + sdkerrors.ErrInsufficientFunds, + }, { "valid collateralize", supplier, coin("u/"+umeeDenom, 80_000000), nil, }, + { + "additional collateralize", + supplier, + coin("u/"+umeeDenom, 10_000000), + nil, + }, { "insufficient balance", supplier, @@ -299,36 +330,35 @@ func (s *IntegrationTestSuite) TestCollateralize() { require.ErrorIs(err, tc.err, tc.msg) } else { denom := types.ToTokenDenom(tc.uToken.Denom) - uDenom := tc.uToken.Denom // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + iCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + iUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) // verify the output of collateralize function err := app.LeverageKeeper.Collateralize(ctx, tc.addr, tc.uToken) require.NoError(err) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + fCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + fUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + fBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) - // verify token balance is unchanged - require.Equal(iBalance, fBalance, tc.msg, "token balance") // verify uToken balance decreased by the expected amount - require.Equal(iUTokens.Sub(tc.uToken), fUTokens, tc.msg, "uToken balance") + require.Equal(iBalance.Sub(tc.uToken), fBalance, tc.msg, "uToken balance") // verify uToken collateral increased by the expected amount require.Equal(iCollateral.Add(tc.uToken), fCollateral, tc.msg, "uToken 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 are unchanged + require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") } } } @@ -361,12 +391,24 @@ func (s *IntegrationTestSuite) TestDecollateralize() { coin(umeeDenom, 80_000000), types.ErrNotUToken, }, + { + "no collateral", + supplier, + coin("u/"+atomDenom, 40_000000), + types.ErrInsufficientCollateral, + }, { "valid decollateralize", supplier, coin("u/"+umeeDenom, 80_000000), nil, }, + { + "additional decollateralize", + supplier, + coin("u/"+umeeDenom, 10_000000), + nil, + }, { "insufficient collateral", supplier, @@ -387,73 +429,144 @@ func (s *IntegrationTestSuite) TestDecollateralize() { require.ErrorIs(err, tc.err, tc.msg) } else { denom := types.ToTokenDenom(tc.uToken.Denom) - uDenom := tc.uToken.Denom // initial state - iBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - iUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - iCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - iUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + iBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + iCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + iUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) // verify the output of decollateralize function err := app.LeverageKeeper.Decollateralize(ctx, tc.addr, tc.uToken) require.NoError(err) // final state - fBalance := app.BankKeeper.GetBalance(ctx, tc.addr, denom) - fUTokens := app.BankKeeper.GetBalance(ctx, tc.addr, uDenom) - fCollateral := app.LeverageKeeper.GetCollateralAmount(ctx, tc.addr, uDenom) - fUTokenSupply := app.LeverageKeeper.GetUTokenSupply(ctx, uDenom) + fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) + fCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.addr) + fUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) fExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, denom) + fBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) - // verify token balance is unchanged - require.Equal(iBalance, fBalance, tc.msg, "token balance") // verify uToken balance increased by the expected amount - require.Equal(iUTokens.Add(tc.uToken), fUTokens, tc.msg, "uToken balance") + require.Equal(iBalance.Add(tc.uToken), fBalance, tc.msg, "uToken balance") // verify uToken collateral decreased by the expected amount require.Equal(iCollateral.Sub(tc.uToken), fCollateral, tc.msg, "uToken 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 are unchanged + require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") } } } -func (s *IntegrationTestSuite) TestBorrow_Invalid() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral - - // user attempts to borrow 200 u/umee, fails because uTokens cannot be borrowed - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 200000000)) - s.Require().Error(err) +func (s *IntegrationTestSuite) TestBorrow() { + type testCase struct { + msg string + addr sdk.AccAddress + coin sdk.Coin + err error + } - // user attempts to borrow 200 abcd, fails because "abcd" is not a valid denom - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin("uabcd", 200000000)) - s.Require().Error(err) -} + app, ctx, require := s.app, s.ctx, s.Require() -func (s *IntegrationTestSuite) TestBorrow_InsufficientCollateral() { - _, bumAddr := s.initBorrowScenario() // create initial conditions + // create and fund a supplier which supplies UMEE and ATOM + supplier := s.newAccount(coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) + s.supply(supplier, coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) - // The "bum" user from the init scenario is being used because it - // possesses no assets or collateral. + // 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)) - // bum attempts to borrow 200 umee, fails because of insufficient collateral - err := s.app.LeverageKeeper.Borrow(s.ctx, bumAddr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().Error(err) -} + tcs := []testCase{ + { + "uToken", + borrower, + coin("u/"+umeeDenom, 100_000000), + types.ErrUToken, + }, + { + "unregistered token", + borrower, + coin("abcd", 100_000000), + types.ErrNotRegisteredToken, + }, + { + "lending pool insufficient", + borrower, + coin(umeeDenom, 200_000000), + types.ErrLendingPoolInsufficient, + }, + { + "valid borrow", + borrower, + coin(umeeDenom, 70_000000), + nil, + }, + { + "additional borrow", + borrower, + coin(umeeDenom, 30_000000), + nil, + }, + { + "atom borrow", + borrower, + coin(atomDenom, 5_000000), + nil, + }, + { + "borrow limit", + borrower, + coin(atomDenom, 100_000000), + types.ErrUndercollaterized, + }, + { + "zero collateral", + supplier, + coin(atomDenom, 100_000000), + types.ErrUndercollaterized, + }, + } -func (s *IntegrationTestSuite) TestBorrow_InsufficientLendingPool() { - // Any user from the init scenario can perform this test, because it errors on module balance - addr, _ := s.initBorrowScenario() + for _, tc := range tcs { + if tc.err != nil { + err := app.LeverageKeeper.Borrow(ctx, tc.addr, tc.coin) + 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 + err := app.LeverageKeeper.Borrow(ctx, tc.addr, tc.coin) + require.NoError(err) - // user attempts to borrow 20000 umee, fails because of insufficient module account balance - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000000)) - s.Require().Error(err) + // 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") + } + } } func (s *IntegrationTestSuite) TestRepay_Invalid() { @@ -469,66 +582,6 @@ func (s *IntegrationTestSuite) TestRepay_Invalid() { s.Require().Error(err) } -func (s *IntegrationTestSuite) TestBorrow_Valid() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral - - // user borrows 20 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - s.Require().NoError(err) - - // verify user's new loan amount in the correct denom (20 umee) - loanBalance := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - - // verify user's total loan balance (sdk.Coins) is also 20 umee (no other coins present) - totalLoanBalance := s.app.LeverageKeeper.GetBorrowerBorrows(s.ctx, addr) - s.Require().Equal(totalLoanBalance, sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 20000000))) - - // verify user's new umee balance (10 - 1k from initial + 20 from loan = 9020 umee) - tokenBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(tokenBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 9020000000)) - - // verify user's uToken balance remains at 0 u/umee from initial conditions - uTokenBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) - - // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) -} - -func (s *IntegrationTestSuite) TestBorrow_BorrowLimit() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral - - // determine an amount of umee to borrow, such that the user will be at about 90% of their borrow limit - token, _ := s.app.LeverageKeeper.GetTokenSettings(s.ctx, umeeapp.BondDenom) - uDenom := types.ToUTokenDenom(umeeapp.BondDenom) - collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, uDenom) - amountToBorrow := token.CollateralWeight.Mul(sdk.MustNewDecFromStr("0.9")).MulInt(collateral.Amount).TruncateInt() - - // user borrows umee up to 90% of their borrow limit - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewCoin(umeeapp.BondDenom, amountToBorrow)) - s.Require().NoError(err) - - // user tries to borrow the same amount again, fails due to borrow limit - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewCoin(umeeapp.BondDenom, amountToBorrow)) - s.Require().Error(err) - - // user tries to disable u/umee as collateral, fails due to borrow limit - err = s.app.LeverageKeeper.Decollateralize(s.ctx, addr, sdk.NewCoin(uDenom, collateral.Amount)) - s.Require().Error(err) - - // user tries to withdraw all its u/umee, fails due to borrow limit - _, err = s.app.LeverageKeeper.Withdraw(s.ctx, addr, sdk.NewCoin(uDenom, collateral.Amount)) - s.Require().Error(err) -} - func (s *IntegrationTestSuite) TestBorrow_Reserved() { // The "supplier" user from the init scenario is being used because it // already has 1k u/umee for collateral. diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 98dca08743..15f33972ba 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -40,6 +40,15 @@ func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } +// requireEqualCoins compares two sdk.Coins in such a way that sdk.Coins(nil) == sdk.Coins([]sdk.Coin{}) +func (s *IntegrationTestSuite) requireEqualCoins(coinsA, coinsB sdk.Coins, msgAndArgs ...interface{}) { + s.Require().Equal( + sdk.NewCoins(coinsA...), + sdk.NewCoins(coinsB...), + msgAndArgs..., + ) +} + func (s *IntegrationTestSuite) SetupTest() { app := umeeapp.Setup(s.T(), false, 1) ctx := app.BaseApp.NewContext(false, tmproto.Header{ @@ -78,12 +87,12 @@ func (s *IntegrationTestSuite) SetupTest() { s.queryClient = types.NewQueryClient(queryHelper) } -// creates a test token with reasonable initial parameters +// newToken creates a test token with reasonable initial parameters func newToken(base, symbol string) types.Token { return fixtures.Token(base, symbol) } -// creates a coin with a given base denom and amount +// coin creates a coin with a given base denom and amount func coin(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) } From 0c89068ef33c45c64887c1650a65e94684712898 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:24:26 -0700 Subject: [PATCH 10/36] add invariant checks to every msg test case --- x/leverage/keeper/invariants.go | 45 ++++++++++++- x/leverage/keeper/invariants_test.go | 70 ++++++++++++++++++++ x/leverage/keeper/keeper_test.go | 99 +++++----------------------- 3 files changed, 130 insertions(+), 84 deletions(-) create mode 100644 x/leverage/keeper/invariants_test.go diff --git a/x/leverage/keeper/invariants.go b/x/leverage/keeper/invariants.go index 497615b6be..135883f13b 100644 --- a/x/leverage/keeper/invariants.go +++ b/x/leverage/keeper/invariants.go @@ -9,6 +9,7 @@ import ( const ( routeInterestScalars = "interest-scalars" + routeExchangeRates = "exchange-rates" routeReserveAmount = "reserve-amount" routeCollateralAmount = "collateral-amount" routeBorrowAmount = "borrow-amount" @@ -24,6 +25,7 @@ func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { ir.RegisterRoute(types.ModuleName, routeBorrowAPY, BorrowAPYInvariant(k)) ir.RegisterRoute(types.ModuleName, routeSupplyAPY, SupplyAPYInvariant(k)) ir.RegisterRoute(types.ModuleName, routeInterestScalars, InterestScalarsInvariant(k)) + ir.RegisterRoute(types.ModuleName, routeExchangeRates, ExchangeRatesInvariant(k)) } // AllInvariants runs all invariants of the x/leverage module. @@ -54,7 +56,12 @@ func AllInvariants(k Keeper) sdk.Invariant { return res, stop } - return InterestScalarsInvariant(k)(ctx) + res, stop = InterestScalarsInvariant(k)(ctx) + if stop { + return res, stop + } + + return ExchangeRatesInvariant(k)(ctx) } } @@ -297,3 +304,39 @@ func InterestScalarsInvariant(k Keeper) sdk.Invariant { ), broken } } + +// ExchangeRatesInvariant checks that all denoms have an uToken exchange rate >= 1 +func ExchangeRatesInvariant(k Keeper) sdk.Invariant { + return func(ctx sdk.Context) (string, bool) { + var ( + msg string + count int + ) + + tokenPrefix := types.KeyPrefixRegisteredToken + + // Iterate through all denoms of registered tokens in the + // keeper, ensuring none have an interest scalar less than one. + err := k.iterate(ctx, tokenPrefix, func(key, _ []byte) error { + denom := types.DenomFromKey(key, tokenPrefix) + + exchangeRate := k.DeriveExchangeRate(ctx, denom) + + if exchangeRate.LT(sdk.OneDec()) { + count++ + msg += fmt.Sprintf("\t%s exchange rate %s is less than one\n", denom, exchangeRate.String()) + } + return nil + }) + if err != nil { + msg += fmt.Sprintf("\tSome error occurred while iterating through the uToken exchange rates %+v\n", err) + } + + broken := count != 0 + + return sdk.FormatInvariant( + types.ModuleName, routeExchangeRates, + fmt.Sprintf("amount of uToken exchange rates lower than one %d\n%s", count, msg), + ), broken + } +} diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go new file mode 100644 index 0000000000..5457717714 --- /dev/null +++ b/x/leverage/keeper/invariants_test.go @@ -0,0 +1,70 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + umeeapp "github.com/umee-network/umee/v2/app" + "github.com/umee-network/umee/v2/x/leverage/keeper" + "github.com/umee-network/umee/v2/x/leverage/types" +) + +// checkInvariants is used during other tests to quickly test all invariants +func (s *IntegrationTestSuite) checkInvariants(msg string) { + desc, broken := keeper.AllInvariants(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken, msg, desc) +} + +func (s *IntegrationTestSuite) TestReserveAmountInvariant() { + // artificially set reserves + err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee + s.Require().NoError(err) + + // check invariants + _, broken := keeper.ReserveAmountInvariant(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken) +} + +func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { + addr, _ := s.initBorrowScenario() + + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee for collateral + + // check invariant + _, broken := keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken) + + uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) + + // withdraw the supplied umee in the initBorrowScenario + _, err := s.app.LeverageKeeper.Withdraw(s.ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) + s.Require().NoError(err) + + // check invariant + _, broken = keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken) +} + +func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { + addr, _ := s.initBorrowScenario() + + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee for collateral + + // user borrows 20 umee + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) + s.Require().NoError(err) + + // check invariant + _, broken := keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken) + + // user repays 30 umee, actually only 20 because is the min between + // the amount borrowed and the amount repaid + _, err = s.app.LeverageKeeper.Repay(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 30000000)) + s.Require().NoError(err) + + // check invariant + _, broken = keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken) +} diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 67fb18a958..7e09a65c0b 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -6,7 +6,6 @@ import ( minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" umeeapp "github.com/umee-network/umee/v2/app" - "github.com/umee-network/umee/v2/x/leverage/keeper" "github.com/umee-network/umee/v2/x/leverage/types" ) @@ -119,6 +118,9 @@ func (s *IntegrationTestSuite) TestSupply() { require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") // verify borrowed coins are unchanged require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) } } } @@ -267,6 +269,9 @@ func (s *IntegrationTestSuite) TestWithdraw() { require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") // verify borrowed coins are unchanged require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) } } } @@ -359,6 +364,9 @@ func (s *IntegrationTestSuite) TestCollateralize() { require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") // verify borrowed coins are unchanged require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) } } } @@ -458,6 +466,9 @@ func (s *IntegrationTestSuite) TestDecollateralize() { require.Equal(iExchangeRate, fExchangeRate, tc.msg, "uToken exchange rate") // verify borrowed coins are unchanged require.Equal(iBorrowed, fBorrowed, tc.msg, "borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) } } } @@ -515,7 +526,7 @@ func (s *IntegrationTestSuite) TestBorrow() { { "atom borrow", borrower, - coin(atomDenom, 5_000000), + coin(atomDenom, 1_000000), nil, }, { @@ -565,6 +576,9 @@ func (s *IntegrationTestSuite) TestBorrow() { 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) } } } @@ -582,32 +596,6 @@ func (s *IntegrationTestSuite) TestRepay_Invalid() { s.Require().Error(err) } -func (s *IntegrationTestSuite) TestBorrow_Reserved() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - addr, _ := s.initBorrowScenario() - - // artifically reserve 200 umee - err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().NoError(err) - - // Note: Setting umee collateral weight to 1.0 to allow user to borrow heavily - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1.0") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // Supplier tries to borrow 1000 umee, insufficient balance because 200 of the - // module's 1000 umee are reserved. - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000)) - s.Require().Error(err) - - // user borrows 800 umee - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 800000000)) - s.Require().NoError(err) -} - func (s *IntegrationTestSuite) TestRepay_Valid() { // The "supplier" user from the init scenario is being used because it // already has 1k u/umee for collateral. @@ -938,61 +926,6 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { s.Require().Equal([]sdk.AccAddress{supplierAddr, anotherSupplier}, supplierAddress) } -func (s *IntegrationTestSuite) TestReserveAmountInvariant() { - // artificially set reserves - err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee - s.Require().NoError(err) - - // check invariant - _, broken := keeper.ReserveAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) -} - -func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral - - // check invariant - _, broken := keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) - - uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) - - // withdraw the supplyed umee in the initBorrowScenario - _, err := s.app.LeverageKeeper.Withdraw(s.ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) - s.Require().NoError(err) - - // check invariant - _, broken = keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) -} - -func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral - - // user borrows 20 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - s.Require().NoError(err) - - // check invariant - _, broken := keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) - - // user repays 30 umee, actually only 20 because is the min between - // the amount borrowed and the amount repaid - _, err = s.app.LeverageKeeper.Repay(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 30000000)) - s.Require().NoError(err) - - // check invariant - _, broken = keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) -} - func (s *IntegrationTestSuite) TestTotalCollateral() { // Test zero collateral uDenom := types.ToUTokenDenom(umeeDenom) From a7c28ada5a87e133839fc4af8880c3009e76f93b Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:31:41 -0700 Subject: [PATCH 11/36] move files --- x/leverage/keeper/collateral_test.go | 14 ++ x/leverage/keeper/exchange_rate_test.go | 30 +++ x/leverage/keeper/interest_test.go | 99 ++++++++++ x/leverage/keeper/iter_test.go | 130 ++++++++++++ x/leverage/keeper/keeper_test.go | 251 ------------------------ 5 files changed, 273 insertions(+), 251 deletions(-) create mode 100644 x/leverage/keeper/exchange_rate_test.go create mode 100644 x/leverage/keeper/interest_test.go create mode 100644 x/leverage/keeper/iter_test.go diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index 7171d85f72..f86dca37b6 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -92,3 +92,17 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { // we do not test empty denom, as that will cause a panic } + +func (s *IntegrationTestSuite) TestTotalCollateral() { + // Test zero collateral + uDenom := types.ToUTokenDenom(umeeDenom) + collateral := s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) + s.Require().Equal(sdk.ZeroInt(), collateral) + + // Uses borrow scenario, because supplier possesses collateral + _, _ = s.initBorrowScenario() + + // Test nonzero collateral + collateral = s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) + s.Require().Equal(sdk.NewInt(1000000000), collateral) +} diff --git a/x/leverage/keeper/exchange_rate_test.go b/x/leverage/keeper/exchange_rate_test.go new file mode 100644 index 0000000000..3299a05685 --- /dev/null +++ b/x/leverage/keeper/exchange_rate_test.go @@ -0,0 +1,30 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + umeeapp "github.com/umee-network/umee/v2/app" +) + +func (s *IntegrationTestSuite) TestDeriveExchangeRate() { + // The init scenario is being used so module balance starts at 1000 umee + // and the uToken supply starts at 1000 due to supplier account + _, addr := s.initBorrowScenario() + + // artificially increase total borrows (by affecting a single address) + err := s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 2000000000)) // 2000 umee + s.Require().NoError(err) + + // artificially set reserves + err = s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee + s.Require().NoError(err) + + // expected token:uToken exchange rate + // = (total borrows + module balance - reserves) / utoken supply + // = 2000 + 1000 - 300 / 1000 + // = 2.7 + + // get derived exchange rate + rate := s.app.LeverageKeeper.DeriveExchangeRate(s.ctx, umeeapp.BondDenom) + s.Require().Equal(sdk.MustNewDecFromStr("2.7"), rate) +} diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go new file mode 100644 index 0000000000..7810754191 --- /dev/null +++ b/x/leverage/keeper/interest_test.go @@ -0,0 +1,99 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + umeeapp "github.com/umee-network/umee/v2/app" +) + + +func (s *IntegrationTestSuite) TestAccrueZeroInterest() { + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee for collateral. + addr, _ := s.initBorrowScenario() + + // user borrows 40 umee + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + s.Require().NoError(err) + + // verify user's loan amount (40 umee) + loanBalance := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) + s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + + // Because no time has passed since genesis (due to test setup) this will not + // increase borrowed amount. + err = s.app.LeverageKeeper.AccrueAllInterest(s.ctx) + s.Require().NoError(err) + + // verify user's loan amount (40 umee) + loanBalance = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) + s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + + // borrow APY at utilization = 4% + // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 + borrowAPY := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().Equal(sdk.MustNewDecFromStr("0.03"), borrowAPY) + + // supply APY when borrow APY is 3% + // and utilization is 4%, and reservefactor is 20%, and OracleRewardFactor is 1% + // 0.03 * 0.04 * (1 - 0.21) = 0.000948 + supplyAPY := s.app.LeverageKeeper.DeriveSupplyAPY(s.ctx, umeeapp.BondDenom) + s.Require().NoError(err) + s.Require().Equal(sdk.MustNewDecFromStr("0.000948"), supplyAPY) +} + +func (s *IntegrationTestSuite) TestDynamicInterest() { + // Init scenario is being used because the module account (lending pool) + // already has 1000 umee. + addr, _ := s.initBorrowScenario() + + umeeToken := newToken("uumee", "UMEE") + umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") // to allow high utilization + umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1.0") // to allow high utilization + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + + // Base interest rate (0% utilization) + rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().Equal(rate, sdk.MustNewDecFromStr("0.02")) + + // user borrows 200 umee, utilization 200/1000 + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + s.Require().NoError(err) + + // Between base interest and kink (20% utilization) + rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().Equal(rate, sdk.MustNewDecFromStr("0.07")) + + // user borrows 600 more umee, utilization 800/1000 + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 600000000)) + s.Require().NoError(err) + + // Kink interest rate (80% utilization) + rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().NoError(err) + s.Require().Equal(rate, sdk.MustNewDecFromStr("0.22")) + + // user borrows 100 more umee, utilization 900/1000 + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + s.Require().NoError(err) + + // Between kink interest and max (90% utilization) + rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().NoError(err) + s.Require().Equal(rate, sdk.MustNewDecFromStr("0.87")) + + // user borrows 100 more umee, utilization 1000/1000 + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + s.Require().NoError(err) + + // Max interest rate (100% utilization) + rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) + s.Require().NoError(err) + s.Require().Equal(rate, sdk.MustNewDecFromStr("1.52")) +} + +func (s *IntegrationTestSuite) TestDynamicInterest_InvalidAsset() { + rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, "uabc") + s.Require().Equal(rate, sdk.ZeroDec()) +} diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go new file mode 100644 index 0000000000..ed40f97d13 --- /dev/null +++ b/x/leverage/keeper/iter_test.go @@ -0,0 +1,130 @@ +package keeper_test + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + umeeapp "github.com/umee-network/umee/v2/app" +) + +func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset() { + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee enabled as collateral. + addr, _ := s.initBorrowScenario() + + // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight + // = 1000 * 0.1 + // = 100 + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + s.Require().NoError(err) + + zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + + // Note: Setting umee liquidation threshold to 0.05 to make the user eligible to liquidation + umeeToken := newToken("uumee", "UMEE") + umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") + umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + + targetAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{addr}, targetAddress) + + // if it tries to borrow any other asset it should return an error + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 1)) + s.Require().Error(err) +} + +func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset() { + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee enabled as collateral. + addr, _ := s.initBorrowScenario() + + // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight + // = 1000 * 0.1 + // = 100 + err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + s.Require().NoError(err) + + zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + + // mints and send to addr 100 atom and already + // enable 50 u/atom as collateral. + s.fundAccount(addr, coin(atomDenom, 100_000000)) + s.supply(addr, coin(atomDenom, 50_000000)) + s.collateralize(addr, coin("u/"+atomDenom, 50_000000)) + + // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight + // = (50 * 0.1) - 1 + // = 4 + err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom + s.Require().NoError(err) + + // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation + umeeToken := newToken("uumee", "UMEE") + umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") + umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + + // Note: Setting atom collateral weight to 0.01 to make the user eligible for liquidation + atomIBCToken := newToken(atomDenom, "ATOM") + atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") + atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) + + targetAddr, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{addr}, targetAddr) +} + +func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { + // The "supplier" user from the init scenario is being used because it + // already has 1k u/umee enabled as collateral. + supplierAddr, anotherSupplier := s.initBorrowScenario() + + // supplier borrows 100 umee (max current allowed) supplier amount enabled as collateral * CollateralWeight + // = 1000 * 0.1 + // = 100 + err := s.app.LeverageKeeper.Borrow(s.ctx, supplierAddr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + s.Require().NoError(err) + + zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + + // mints and send to anotherSupplier 100 atom and already + // enable 50 u/atom as collateral. + s.fundAccount(anotherSupplier, coin(atomDenom, 100_000000)) + s.supply(anotherSupplier, coin(atomDenom, 50_000000)) + s.collateralize(anotherSupplier, coin("u/"+atomDenom, 50_000000)) + + // anotherSupplier borrows 4 atom (max current allowed - 1) anotherSupplier amount enabled as collateral * CollateralWeight + // = (50 * 0.1) - 1 + // = 4 + err = s.app.LeverageKeeper.Borrow(s.ctx, anotherSupplier, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom + s.Require().NoError(err) + + // Note: Setting umee liquidation threshold to 0.05 to make the supplier eligible for liquidation + umeeToken := newToken("uumee", "UMEE") + umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") + umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + + // Note: Setting atom collateral weight to 0.01 to make the supplier eligible for liquidation + atomIBCToken := newToken(atomDenom, "ATOM") + atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") + atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") + + s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) + + supplierAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + s.Require().NoError(err) + s.Require().Equal([]sdk.AccAddress{supplierAddr, anotherSupplier}, supplierAddress) +} diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 7e09a65c0b..4c15aee2d9 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -689,257 +689,6 @@ func (s *IntegrationTestSuite) TestRepay_Overpay() { s.Require().Error(err) } -func (s *IntegrationTestSuite) TestDeriveExchangeRate() { - // The init scenario is being used so module balance starts at 1000 umee - // and the uToken supply starts at 1000 due to supplier account - _, addr := s.initBorrowScenario() - - // artificially increase total borrows (by affecting a single address) - err := s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 2000000000)) // 2000 umee - s.Require().NoError(err) - - // artificially set reserves - err = s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee - s.Require().NoError(err) - - // expected token:uToken exchange rate - // = (total borrows + module balance - reserves) / utoken supply - // = 2000 + 1000 - 300 / 1000 - // = 2.7 - - // get derived exchange rate - rate := s.app.LeverageKeeper.DeriveExchangeRate(s.ctx, umeeapp.BondDenom) - s.Require().Equal(sdk.MustNewDecFromStr("2.7"), rate) -} - -func (s *IntegrationTestSuite) TestAccrueZeroInterest() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - addr, _ := s.initBorrowScenario() - - // user borrows 40 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) - s.Require().NoError(err) - - // verify user's loan amount (40 umee) - loanBalance := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) - - // Because no time has passed since genesis (due to test setup) this will not - // increase borrowed amount. - err = s.app.LeverageKeeper.AccrueAllInterest(s.ctx) - s.Require().NoError(err) - - // verify user's loan amount (40 umee) - loanBalance = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) - - // borrow APY at utilization = 4% - // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 - borrowAPY := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(sdk.MustNewDecFromStr("0.03"), borrowAPY) - - // supply APY when borrow APY is 3% - // and utilization is 4%, and reservefactor is 20%, and OracleRewardFactor is 1% - // 0.03 * 0.04 * (1 - 0.21) = 0.000948 - supplyAPY := s.app.LeverageKeeper.DeriveSupplyAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("0.000948"), supplyAPY) -} - -func (s *IntegrationTestSuite) TestDynamicInterest() { - // Init scenario is being used because the module account (lending pool) - // already has 1000 umee. - addr, _ := s.initBorrowScenario() - - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") // to allow high utilization - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1.0") // to allow high utilization - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // Base interest rate (0% utilization) - rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.02")) - - // user borrows 200 umee, utilization 200/1000 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().NoError(err) - - // Between base interest and kink (20% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.07")) - - // user borrows 600 more umee, utilization 800/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 600000000)) - s.Require().NoError(err) - - // Kink interest rate (80% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.22")) - - // user borrows 100 more umee, utilization 900/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) - - // Between kink interest and max (90% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.87")) - - // user borrows 100 more umee, utilization 1000/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) - - // Max interest rate (100% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("1.52")) -} - -func (s *IntegrationTestSuite) TestDynamicInterest_InvalidAsset() { - rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, "uabc") - s.Require().Equal(rate, sdk.ZeroDec()) -} - -func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - addr, _ := s.initBorrowScenario() - - // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) - - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - - // Note: Setting umee liquidation threshold to 0.05 to make the user eligible to liquidation - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - targetAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{addr}, targetAddress) - - // if it tries to borrow any other asset it should return an error - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 1)) - s.Require().Error(err) -} - -func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - addr, _ := s.initBorrowScenario() - - // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) - - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - - // mints and send to addr 100 atom and already - // enable 50 u/atom as collateral. - s.fundAccount(addr, coin(atomDenom, 100_000000)) - s.supply(addr, coin(atomDenom, 50_000000)) - s.collateralize(addr, coin("u/"+atomDenom, 50_000000)) - - // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight - // = (50 * 0.1) - 1 - // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom - s.Require().NoError(err) - - // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // Note: Setting atom collateral weight to 0.01 to make the user eligible for liquidation - atomIBCToken := newToken(atomDenom, "ATOM") - atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") - atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) - - targetAddr, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{addr}, targetAddr) -} - -func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - supplierAddr, anotherSupplier := s.initBorrowScenario() - - // supplier borrows 100 umee (max current allowed) supplier amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, supplierAddr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) - - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - - // mints and send to anotherSupplier 100 atom and already - // enable 50 u/atom as collateral. - s.fundAccount(anotherSupplier, coin(atomDenom, 100_000000)) - s.supply(anotherSupplier, coin(atomDenom, 50_000000)) - s.collateralize(anotherSupplier, coin("u/"+atomDenom, 50_000000)) - - // anotherSupplier borrows 4 atom (max current allowed - 1) anotherSupplier amount enabled as collateral * CollateralWeight - // = (50 * 0.1) - 1 - // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, anotherSupplier, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom - s.Require().NoError(err) - - // Note: Setting umee liquidation threshold to 0.05 to make the supplier eligible for liquidation - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // Note: Setting atom collateral weight to 0.01 to make the supplier eligible for liquidation - atomIBCToken := newToken(atomDenom, "ATOM") - atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") - atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) - - supplierAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{supplierAddr, anotherSupplier}, supplierAddress) -} - -func (s *IntegrationTestSuite) TestTotalCollateral() { - // Test zero collateral - uDenom := types.ToUTokenDenom(umeeDenom) - collateral := s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) - s.Require().Equal(sdk.ZeroInt(), collateral) - - // Uses borrow scenario, because supplier possesses collateral - _, _ = s.initBorrowScenario() - - // Test nonzero collateral - collateral = s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) - s.Require().Equal(sdk.NewInt(1000000000), collateral) -} - func (s *IntegrationTestSuite) TestLiqudate() { addr, _ := s.initBorrowScenario() From c24c19b340e946579a7c90217aef9d2422a8ee0e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:33:35 -0700 Subject: [PATCH 12/36] ++ --- x/leverage/keeper/keeper_test.go | 2 +- x/leverage/keeper/suite_test.go | 26 ++++++++++---------------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 4c15aee2d9..4254157c64 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -689,7 +689,7 @@ func (s *IntegrationTestSuite) TestRepay_Overpay() { s.Require().Error(err) } -func (s *IntegrationTestSuite) TestLiqudate() { +func (s *IntegrationTestSuite) TestLiquidate() { addr, _ := s.initBorrowScenario() // The "supplier" user from the init scenario is being used because it diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 15f33972ba..c29b4b0582 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -40,15 +40,6 @@ func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(IntegrationTestSuite)) } -// requireEqualCoins compares two sdk.Coins in such a way that sdk.Coins(nil) == sdk.Coins([]sdk.Coin{}) -func (s *IntegrationTestSuite) requireEqualCoins(coinsA, coinsB sdk.Coins, msgAndArgs ...interface{}) { - s.Require().Equal( - sdk.NewCoins(coinsA...), - sdk.NewCoins(coinsB...), - msgAndArgs..., - ) -} - func (s *IntegrationTestSuite) SetupTest() { app := umeeapp.Setup(s.T(), false, 1) ctx := app.BaseApp.NewContext(false, tmproto.Header{ @@ -87,6 +78,15 @@ func (s *IntegrationTestSuite) SetupTest() { s.queryClient = types.NewQueryClient(queryHelper) } +// requireEqualCoins compares two sdk.Coins in such a way that sdk.Coins(nil) == sdk.Coins([]sdk.Coin{}) +func (s *IntegrationTestSuite) requireEqualCoins(coinsA, coinsB sdk.Coins, msgAndArgs ...interface{}) { + s.Require().Equal( + sdk.NewCoins(coinsA...), + sdk.NewCoins(coinsB...), + msgAndArgs..., + ) +} + // newToken creates a test token with reasonable initial parameters func newToken(base, symbol string) types.Token { return fixtures.Token(base, symbol) @@ -137,7 +137,6 @@ func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { } } -/* // withdraw uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -147,7 +146,6 @@ func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin require.NoError(err, "withdraw") } } -*/ // collateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { @@ -159,7 +157,6 @@ func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk } } -/* // decollateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -169,7 +166,6 @@ func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...s require.NoError(err, "decollateralize") } } -*/ // borrow tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { @@ -181,7 +177,6 @@ func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { } } -/* // repay tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -189,11 +184,10 @@ func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { for _, coin := range coins { repaid, err := app.LeverageKeeper.Repay(ctx, addr, coin) require.NoError(err, "repay") - // ensure intended repayment amount was not reduced, as doing so would create a misleading test + // ensure intended repayment amount was not reduced, as that would create a misleading test require.Equal(repaid, coin, "repay") } } -*/ // setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, // may also supply them to receive uTokens, and may also enable those uTokens as collateral and borrow tokens in the same denom. From 7bf90efa59b58de9730bbfd24e04b8277660ef04 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 11:22:40 -0700 Subject: [PATCH 13/36] TestRepay --- x/leverage/keeper/keeper.go | 14 +--- x/leverage/keeper/keeper_test.go | 127 +++++++++++++++++++++++++++++-- x/leverage/keeper/validate.go | 26 ++++++- 3 files changed, 150 insertions(+), 17 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index 680f3bea06..f34dca2ff0 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -118,12 +118,9 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk.Co // balances are insufficient to withdraw the full amount requested, returns an error. // Returns the amount of base tokens received. func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) (sdk.Coin, error) { - if err := uToken.Validate(); err != nil { + if err := k.validateUToken(ctx, uToken); err != nil { return sdk.Coin{}, err } - if !types.HasUTokenPrefix(uToken.Denom) { - return sdk.Coin{}, types.ErrNotUToken.Wrap(uToken.Denom) - } // calculate base asset amount to withdraw token, err := k.ExchangeUToken(ctx, uToken) @@ -256,7 +253,7 @@ func (k Keeper) Borrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk. // necessary amount is transferred. Because amount repaid may be less than the repayment attempted, // Repay returns the actual amount repaid. func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk.Coin) (sdk.Coin, error) { - if err := payment.Validate(); err != nil { + if err := k.validateRepay(ctx, payment); err != nil { return sdk.Coin{}, err } @@ -278,7 +275,7 @@ func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk. // Collateralize enables selected uTokens for use as collateral by a single borrower. func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.validateCollateralAsset(ctx, coin); err != nil { + if err := k.validateCollateralize(ctx, coin); err != nil { return err } @@ -292,12 +289,9 @@ func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin // Decollateralize disables selected uTokens for use as collateral by a single borrower. func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := coin.Validate(); err != nil { + if err := k.validateUToken(ctx, coin); err != nil { return err } - if !types.HasUTokenPrefix(coin.Denom) { - return types.ErrNotUToken.Wrap(coin.Denom) - } // Detect where sufficient collateral exists to disable collateral := k.GetBorrowerCollateral(ctx, borrowerAddr) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 4254157c64..46851e1a75 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -98,7 +98,7 @@ func (s *IntegrationTestSuite) TestSupply() { // verify the outputs of supply function uToken, err := app.LeverageKeeper.Supply(ctx, tc.addr, tc.coin) - require.NoError(err) + require.NoError(err, tc.msg) require.Equal(tc.expectedUTokens, uToken, tc.msg) // final state @@ -248,7 +248,7 @@ func (s *IntegrationTestSuite) TestWithdraw() { // verify the outputs of withdraw function token, err := app.LeverageKeeper.Withdraw(ctx, tc.addr, tc.uToken) - require.NoError(err) + require.NoError(err, tc.msg) require.Equal(tc.expectedTokens, token, tc.msg) // final state @@ -345,7 +345,7 @@ func (s *IntegrationTestSuite) TestCollateralize() { // verify the output of collateralize function err := app.LeverageKeeper.Collateralize(ctx, tc.addr, tc.uToken) - require.NoError(err) + require.NoError(err, tc.msg) // final state fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) @@ -447,7 +447,7 @@ func (s *IntegrationTestSuite) TestDecollateralize() { // verify the output of decollateralize function err := app.LeverageKeeper.Decollateralize(ctx, tc.addr, tc.uToken) - require.NoError(err) + require.NoError(err, tc.msg) // final state fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) @@ -557,7 +557,7 @@ func (s *IntegrationTestSuite) TestBorrow() { // verify the output of borrow function err := app.LeverageKeeper.Borrow(ctx, tc.addr, tc.coin) - require.NoError(err) + require.NoError(err, tc.msg) // final state fBalance := app.BankKeeper.GetAllBalances(ctx, tc.addr) @@ -583,6 +583,123 @@ func (s *IntegrationTestSuite) TestBorrow() { } } +func (s *IntegrationTestSuite) TestRepay() { + type testCase struct { + msg string + addr sdk.AccAddress + coin sdk.Coin + expectedRepay sdk.Coin + err error + } + + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a borrower which supplies and collateralizes UMEE, the borrows 10 UMEE + borrower := s.newAccount(coin(umeeDenom, 200_000000)) + s.supply(borrower, coin(umeeDenom, 150_000000)) + s.collateralize(borrower, coin("u/"+umeeDenom, 120_000000)) + s.borrow(borrower, coin(umeeDenom, 10_000000)) + + // create and fund a borrower which engages in a supply->borrow->supply loop + looper := s.newAccount(coin(umeeDenom, 50_000000)) + s.supply(looper, coin(umeeDenom, 50_000000)) + s.collateralize(looper, coin("u/"+umeeDenom, 50_000000)) + s.borrow(looper, coin(umeeDenom, 5_000000)) + s.supply(looper, coin(umeeDenom, 5_000000)) + + tcs := []testCase{ + { + "uToken", + borrower, + coin("u/"+umeeDenom, 100_000000), + sdk.Coin{}, + types.ErrUToken, + }, + { + "unregistered token", + borrower, + coin("abcd", 100_000000), + sdk.Coin{}, + types.ErrDenomNotBorrowed, + }, + { + "not borrowed", + borrower, + coin(atomDenom, 100_000000), + sdk.Coin{}, + types.ErrDenomNotBorrowed, + }, + { + "valid repay", + borrower, + coin(umeeDenom, 1_000000), + coin(umeeDenom, 1_000000), + nil, + }, + { + "additional repay", + borrower, + coin(umeeDenom, 3_000000), + coin(umeeDenom, 3_000000), + nil, + }, + { + "overpay", + borrower, + coin(umeeDenom, 30_000000), + coin(umeeDenom, 6_000000), + nil, + }, + { + "insufficient balance", + looper, + coin(umeeDenom, 1_000000), + sdk.Coin{}, + sdkerrors.ErrInsufficientFunds, + }, + } + + for _, tc := range tcs { + if tc.err != nil { + _, err := app.LeverageKeeper.Repay(ctx, tc.addr, tc.coin) + 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 + repaid, err := app.LeverageKeeper.Repay(ctx, tc.addr, tc.coin) + require.NoError(err, tc.msg) + require.Equal(tc.expectedRepay, repaid, 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 decreased by expected amount + require.Equal(iBalance.Sub(tc.expectedRepay), 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 decreased by expected amount + s.requireEqualCoins(iBorrowed.Sub(tc.expectedRepay), fBorrowed, "borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) + } + } +} + func (s *IntegrationTestSuite) TestRepay_Invalid() { // Any user from the init scenario can be used for this test. addr, _ := s.initBorrowScenario() diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index e4644120fc..d78279d91b 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -61,6 +61,17 @@ func (k Keeper) validateSupply(ctx sdk.Context, coin sdk.Coin) error { return token.AssertSupplyEnabled() } +// validateUToken validates an sdk.Coin and ensures its Denom is a uToken. Used by Withdraw and Decollateralize. +func (k Keeper) validateUToken(ctx sdk.Context, coin sdk.Coin) error { + if err := coin.Validate(); err != nil { + return err + } + if !types.HasUTokenPrefix(coin.Denom) { + return types.ErrNotUToken.Wrap(coin.Denom) + } + return nil +} + // validateBorrow validates an sdk.Coin and ensures its Denom is a Token with EnableMsgBorrow func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { if err := borrow.Validate(); err != nil { @@ -76,9 +87,20 @@ func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { return token.AssertBorrowEnabled() } -// validateCollateralAsset validates an sdk.Coin and ensures it is a uToken of an accepted +// validateRepay validates an sdk.Coin and ensures its Denom is not a uToken +func (k Keeper) validateRepay(ctx sdk.Context, coin sdk.Coin) error { + if err := coin.Validate(); err != nil { + return err + } + if types.HasUTokenPrefix(coin.Denom) { + return types.ErrUToken.Wrap(coin.Denom) + } + return nil +} + +// validateCollateralize validates an sdk.Coin and ensures it is a uToken of an accepted // Token with EnableMsgSupply and CollateralWeight > 0 -func (k Keeper) validateCollateralAsset(ctx sdk.Context, collateral sdk.Coin) error { +func (k Keeper) validateCollateralize(ctx sdk.Context, collateral sdk.Coin) error { if err := collateral.Validate(); err != nil { return err } From da7a29d9c3243c2b4e7fa782416283f943331475 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 11:23:55 -0700 Subject: [PATCH 14/36] -- --- x/leverage/keeper/keeper_test.go | 106 ------------------------------- 1 file changed, 106 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 46851e1a75..5ba6410213 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -700,112 +700,6 @@ func (s *IntegrationTestSuite) TestRepay() { } } -func (s *IntegrationTestSuite) TestRepay_Invalid() { - // Any user from the init scenario can be used for this test. - addr, _ := s.initBorrowScenario() - - // user attempts to repay 200 abcd, fails because "abcd" is not an accepted asset type - _, err := s.app.LeverageKeeper.Repay(s.ctx, addr, sdk.NewInt64Coin("uabcd", 200000000)) - s.Require().Error(err) - - // user attempts to repay 200 u/umee, fails because utokens are not loanable assets - _, err = s.app.LeverageKeeper.Repay(s.ctx, addr, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 200000000)) - s.Require().Error(err) -} - -func (s *IntegrationTestSuite) TestRepay_Valid() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - addr, _ := s.initBorrowScenario() - app, ctx := s.app, s.ctx - - // user borrows 20 umee - err := s.app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - s.Require().NoError(err) - - // user repays 8 umee - repaid, err := s.app.LeverageKeeper.Repay(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 8000000)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 8000000), repaid) - - // verify user's new loan amount (12 umee) - loanBalance := s.app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 12000000)) - - // verify user's new umee balance (10 - 1k from initial + 20 from loan - 8 repaid = 9012 umee) - tokenBalance := app.BankKeeper.GetBalance(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(tokenBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 9012000000)) - - // verify user's uToken balance remains at 0 u/umee from initial conditions - uTokenBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) - - // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) - - // user repays 12 umee (loan repaid in full) - repaid, err = s.app.LeverageKeeper.Repay(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 12000000)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 12000000), repaid) - - // verify user's new loan amount in the correct denom (zero) - loanBalance = s.app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 0)) - - // verify user's new umee balance (10 - 1k from initial + 20 from loan - 20 repaid = 9000 umee) - tokenBalance = app.BankKeeper.GetBalance(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(tokenBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 9000000000)) - - // verify user's uToken balance remains at 0 u/umee from initial conditions - uTokenBalance = s.app.BankKeeper.GetBalance(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) - - // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance = s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) -} - -func (s *IntegrationTestSuite) TestRepay_Overpay() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - addr, _ := s.initBorrowScenario() - app, ctx := s.app, s.ctx - - // user borrows 20 umee - err := s.app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - s.Require().NoError(err) - - // user repays 30 umee - should automatically reduce to 20 (the loan amount) and succeed - coinToRepay := sdk.NewInt64Coin(umeeapp.BondDenom, 30000000) - repaid, err := s.app.LeverageKeeper.Repay(ctx, addr, coinToRepay) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 20000000), repaid) - - // verify that coinToRepay has not been modified - s.Require().Equal(sdk.NewInt(30000000), coinToRepay.Amount) - - // verify user's new loan amount is 0 umee - loanBalance := s.app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 0)) - - // verify user's new umee balance (10 - 1k from initial + 20 from loan - 20 repaid = 9000 umee) - tokenBalance := app.BankKeeper.GetBalance(ctx, addr, umeeapp.BondDenom) - s.Require().Equal(tokenBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 9000000000)) - - // verify user's uToken balance remains at 0 u/umee from initial conditions - uTokenBalance := s.app.BankKeeper.GetBalance(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(uTokenBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 0)) - - // verify user's uToken collateral remains at 1000 u/umee from initial conditions - collateralBalance := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, "u/"+umeeapp.BondDenom) - s.Require().Equal(collateralBalance, sdk.NewInt64Coin("u/"+umeeapp.BondDenom, 1000000000)) - - // user repays 50 umee - this time it fails because the loan no longer exists - _, err = s.app.LeverageKeeper.Repay(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 50000000)) - s.Require().Error(err) -} - func (s *IntegrationTestSuite) TestLiquidate() { addr, _ := s.initBorrowScenario() From 8bdbc1f58b993cd4af194451425abc15a845a9b0 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 15:01:29 -0700 Subject: [PATCH 15/36] Liquidate single-denom test cases --- x/leverage/keeper/keeper_test.go | 183 ++++++++++++++++++++++++++++++- x/leverage/keeper/suite_test.go | 34 ++---- 2 files changed, 189 insertions(+), 28 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 5ba6410213..d283576094 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -594,7 +594,7 @@ func (s *IntegrationTestSuite) TestRepay() { app, ctx, require := s.app, s.ctx, s.Require() - // create and fund a borrower which supplies and collateralizes UMEE, the borrows 10 UMEE + // create and fund a borrower which supplies and collateralizes UMEE, then borrows 10 UMEE borrower := s.newAccount(coin(umeeDenom, 200_000000)) s.supply(borrower, coin(umeeDenom, 150_000000)) s.collateralize(borrower, coin("u/"+umeeDenom, 120_000000)) @@ -671,7 +671,7 @@ func (s *IntegrationTestSuite) TestRepay() { iExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.coin.Denom) iBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.addr) - // verify the output of borrow function + // verify the output of repay function repaid, err := app.LeverageKeeper.Repay(ctx, tc.addr, tc.coin) require.NoError(err, tc.msg) require.Equal(tc.expectedRepay, repaid, tc.msg) @@ -701,6 +701,185 @@ func (s *IntegrationTestSuite) TestRepay() { } func (s *IntegrationTestSuite) TestLiquidate() { + type testCase struct { + msg string + liquidator sdk.AccAddress + borrower sdk.AccAddress + attemptedRepay sdk.Coin + rewardDenom string + expectedRepay sdk.Coin + expectedLiquidate sdk.Coin + expectedReward sdk.Coin + err error + } + + app, ctx, require := s.app, s.ctx, s.Require() + + // create and fund a liquidator which supplies plenty of UMEE and ATOM to the module + supplier := s.newAccount(coin(umeeDenom, 1000_000000), coin(atomDenom, 1000_000000)) + s.supply(supplier, coin(umeeDenom, 1000_000000), coin(atomDenom, 1000_000000)) + + // create and fund a liquidator which has 1000 UMEE and 40 ATOM + liquidator := s.newAccount(coin(umeeDenom, 1000_000000), coin(atomDenom, 40_000000)) + + // create a healthy borrower + healthyBorrower := s.newAccount(coin(umeeDenom, 100_000000)) + s.supply(healthyBorrower, coin(umeeDenom, 100_000000)) + s.collateralize(healthyBorrower, coin("u/"+umeeDenom, 100_000000)) + s.borrow(healthyBorrower, coin(umeeDenom, 10_000000)) + + // create a borrower which supplies and collateralizes 100 ATOM + atomBorrower := s.newAccount(coin(atomDenom, 100_000000)) + s.supply(atomBorrower, coin(atomDenom, 100_000000)) + s.collateralize(atomBorrower, coin("u/"+atomDenom, 100_000000)) + // artificially borrow 50 ATOM + s.forceBorrow(atomBorrower, coin(atomDenom, 50_000000)) + + // create a borrower which supplies and collateralizes 100 UMEE + umeeBorrower := s.newAccount(coin(umeeDenom, 100_000000)) + s.supply(umeeBorrower, coin(umeeDenom, 100_000000)) + s.collateralize(umeeBorrower, coin("u/"+umeeDenom, 100_000000)) + // artificially borrow 200 UMEE + s.forceBorrow(umeeBorrower, coin(umeeDenom, 200_000000)) + + // TODO: complex liquidations + // TODO: partial liquidations + // TODO: zero liquidations + // TODO: expect bad debt repayment + // TODO: expect reserve exhaustion + // TODO: direct and indirect liquidation + // TODO: close factor limited (use atom borrower) + + tcs := []testCase{ + { + "healthy borrower", + liquidator, + healthyBorrower, + coin(atomDenom, 1_000000), + atomDenom, + sdk.Coin{}, + sdk.Coin{}, + sdk.Coin{}, + types.ErrLiquidationIneligible, + }, + { + "direct atom liquidation", + liquidator, + atomBorrower, + coin(atomDenom, 1_000000), + atomDenom, + coin(atomDenom, 1_000000), + coin("u/"+atomDenom, 1_090000), + coin(atomDenom, 1_090000), + nil, + }, + { + "u/atom liquidation", + liquidator, + atomBorrower, + coin(atomDenom, 1_000000), + "u/" + atomDenom, + coin(atomDenom, 1_000000), + coin("u/"+atomDenom, 1_100000), + coin("u/"+atomDenom, 1_100000), + nil, + }, + } + + for _, tc := range tcs { + if tc.err != nil { + _, _, _, err := app.LeverageKeeper.Liquidate( + ctx, tc.liquidator, tc.borrower, tc.attemptedRepay, tc.rewardDenom, + ) + require.ErrorIs(err, tc.err, tc.msg) + } else { + baseRewardDenom := types.ToTokenDenom(tc.expectedLiquidate.Denom) + + // initial state (borrowed denom) + biUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) + biExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.attemptedRepay.Denom) + + // initial state (liquidated denom) + liUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) + liExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, baseRewardDenom) + + // borrower initial state + biBalance := app.BankKeeper.GetAllBalances(ctx, tc.borrower) + biCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.borrower) + biBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.borrower) + + // liquidator initial state + liBalance := app.BankKeeper.GetAllBalances(ctx, tc.liquidator) + liCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.liquidator) + liBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.liquidator) + + // verify the output of liquidate function + repaid, liquidated, reward, err := app.LeverageKeeper.Liquidate( + ctx, tc.liquidator, tc.borrower, tc.attemptedRepay, tc.rewardDenom, + ) + require.NoError(err, tc.msg) + require.Equal(tc.expectedRepay, repaid, tc.msg, "repaid") + require.Equal(tc.expectedLiquidate, liquidated, tc.msg, "liquidated") + require.Equal(tc.expectedReward, reward, tc.msg, "reward") + + // final state (liquidated denom) + lfUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) + lfExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, baseRewardDenom) + + // borrower final state + bfBalance := app.BankKeeper.GetAllBalances(ctx, tc.borrower) + bfCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.borrower) + bfBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.borrower) + + // liquidator final state + lfBalance := app.BankKeeper.GetAllBalances(ctx, tc.liquidator) + lfCollateral := app.LeverageKeeper.GetBorrowerCollateral(ctx, tc.liquidator) + lfBorrowed := app.LeverageKeeper.GetBorrowerBorrows(ctx, tc.liquidator) + + // if borrowed denom and reward denom are different, then borrowed denom uTokens should be unaffected + if tc.rewardDenom != tc.attemptedRepay.Denom { + // final state (borrowed denom) + bfUTokenSupply := app.LeverageKeeper.GetAllUTokenSupply(ctx) + bfExchangeRate := app.LeverageKeeper.DeriveExchangeRate(ctx, tc.attemptedRepay.Denom) + + // verify borrowed denom uToken supply is unchanged + require.Equal(biUTokenSupply, bfUTokenSupply, tc.msg, "uToken supply (borrowed denom") + // verify borrowed denom uToken exchange rate is unchanged + require.Equal(biExchangeRate, bfExchangeRate, tc.msg, "uToken exchange rate (borrowed denom") + } + + // verify liquidated denom uToken supply is unchanged if indirect liquidation, or reduced if direct + expectedLiquidatedUTokenSupply := liUTokenSupply + if !types.HasUTokenPrefix(tc.rewardDenom) { + expectedLiquidatedUTokenSupply = expectedLiquidatedUTokenSupply.Sub(tc.expectedLiquidate) + } + require.Equal(expectedLiquidatedUTokenSupply, lfUTokenSupply, tc.msg, "uToken supply (liquidated denom") + // verify liquidated denom uToken exchange rate is unchanged + require.Equal(liExchangeRate, lfExchangeRate, tc.msg, "uToken exchange rate (liquidated denom") + + // verify borrower balances unchanged + require.Equal(biBalance, bfBalance, tc.msg, "borrower balances") + // verify borrower collateral reduced by the expected amount + require.Equal(biCollateral.Sub(tc.expectedLiquidate), bfCollateral, tc.msg, "borrower collateral") + // verify borrowed coins decreased by expected amount + s.requireEqualCoins(biBorrowed.Sub(tc.expectedRepay), bfBorrowed, "borrowed coins") + + // verify liquidator balance changes by expected amounts + require.Equal(liBalance.Sub(tc.expectedRepay).Add(tc.expectedReward), lfBalance, + tc.msg, "liquidator balances") + // verify liquidator collateral unchanged + require.Equal(liCollateral, lfCollateral, tc.msg, "liquidator collateral") + // verify liquidator borrowed coins unchanged + s.requireEqualCoins(liBorrowed, lfBorrowed, "liquidator borrowed coins") + + // check all available invariants + s.checkInvariants(tc.msg) + + } + } +} + +func (s *IntegrationTestSuite) TestLiquidate_Old() { addr, _ := s.initBorrowScenario() // The "supplier" user from the init scenario is being used because it diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index c29b4b0582..a4a6d0d560 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -137,16 +137,6 @@ func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { } } -// withdraw uTokens from an account and require no errors. Use when setting up leverage scenarios. -func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, uTokens ...sdk.Coin) { - app, ctx, require := s.app, s.ctx, s.Require() - - for _, coin := range uTokens { - _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin) - require.NoError(err, "withdraw") - } -} - // collateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -157,16 +147,6 @@ func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk } } -// decollateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. -func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { - app, ctx, require := s.app, s.ctx, s.Require() - - for _, coin := range uTokens { - err := app.LeverageKeeper.Decollateralize(ctx, addr, coin) - require.NoError(err, "decollateralize") - } -} - // borrow tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -177,16 +157,18 @@ func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { } } -// repay tokens as an account and require no errors. Use when setting up leverage scenarios. -func (s *IntegrationTestSuite) repay(addr sdk.AccAddress, coins ...sdk.Coin) { +// forceBorrow artificially borrows tokens with an account, ignoring collateral, to set up liquidation scenarios. +// this does not alter uToken exchange rates as artificially accruing interest would. +func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() for _, coin := range coins { - repaid, err := app.LeverageKeeper.Repay(ctx, addr, coin) - require.NoError(err, "repay") - // ensure intended repayment amount was not reduced, as that would create a misleading test - require.Equal(repaid, coin, "repay") + err := s.tk.SetBorrow(ctx, addr, coin) + require.NoError(err, "forceBorrow") } + + err := app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, coins) + require.NoError(err, "forceBorroww") } // setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, From 29075d08faf5307f790fd5218944dabb225db9e0 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:00:53 -0700 Subject: [PATCH 16/36] liquidate cases --- x/leverage/keeper/keeper_test.go | 122 +++++++++++++++++++++++-------- x/leverage/keeper/suite_test.go | 3 +- 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index d283576094..ef237aaa63 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -719,8 +719,8 @@ func (s *IntegrationTestSuite) TestLiquidate() { supplier := s.newAccount(coin(umeeDenom, 1000_000000), coin(atomDenom, 1000_000000)) s.supply(supplier, coin(umeeDenom, 1000_000000), coin(atomDenom, 1000_000000)) - // create and fund a liquidator which has 1000 UMEE and 40 ATOM - liquidator := s.newAccount(coin(umeeDenom, 1000_000000), coin(atomDenom, 40_000000)) + // create and fund a liquidator which has 1000 UMEE and 1000 ATOM + liquidator := s.newAccount(coin(umeeDenom, 1000_000000), coin(atomDenom, 1000_000000)) // create a healthy borrower healthyBorrower := s.newAccount(coin(umeeDenom, 100_000000)) @@ -728,27 +728,33 @@ func (s *IntegrationTestSuite) TestLiquidate() { s.collateralize(healthyBorrower, coin("u/"+umeeDenom, 100_000000)) s.borrow(healthyBorrower, coin(umeeDenom, 10_000000)) - // create a borrower which supplies and collateralizes 100 ATOM - atomBorrower := s.newAccount(coin(atomDenom, 100_000000)) - s.supply(atomBorrower, coin(atomDenom, 100_000000)) - s.collateralize(atomBorrower, coin("u/"+atomDenom, 100_000000)) - // artificially borrow 50 ATOM - s.forceBorrow(atomBorrower, coin(atomDenom, 50_000000)) - - // create a borrower which supplies and collateralizes 100 UMEE - umeeBorrower := s.newAccount(coin(umeeDenom, 100_000000)) - s.supply(umeeBorrower, coin(umeeDenom, 100_000000)) - s.collateralize(umeeBorrower, coin("u/"+umeeDenom, 100_000000)) - // artificially borrow 200 UMEE + // create a borrower which supplies and collateralizes 1000 ATOM + atomBorrower := s.newAccount(coin(atomDenom, 1000_000000)) + s.supply(atomBorrower, coin(atomDenom, 1000_000000)) + s.collateralize(atomBorrower, coin("u/"+atomDenom, 1000_000000)) + // artificially borrow 500 ATOM - this can be liquidated without bad debt + s.forceBorrow(atomBorrower, coin(atomDenom, 500_000000)) + + // create a borrower which collateralizes 110 UMEE + umeeBorrower := s.newAccount(coin(umeeDenom, 300_000000)) + s.supply(umeeBorrower, coin(umeeDenom, 200_000000)) + s.collateralize(umeeBorrower, coin("u/"+umeeDenom, 110_000000)) + // artificially borrow 200 UMEE - this will create a bad debt when liquidated s.forceBorrow(umeeBorrower, coin(umeeDenom, 200_000000)) - // TODO: complex liquidations - // TODO: partial liquidations - // TODO: zero liquidations - // TODO: expect bad debt repayment - // TODO: expect reserve exhaustion - // TODO: direct and indirect liquidation - // TODO: close factor limited (use atom borrower) + // creates a complex borrower with multiple denoms active + complexBorrower := s.newAccount(coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) + s.supply(complexBorrower, coin(umeeDenom, 100_000000), coin(atomDenom, 100_000000)) + s.collateralize(complexBorrower, coin("u/"+umeeDenom, 100_000000), coin("u/"+atomDenom, 100_000000)) + // artificially borrow multiple denoms + s.forceBorrow(complexBorrower, coin(atomDenom, 30_000000), coin(umeeDenom, 30_000000)) + + // creates a realistic borrower with 400 UMEE collateral which will have a close factor < 1 + closeBorrower := s.newAccount(coin(umeeDenom, 400_000000)) + s.supply(closeBorrower, coin(umeeDenom, 400_000000)) + s.collateralize(closeBorrower, coin("u/"+umeeDenom, 400_000000)) + // artificially borrow just barely above liquidation threshold to simulate interest accruing + s.forceBorrow(closeBorrower, coin(umeeDenom, 102_000000)) tcs := []testCase{ { @@ -762,26 +768,81 @@ func (s *IntegrationTestSuite) TestLiquidate() { sdk.Coin{}, types.ErrLiquidationIneligible, }, + { + "not borrowed denom", + liquidator, + umeeBorrower, + coin(atomDenom, 1_000000), + atomDenom, + sdk.Coin{}, + sdk.Coin{}, + sdk.Coin{}, + types.ErrLiquidationRepayZero, + }, { "direct atom liquidation", liquidator, atomBorrower, - coin(atomDenom, 1_000000), + coin(atomDenom, 100_000000), atomDenom, - coin(atomDenom, 1_000000), - coin("u/"+atomDenom, 1_090000), - coin(atomDenom, 1_090000), + coin(atomDenom, 100_000000), + coin("u/"+atomDenom, 109_000000), + coin(atomDenom, 109_000000), nil, }, { "u/atom liquidation", liquidator, atomBorrower, - coin(atomDenom, 1_000000), + coin(atomDenom, 100_000000), "u/" + atomDenom, - coin(atomDenom, 1_000000), - coin("u/"+atomDenom, 1_100000), - coin("u/"+atomDenom, 1_100000), + coin(atomDenom, 100_000000), + coin("u/"+atomDenom, 110_000000), + coin("u/"+atomDenom, 110_000000), + nil, + }, + { + "complete u/atom liquidation", + liquidator, + atomBorrower, + coin(atomDenom, 500_000000), + "u/" + atomDenom, + coin(atomDenom, 300_000000), + coin("u/"+atomDenom, 330_000000), + coin("u/"+atomDenom, 330_000000), + nil, + }, + { + "bad debt u/umee liquidation", + liquidator, + umeeBorrower, + coin(umeeDenom, 200_000000), + "u/" + umeeDenom, + coin(umeeDenom, 100_000000), + coin("u/"+umeeDenom, 110_000000), + coin("u/"+umeeDenom, 110_000000), + nil, + }, + { + "complex borrower", + liquidator, + complexBorrower, + coin(umeeDenom, 200_000000), + "u/" + atomDenom, + coin(umeeDenom, 30_000000), + coin("u/"+atomDenom, 3_527932), + coin("u/"+atomDenom, 3_527932), + nil, + }, + { + "close factor < 1", + liquidator, + closeBorrower, + coin(umeeDenom, 200_000000), + "u/" + umeeDenom, + coin(umeeDenom, 21_216000), + coin("u/"+umeeDenom, 23_337600), + coin("u/"+umeeDenom, 23_337600), nil, }, } @@ -860,7 +921,7 @@ func (s *IntegrationTestSuite) TestLiquidate() { // verify borrower balances unchanged require.Equal(biBalance, bfBalance, tc.msg, "borrower balances") // verify borrower collateral reduced by the expected amount - require.Equal(biCollateral.Sub(tc.expectedLiquidate), bfCollateral, tc.msg, "borrower collateral") + s.requireEqualCoins(biCollateral.Sub(tc.expectedLiquidate), bfCollateral, tc.msg, "borrower collateral") // verify borrowed coins decreased by expected amount s.requireEqualCoins(biBorrowed.Sub(tc.expectedRepay), bfBorrowed, "borrowed coins") @@ -874,7 +935,6 @@ func (s *IntegrationTestSuite) TestLiquidate() { // check all available invariants s.checkInvariants(tc.msg) - } } } diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index a4a6d0d560..7bf7f2fe09 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -163,7 +163,8 @@ func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coi app, ctx, require := s.app, s.ctx, s.Require() for _, coin := range coins { - err := s.tk.SetBorrow(ctx, addr, coin) + borrowed := s.tk.GetBorrow(ctx, addr, coin.Denom) + err := s.tk.SetBorrow(ctx, addr, borrowed.Add(coin)) require.NoError(err, "forceBorrow") } From f4d3fbfeb0aa7b3abc970afbcbd37df9cf4b7401 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:03:58 -0700 Subject: [PATCH 17/36] -- --- x/leverage/keeper/keeper_test.go | 120 ------------------------------- 1 file changed, 120 deletions(-) diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index ef237aaa63..ef83273d91 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -3,9 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" - umeeapp "github.com/umee-network/umee/v2/app" "github.com/umee-network/umee/v2/x/leverage/types" ) @@ -938,121 +936,3 @@ func (s *IntegrationTestSuite) TestLiquidate() { } } } - -func (s *IntegrationTestSuite) TestLiquidate_Old() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - - // user borrows 90 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 90000000)) - s.Require().NoError(err) - - // create an account and address which will represent a liquidator - liquidatorAddr := sdk.AccAddress([]byte("addr______________03")) - liquidatorAcc := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, liquidatorAddr) - s.app.AccountKeeper.SetAccount(s.ctx, liquidatorAcc) - - // mint and send 10k umee to liquidator - s.Require().NoError(s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee - )) - s.Require().NoError(s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, liquidatorAddr, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee, - )) - - // liquidator attempts to liquidate user, but user is ineligible (not over borrow limit) - repayment := sdk.NewInt64Coin(umeeapp.BondDenom, 30000000) // 30 umee - rewardDenom := types.ToUTokenDenom(umeeapp.BondDenom) - _, _, _, err = s.app.LeverageKeeper.Liquidate(s.ctx, liquidatorAddr, addr, repayment, rewardDenom) - s.Require().Error(err) - - // set umee liquidation threshold to 0.01 to allow liquidation - umeeToken, err := s.app.LeverageKeeper.GetTokenSettings(s.ctx, umeeDenom) - s.Require().NoError(err) - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // liquidator partially liquidates user, receiving some uTokens - repayment = sdk.NewInt64Coin(umeeapp.BondDenom, 10000000) // 10 umee - repaid, liquidated, reward, err := s.app.LeverageKeeper.Liquidate( - s.ctx, liquidatorAddr, addr, repayment, types.ToUTokenDenom(umeeDenom), - ) - s.Require().NoError(err) - s.Require().Equal(repayment, repaid) // 10 umee - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 11000000), liquidated) // 11 u/umee - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 11000000), reward) // 11 u/umee - - // verify borrower's new borrowed amount is 80 umee (still over borrow limit) - borrowed := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 80000000), borrowed) - - // verify borrower's new collateral amount (1k - 11) = 989 u/umee - collateral := s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, types.ToUTokenDenom(umeeDenom)) - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 989000000), collateral) - - // verify liquidator's new u/umee balance = 11 = (10 + liquidation incentive) - uTokenBalance := s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, rewardDenom) - s.Require().Equal(sdk.NewInt64Coin(rewardDenom, 11000000), uTokenBalance) - - // verify liquidator's new umee balance (10k - 11) = 9990 umee - tokenBalance := s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 9990000000), tokenBalance) - - // liquidator partially liquidates user, receiving base tokens directly at slightly reduced incentive - repaid, liquidated, reward, err = s.app.LeverageKeeper.Liquidate( - s.ctx, liquidatorAddr, addr, repayment, umeeDenom, - ) - s.Require().NoError(err) - s.Require().Equal(repayment, repaid) // 10 umee - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 10900000), liquidated) // 10.9 u/umee - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 10900000), reward) // 10.9 umee - - // verify borrower's new borrow amount is 70 umee (still over borrow limit) - borrowed = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 70000000), borrowed) - - // verify borrower's new collateral amount (989 - 10.9) = 978.1 u/umee - collateral = s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, types.ToUTokenDenom(umeeDenom)) - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 978100000), collateral) - - // verify liquidator's u/umee balance = 11 (unchanged) - uTokenBalance = s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, rewardDenom) - s.Require().Equal(sdk.NewInt64Coin(rewardDenom, 11000000), uTokenBalance) - - // verify liquidator's new umee balance (9990 - 10 + 10.9) = 9990.9 umee - tokenBalance = s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 9990900000), tokenBalance) - - // liquidator fully liquidates user, receiving more collateral and reducing borrowed amount to zero - repayment = sdk.NewInt64Coin(umeeapp.BondDenom, 300000000) // 300 umee - repaid, liquidated, reward, err = s.app.LeverageKeeper.Liquidate( - s.ctx, liquidatorAddr, addr, repayment, types.ToUTokenDenom(umeeDenom), - ) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 70000000), repaid) // 70 umee - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 77000000), liquidated) // 77 u/umee - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 77000000), reward) // 77 u/umee - - // verify that repayment has not been modified - s.Require().Equal(sdk.NewInt(300000000), repayment.Amount) - - // verify liquidator's new u/umee balance = 88 = (11 + 77) - uTokenBalance = s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, rewardDenom) - s.Require().Equal(sdk.NewInt64Coin(rewardDenom, 88000000), uTokenBalance) - - // verify borrower's new borrowed amount is zero - borrowed = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 0), borrowed) - - // verify borrower's new collateral amount (978.1 - 77) = 901.1 u/umee - collateral = s.app.LeverageKeeper.GetCollateralAmount(s.ctx, addr, types.ToUTokenDenom(umeeDenom)) - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(umeeDenom), 901100000), collateral) - - // verify liquidator's new umee balance (9990.9 - 70) = 9920.9 umee - tokenBalance = s.app.BankKeeper.GetBalance(s.ctx, liquidatorAddr, umeeapp.BondDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeapp.BondDenom, 9920900000), tokenBalance) -} From b6c065b9b3a47112e8da2246d26cccb00374fbb6 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 17:54:55 -0700 Subject: [PATCH 18/36] remove setupAccount and initBorrowScenario --- x/leverage/keeper/borrows_test.go | 59 ++++++++------- x/leverage/keeper/collateral_test.go | 74 ++++++++++--------- x/leverage/keeper/exchange_rate_test.go | 11 +-- x/leverage/keeper/grpc_query_test.go | 22 ++++-- x/leverage/keeper/interest_test.go | 15 ++-- x/leverage/keeper/invariants_test.go | 16 ++-- x/leverage/keeper/iter_test.go | 78 ++++++++------------ x/leverage/keeper/reserves_test.go | 13 ++-- x/leverage/keeper/suite_test.go | 97 +++---------------------- 9 files changed, 157 insertions(+), 228 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index fdaca4b6ee..2c70d2c0df 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -11,8 +11,11 @@ func (s *IntegrationTestSuite) TestGetBorrow() { borrowed := s.tk.GetBorrow(s.ctx, sdk.AccAddress{}, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), borrowed) - // creates account which has borrowed 123 uumee - addr := s.setupAccount(umeeDenom, 1000, 1000, 123, true) + // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) + s.borrow(addr, coin(umeeDenom, 123)) // confirm borrowed amount is 123 uumee borrowed = s.tk.GetBorrow(s.ctx, addr, umeeDenom) @@ -30,7 +33,7 @@ func (s *IntegrationTestSuite) TestSetBorrow() { err := s.tk.SetBorrow(s.ctx, sdk.AccAddress{}, sdk.NewInt64Coin(umeeDenom, 123)) s.Require().EqualError(err, "empty address") - addr := sdk.AccAddress{0x00} + addr := s.newAccount() // set nonzero borrow, and confirm effect err = s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 123)) @@ -72,15 +75,21 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { borrowed := s.tk.GetTotalBorrowed(s.ctx, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), borrowed) - // creates account which has borrowed 123 uumee - _ = s.setupAccount(umeeDenom, 1000, 1000, 123, true) + // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee + borrower := s.newAccount(coin(umeeDenom, 1000)) + s.supply(borrower, coin(umeeDenom, 1000)) + s.collateralize(borrower, coin("u/"+umeeDenom, 1000)) + s.borrow(borrower, coin(umeeDenom, 123)) // confirm total borrowed amount is 123 uumee borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) - // creates account which has borrowed 456 uumee - _ = s.setupAccount(umeeDenom, 2000, 2000, 456, true) + // creates account which has supplied and collateralized 1000 uumee, and borrowed 456 uumee + borrower2 := s.newAccount(coin(umeeDenom, 1000)) + s.supply(borrower2, coin(umeeDenom, 1000)) + s.collateralize(borrower2, coin("u/"+umeeDenom, 1000)) + s.borrow(borrower2, coin(umeeDenom, 123)) // confirm total borrowed amount is 579 uumee borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) @@ -89,8 +98,6 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { // interest scalar test - scalar changing after borrow (as it does when interest accrues) s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("2.00"))) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 1158), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) - - // we do not test empty denom, as that will cause a panic } func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { @@ -98,15 +105,20 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { available := s.tk.GetAvailableToBorrow(s.ctx, "abcd") s.Require().Equal(sdk.ZeroInt(), available) - // creates account which has supplied 1000 uumee, and borrowed 0 uumee - _ = s.setupAccount(umeeDenom, 1000, 1000, 0, true) + // creates account which has supplied and collateralized 1000 uumee + supplier := s.newAccount(coin(umeeDenom, 1000)) + s.supply(supplier, coin(umeeDenom, 1000)) + s.collateralize(supplier, coin("u/"+umeeDenom, 1000)) // confirm lending pool is 1000 uumee available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) s.Require().Equal(sdk.NewInt(1000), available) - // creates account which has supplied 1000 uumee, and borrowed 123 uumee - _ = s.setupAccount(umeeDenom, 1000, 1000, 123, true) + // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee + borrower := s.newAccount(coin(umeeDenom, 1000)) + s.supply(borrower, coin(umeeDenom, 1000)) + s.collateralize(borrower, coin("u/"+umeeDenom, 1000)) + s.borrow(borrower, coin(umeeDenom, 123)) // confirm lending pool is 1877 uumee available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) @@ -116,8 +128,6 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { s.Require().NoError(s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeDenom, 200))) available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) s.Require().Equal(sdk.NewInt(1677), available) - - // we do not test empty denom, as that will cause a panic } func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { @@ -125,8 +135,10 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { utilization := s.tk.SupplyUtilization(s.ctx, "abcd") s.Require().Equal(sdk.OneDec(), utilization) - // creates account which has supplied 1000 uumee, and borrowed 0 uumee - addr := s.setupAccount(umeeDenom, 1000, 1000, 0, true) + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) // All tests below are commented with the following equation in mind: // utilization = (Total Borrowed / (Total Borrowed + Module Balance - Reserved Amount)) @@ -136,7 +148,7 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { s.Require().Equal(sdk.ZeroDec(), utilization) // user borrows 200 uumee, reducing module account to 800 uumee - s.Require().NoError(s.tk.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 200))) + s.borrow(addr, coin(umeeDenom, 200)) // 20% utilization (200 / 200+800-0) utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) @@ -149,15 +161,8 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) s.Require().Equal(sdk.MustNewDecFromStr("0.25"), utilization) - // Setting umee collateral weight to 1.0 to allow user to borrow heavily - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1") - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1") - - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - - // user borrows 600 uumee, reducing module account to 0 uumee - s.Require().NoError(s.tk.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 600))) + // user borrows 600 uumee (disregard borrow limit), reducing module account to 0 uumee + s.forceBorrow(addr, coin(umeeDenom, 600)) // 100% utilization (800 / 800+200-200)) utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index f86dca37b6..ab63ef25d8 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -9,100 +9,108 @@ import ( func (s *IntegrationTestSuite) TestGetCollateralAmount() { uDenom := types.ToUTokenDenom(umeeDenom) - // get u/umee collateral amount of empty account address (zero) + // get u/umee collateral amount of empty account address collateral := s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{}, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + s.Require().Equal(coin(uDenom, 0), collateral) - // get u/umee collateral amount of non-empty account address (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{0x01}, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + // fund an account + addr := s.newAccount(coin(umeeDenom, 1000)) + + // get u/umee collateral amount of non-empty account address + collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + s.Require().Equal(coin(uDenom, 0), collateral) - // creates account which has 1000 u/umee but not enabled as collateral - addr := s.setupAccount(umeeDenom, 1000, 1000, 0, false) + // supply 1000 u/uumee but do not collateralize + s.supply(addr, coin(umeeDenom, 1000)) // confirm collateral amount is 0 u/uumee collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + s.Require().Equal(coin(uDenom, 0), collateral) // enable u/umee as collateral - s.Require().NoError(s.tk.Collateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) + s.collateralize(addr, coin(uDenom, 1000)) // confirm collateral amount is 1000 u/uumee collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 1000), collateral) + s.Require().Equal(coin(uDenom, 1000), collateral) // collateral amount of non-utoken denom (zero) collateral = s.tk.GetCollateralAmount(s.ctx, addr, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), collateral) + s.Require().Equal(coin(umeeDenom, 0), collateral) // collateral amount of unregistered denom (zero) collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") - s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) + s.Require().Equal(coin("abcd", 0), collateral) // disable u/umee as collateral - s.Require().NoError(s.tk.Decollateralize(s.ctx, addr, sdk.NewInt64Coin(uDenom, 1000))) + s.decollateralize(addr, coin(uDenom, 1000)) // confirm collateral amount is 0 u/uumee collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + s.Require().Equal(coin(uDenom, 0), collateral) // we do not test empty denom, as that will cause a panic } func (s *IntegrationTestSuite) TestSetCollateralAmount() { + ctx, require := s.ctx, s.Require() uDenom := types.ToUTokenDenom(umeeDenom) // set u/umee collateral amount of empty account address (error) - err := s.tk.SetCollateralAmount(s.ctx, sdk.AccAddress{}, sdk.NewInt64Coin(uDenom, 0)) - s.Require().EqualError(err, "empty address") + err := s.tk.SetCollateralAmount(ctx, sdk.AccAddress{}, sdk.NewInt64Coin(uDenom, 0)) + require.EqualError(err, "empty address") - addr := sdk.AccAddress{0x01} + addr := s.newAccount() // force invalid denom - err = s.tk.SetCollateralAmount(s.ctx, addr, sdk.Coin{Denom: "", Amount: sdk.ZeroInt()}) - s.Require().ErrorContains(err, "invalid denom") + err = s.tk.SetCollateralAmount(ctx, addr, sdk.Coin{Denom: "", Amount: sdk.ZeroInt()}) + require.ErrorContains(err, "invalid denom") // set u/umee collateral amount - s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 10))) + s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 10))) // confirm effect - collateral := s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral := s.tk.GetCollateralAmount(ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 10), collateral) // set u/umee collateral amount to zero - s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin(uDenom, 0))) + s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 0))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) + collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // set unregistered token collateral amount - s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 10))) + s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 10))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 10), collateral) // set unregistered token collateral amount to zero - s.Require().NoError(s.tk.SetCollateralAmount(s.ctx, addr, sdk.NewInt64Coin("abcd", 0))) + s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 0))) // confirm effect - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") + collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) // we do not test empty denom, as that will cause a panic } func (s *IntegrationTestSuite) TestTotalCollateral() { + app, ctx, require := s.app, s.ctx, s.Require() + // Test zero collateral uDenom := types.ToUTokenDenom(umeeDenom) - collateral := s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) - s.Require().Equal(sdk.ZeroInt(), collateral) + collateral := app.LeverageKeeper.GetTotalCollateral(ctx, uDenom) + require.Equal(sdk.ZeroInt(), collateral, "zero collateral") - // Uses borrow scenario, because supplier possesses collateral - _, _ = s.initBorrowScenario() + // create a supplier which will have 100 u/UMEE collateral + addr := s.newAccount(coin(umeeDenom, 100_000000)) + s.supply(addr, coin(umeeDenom, 100_000000)) + s.collateralize(addr, coin(uDenom, 100_000000)) // Test nonzero collateral - collateral = s.app.LeverageKeeper.GetTotalCollateral(s.ctx, uDenom) - s.Require().Equal(sdk.NewInt(1000000000), collateral) + collateral = s.app.LeverageKeeper.GetTotalCollateral(ctx, uDenom) + require.Equal(sdk.NewInt(100_000000), collateral, "nonzero collateral") } diff --git a/x/leverage/keeper/exchange_rate_test.go b/x/leverage/keeper/exchange_rate_test.go index 3299a05685..0862c1fc0c 100644 --- a/x/leverage/keeper/exchange_rate_test.go +++ b/x/leverage/keeper/exchange_rate_test.go @@ -7,16 +7,17 @@ import ( ) func (s *IntegrationTestSuite) TestDeriveExchangeRate() { - // The init scenario is being used so module balance starts at 1000 umee - // and the uToken supply starts at 1000 due to supplier account - _, addr := s.initBorrowScenario() + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) // artificially increase total borrows (by affecting a single address) - err := s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 2000000000)) // 2000 umee + err := s.tk.SetBorrow(s.ctx, addr, coin(umeeDenom, 2000)) s.Require().NoError(err) // artificially set reserves - err = s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee + err = s.tk.SetReserveAmount(s.ctx, coin(umeeDenom, 300)) s.Require().NoError(err) // expected token:uToken exchange rate diff --git a/x/leverage/keeper/grpc_query_test.go b/x/leverage/keeper/grpc_query_test.go index ff3fcffd4e..d3a7687ba4 100644 --- a/x/leverage/keeper/grpc_query_test.go +++ b/x/leverage/keeper/grpc_query_test.go @@ -55,17 +55,20 @@ func (s *IntegrationTestSuite) TestQuerier_MarketSummary() { } func (s *IntegrationTestSuite) TestQuerier_AccountBalances() { - addr, _ := s.initBorrowScenario() + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) resp, err := s.queryClient.AccountBalances(s.ctx.Context(), &types.QueryAccountBalances{Address: addr.String()}) s.Require().NoError(err) expected := types.QueryAccountBalancesResponse{ Supplied: sdk.NewCoins( - sdk.NewCoin(umeeDenom, sdk.NewInt(1000000000)), + coin(umeeDenom, 1000), ), Collateral: sdk.NewCoins( - sdk.NewCoin(types.ToUTokenDenom(umeeDenom), sdk.NewInt(1000000000)), + coin("u/"+umeeDenom, 1000), ), Borrowed: nil, } @@ -74,7 +77,10 @@ func (s *IntegrationTestSuite) TestQuerier_AccountBalances() { } func (s *IntegrationTestSuite) TestQuerier_AccountSummary() { - addr, _ := s.initBorrowScenario() + // creates account which has supplied and collateralized 1000 UMEE + addr := s.newAccount(coin(umeeDenom, 1000_000000)) + s.supply(addr, coin(umeeDenom, 1000_000000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) resp, err := s.queryClient.AccountSummary(s.ctx.Context(), &types.QueryAccountSummary{Address: addr.String()}) s.Require().NoError(err) @@ -84,15 +90,15 @@ func (s *IntegrationTestSuite) TestQuerier_AccountSummary() { // from .Reset() in x/leverage/keeper/oracle_test.go // times the amount of umee, then sometimes times params // from newToken in x/leverage/keeper/keeper_test.go - // (1000 / 1000000) * 4.21 = 4210 + // (1000) * 4.21 = 4210 SuppliedValue: sdk.MustNewDecFromStr("4210"), - // (1000 / 1000000) * 4.21 = 4210 + // (1000) * 4.21 = 4210 CollateralValue: sdk.MustNewDecFromStr("4210"), // Nothing borrowed BorrowedValue: sdk.ZeroDec(), - // (1000 / 1000000) * 4.21 * 0.25 = 1052.5 + // (1000) * 4.21 * 0.25 = 1052.5 BorrowLimit: sdk.MustNewDecFromStr("1052.5"), - // (1000 / 1000000) * 4.21 * 0.25 = 1052.5 + // (1000) * 4.21 * 0.25 = 1052.5 LiquidationThreshold: sdk.MustNewDecFromStr("1052.5"), } diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 7810754191..7f8cedf513 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -6,11 +6,11 @@ import ( umeeapp "github.com/umee-network/umee/v2/app" ) - func (s *IntegrationTestSuite) TestAccrueZeroInterest() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral. - addr, _ := s.initBorrowScenario() + // creates account which has supplied and collateralized 1000 UMEE + addr := s.newAccount(coin(umeeDenom, 1000_000000)) + s.supply(addr, coin(umeeDenom, 1000_000000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 40 umee err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) @@ -43,9 +43,10 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { } func (s *IntegrationTestSuite) TestDynamicInterest() { - // Init scenario is being used because the module account (lending pool) - // already has 1000 umee. - addr, _ := s.initBorrowScenario() + // creates account which has supplied and collateralized 1000 UMEE + addr := s.newAccount(coin(umeeDenom, 1000_000000)) + s.supply(addr, coin(umeeDenom, 1000_000000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) umeeToken := newToken("uumee", "UMEE") umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") // to allow high utilization diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 5457717714..c5e71fa2e4 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -25,10 +25,10 @@ func (s *IntegrationTestSuite) TestReserveAmountInvariant() { } func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral + // creates account which has supplied and collateralized 1000 UMEE + addr := s.newAccount(coin(umeeDenom, 1000_000000)) + s.supply(addr, coin(umeeDenom, 1000_000000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // check invariant _, broken := keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) @@ -46,10 +46,10 @@ func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { } func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { - addr, _ := s.initBorrowScenario() - - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee for collateral + // creates account which has supplied and collateralized 1000 UMEE + addr := s.newAccount(coin(umeeDenom, 1000_000000)) + s.supply(addr, coin(umeeDenom, 1000_000000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 20 umee err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go index ed40f97d13..1df5dc0579 100644 --- a/x/leverage/keeper/iter_test.go +++ b/x/leverage/keeper/iter_test.go @@ -2,20 +2,16 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - - umeeapp "github.com/umee-network/umee/v2/app" ) func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - addr, _ := s.initBorrowScenario() - - // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) + + // user borrows 250 umee (max current allowed) + s.borrow(addr, coin(umeeDenom, 250)) zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) s.Require().NoError(err) @@ -31,22 +27,16 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset targetAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) s.Require().NoError(err) s.Require().Equal([]sdk.AccAddress{addr}, targetAddress) - - // if it tries to borrow any other asset it should return an error - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 1)) - s.Require().Error(err) } func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - addr, _ := s.initBorrowScenario() - - // user borrows 100 umee (max current allowed) user amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) + + // user borrows 250 umee (max current allowed) + s.borrow(addr, coin(umeeDenom, 250)) zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) s.Require().NoError(err) @@ -84,47 +74,41 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset } func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { - // The "supplier" user from the init scenario is being used because it - // already has 1k u/umee enabled as collateral. - supplierAddr, anotherSupplier := s.initBorrowScenario() - - // supplier borrows 100 umee (max current allowed) supplier amount enabled as collateral * CollateralWeight - // = 1000 * 0.1 - // = 100 - err := s.app.LeverageKeeper.Borrow(s.ctx, supplierAddr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) + // creates account which has supplied and collateralized 1000 uumee + addr := s.newAccount(coin(umeeDenom, 1000)) + s.supply(addr, coin(umeeDenom, 1000)) + s.collateralize(addr, coin("u/"+umeeDenom, 1000)) + + // user borrows 250 umee (max current allowed) + s.borrow(addr, coin(umeeDenom, 250)) zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) s.Require().NoError(err) s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) - // mints and send to anotherSupplier 100 atom and already - // enable 50 u/atom as collateral. - s.fundAccount(anotherSupplier, coin(atomDenom, 100_000000)) - s.supply(anotherSupplier, coin(atomDenom, 50_000000)) - s.collateralize(anotherSupplier, coin("u/"+atomDenom, 50_000000)) + // creates another account which has supplied and collateralized 100 uatom + addr2 := s.newAccount(coin(atomDenom, 100)) + s.supply(addr2, coin(atomDenom, 100)) + s.collateralize(addr2, coin("u/"+atomDenom, 100)) - // anotherSupplier borrows 4 atom (max current allowed - 1) anotherSupplier amount enabled as collateral * CollateralWeight - // = (50 * 0.1) - 1 - // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, anotherSupplier, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom - s.Require().NoError(err) + // borrows atom (max current allowed - 1) + s.borrow(addr2, coin(atomDenom, 24)) - // Note: Setting umee liquidation threshold to 0.05 to make the supplier eligible for liquidation + // Note: Setting umee liquidation threshold to 0.05 to make the first supplier eligible for liquidation umeeToken := newToken("uumee", "UMEE") umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) - // Note: Setting atom collateral weight to 0.01 to make the supplier eligible for liquidation + // Note: Setting atom collateral weight to 0.01 to make the second supplier eligible for liquidation atomIBCToken := newToken(atomDenom, "ATOM") atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) - supplierAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) + targets, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{supplierAddr, anotherSupplier}, supplierAddress) + s.Require().Equal([]sdk.AccAddress{addr, addr2}, targets) } diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 47bc9b7f5c..02e6077d61 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -22,18 +22,19 @@ func (s *IntegrationTestSuite) TestSetReserves() { func (s *IntegrationTestSuite) TestRepayBadDebt() { // Creating a supplier so module account has some uumee - _ = s.setupAccount(umeeDenom, 200000000, 200000000, 0, false) // 200 umee + addr := s.newAccount(coin(umeeDenom, 200_000000)) + s.supply(addr, coin(umeeDenom, 200_000000)) // Using an address with no assets - addr := s.setupAccount(umeeDenom, 0, 0, 0, false) + addr2 := s.newAccount() // Create an uncollateralized debt position badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee - err := s.tk.SetBorrow(s.ctx, addr, badDebt) + err := s.tk.SetBorrow(s.ctx, addr2, badDebt) s.Require().NoError(err) // Manually mark the bad debt for repayment - s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr, umeeDenom, true)) + s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr2, umeeDenom, true)) // Manually set reserves to 60 umee reserve := sdk.NewInt64Coin(umeeDenom, 60000000) @@ -45,7 +46,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { s.Require().NoError(err) // Confirm that a debt of 40 umee remains - remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) + remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr2, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) // Confirm that reserves are exhausted @@ -62,7 +63,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { s.Require().NoError(err) // Confirm that the debt is eliminated - remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeDenom) + remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr2, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) // Confirm that reserves are now at 30 umee diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 7bf7f2fe09..b6aa01935a 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -147,6 +147,16 @@ func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk } } +// decollateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) decollateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range uTokens { + err := app.LeverageKeeper.Decollateralize(ctx, addr, coin) + require.NoError(err, "decollateralize") + } +} + // borrow tokens as an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) borrow(addr sdk.AccAddress, coins ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() @@ -171,90 +181,3 @@ func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coi err := app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, coins) require.NoError(err, "forceBorroww") } - -// setupAccount executes some common boilerplate before a test, where a user account is given tokens of a given denom, -// may also supply them to receive uTokens, and may also enable those uTokens as collateral and borrow tokens in the same denom. -func (s *IntegrationTestSuite) setupAccount(denom string, mintAmount, supplyAmount, borrowAmount int64, collateral bool) sdk.AccAddress { - // create a unique address - s.setupAccountCounter = s.setupAccountCounter.Add(sdk.OneInt()) - addrStr := fmt.Sprintf("%-20s", "addr"+s.setupAccountCounter.String()+"_______________") - addr := sdk.AccAddress([]byte(addrStr)) - - // register the account in AccountKeeper - acct := s.app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) - s.app.AccountKeeper.SetAccount(s.ctx, acct) - - if mintAmount > 0 { - // mint and send mintAmount tokens to account - s.Require().NoError(s.app.BankKeeper.MintCoins(s.ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), - )) - s.Require().NoError(s.app.BankKeeper.SendCoinsFromModuleToAccount(s.ctx, minttypes.ModuleName, addr, - sdk.NewCoins(sdk.NewInt64Coin(denom, mintAmount)), - )) - } - - if supplyAmount > 0 { - // account supplies supplyAmount tokens and receives uTokens - uTokens, err := s.app.LeverageKeeper.Supply(s.ctx, addr, sdk.NewInt64Coin(denom, supplyAmount)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(types.ToUTokenDenom(denom), supplyAmount), uTokens) - } - - if collateral { - // account enables associated uToken as collateral - collat, err := s.app.LeverageKeeper.ExchangeToken(s.ctx, sdk.NewInt64Coin(denom, supplyAmount)) - s.Require().NoError(err) - err = s.app.LeverageKeeper.Collateralize(s.ctx, addr, collat) - s.Require().NoError(err) - } - - if borrowAmount > 0 { - // account borrows borrowAmount tokens - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(denom, borrowAmount)) - s.Require().NoError(err) - } - - // return the account addresse - return addr -} - -// initialize the common starting scenario from which borrow and repay tests stem: -// Umee and u/umee are registered assets; a "supplier" account has 9k umee and 1k u/umee; -// the leverage module has 1k umee in its lending pool (module account); and a "bum" -// account has been created with no assets. -func (s *IntegrationTestSuite) initBorrowScenario() (supplier, bum sdk.AccAddress) { - app, ctx := s.app, s.ctx - - // create an account and address which will represent a supplier - supplierAddr := sdk.AccAddress([]byte("addr______________01")) - supplierAcc := app.AccountKeeper.NewAccountWithAddress(ctx, supplierAddr) - app.AccountKeeper.SetAccount(ctx, supplierAcc) - - // create an account and address which will represent a user with no assets - bumAddr := sdk.AccAddress([]byte("addr______________02")) - bumAcc := app.AccountKeeper.NewAccountWithAddress(ctx, bumAddr) - app.AccountKeeper.SetAccount(ctx, bumAcc) - - // mint and send 10k umee to supplier - s.Require().NoError(app.BankKeeper.MintCoins(ctx, minttypes.ModuleName, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee - )) - s.Require().NoError(app.BankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, supplierAddr, - sdk.NewCoins(sdk.NewInt64Coin(umeeapp.BondDenom, 10000000000)), // 10k umee, - )) - - // supplier supplies 1000 umee and receives 1k u/umee - supplyCoin := sdk.NewInt64Coin(umeeapp.BondDenom, 1000000000) - _, err := s.app.LeverageKeeper.Supply(ctx, supplierAddr, supplyCoin) - s.Require().NoError(err) - - // supplier enables u/umee as collateral - collat, err := s.app.LeverageKeeper.ExchangeToken(ctx, supplyCoin) - s.Require().NoError(err) - err = s.app.LeverageKeeper.Collateralize(ctx, supplierAddr, collat) - s.Require().NoError(err) - - // return the account addresses - return supplierAddr, bumAddr -} From c317faef4e0479ae5cd4b1857b711226ad27c61e Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:02:38 -0700 Subject: [PATCH 19/36] imports --- x/leverage/keeper/exchange_rate_test.go | 2 +- x/leverage/keeper/interest_test.go | 2 +- x/leverage/keeper/invariants_test.go | 6 +++--- x/leverage/keeper/reserves_test.go | 2 +- x/leverage/keeper/suite_test.go | 10 +++++----- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x/leverage/keeper/exchange_rate_test.go b/x/leverage/keeper/exchange_rate_test.go index 0862c1fc0c..42c0da04da 100644 --- a/x/leverage/keeper/exchange_rate_test.go +++ b/x/leverage/keeper/exchange_rate_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - umeeapp "github.com/umee-network/umee/v2/app" + umeeapp "github.com/umee-network/umee/v3/app" ) func (s *IntegrationTestSuite) TestDeriveExchangeRate() { diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 7f8cedf513..3b92303c55 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - umeeapp "github.com/umee-network/umee/v2/app" + umeeapp "github.com/umee-network/umee/v3/app" ) func (s *IntegrationTestSuite) TestAccrueZeroInterest() { diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index c5e71fa2e4..679c044ac8 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -3,9 +3,9 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - umeeapp "github.com/umee-network/umee/v2/app" - "github.com/umee-network/umee/v2/x/leverage/keeper" - "github.com/umee-network/umee/v2/x/leverage/types" + umeeapp "github.com/umee-network/umee/v3/app" + "github.com/umee-network/umee/v3/x/leverage/keeper" + "github.com/umee-network/umee/v3/x/leverage/types" ) // checkInvariants is used during other tests to quickly test all invariants diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 02e6077d61..48cf3bafd4 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" - umeeapp "github.com/umee-network/umee/v2/app" + umeeapp "github.com/umee-network/umee/v3/app" ) func (s *IntegrationTestSuite) TestSetReserves() { diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index b6aa01935a..868d1c5779 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -13,11 +13,11 @@ import ( tmrand "github.com/tendermint/tendermint/libs/rand" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - umeeapp "github.com/umee-network/umee/v2/app" - "github.com/umee-network/umee/v2/x/leverage" - "github.com/umee-network/umee/v2/x/leverage/fixtures" - "github.com/umee-network/umee/v2/x/leverage/keeper" - "github.com/umee-network/umee/v2/x/leverage/types" + umeeapp "github.com/umee-network/umee/v3/app" + "github.com/umee-network/umee/v3/x/leverage" + "github.com/umee-network/umee/v3/x/leverage/fixtures" + "github.com/umee-network/umee/v3/x/leverage/keeper" + "github.com/umee-network/umee/v3/x/leverage/types" ) const ( From 84b454b3a68cdc3ec7f18f459950938e90ce5682 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:06:54 -0700 Subject: [PATCH 20/36] lint++ --- x/leverage/keeper/keeper.go | 6 +++--- x/leverage/keeper/validate.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x/leverage/keeper/keeper.go b/x/leverage/keeper/keeper.go index cfa3ea063b..c87efa4dd0 100644 --- a/x/leverage/keeper/keeper.go +++ b/x/leverage/keeper/keeper.go @@ -118,7 +118,7 @@ func (k Keeper) Supply(ctx sdk.Context, supplierAddr sdk.AccAddress, coin sdk.Co // balances are insufficient to withdraw the full amount requested, returns an error. // Returns the amount of base tokens received. func (k Keeper) Withdraw(ctx sdk.Context, supplierAddr sdk.AccAddress, uToken sdk.Coin) (sdk.Coin, error) { - if err := k.validateUToken(ctx, uToken); err != nil { + if err := k.validateUToken(uToken); err != nil { return sdk.Coin{}, err } @@ -253,7 +253,7 @@ func (k Keeper) Borrow(ctx sdk.Context, borrowerAddr sdk.AccAddress, borrow sdk. // necessary amount is transferred. Because amount repaid may be less than the repayment attempted, // Repay returns the actual amount repaid. func (k Keeper) Repay(ctx sdk.Context, borrowerAddr sdk.AccAddress, payment sdk.Coin) (sdk.Coin, error) { - if err := k.validateRepay(ctx, payment); err != nil { + if err := k.validateRepay(payment); err != nil { return sdk.Coin{}, err } @@ -289,7 +289,7 @@ func (k Keeper) Collateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin // Decollateralize disables selected uTokens for use as collateral by a single borrower. func (k Keeper) Decollateralize(ctx sdk.Context, borrowerAddr sdk.AccAddress, coin sdk.Coin) error { - if err := k.validateUToken(ctx, coin); err != nil { + if err := k.validateUToken(coin); err != nil { return err } diff --git a/x/leverage/keeper/validate.go b/x/leverage/keeper/validate.go index 33c0d515ef..59e5eaedb3 100644 --- a/x/leverage/keeper/validate.go +++ b/x/leverage/keeper/validate.go @@ -62,7 +62,7 @@ func (k Keeper) validateSupply(ctx sdk.Context, coin sdk.Coin) error { } // validateUToken validates an sdk.Coin and ensures its Denom is a uToken. Used by Withdraw and Decollateralize. -func (k Keeper) validateUToken(ctx sdk.Context, coin sdk.Coin) error { +func (k Keeper) validateUToken(coin sdk.Coin) error { if err := coin.Validate(); err != nil { return err } @@ -88,7 +88,7 @@ func (k Keeper) validateBorrow(ctx sdk.Context, borrow sdk.Coin) error { } // validateRepay validates an sdk.Coin and ensures its Denom is not a uToken -func (k Keeper) validateRepay(ctx sdk.Context, coin sdk.Coin) error { +func (k Keeper) validateRepay(coin sdk.Coin) error { if err := coin.Validate(); err != nil { return err } From 137b60b630d0af5bec8ae0d9a80030955c4323b1 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:12:25 -0700 Subject: [PATCH 21/36] fix test --- x/leverage/keeper/borrows_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index eee0c77ee8..e3c008225f 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -89,15 +89,15 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { borrower2 := s.newAccount(coin(umeeDenom, 1000)) s.supply(borrower2, coin(umeeDenom, 1000)) s.collateralize(borrower2, coin("u/"+umeeDenom, 1000)) - s.borrow(borrower2, coin(umeeDenom, 123)) + s.borrow(borrower2, coin(umeeDenom, 456)) // confirm total borrowed amount is 579 uumee borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 579), borrowed) + s.Require().Equal(coin(umeeDenom, 579), borrowed) // interest scalar test - scalar changing after borrow (as it does when interest accrues) s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("2.00"))) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 1158), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) + s.Require().Equal(coin(umeeDenom, 1158), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) } func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { From 16f6087bdb62392f907b5f9f7a2684a40e31f539 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:21:46 -0700 Subject: [PATCH 22/36] ++ --- x/leverage/keeper/borrows_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index e3c008225f..5b83b99e6a 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -85,19 +85,19 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) - // creates account which has supplied and collateralized 1000 uumee, and borrowed 456 uumee + // creates account which has supplied and collateralized 1000 uumee, and borrowed 234 uumee borrower2 := s.newAccount(coin(umeeDenom, 1000)) s.supply(borrower2, coin(umeeDenom, 1000)) s.collateralize(borrower2, coin("u/"+umeeDenom, 1000)) - s.borrow(borrower2, coin(umeeDenom, 456)) + s.borrow(borrower2, coin(umeeDenom, 234)) - // confirm total borrowed amount is 579 uumee + // confirm total borrowed amount is 357 uumee borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) - s.Require().Equal(coin(umeeDenom, 579), borrowed) + s.Require().Equal(coin(umeeDenom, 357), borrowed) // interest scalar test - scalar changing after borrow (as it does when interest accrues) s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("2.00"))) - s.Require().Equal(coin(umeeDenom, 1158), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) + s.Require().Equal(coin(umeeDenom, 714), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) } func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { From c951541bc4ebcc2edf60f83ee2eb354264e7dfa8 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:23:12 -0700 Subject: [PATCH 23/36] cl++ --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b85f0d7a8c..167cf18bae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -98,6 +98,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - [1012](https://github.com/umee-network/umee/pull/1012) Improve negative time elapsed error message - [1236](https://github.com/umee-network/umee/pull/1236) Improve leverage event fields. - [1294](https://github.com/umee-network/umee/pull/1294) Simplify window progress query math. +- [1300](https://github.com/umee-network/umee/pull/1300) Improve leverage test suite and error specificity. ### Bug Fixes From abc1520072bc243ba73dcda77b1ddb01135e4da8 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:29:20 -0700 Subject: [PATCH 24/36] remove unnecessary interface --- x/leverage/client/tests/suite.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/x/leverage/client/tests/suite.go b/x/leverage/client/tests/suite.go index 450140855a..b4a55e910a 100644 --- a/x/leverage/client/tests/suite.go +++ b/x/leverage/client/tests/suite.go @@ -42,12 +42,6 @@ func (s *IntegrationTestSuite) TearDownSuite() { s.network.Cleanup() } -// TestCases are queries and transactions that can be run, and return a boolean -// which indicates to abort the test suite if true -type TestCase interface { - Run(s *IntegrationTestSuite) -} - // runTestQuery func (s *IntegrationTestSuite) runTestQueries(tqs ...testQuery) { for _, t := range tqs { From 58d3dabc3741595aa460038511c94fef18650f90 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Mon, 29 Aug 2022 18:45:57 -0700 Subject: [PATCH 25/36] move function --- x/leverage/keeper/invariants_test.go | 6 ------ x/leverage/keeper/suite_test.go | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 679c044ac8..261a46def7 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -8,12 +8,6 @@ import ( "github.com/umee-network/umee/v3/x/leverage/types" ) -// checkInvariants is used during other tests to quickly test all invariants -func (s *IntegrationTestSuite) checkInvariants(msg string) { - desc, broken := keeper.AllInvariants(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken, msg, desc) -} - func (s *IntegrationTestSuite) TestReserveAmountInvariant() { // artificially set reserves err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 868d1c5779..50716ef53e 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -181,3 +181,9 @@ func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coi err := app.BankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, addr, coins) require.NoError(err, "forceBorroww") } + +// checkInvariants is used during other tests to quickly test all invariants +func (s *IntegrationTestSuite) checkInvariants(msg string) { + desc, broken := keeper.AllInvariants(s.app.LeverageKeeper)(s.ctx) + s.Require().False(broken, msg, desc) +} From 357b4c3e5330ecf7b0649a6c3acb8cc25d94953b Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:11:58 -0700 Subject: [PATCH 26/36] apply s.app, s.ctx, and s.Require() shorthands --- x/leverage/keeper/borrows_test.go | 172 +++++++++++++----------- x/leverage/keeper/collateral_test.go | 47 +++---- x/leverage/keeper/exchange_rate_test.go | 14 +- x/leverage/keeper/grpc_query_test.go | 48 ++++--- x/leverage/keeper/interest_test.go | 80 ++++++----- x/leverage/keeper/invariants_test.go | 42 +++--- x/leverage/keeper/iter_test.go | 58 ++++---- x/leverage/keeper/keeper_test.go | 2 +- x/leverage/keeper/oracle_test.go | 74 +++++----- x/leverage/keeper/reserves_test.go | 58 ++++---- x/leverage/keeper/suite_test.go | 13 +- x/leverage/keeper/token_test.go | 30 +++-- 12 files changed, 349 insertions(+), 289 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index 5b83b99e6a..c7381f7c59 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -7,9 +7,11 @@ import ( ) func (s *IntegrationTestSuite) TestGetBorrow() { + ctx, require := s.ctx, s.Require() + // get uumee borrow amount of empty account address (zero) - borrowed := s.tk.GetBorrow(s.ctx, sdk.AccAddress{}, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), borrowed) + borrowed := s.tk.GetBorrow(ctx, sdk.AccAddress{}, umeeDenom) + require.Equal(sdk.NewInt64Coin(umeeDenom, 0), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee addr := s.newAccount(coin(umeeDenom, 1000)) @@ -18,62 +20,64 @@ func (s *IntegrationTestSuite) TestGetBorrow() { s.borrow(addr, coin(umeeDenom, 123)) // confirm borrowed amount is 123 uumee - borrowed = s.tk.GetBorrow(s.ctx, addr, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) + borrowed = s.tk.GetBorrow(ctx, addr, umeeDenom) + require.Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) // unregistered denom (zero) - borrowed = s.tk.GetBorrow(s.ctx, addr, "abcd") - s.Require().Equal(sdk.NewInt64Coin("abcd", 0), borrowed) + borrowed = s.tk.GetBorrow(ctx, addr, "abcd") + require.Equal(sdk.NewInt64Coin("abcd", 0), borrowed) // we do not test empty denom, as that will cause a panic } func (s *IntegrationTestSuite) TestSetBorrow() { + ctx, require := s.ctx, s.Require() + // empty account address - err := s.tk.SetBorrow(s.ctx, sdk.AccAddress{}, sdk.NewInt64Coin(umeeDenom, 123)) - s.Require().EqualError(err, "empty address") + err := s.tk.SetBorrow(ctx, sdk.AccAddress{}, sdk.NewInt64Coin(umeeDenom, 123)) + require.EqualError(err, "empty address") addr := s.newAccount() // set nonzero borrow, and confirm effect - err = s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 123)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 123), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) + err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 123)) + require.NoError(err) + require.Equal(sdk.NewInt64Coin(umeeDenom, 123), s.tk.GetBorrow(ctx, addr, umeeDenom)) // set zero borrow, and confirm effect - err = s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 0)) - s.Require().NoError(err) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) + err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 0)) + require.NoError(err) + require.Equal(sdk.NewInt64Coin(umeeDenom, 0), s.tk.GetBorrow(ctx, addr, umeeDenom)) // unregistered (but valid) denom - err = s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin("abcd", 123)) - s.Require().NoError(err) + err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin("abcd", 123)) + require.NoError(err) // interest scalar test - ensure borrowing smallest possible amount doesn't round to zero at scalar = 1.0001 - s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("1.0001"))) - s.Require().NoError(s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) + require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("1.0001"))) + require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) + require.Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) // interest scalar test - scalar changing after borrow (as it does when interest accrues) - s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("1.0"))) - s.Require().NoError(s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 200))) - s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("2.33"))) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 466), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) + require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("1.0"))) + require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 200))) + require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("2.33"))) + require.Equal(sdk.NewInt64Coin(umeeDenom, 466), s.tk.GetBorrow(ctx, addr, umeeDenom)) // interest scalar extreme case - rounding up becomes apparent at high borrow amount - s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("555444333222111"))) - s.Require().NoError(s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) - s.Require().NoError(s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 20000))) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 20001), s.tk.GetBorrow(s.ctx, addr, umeeDenom)) - - // we do not test empty denom, as that will cause a panic + require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("555444333222111"))) + require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) + require.Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 20000))) + require.Equal(sdk.NewInt64Coin(umeeDenom, 20001), s.tk.GetBorrow(ctx, addr, umeeDenom)) } func (s *IntegrationTestSuite) TestGetTotalBorrowed() { + ctx, require := s.ctx, s.Require() + // unregistered denom (zero) - borrowed := s.tk.GetTotalBorrowed(s.ctx, "abcd") - s.Require().Equal(sdk.NewInt64Coin("abcd", 0), borrowed) + borrowed := s.tk.GetTotalBorrowed(ctx, "abcd") + require.Equal(sdk.NewInt64Coin("abcd", 0), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee borrower := s.newAccount(coin(umeeDenom, 1000)) @@ -82,8 +86,8 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { s.borrow(borrower, coin(umeeDenom, 123)) // confirm total borrowed amount is 123 uumee - borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) + borrowed = s.tk.GetTotalBorrowed(ctx, umeeDenom) + require.Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 234 uumee borrower2 := s.newAccount(coin(umeeDenom, 1000)) @@ -92,18 +96,20 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { s.borrow(borrower2, coin(umeeDenom, 234)) // confirm total borrowed amount is 357 uumee - borrowed = s.tk.GetTotalBorrowed(s.ctx, umeeDenom) - s.Require().Equal(coin(umeeDenom, 357), borrowed) + borrowed = s.tk.GetTotalBorrowed(ctx, umeeDenom) + require.Equal(coin(umeeDenom, 357), borrowed) // interest scalar test - scalar changing after borrow (as it does when interest accrues) - s.Require().NoError(s.tk.SetInterestScalar(s.ctx, umeeDenom, sdk.MustNewDecFromStr("2.00"))) - s.Require().Equal(coin(umeeDenom, 714), s.tk.GetTotalBorrowed(s.ctx, umeeDenom)) + require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("2.00"))) + require.Equal(coin(umeeDenom, 714), s.tk.GetTotalBorrowed(ctx, umeeDenom)) } func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { + ctx, require := s.ctx, s.Require() + // unregistered denom (zero) - available := s.tk.GetAvailableToBorrow(s.ctx, "abcd") - s.Require().Equal(sdk.ZeroInt(), available) + available := s.tk.GetAvailableToBorrow(ctx, "abcd") + require.Equal(sdk.ZeroInt(), available) // creates account which has supplied and collateralized 1000 uumee supplier := s.newAccount(coin(umeeDenom, 1000)) @@ -111,8 +117,8 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { s.collateralize(supplier, coin("u/"+umeeDenom, 1000)) // confirm lending pool is 1000 uumee - available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt(1000), available) + available = s.tk.GetAvailableToBorrow(ctx, umeeDenom) + require.Equal(sdk.NewInt(1000), available) // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee borrower := s.newAccount(coin(umeeDenom, 1000)) @@ -121,19 +127,21 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { s.borrow(borrower, coin(umeeDenom, 123)) // confirm lending pool is 1877 uumee - available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt(1877), available) + available = s.tk.GetAvailableToBorrow(ctx, umeeDenom) + require.Equal(sdk.NewInt(1877), available) // artificially reserve 200 uumee, reducing available amount to 1677 - s.Require().NoError(s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeDenom, 200))) - available = s.tk.GetAvailableToBorrow(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt(1677), available) + require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 200))) + available = s.tk.GetAvailableToBorrow(ctx, umeeDenom) + require.Equal(sdk.NewInt(1677), available) } func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { + ctx, require := s.ctx, s.Require() + // unregistered denom (0 borrowed and 0 lending pool is considered 100%) - utilization := s.tk.SupplyUtilization(s.ctx, "abcd") - s.Require().Equal(sdk.OneDec(), utilization) + utilization := s.tk.SupplyUtilization(ctx, "abcd") + require.Equal(sdk.OneDec(), utilization) // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) @@ -144,62 +152,64 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { // utilization = (Total Borrowed / (Total Borrowed + Module Balance - Reserved Amount)) // 0% utilization (0 / 0+1000-0) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.ZeroDec(), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.ZeroDec(), utilization) // user borrows 200 uumee, reducing module account to 800 uumee s.borrow(addr, coin(umeeDenom, 200)) // 20% utilization (200 / 200+800-0) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("0.2"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("0.2"), utilization) // artificially reserve 200 uumee - s.Require().NoError(s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeDenom, 200))) + require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 200))) // 25% utilization (200 / 200+800-200) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("0.25"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("0.25"), utilization) // user borrows 600 uumee (disregard borrow limit), reducing module account to 0 uumee s.forceBorrow(addr, coin(umeeDenom, 600)) // 100% utilization (800 / 800+200-200)) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("1.0"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set user borrow to 1200 umee - s.Require().NoError(s.tk.SetBorrow(s.ctx, addr, sdk.NewInt64Coin(umeeDenom, 1200))) + require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1200))) // still 100% utilization (1200 / 1200+200-200) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("1.0"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 800 uumee - s.Require().NoError(s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeDenom, 800))) + require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 800))) // edge case interpreted as 100% utilization (1200 / 1200+200-800) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("1.0"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 4000 uumee - s.Require().NoError(s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeDenom, 4000))) + require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 4000))) // impossible case interpreted as 100% utilization (1200 / 1200+200-4000) - utilization = s.tk.SupplyUtilization(s.ctx, umeeDenom) - s.Require().Equal(sdk.MustNewDecFromStr("1.0"), utilization) + utilization = s.tk.SupplyUtilization(ctx, umeeDenom) + require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) } func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { + app, ctx, require := s.app, s.ctx, s.Require() + // Empty coins - borrowLimit, err := s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, sdk.NewCoins()) - s.Require().NoError(err) - s.Require().Equal(sdk.ZeroDec(), borrowLimit) + borrowLimit, err := app.LeverageKeeper.CalculateBorrowLimit(ctx, sdk.NewCoins()) + require.NoError(err) + require.Equal(sdk.ZeroDec(), borrowLimit) // Unregistered asset invalidCoins := sdk.NewCoins(sdk.NewInt64Coin("abcd", 1000)) - _, err = s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, invalidCoins) - s.Require().ErrorIs(err, types.ErrNotUToken) + _, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, invalidCoins) + require.ErrorIs(err, types.ErrNotUToken) // Create collateral uTokens (1k u/umee) umeeCollatDenom := types.ToUTokenDenom(umeeDenom) @@ -212,9 +222,9 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { Mul(sdk.MustNewDecFromStr("0.25")) // Check borrow limit vs. manually computed value - borrowLimit, err = s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, umeeCollateral) - s.Require().NoError(err) - s.Require().Equal(expectedUmeeLimit, borrowLimit) + borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, umeeCollateral) + require.NoError(err) + require.Equal(expectedUmeeLimit, borrowLimit) // Create collateral atom uTokens (1k u/uatom) atomCollatDenom := types.ToUTokenDenom(atomDenom) @@ -227,16 +237,16 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { Mul(sdk.MustNewDecFromStr("0.25")) // Check borrow limit vs. manually computed value - borrowLimit, err = s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, atomCollateral) - s.Require().NoError(err) - s.Require().Equal(expectedAtomLimit, borrowLimit) + borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, atomCollateral) + require.NoError(err) + require.Equal(expectedAtomLimit, borrowLimit) // Compute the expected borrow limit of the two combined collateral coins expectedCombinedLimit := expectedUmeeLimit.Add(expectedAtomLimit) combinedCollateral := umeeCollateral.Add(atomCollateral...) // Check borrow limit vs. manually computed value - borrowLimit, err = s.app.LeverageKeeper.CalculateBorrowLimit(s.ctx, combinedCollateral) - s.Require().NoError(err) - s.Require().Equal(expectedCombinedLimit, borrowLimit) + borrowLimit, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, combinedCollateral) + require.NoError(err) + require.Equal(expectedCombinedLimit, borrowLimit) } diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index 049f75e0db..ee1dc4415f 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -7,47 +7,48 @@ import ( ) func (s *IntegrationTestSuite) TestGetCollateralAmount() { + ctx, require := s.ctx, s.Require() uDenom := types.ToUTokenDenom(umeeDenom) // get u/umee collateral amount of empty account address - collateral := s.tk.GetCollateralAmount(s.ctx, sdk.AccAddress{}, uDenom) - s.Require().Equal(coin(uDenom, 0), collateral) + collateral := s.tk.GetCollateralAmount(ctx, sdk.AccAddress{}, uDenom) + require.Equal(coin(uDenom, 0), collateral) // fund an account addr := s.newAccount(coin(umeeDenom, 1000)) // get u/umee collateral amount of non-empty account address - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(coin(uDenom, 0), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) + require.Equal(coin(uDenom, 0), collateral) // supply 1000 u/uumee but do not collateralize s.supply(addr, coin(umeeDenom, 1000)) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(coin(uDenom, 0), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) + require.Equal(coin(uDenom, 0), collateral) // enable u/umee as collateral s.collateralize(addr, coin(uDenom, 1000)) // confirm collateral amount is 1000 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(coin(uDenom, 1000), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) + require.Equal(coin(uDenom, 1000), collateral) // collateral amount of non-utoken denom (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, addr, umeeDenom) - s.Require().Equal(coin(umeeDenom, 0), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, umeeDenom) + require.Equal(coin(umeeDenom, 0), collateral) // collateral amount of unregistered denom (zero) - collateral = s.tk.GetCollateralAmount(s.ctx, addr, "abcd") - s.Require().Equal(coin("abcd", 0), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") + require.Equal(coin("abcd", 0), collateral) // disable u/umee as collateral s.decollateralize(addr, coin(uDenom, 1000)) // confirm collateral amount is 0 u/uumee - collateral = s.tk.GetCollateralAmount(s.ctx, addr, uDenom) - s.Require().Equal(coin(uDenom, 0), collateral) + collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) + require.Equal(coin(uDenom, 0), collateral) // we do not test empty denom, as that will cause a panic } @@ -67,32 +68,32 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { require.ErrorContains(err, "invalid denom") // set u/umee collateral amount - s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 10))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 10))) // confirm effect collateral := s.tk.GetCollateralAmount(ctx, addr, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 10), collateral) + require.Equal(sdk.NewInt64Coin(uDenom, 10), collateral) // set u/umee collateral amount to zero - s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 0))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 0))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) - s.Require().Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + require.Equal(sdk.NewInt64Coin(uDenom, 0), collateral) // set unregistered token collateral amount - s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 10))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 10))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") - s.Require().Equal(sdk.NewInt64Coin("abcd", 10), collateral) + require.Equal(sdk.NewInt64Coin("abcd", 10), collateral) // set unregistered token collateral amount to zero - s.Require().NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 0))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 0))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") - s.Require().Equal(sdk.NewInt64Coin("abcd", 0), collateral) + require.Equal(sdk.NewInt64Coin("abcd", 0), collateral) // we do not test empty denom, as that will cause a panic } @@ -111,6 +112,6 @@ func (s *IntegrationTestSuite) TestTotalCollateral() { s.collateralize(addr, coin(uDenom, 100_000000)) // Test nonzero collateral - collateral = s.app.LeverageKeeper.GetTotalCollateral(ctx, uDenom) + collateral = app.LeverageKeeper.GetTotalCollateral(ctx, uDenom) require.Equal(sdk.NewInt(100_000000), collateral, "nonzero collateral") } diff --git a/x/leverage/keeper/exchange_rate_test.go b/x/leverage/keeper/exchange_rate_test.go index 42c0da04da..6956ca1084 100644 --- a/x/leverage/keeper/exchange_rate_test.go +++ b/x/leverage/keeper/exchange_rate_test.go @@ -7,18 +7,20 @@ import ( ) func (s *IntegrationTestSuite) TestDeriveExchangeRate() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) s.supply(addr, coin(umeeDenom, 1000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000)) // artificially increase total borrows (by affecting a single address) - err := s.tk.SetBorrow(s.ctx, addr, coin(umeeDenom, 2000)) - s.Require().NoError(err) + err := s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 2000)) + require.NoError(err) // artificially set reserves - err = s.tk.SetReserveAmount(s.ctx, coin(umeeDenom, 300)) - s.Require().NoError(err) + err = s.tk.SetReserveAmount(ctx, coin(umeeDenom, 300)) + require.NoError(err) // expected token:uToken exchange rate // = (total borrows + module balance - reserves) / utoken supply @@ -26,6 +28,6 @@ func (s *IntegrationTestSuite) TestDeriveExchangeRate() { // = 2.7 // get derived exchange rate - rate := s.app.LeverageKeeper.DeriveExchangeRate(s.ctx, umeeapp.BondDenom) - s.Require().Equal(sdk.MustNewDecFromStr("2.7"), rate) + rate := app.LeverageKeeper.DeriveExchangeRate(ctx, umeeapp.BondDenom) + require.Equal(sdk.MustNewDecFromStr("2.7"), rate) } diff --git a/x/leverage/keeper/grpc_query_test.go b/x/leverage/keeper/grpc_query_test.go index 5d3dccf291..069e5bcacc 100644 --- a/x/leverage/keeper/grpc_query_test.go +++ b/x/leverage/keeper/grpc_query_test.go @@ -9,25 +9,31 @@ import ( ) func (s *IntegrationTestSuite) TestQuerier_RegisteredTokens() { - resp, err := s.queryClient.RegisteredTokens(s.ctx.Context(), &types.QueryRegisteredTokens{}) - s.Require().NoError(err) - s.Require().Len(resp.Registry, 2, "token registry length") + ctx, require := s.ctx, s.Require() + + resp, err := s.queryClient.RegisteredTokens(ctx.Context(), &types.QueryRegisteredTokens{}) + require.NoError(err) + require.Len(resp.Registry, 2, "token registry length") } func (s *IntegrationTestSuite) TestQuerier_Params() { - resp, err := s.queryClient.Params(s.ctx.Context(), &types.QueryParams{}) - s.Require().NoError(err) - s.Require().Equal(types.DefaultParams(), resp.Params) + ctx, require := s.ctx, s.Require() + + resp, err := s.queryClient.Params(ctx.Context(), &types.QueryParams{}) + require.NoError(err) + require.Equal(types.DefaultParams(), resp.Params) } func (s *IntegrationTestSuite) TestQuerier_MarketSummary() { + require := s.Require() + req := &types.QueryMarketSummary{} _, err := s.queryClient.MarketSummary(context.Background(), req) - s.Require().Error(err) + require.Error(err) req = &types.QueryMarketSummary{Denom: "uumee"} resp, err := s.queryClient.MarketSummary(context.Background(), req) - s.Require().NoError(err) + require.NoError(err) oraclePrice := sdk.MustNewDecFromStr("0.00000421") @@ -51,17 +57,19 @@ func (s *IntegrationTestSuite) TestQuerier_MarketSummary() { AvailableWithdraw: sdk.ZeroInt(), AvailableCollateralize: sdk.ZeroInt(), } - s.Require().Equal(expected, *resp) + require.Equal(expected, *resp) } func (s *IntegrationTestSuite) TestQuerier_AccountBalances() { + ctx, require := s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) s.supply(addr, coin(umeeDenom, 1000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000)) - resp, err := s.queryClient.AccountBalances(s.ctx.Context(), &types.QueryAccountBalances{Address: addr.String()}) - s.Require().NoError(err) + resp, err := s.queryClient.AccountBalances(ctx.Context(), &types.QueryAccountBalances{Address: addr.String()}) + require.NoError(err) expected := types.QueryAccountBalancesResponse{ Supplied: sdk.NewCoins( @@ -73,17 +81,19 @@ func (s *IntegrationTestSuite) TestQuerier_AccountBalances() { Borrowed: nil, } - s.Require().Equal(expected, *resp) + require.Equal(expected, *resp) } func (s *IntegrationTestSuite) TestQuerier_AccountSummary() { + ctx, require := s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 UMEE addr := s.newAccount(coin(umeeDenom, 1000_000000)) s.supply(addr, coin(umeeDenom, 1000_000000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) - resp, err := s.queryClient.AccountSummary(s.ctx.Context(), &types.QueryAccountSummary{Address: addr.String()}) - s.Require().NoError(err) + resp, err := s.queryClient.AccountSummary(ctx.Context(), &types.QueryAccountSummary{Address: addr.String()}) + require.NoError(err) expected := types.QueryAccountSummaryResponse{ // This result is umee's oracle exchange rate from @@ -102,16 +112,18 @@ func (s *IntegrationTestSuite) TestQuerier_AccountSummary() { LiquidationThreshold: sdk.MustNewDecFromStr("1052.5"), } - s.Require().Equal(expected, *resp) + require.Equal(expected, *resp) } func (s *IntegrationTestSuite) TestQuerier_LiquidationTargets() { - resp, err := s.queryClient.LiquidationTargets(s.ctx.Context(), &types.QueryLiquidationTargets{}) - s.Require().NoError(err) + ctx, require := s.ctx, s.Require() + + resp, err := s.queryClient.LiquidationTargets(ctx.Context(), &types.QueryLiquidationTargets{}) + require.NoError(err) expected := types.QueryLiquidationTargetsResponse{ Targets: nil, } - s.Require().Equal(expected, *resp) + require.Equal(expected, *resp) } diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 3b92303c55..785d81732e 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -7,42 +7,46 @@ import ( ) func (s *IntegrationTestSuite) TestAccrueZeroInterest() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 UMEE addr := s.newAccount(coin(umeeDenom, 1000_000000)) s.supply(addr, coin(umeeDenom, 1000_000000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 40 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) - s.Require().NoError(err) + err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + require.NoError(err) // verify user's loan amount (40 umee) - loanBalance := s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + loanBalance := app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) + require.Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) // Because no time has passed since genesis (due to test setup) this will not // increase borrowed amount. - err = s.app.LeverageKeeper.AccrueAllInterest(s.ctx) - s.Require().NoError(err) + err = app.LeverageKeeper.AccrueAllInterest(ctx) + require.NoError(err) // verify user's loan amount (40 umee) - loanBalance = s.app.LeverageKeeper.GetBorrow(s.ctx, addr, umeeapp.BondDenom) - s.Require().Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + loanBalance = app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) + require.Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) // borrow APY at utilization = 4% // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 - borrowAPY := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(sdk.MustNewDecFromStr("0.03"), borrowAPY) + borrowAPY := app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.Equal(sdk.MustNewDecFromStr("0.03"), borrowAPY) // supply APY when borrow APY is 3% // and utilization is 4%, and reservefactor is 20%, and OracleRewardFactor is 1% // 0.03 * 0.04 * (1 - 0.21) = 0.000948 - supplyAPY := s.app.LeverageKeeper.DeriveSupplyAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("0.000948"), supplyAPY) + supplyAPY := app.LeverageKeeper.DeriveSupplyAPY(ctx, umeeapp.BondDenom) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("0.000948"), supplyAPY) } func (s *IntegrationTestSuite) TestDynamicInterest() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 UMEE addr := s.newAccount(coin(umeeDenom, 1000_000000)) s.supply(addr, coin(umeeDenom, 1000_000000)) @@ -52,49 +56,51 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") // to allow high utilization umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1.0") // to allow high utilization - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) // Base interest rate (0% utilization) - rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.02")) + rate := app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.Equal(rate, sdk.MustNewDecFromStr("0.02")) // user borrows 200 umee, utilization 200/1000 - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().NoError(err) + err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + require.NoError(err) // Between base interest and kink (20% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.07")) + rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.Equal(rate, sdk.MustNewDecFromStr("0.07")) // user borrows 600 more umee, utilization 800/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 600000000)) - s.Require().NoError(err) + err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 600000000)) + require.NoError(err) // Kink interest rate (80% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.22")) + rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.NoError(err) + require.Equal(rate, sdk.MustNewDecFromStr("0.22")) // user borrows 100 more umee, utilization 900/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) + err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + require.NoError(err) // Between kink interest and max (90% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("0.87")) + rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.NoError(err) + require.Equal(rate, sdk.MustNewDecFromStr("0.87")) // user borrows 100 more umee, utilization 1000/1000 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) - s.Require().NoError(err) + err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + require.NoError(err) // Max interest rate (100% utilization) - rate = s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(rate, sdk.MustNewDecFromStr("1.52")) + rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) + require.NoError(err) + require.Equal(rate, sdk.MustNewDecFromStr("1.52")) } func (s *IntegrationTestSuite) TestDynamicInterest_InvalidAsset() { - rate := s.app.LeverageKeeper.DeriveBorrowAPY(s.ctx, "uabc") - s.Require().Equal(rate, sdk.ZeroDec()) + app, ctx, require := s.app, s.ctx, s.Require() + + rate := app.LeverageKeeper.DeriveBorrowAPY(ctx, "uabc") + require.Equal(rate, sdk.ZeroDec()) } diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 261a46def7..1be7b14e56 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -9,56 +9,62 @@ import ( ) func (s *IntegrationTestSuite) TestReserveAmountInvariant() { + app, ctx, require := s.app, s.ctx, s.Require() + // artificially set reserves - err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee - s.Require().NoError(err) + err := s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee + require.NoError(err) // check invariants - _, broken := keeper.ReserveAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) + _, broken := keeper.ReserveAmountInvariant(app.LeverageKeeper)(ctx) + require.False(broken) } func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 UMEE addr := s.newAccount(coin(umeeDenom, 1000_000000)) s.supply(addr, coin(umeeDenom, 1000_000000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // check invariant - _, broken := keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) + _, broken := keeper.CollateralAmountInvariant(app.LeverageKeeper)(ctx) + require.False(broken) uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) // withdraw the supplied umee in the initBorrowScenario - _, err := s.app.LeverageKeeper.Withdraw(s.ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) - s.Require().NoError(err) + _, err := app.LeverageKeeper.Withdraw(ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) + require.NoError(err) // check invariant - _, broken = keeper.CollateralAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) + _, broken = keeper.CollateralAmountInvariant(app.LeverageKeeper)(ctx) + require.False(broken) } func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 UMEE addr := s.newAccount(coin(umeeDenom, 1000_000000)) s.supply(addr, coin(umeeDenom, 1000_000000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 20 umee - err := s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) - s.Require().NoError(err) + err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) + require.NoError(err) // check invariant - _, broken := keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) + _, broken := keeper.BorrowAmountInvariant(app.LeverageKeeper)(ctx) + require.False(broken) // user repays 30 umee, actually only 20 because is the min between // the amount borrowed and the amount repaid - _, err = s.app.LeverageKeeper.Repay(s.ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 30000000)) - s.Require().NoError(err) + _, err = app.LeverageKeeper.Repay(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 30000000)) + require.NoError(err) // check invariant - _, broken = keeper.BorrowAmountInvariant(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken) + _, broken = keeper.BorrowAmountInvariant(app.LeverageKeeper)(ctx) + require.False(broken) } diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go index 1df5dc0579..1c6db1f70a 100644 --- a/x/leverage/keeper/iter_test.go +++ b/x/leverage/keeper/iter_test.go @@ -5,6 +5,8 @@ import ( ) func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) s.supply(addr, coin(umeeDenom, 1000)) @@ -13,23 +15,25 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrOneAsset // user borrows 250 umee (max current allowed) s.borrow(addr, coin(umeeDenom, 250)) - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + zeroAddresses, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{}, zeroAddresses) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible to liquidation umeeToken := newToken("uumee", "UMEE") umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) - targetAddress, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{addr}, targetAddress) + targetAddress, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{addr}, targetAddress) } func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) s.supply(addr, coin(umeeDenom, 1000)) @@ -38,9 +42,9 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset // user borrows 250 umee (max current allowed) s.borrow(addr, coin(umeeDenom, 250)) - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + zeroAddresses, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{}, zeroAddresses) // mints and send to addr 100 atom and already // enable 50 u/atom as collateral. @@ -50,30 +54,32 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 - // = 4 - err = s.app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom - s.Require().NoError(err) + // = 4app. + err = app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom + require.NoError(err) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation umeeToken := newToken("uumee", "UMEE") umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) // Note: Setting atom collateral weight to 0.01 to make the user eligible for liquidation atomIBCToken := newToken(atomDenom, "ATOM") atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) - targetAddr, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{addr}, targetAddr) + targetAddr, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{addr}, targetAddr) } func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { + app, ctx, require := s.app, s.ctx, s.Require() + // creates account which has supplied and collateralized 1000 uumee addr := s.newAccount(coin(umeeDenom, 1000)) s.supply(addr, coin(umeeDenom, 1000)) @@ -82,9 +88,9 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { // user borrows 250 umee (max current allowed) s.borrow(addr, coin(umeeDenom, 250)) - zeroAddresses, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{}, zeroAddresses) + zeroAddresses, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{}, zeroAddresses) // creates another account which has supplied and collateralized 100 uatom addr2 := s.newAccount(coin(atomDenom, 100)) @@ -99,16 +105,16 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_TwoAddr() { umeeToken.CollateralWeight = sdk.MustNewDecFromStr("0.05") umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.05") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(s.ctx, umeeToken)) // Note: Setting atom collateral weight to 0.01 to make the second supplier eligible for liquidation atomIBCToken := newToken(atomDenom, "ATOM") atomIBCToken.CollateralWeight = sdk.MustNewDecFromStr("0.01") atomIBCToken.LiquidationThreshold = sdk.MustNewDecFromStr("0.01") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(s.ctx, atomIBCToken)) - targets, err := s.app.LeverageKeeper.GetEligibleLiquidationTargets(s.ctx) - s.Require().NoError(err) - s.Require().Equal([]sdk.AccAddress{addr, addr2}, targets) + targets, err := app.LeverageKeeper.GetEligibleLiquidationTargets(ctx) + require.NoError(err) + require.Equal([]sdk.AccAddress{addr, addr2}, targets) } diff --git a/x/leverage/keeper/keeper_test.go b/x/leverage/keeper/keeper_test.go index 1abc1307eb..683e78a644 100644 --- a/x/leverage/keeper/keeper_test.go +++ b/x/leverage/keeper/keeper_test.go @@ -3,7 +3,7 @@ package keeper_test import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - + "github.com/umee-network/umee/v3/x/leverage/types" ) diff --git a/x/leverage/keeper/oracle_test.go b/x/leverage/keeper/oracle_test.go index 69270e8e65..26fa62d9bc 100644 --- a/x/leverage/keeper/oracle_test.go +++ b/x/leverage/keeper/oracle_test.go @@ -48,64 +48,72 @@ func (m *mockOracleKeeper) Reset() { } func (s *IntegrationTestSuite) TestOracle_TokenPrice() { - p, err := s.app.LeverageKeeper.TokenPrice(s.ctx, umeeapp.BondDenom) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("0.00000421"), p) + app, ctx, require := s.app, s.ctx, s.Require() - p, err = s.app.LeverageKeeper.TokenPrice(s.ctx, atomDenom) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("0.00003938"), p) + p, err := app.LeverageKeeper.TokenPrice(ctx, umeeapp.BondDenom) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("0.00000421"), p) - p, err = s.app.LeverageKeeper.TokenPrice(s.ctx, "foo") - s.Require().Error(err) - s.Require().Equal(sdk.ZeroDec(), p) + p, err = app.LeverageKeeper.TokenPrice(ctx, atomDenom) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("0.00003938"), p) + + p, err = app.LeverageKeeper.TokenPrice(ctx, "foo") + require.Error(err) + require.Equal(sdk.ZeroDec(), p) } func (s *IntegrationTestSuite) TestOracle_TokenValue() { - // 2.4umee * $4.21 - v, err := s.app.LeverageKeeper.TokenValue(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 2400000)) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("10.104"), v) - - v, err = s.app.LeverageKeeper.TokenValue(s.ctx, sdk.NewInt64Coin("foo", 2400000)) - s.Require().Error(err) - s.Require().Equal(sdk.ZeroDec(), v) + app, ctx, require := s.app, s.ctx, s.Require() + + // 2.4 UMEE * $4.21 + v, err := app.LeverageKeeper.TokenValue(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 2400000)) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("10.104"), v) + + v, err = app.LeverageKeeper.TokenValue(ctx, sdk.NewInt64Coin("foo", 2400000)) + require.Error(err) + require.Equal(sdk.ZeroDec(), v) } func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { - // (2.4umee * $4.21) + (4.7atom * $39.38) - v, err := s.app.LeverageKeeper.TotalTokenValue( - s.ctx, + app, ctx, require := s.app, s.ctx, s.Require() + + // (2.4 UMEE * $4.21) + (4.7 ATOM * $39.38) + v, err := app.LeverageKeeper.TotalTokenValue( + ctx, sdk.NewCoins( sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), sdk.NewInt64Coin(atomDenom, 4700000), ), ) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("195.19"), v) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("195.19"), v) // same result, as unregistered token is ignored - v, err = s.app.LeverageKeeper.TotalTokenValue( - s.ctx, + v, err = app.LeverageKeeper.TotalTokenValue( + ctx, sdk.NewCoins( sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), sdk.NewInt64Coin(atomDenom, 4700000), sdk.NewInt64Coin("foo", 4700000), ), ) - s.Require().NoError(err) - s.Require().Equal(sdk.MustNewDecFromStr("195.19"), v) + require.NoError(err) + require.Equal(sdk.MustNewDecFromStr("195.19"), v) } func (s *IntegrationTestSuite) TestOracle_PriceRatio() { - r, err := s.app.LeverageKeeper.PriceRatio(s.ctx, umeeapp.BondDenom, atomDenom) - s.Require().NoError(err) + app, ctx, require := s.app, s.ctx, s.Require() + + r, err := app.LeverageKeeper.PriceRatio(ctx, umeeapp.BondDenom, atomDenom) + require.NoError(err) // $4.21 / $39.38 - s.Require().Equal(sdk.MustNewDecFromStr("0.106907059421025901"), r) + require.Equal(sdk.MustNewDecFromStr("0.106907059421025901"), r) - _, err = s.app.LeverageKeeper.PriceRatio(s.ctx, "foo", atomDenom) - s.Require().Error(err) + _, err = app.LeverageKeeper.PriceRatio(ctx, "foo", atomDenom) + require.Error(err) - _, err = s.app.LeverageKeeper.PriceRatio(s.ctx, umeeapp.BondDenom, "foo") - s.Require().Error(err) + _, err = app.LeverageKeeper.PriceRatio(ctx, umeeapp.BondDenom, "foo") + require.Error(err) } diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 48cf3bafd4..4309e9a949 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -7,20 +7,24 @@ import ( ) func (s *IntegrationTestSuite) TestSetReserves() { + app, ctx, require := s.app, s.ctx, s.Require() + // get initial reserves - amount := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) - s.Require().Equal(amount, sdk.ZeroInt()) + amount := app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) + require.Equal(amount, sdk.ZeroInt()) // artifically reserve 200 umee - err := s.tk.SetReserveAmount(s.ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) - s.Require().NoError(err) + err := s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + require.NoError(err) // get new reserves - amount = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeapp.BondDenom) - s.Require().Equal(amount, sdk.NewInt(200000000)) + amount = app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) + require.Equal(amount, sdk.NewInt(200000000)) } func (s *IntegrationTestSuite) TestRepayBadDebt() { + app, ctx, require := s.app, s.ctx, s.Require() + // Creating a supplier so module account has some uumee addr := s.newAccount(coin(umeeDenom, 200_000000)) s.supply(addr, coin(umeeDenom, 200_000000)) @@ -30,47 +34,47 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Create an uncollateralized debt position badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee - err := s.tk.SetBorrow(s.ctx, addr2, badDebt) - s.Require().NoError(err) + err := s.tk.SetBorrow(ctx, addr2, badDebt) + require.NoError(err) // Manually mark the bad debt for repayment - s.Require().NoError(s.tk.SetBadDebtAddress(s.ctx, addr2, umeeDenom, true)) + require.NoError(s.tk.SetBadDebtAddress(ctx, addr2, umeeDenom, true)) // Manually set reserves to 60 umee reserve := sdk.NewInt64Coin(umeeDenom, 60000000) - err = s.tk.SetReserveAmount(s.ctx, reserve) - s.Require().NoError(err) + err = s.tk.SetReserveAmount(ctx, reserve) + require.NoError(err) // Sweep all bad debts, which should repay 60 umee of the bad debt (partial repayment) - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) + err = app.LeverageKeeper.SweepBadDebts(ctx) + require.NoError(err) // Confirm that a debt of 40 umee remains - remainingDebt := s.app.LeverageKeeper.GetBorrow(s.ctx, addr2, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) + remainingDebt := app.LeverageKeeper.GetBorrow(ctx, addr2, umeeDenom) + require.Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) // Confirm that reserves are exhausted - remainingReserve := s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) - s.Require().Equal(sdk.ZeroInt(), remainingReserve) + remainingReserve := app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) + require.Equal(sdk.ZeroInt(), remainingReserve) // Manually set reserves to 70 umee reserve = sdk.NewInt64Coin(umeeDenom, 70000000) - err = s.tk.SetReserveAmount(s.ctx, reserve) - s.Require().NoError(err) + err = s.tk.SetReserveAmount(ctx, reserve) + require.NoError(err) // Sweep all bad debts, which should fully repay the bad debt this time - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) + err = app.LeverageKeeper.SweepBadDebts(ctx) + require.NoError(err) // Confirm that the debt is eliminated - remainingDebt = s.app.LeverageKeeper.GetBorrow(s.ctx, addr2, umeeDenom) - s.Require().Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) + remainingDebt = app.LeverageKeeper.GetBorrow(ctx, addr2, umeeDenom) + require.Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) // Confirm that reserves are now at 30 umee - remainingReserve = s.app.LeverageKeeper.GetReserveAmount(s.ctx, umeeDenom) - s.Require().Equal(sdk.NewInt(30000000), remainingReserve) + remainingReserve = app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) + require.Equal(sdk.NewInt(30000000), remainingReserve) // Sweep all bad debts - but there are none - err = s.app.LeverageKeeper.SweepBadDebts(s.ctx) - s.Require().NoError(err) + err = app.LeverageKeeper.SweepBadDebts(ctx) + require.NoError(err) } diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 50716ef53e..5fee4ed562 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -41,6 +41,7 @@ func TestKeeperTestSuite(t *testing.T) { } func (s *IntegrationTestSuite) SetupTest() { + require := s.Require() app := umeeapp.Setup(s.T(), false, 1) ctx := app.BaseApp.NewContext(false, tmproto.Header{ ChainID: fmt.Sprintf("test-chain-%s", tmrand.Str(4)), @@ -66,8 +67,8 @@ func (s *IntegrationTestSuite) SetupTest() { app.LeverageKeeper = *app.LeverageKeeper.SetHooks(types.NewMultiHooks()) leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, atomIBCToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) + require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, atomIBCToken)) queryHelper := baseapp.NewQueryServerTestHelper(ctx, app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, keeper.NewQuerier(app.LeverageKeeper)) @@ -107,7 +108,7 @@ func (s *IntegrationTestSuite) newAccount(funds ...sdk.Coin) sdk.AccAddress { addr := sdk.AccAddress([]byte(addrStr)) // register the account in AccountKeeper - acct := app.AccountKeeper.NewAccountWithAddress(s.ctx, addr) + acct := app.AccountKeeper.NewAccountWithAddress(ctx, addr) app.AccountKeeper.SetAccount(ctx, acct) s.fundAccount(addr, funds...) @@ -184,6 +185,8 @@ func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coi // checkInvariants is used during other tests to quickly test all invariants func (s *IntegrationTestSuite) checkInvariants(msg string) { - desc, broken := keeper.AllInvariants(s.app.LeverageKeeper)(s.ctx) - s.Require().False(broken, msg, desc) + app, ctx, require := s.app, s.ctx, s.Require() + + desc, broken := keeper.AllInvariants(app.LeverageKeeper)(ctx) + require.False(broken, msg, desc) } diff --git a/x/leverage/keeper/token_test.go b/x/leverage/keeper/token_test.go index 49b56c8718..901f0ee1ef 100644 --- a/x/leverage/keeper/token_test.go +++ b/x/leverage/keeper/token_test.go @@ -5,21 +5,23 @@ import ( ) func (s *IntegrationTestSuite) TestGetToken() { + app, ctx, require := s.app, s.ctx, s.Require() + uabc := newToken("uabc", "ABC") - s.Require().NoError(s.app.LeverageKeeper.SetTokenSettings(s.ctx, uabc)) + require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, uabc)) - t, err := s.app.LeverageKeeper.GetTokenSettings(s.ctx, "uabc") - s.Require().NoError(err) - s.Require().Equal(t.ReserveFactor, sdk.MustNewDecFromStr("0.2")) - s.Require().Equal(t.CollateralWeight, sdk.MustNewDecFromStr("0.25")) - s.Require().Equal(t.LiquidationThreshold, sdk.MustNewDecFromStr("0.25")) - s.Require().Equal(t.BaseBorrowRate, sdk.MustNewDecFromStr("0.02")) - s.Require().Equal(t.KinkBorrowRate, sdk.MustNewDecFromStr("0.22")) - s.Require().Equal(t.MaxBorrowRate, sdk.MustNewDecFromStr("1.52")) - s.Require().Equal(t.KinkUtilization, sdk.MustNewDecFromStr("0.8")) - s.Require().Equal(t.LiquidationIncentive, sdk.MustNewDecFromStr("0.1")) + t, err := app.LeverageKeeper.GetTokenSettings(ctx, "uabc") + require.NoError(err) + require.Equal(t.ReserveFactor, sdk.MustNewDecFromStr("0.2")) + require.Equal(t.CollateralWeight, sdk.MustNewDecFromStr("0.25")) + require.Equal(t.LiquidationThreshold, sdk.MustNewDecFromStr("0.25")) + require.Equal(t.BaseBorrowRate, sdk.MustNewDecFromStr("0.02")) + require.Equal(t.KinkBorrowRate, sdk.MustNewDecFromStr("0.22")) + require.Equal(t.MaxBorrowRate, sdk.MustNewDecFromStr("1.52")) + require.Equal(t.KinkUtilization, sdk.MustNewDecFromStr("0.8")) + require.Equal(t.LiquidationIncentive, sdk.MustNewDecFromStr("0.1")) - s.Require().NoError(t.AssertBorrowEnabled()) - s.Require().NoError(t.AssertSupplyEnabled()) - s.Require().NoError(t.AssertNotBlacklisted()) + require.NoError(t.AssertBorrowEnabled()) + require.NoError(t.AssertSupplyEnabled()) + require.NoError(t.AssertNotBlacklisted()) } From 2d7c25344feef00412de129f459f63a28009a365 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:17:21 -0700 Subject: [PATCH 27/36] apply coin shorthand in keeper_test package --- x/leverage/keeper/borrows_test.go | 54 ++++++++++++++-------------- x/leverage/keeper/collateral_test.go | 18 +++++----- x/leverage/keeper/interest_test.go | 14 ++++---- x/leverage/keeper/invariants_test.go | 10 +++--- x/leverage/keeper/iter_test.go | 2 +- x/leverage/keeper/oracle_test.go | 14 ++++---- x/leverage/keeper/reserves_test.go | 12 +++---- 7 files changed, 61 insertions(+), 63 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index c7381f7c59..52ecab4f62 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -11,7 +11,7 @@ func (s *IntegrationTestSuite) TestGetBorrow() { // get uumee borrow amount of empty account address (zero) borrowed := s.tk.GetBorrow(ctx, sdk.AccAddress{}, umeeDenom) - require.Equal(sdk.NewInt64Coin(umeeDenom, 0), borrowed) + require.Equal(coin(umeeDenom, 0), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee addr := s.newAccount(coin(umeeDenom, 1000)) @@ -21,11 +21,11 @@ func (s *IntegrationTestSuite) TestGetBorrow() { // confirm borrowed amount is 123 uumee borrowed = s.tk.GetBorrow(ctx, addr, umeeDenom) - require.Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) + require.Equal(coin(umeeDenom, 123), borrowed) // unregistered denom (zero) borrowed = s.tk.GetBorrow(ctx, addr, "abcd") - require.Equal(sdk.NewInt64Coin("abcd", 0), borrowed) + require.Equal(coin("abcd", 0), borrowed) // we do not test empty denom, as that will cause a panic } @@ -34,42 +34,42 @@ func (s *IntegrationTestSuite) TestSetBorrow() { ctx, require := s.ctx, s.Require() // empty account address - err := s.tk.SetBorrow(ctx, sdk.AccAddress{}, sdk.NewInt64Coin(umeeDenom, 123)) + err := s.tk.SetBorrow(ctx, sdk.AccAddress{}, coin(umeeDenom, 123)) require.EqualError(err, "empty address") addr := s.newAccount() // set nonzero borrow, and confirm effect - err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 123)) + err = s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 123)) require.NoError(err) - require.Equal(sdk.NewInt64Coin(umeeDenom, 123), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.Equal(coin(umeeDenom, 123), s.tk.GetBorrow(ctx, addr, umeeDenom)) // set zero borrow, and confirm effect - err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 0)) + err = s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 0)) require.NoError(err) - require.Equal(sdk.NewInt64Coin(umeeDenom, 0), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.Equal(coin(umeeDenom, 0), s.tk.GetBorrow(ctx, addr, umeeDenom)) // unregistered (but valid) denom - err = s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin("abcd", 123)) + err = s.tk.SetBorrow(ctx, addr, coin("abcd", 123)) require.NoError(err) // interest scalar test - ensure borrowing smallest possible amount doesn't round to zero at scalar = 1.0001 require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("1.0001"))) - require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) - require.Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.NoError(s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 1))) + require.Equal(coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) // interest scalar test - scalar changing after borrow (as it does when interest accrues) require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("1.0"))) - require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 200))) + require.NoError(s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 200))) require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("2.33"))) - require.Equal(sdk.NewInt64Coin(umeeDenom, 466), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.Equal(coin(umeeDenom, 466), s.tk.GetBorrow(ctx, addr, umeeDenom)) // interest scalar extreme case - rounding up becomes apparent at high borrow amount require.NoError(s.tk.SetInterestScalar(ctx, umeeDenom, sdk.MustNewDecFromStr("555444333222111"))) - require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1))) - require.Equal(sdk.NewInt64Coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) - require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 20000))) - require.Equal(sdk.NewInt64Coin(umeeDenom, 20001), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.NoError(s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 1))) + require.Equal(coin(umeeDenom, 1), s.tk.GetBorrow(ctx, addr, umeeDenom)) + require.NoError(s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 20000))) + require.Equal(coin(umeeDenom, 20001), s.tk.GetBorrow(ctx, addr, umeeDenom)) } func (s *IntegrationTestSuite) TestGetTotalBorrowed() { @@ -77,7 +77,7 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { // unregistered denom (zero) borrowed := s.tk.GetTotalBorrowed(ctx, "abcd") - require.Equal(sdk.NewInt64Coin("abcd", 0), borrowed) + require.Equal(coin("abcd", 0), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 123 uumee borrower := s.newAccount(coin(umeeDenom, 1000)) @@ -87,7 +87,7 @@ func (s *IntegrationTestSuite) TestGetTotalBorrowed() { // confirm total borrowed amount is 123 uumee borrowed = s.tk.GetTotalBorrowed(ctx, umeeDenom) - require.Equal(sdk.NewInt64Coin(umeeDenom, 123), borrowed) + require.Equal(coin(umeeDenom, 123), borrowed) // creates account which has supplied and collateralized 1000 uumee, and borrowed 234 uumee borrower2 := s.newAccount(coin(umeeDenom, 1000)) @@ -131,7 +131,7 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { require.Equal(sdk.NewInt(1877), available) // artificially reserve 200 uumee, reducing available amount to 1677 - require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 200))) + require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 200))) available = s.tk.GetAvailableToBorrow(ctx, umeeDenom) require.Equal(sdk.NewInt(1677), available) } @@ -163,7 +163,7 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { require.Equal(sdk.MustNewDecFromStr("0.2"), utilization) // artificially reserve 200 uumee - require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 200))) + require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 200))) // 25% utilization (200 / 200+800-200) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) @@ -177,21 +177,21 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set user borrow to 1200 umee - require.NoError(s.tk.SetBorrow(ctx, addr, sdk.NewInt64Coin(umeeDenom, 1200))) + require.NoError(s.tk.SetBorrow(ctx, addr, coin(umeeDenom, 1200))) // still 100% utilization (1200 / 1200+200-200) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 800 uumee - require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 800))) + require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 800))) // edge case interpreted as 100% utilization (1200 / 1200+200-800) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 4000 uumee - require.NoError(s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeDenom, 4000))) + require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 4000))) // impossible case interpreted as 100% utilization (1200 / 1200+200-4000) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) @@ -207,13 +207,13 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { require.Equal(sdk.ZeroDec(), borrowLimit) // Unregistered asset - invalidCoins := sdk.NewCoins(sdk.NewInt64Coin("abcd", 1000)) + invalidCoins := sdk.NewCoins(coin("abcd", 1000)) _, err = app.LeverageKeeper.CalculateBorrowLimit(ctx, invalidCoins) require.ErrorIs(err, types.ErrNotUToken) // Create collateral uTokens (1k u/umee) umeeCollatDenom := types.ToUTokenDenom(umeeDenom) - umeeCollateral := sdk.NewCoins(sdk.NewInt64Coin(umeeCollatDenom, 1000000000)) + umeeCollateral := sdk.NewCoins(coin(umeeCollatDenom, 1000000000)) // Manually compute borrow limit using collateral weight of 0.25 // and placeholder of 1 umee = $4.21. @@ -228,7 +228,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { // Create collateral atom uTokens (1k u/uatom) atomCollatDenom := types.ToUTokenDenom(atomDenom) - atomCollateral := sdk.NewCoins(sdk.NewInt64Coin(atomCollatDenom, 1000000000)) + atomCollateral := sdk.NewCoins(coin(atomCollatDenom, 1000000000)) // Manually compute borrow limit using collateral weight of 0.25 // and placeholder of 1 atom = $39.38 diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index ee1dc4415f..245cdbddef 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -58,7 +58,7 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { uDenom := types.ToUTokenDenom(umeeDenom) // set u/umee collateral amount of empty account address (error) - err := s.tk.SetCollateralAmount(ctx, sdk.AccAddress{}, sdk.NewInt64Coin(uDenom, 0)) + err := s.tk.SetCollateralAmount(ctx, sdk.AccAddress{}, coin(uDenom, 0)) require.EqualError(err, "empty address") addr := s.newAccount() @@ -68,32 +68,32 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { require.ErrorContains(err, "invalid denom") // set u/umee collateral amount - require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 10))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, coin(uDenom, 10))) // confirm effect collateral := s.tk.GetCollateralAmount(ctx, addr, uDenom) - require.Equal(sdk.NewInt64Coin(uDenom, 10), collateral) + require.Equal(coin(uDenom, 10), collateral) // set u/umee collateral amount to zero - require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin(uDenom, 0))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, coin(uDenom, 0))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, uDenom) - require.Equal(sdk.NewInt64Coin(uDenom, 0), collateral) + require.Equal(coin(uDenom, 0), collateral) // set unregistered token collateral amount - require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 10))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, coin("abcd", 10))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") - require.Equal(sdk.NewInt64Coin("abcd", 10), collateral) + require.Equal(coin("abcd", 10), collateral) // set unregistered token collateral amount to zero - require.NoError(s.tk.SetCollateralAmount(ctx, addr, sdk.NewInt64Coin("abcd", 0))) + require.NoError(s.tk.SetCollateralAmount(ctx, addr, coin("abcd", 0))) // confirm effect collateral = s.tk.GetCollateralAmount(ctx, addr, "abcd") - require.Equal(sdk.NewInt64Coin("abcd", 0), collateral) + require.Equal(coin("abcd", 0), collateral) // we do not test empty denom, as that will cause a panic } diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 785d81732e..8ca5ebdb6d 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -15,12 +15,12 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 40 umee - err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 40000000)) require.NoError(err) // verify user's loan amount (40 umee) loanBalance := app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + require.Equal(loanBalance, coin(umeeapp.BondDenom, 40000000)) // Because no time has passed since genesis (due to test setup) this will not // increase borrowed amount. @@ -29,7 +29,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // verify user's loan amount (40 umee) loanBalance = app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, sdk.NewInt64Coin(umeeapp.BondDenom, 40000000)) + require.Equal(loanBalance, coin(umeeapp.BondDenom, 40000000)) // borrow APY at utilization = 4% // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 @@ -63,7 +63,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.02")) // user borrows 200 umee, utilization 200/1000 - err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 200000000)) require.NoError(err) // Between base interest and kink (20% utilization) @@ -71,7 +71,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.07")) // user borrows 600 more umee, utilization 800/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 600000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 600000000)) require.NoError(err) // Kink interest rate (80% utilization) @@ -80,7 +80,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.22")) // user borrows 100 more umee, utilization 900/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100000000)) require.NoError(err) // Between kink interest and max (90% utilization) @@ -89,7 +89,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.87")) // user borrows 100 more umee, utilization 1000/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 100000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100000000)) require.NoError(err) // Max interest rate (100% utilization) diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 1be7b14e56..487c1f8c41 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -1,8 +1,6 @@ package keeper_test import ( - sdk "github.com/cosmos/cosmos-sdk/types" - umeeapp "github.com/umee-network/umee/v3/app" "github.com/umee-network/umee/v3/x/leverage/keeper" "github.com/umee-network/umee/v3/x/leverage/types" @@ -12,7 +10,7 @@ func (s *IntegrationTestSuite) TestReserveAmountInvariant() { app, ctx, require := s.app, s.ctx, s.Require() // artificially set reserves - err := s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 300000000)) // 300 umee + err := s.tk.SetReserveAmount(ctx, coin(umeeapp.BondDenom, 300000000)) // 300 umee require.NoError(err) // check invariants @@ -35,7 +33,7 @@ func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) // withdraw the supplied umee in the initBorrowScenario - _, err := app.LeverageKeeper.Withdraw(ctx, addr, sdk.NewInt64Coin(uTokenDenom, 1000000000)) + _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin(uTokenDenom, 1000000000)) require.NoError(err) // check invariant @@ -52,7 +50,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 20 umee - err := app.LeverageKeeper.Borrow(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 20000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 20000000)) require.NoError(err) // check invariant @@ -61,7 +59,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { // user repays 30 umee, actually only 20 because is the min between // the amount borrowed and the amount repaid - _, err = app.LeverageKeeper.Repay(ctx, addr, sdk.NewInt64Coin(umeeapp.BondDenom, 30000000)) + _, err = app.LeverageKeeper.Repay(ctx, addr, coin(umeeapp.BondDenom, 30000000)) require.NoError(err) // check invariant diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go index 1c6db1f70a..d77dba52ed 100644 --- a/x/leverage/keeper/iter_test.go +++ b/x/leverage/keeper/iter_test.go @@ -55,7 +55,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 // = 4app. - err = app.LeverageKeeper.Borrow(s.ctx, addr, sdk.NewInt64Coin(atomDenom, 4000000)) // 4 atom + err = app.LeverageKeeper.Borrow(s.ctx, addr, coin(atomDenom, 4000000)) // 4 atom require.NoError(err) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation diff --git a/x/leverage/keeper/oracle_test.go b/x/leverage/keeper/oracle_test.go index 26fa62d9bc..d114cedb63 100644 --- a/x/leverage/keeper/oracle_test.go +++ b/x/leverage/keeper/oracle_test.go @@ -67,11 +67,11 @@ func (s *IntegrationTestSuite) TestOracle_TokenValue() { app, ctx, require := s.app, s.ctx, s.Require() // 2.4 UMEE * $4.21 - v, err := app.LeverageKeeper.TokenValue(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 2400000)) + v, err := app.LeverageKeeper.TokenValue(ctx, coin(umeeapp.BondDenom, 2400000)) require.NoError(err) require.Equal(sdk.MustNewDecFromStr("10.104"), v) - v, err = app.LeverageKeeper.TokenValue(ctx, sdk.NewInt64Coin("foo", 2400000)) + v, err = app.LeverageKeeper.TokenValue(ctx, coin("foo", 2400000)) require.Error(err) require.Equal(sdk.ZeroDec(), v) } @@ -83,8 +83,8 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { v, err := app.LeverageKeeper.TotalTokenValue( ctx, sdk.NewCoins( - sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), - sdk.NewInt64Coin(atomDenom, 4700000), + coin(umeeapp.BondDenom, 2400000), + coin(atomDenom, 4700000), ), ) require.NoError(err) @@ -94,9 +94,9 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { v, err = app.LeverageKeeper.TotalTokenValue( ctx, sdk.NewCoins( - sdk.NewInt64Coin(umeeapp.BondDenom, 2400000), - sdk.NewInt64Coin(atomDenom, 4700000), - sdk.NewInt64Coin("foo", 4700000), + coin(umeeapp.BondDenom, 2400000), + coin(atomDenom, 4700000), + coin("foo", 4700000), ), ) require.NoError(err) diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 4309e9a949..77fbdc759e 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -14,7 +14,7 @@ func (s *IntegrationTestSuite) TestSetReserves() { require.Equal(amount, sdk.ZeroInt()) // artifically reserve 200 umee - err := s.tk.SetReserveAmount(ctx, sdk.NewInt64Coin(umeeapp.BondDenom, 200000000)) + err := s.tk.SetReserveAmount(ctx, coin(umeeapp.BondDenom, 200000000)) require.NoError(err) // get new reserves @@ -33,7 +33,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { addr2 := s.newAccount() // Create an uncollateralized debt position - badDebt := sdk.NewInt64Coin(umeeDenom, 100000000) // 100 umee + badDebt := coin(umeeDenom, 100000000) // 100 umee err := s.tk.SetBorrow(ctx, addr2, badDebt) require.NoError(err) @@ -41,7 +41,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { require.NoError(s.tk.SetBadDebtAddress(ctx, addr2, umeeDenom, true)) // Manually set reserves to 60 umee - reserve := sdk.NewInt64Coin(umeeDenom, 60000000) + reserve := coin(umeeDenom, 60000000) err = s.tk.SetReserveAmount(ctx, reserve) require.NoError(err) @@ -51,14 +51,14 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Confirm that a debt of 40 umee remains remainingDebt := app.LeverageKeeper.GetBorrow(ctx, addr2, umeeDenom) - require.Equal(sdk.NewInt64Coin(umeeDenom, 40000000), remainingDebt) + require.Equal(coin(umeeDenom, 40000000), remainingDebt) // Confirm that reserves are exhausted remainingReserve := app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) require.Equal(sdk.ZeroInt(), remainingReserve) // Manually set reserves to 70 umee - reserve = sdk.NewInt64Coin(umeeDenom, 70000000) + reserve = coin(umeeDenom, 70000000) err = s.tk.SetReserveAmount(ctx, reserve) require.NoError(err) @@ -68,7 +68,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Confirm that the debt is eliminated remainingDebt = app.LeverageKeeper.GetBorrow(ctx, addr2, umeeDenom) - require.Equal(sdk.NewInt64Coin(umeeDenom, 0), remainingDebt) + require.Equal(coin(umeeDenom, 0), remainingDebt) // Confirm that reserves are now at 30 umee remainingReserve = app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) From 104e8f217f57f475565f0f2e6f483abf8e48eb8c Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:26:03 -0700 Subject: [PATCH 28/36] use setReserves helper --- x/leverage/keeper/borrows_test.go | 8 ++++---- x/leverage/keeper/exchange_rate_test.go | 3 +-- x/leverage/keeper/invariants_test.go | 3 +-- x/leverage/keeper/reserves_test.go | 10 +++------- x/leverage/keeper/suite_test.go | 10 ++++++++++ 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index 52ecab4f62..ed98ac80e0 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -131,7 +131,7 @@ func (s *IntegrationTestSuite) TestGetAvailableToBorrow() { require.Equal(sdk.NewInt(1877), available) // artificially reserve 200 uumee, reducing available amount to 1677 - require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 200))) + s.setReserves(coin(umeeDenom, 200)) available = s.tk.GetAvailableToBorrow(ctx, umeeDenom) require.Equal(sdk.NewInt(1677), available) } @@ -163,7 +163,7 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { require.Equal(sdk.MustNewDecFromStr("0.2"), utilization) // artificially reserve 200 uumee - require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 200))) + s.setReserves(coin(umeeDenom, 200)) // 25% utilization (200 / 200+800-200) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) @@ -184,14 +184,14 @@ func (s *IntegrationTestSuite) TestDeriveBorrowUtilization() { require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 800 uumee - require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 800))) + s.setReserves(coin(umeeDenom, 800)) // edge case interpreted as 100% utilization (1200 / 1200+200-800) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) require.Equal(sdk.MustNewDecFromStr("1.0"), utilization) // artificially set reserves to 4000 uumee - require.NoError(s.tk.SetReserveAmount(ctx, coin(umeeDenom, 4000))) + s.setReserves(coin(umeeDenom, 4000)) // impossible case interpreted as 100% utilization (1200 / 1200+200-4000) utilization = s.tk.SupplyUtilization(ctx, umeeDenom) diff --git a/x/leverage/keeper/exchange_rate_test.go b/x/leverage/keeper/exchange_rate_test.go index 6956ca1084..d215b73351 100644 --- a/x/leverage/keeper/exchange_rate_test.go +++ b/x/leverage/keeper/exchange_rate_test.go @@ -19,8 +19,7 @@ func (s *IntegrationTestSuite) TestDeriveExchangeRate() { require.NoError(err) // artificially set reserves - err = s.tk.SetReserveAmount(ctx, coin(umeeDenom, 300)) - require.NoError(err) + s.setReserves(coin(umeeDenom, 300)) // expected token:uToken exchange rate // = (total borrows + module balance - reserves) / utoken supply diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 487c1f8c41..c56d2f05d3 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -10,8 +10,7 @@ func (s *IntegrationTestSuite) TestReserveAmountInvariant() { app, ctx, require := s.app, s.ctx, s.Require() // artificially set reserves - err := s.tk.SetReserveAmount(ctx, coin(umeeapp.BondDenom, 300000000)) // 300 umee - require.NoError(err) + s.setReserves(coin(umeeapp.BondDenom, 300_000000)) // check invariants _, broken := keeper.ReserveAmountInvariant(app.LeverageKeeper)(ctx) diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 77fbdc759e..76b2326680 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -14,9 +14,7 @@ func (s *IntegrationTestSuite) TestSetReserves() { require.Equal(amount, sdk.ZeroInt()) // artifically reserve 200 umee - err := s.tk.SetReserveAmount(ctx, coin(umeeapp.BondDenom, 200000000)) - require.NoError(err) - + s.setReserves(coin(umeeapp.BondDenom, 200_000000)) // get new reserves amount = app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) require.Equal(amount, sdk.NewInt(200000000)) @@ -42,8 +40,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Manually set reserves to 60 umee reserve := coin(umeeDenom, 60000000) - err = s.tk.SetReserveAmount(ctx, reserve) - require.NoError(err) + s.setReserves(reserve) // Sweep all bad debts, which should repay 60 umee of the bad debt (partial repayment) err = app.LeverageKeeper.SweepBadDebts(ctx) @@ -59,8 +56,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Manually set reserves to 70 umee reserve = coin(umeeDenom, 70000000) - err = s.tk.SetReserveAmount(ctx, reserve) - require.NoError(err) + s.setReserves(reserve) // Sweep all bad debts, which should fully repay the bad debt this time err = app.LeverageKeeper.SweepBadDebts(ctx) diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 5fee4ed562..4d555d6f97 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -183,6 +183,16 @@ func (s *IntegrationTestSuite) forceBorrow(addr sdk.AccAddress, coins ...sdk.Coi require.NoError(err, "forceBorroww") } +// setReserves artificially sets reserves of one or more tokens to given values +func (s *IntegrationTestSuite) setReserves(coins ...sdk.Coin) { + ctx, require := s.ctx, s.Require() + + for _, coin := range coins { + err := s.tk.SetReserveAmount(ctx, coin) + require.NoError(err, "setReserves") + } +} + // checkInvariants is used during other tests to quickly test all invariants func (s *IntegrationTestSuite) checkInvariants(msg string) { app, ctx, require := s.app, s.ctx, s.Require() From f19ad7320674f97ee514c127ff908dd549b791b1 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:35:47 -0700 Subject: [PATCH 29/36] improve big number readability --- x/leverage/keeper/borrows_test.go | 4 ++-- x/leverage/keeper/interest_test.go | 14 +++++++------- x/leverage/keeper/invariants_test.go | 6 +++--- x/leverage/keeper/iter_test.go | 2 +- x/leverage/keeper/oracle_test.go | 14 +++++++------- x/leverage/keeper/reserves_test.go | 12 ++++++------ x/leverage/keeper/suite_test.go | 5 ++--- 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index ed98ac80e0..3452c45222 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -213,7 +213,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { // Create collateral uTokens (1k u/umee) umeeCollatDenom := types.ToUTokenDenom(umeeDenom) - umeeCollateral := sdk.NewCoins(coin(umeeCollatDenom, 1000000000)) + umeeCollateral := sdk.NewCoins(coin(umeeCollatDenom, 1000_000000)) // Manually compute borrow limit using collateral weight of 0.25 // and placeholder of 1 umee = $4.21. @@ -228,7 +228,7 @@ func (s *IntegrationTestSuite) TestCalculateBorrowLimit() { // Create collateral atom uTokens (1k u/uatom) atomCollatDenom := types.ToUTokenDenom(atomDenom) - atomCollateral := sdk.NewCoins(coin(atomCollatDenom, 1000000000)) + atomCollateral := sdk.NewCoins(coin(atomCollatDenom, 1000_000000)) // Manually compute borrow limit using collateral weight of 0.25 // and placeholder of 1 atom = $39.38 diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 8ca5ebdb6d..702348dfd6 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -15,12 +15,12 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 40 umee - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 40000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 40_000000)) require.NoError(err) // verify user's loan amount (40 umee) loanBalance := app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, coin(umeeapp.BondDenom, 40000000)) + require.Equal(loanBalance, coin(umeeapp.BondDenom, 40_000000)) // Because no time has passed since genesis (due to test setup) this will not // increase borrowed amount. @@ -29,7 +29,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // verify user's loan amount (40 umee) loanBalance = app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, coin(umeeapp.BondDenom, 40000000)) + require.Equal(loanBalance, coin(umeeapp.BondDenom, 40_000000)) // borrow APY at utilization = 4% // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 @@ -63,7 +63,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.02")) // user borrows 200 umee, utilization 200/1000 - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 200000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 200_000000)) require.NoError(err) // Between base interest and kink (20% utilization) @@ -71,7 +71,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.07")) // user borrows 600 more umee, utilization 800/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 600000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 600_000000)) require.NoError(err) // Kink interest rate (80% utilization) @@ -80,7 +80,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.22")) // user borrows 100 more umee, utilization 900/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) require.NoError(err) // Between kink interest and max (90% utilization) @@ -89,7 +89,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(rate, sdk.MustNewDecFromStr("0.87")) // user borrows 100 more umee, utilization 1000/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100000000)) + err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) require.NoError(err) // Max interest rate (100% utilization) diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index c56d2f05d3..063b4da0c6 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -32,7 +32,7 @@ func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) // withdraw the supplied umee in the initBorrowScenario - _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin(uTokenDenom, 1000000000)) + _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin(uTokenDenom, 1000_000000)) require.NoError(err) // check invariant @@ -49,7 +49,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 20 umee - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 20000000)) + err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 20_000000)) require.NoError(err) // check invariant @@ -58,7 +58,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { // user repays 30 umee, actually only 20 because is the min between // the amount borrowed and the amount repaid - _, err = app.LeverageKeeper.Repay(ctx, addr, coin(umeeapp.BondDenom, 30000000)) + _, err = app.LeverageKeeper.Repay(ctx, addr, coin(umeeapp.BondDenom, 30_000000)) require.NoError(err) // check invariant diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go index d77dba52ed..cbc8c756dd 100644 --- a/x/leverage/keeper/iter_test.go +++ b/x/leverage/keeper/iter_test.go @@ -55,7 +55,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 // = 4app. - err = app.LeverageKeeper.Borrow(s.ctx, addr, coin(atomDenom, 4000000)) // 4 atom + err = app.LeverageKeeper.Borrow(s.ctx, addr, coin(atomDenom, 4_000000)) require.NoError(err) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation diff --git a/x/leverage/keeper/oracle_test.go b/x/leverage/keeper/oracle_test.go index d114cedb63..827e9df9b0 100644 --- a/x/leverage/keeper/oracle_test.go +++ b/x/leverage/keeper/oracle_test.go @@ -67,11 +67,11 @@ func (s *IntegrationTestSuite) TestOracle_TokenValue() { app, ctx, require := s.app, s.ctx, s.Require() // 2.4 UMEE * $4.21 - v, err := app.LeverageKeeper.TokenValue(ctx, coin(umeeapp.BondDenom, 2400000)) + v, err := app.LeverageKeeper.TokenValue(ctx, coin(umeeapp.BondDenom, 2_400000)) require.NoError(err) require.Equal(sdk.MustNewDecFromStr("10.104"), v) - v, err = app.LeverageKeeper.TokenValue(ctx, coin("foo", 2400000)) + v, err = app.LeverageKeeper.TokenValue(ctx, coin("foo", 2_400000)) require.Error(err) require.Equal(sdk.ZeroDec(), v) } @@ -83,8 +83,8 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { v, err := app.LeverageKeeper.TotalTokenValue( ctx, sdk.NewCoins( - coin(umeeapp.BondDenom, 2400000), - coin(atomDenom, 4700000), + coin(umeeapp.BondDenom, 2_400000), + coin(atomDenom, 4_700000), ), ) require.NoError(err) @@ -94,9 +94,9 @@ func (s *IntegrationTestSuite) TestOracle_TotalTokenValue() { v, err = app.LeverageKeeper.TotalTokenValue( ctx, sdk.NewCoins( - coin(umeeapp.BondDenom, 2400000), - coin(atomDenom, 4700000), - coin("foo", 4700000), + coin(umeeapp.BondDenom, 2_400000), + coin(atomDenom, 4_700000), + coin("foo", 4_700000), ), ) require.NoError(err) diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index 76b2326680..afbb1b118b 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -17,7 +17,7 @@ func (s *IntegrationTestSuite) TestSetReserves() { s.setReserves(coin(umeeapp.BondDenom, 200_000000)) // get new reserves amount = app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) - require.Equal(amount, sdk.NewInt(200000000)) + require.Equal(amount, sdk.NewInt(200_000000)) } func (s *IntegrationTestSuite) TestRepayBadDebt() { @@ -31,7 +31,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { addr2 := s.newAccount() // Create an uncollateralized debt position - badDebt := coin(umeeDenom, 100000000) // 100 umee + badDebt := coin(umeeDenom, 100_000000) err := s.tk.SetBorrow(ctx, addr2, badDebt) require.NoError(err) @@ -39,7 +39,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { require.NoError(s.tk.SetBadDebtAddress(ctx, addr2, umeeDenom, true)) // Manually set reserves to 60 umee - reserve := coin(umeeDenom, 60000000) + reserve := coin(umeeDenom, 60_000000) s.setReserves(reserve) // Sweep all bad debts, which should repay 60 umee of the bad debt (partial repayment) @@ -48,14 +48,14 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Confirm that a debt of 40 umee remains remainingDebt := app.LeverageKeeper.GetBorrow(ctx, addr2, umeeDenom) - require.Equal(coin(umeeDenom, 40000000), remainingDebt) + require.Equal(coin(umeeDenom, 40_000000), remainingDebt) // Confirm that reserves are exhausted remainingReserve := app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) require.Equal(sdk.ZeroInt(), remainingReserve) // Manually set reserves to 70 umee - reserve = coin(umeeDenom, 70000000) + reserve = coin(umeeDenom, 70_000000) s.setReserves(reserve) // Sweep all bad debts, which should fully repay the bad debt this time @@ -68,7 +68,7 @@ func (s *IntegrationTestSuite) TestRepayBadDebt() { // Confirm that reserves are now at 30 umee remainingReserve = app.LeverageKeeper.GetReserveAmount(ctx, umeeDenom) - require.Equal(sdk.NewInt(30000000), remainingReserve) + require.Equal(sdk.NewInt(30_000000), remainingReserve) // Sweep all bad debts - but there are none err = app.LeverageKeeper.SweepBadDebts(ctx) diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 4d555d6f97..8b012360fd 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -21,9 +21,8 @@ import ( ) const ( - initialPower = int64(10000000000) - umeeDenom = umeeapp.BondDenom - atomDenom = fixtures.AtomDenom + umeeDenom = umeeapp.BondDenom + atomDenom = fixtures.AtomDenom ) type IntegrationTestSuite struct { From 29464d241f29ea16cc94be9ab8d42ace9480fb9c Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 09:55:23 -0700 Subject: [PATCH 30/36] ensure espected value is on the left for all require.Equal --- x/leverage/keeper/interest_test.go | 16 +++++++-------- x/leverage/keeper/math_test.go | 32 +++++++++++++++--------------- x/leverage/keeper/reserves_test.go | 4 ++-- x/leverage/keeper/token_test.go | 16 +++++++-------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 702348dfd6..a2fc06d6c0 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -20,7 +20,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // verify user's loan amount (40 umee) loanBalance := app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, coin(umeeapp.BondDenom, 40_000000)) + require.Equal(coin(umeeapp.BondDenom, 40_000000), loanBalance) // Because no time has passed since genesis (due to test setup) this will not // increase borrowed amount. @@ -29,7 +29,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // verify user's loan amount (40 umee) loanBalance = app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) - require.Equal(loanBalance, coin(umeeapp.BondDenom, 40_000000)) + require.Equal(coin(umeeapp.BondDenom, 40_000000), loanBalance) // borrow APY at utilization = 4% // when kink utilization = 80%, and base/kink APY are 0.02 and 0.22 @@ -60,7 +60,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { // Base interest rate (0% utilization) rate := app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) - require.Equal(rate, sdk.MustNewDecFromStr("0.02")) + require.Equal(sdk.MustNewDecFromStr("0.02"), rate) // user borrows 200 umee, utilization 200/1000 err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 200_000000)) @@ -68,7 +68,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { // Between base interest and kink (20% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) - require.Equal(rate, sdk.MustNewDecFromStr("0.07")) + require.Equal(sdk.MustNewDecFromStr("0.07"), rate) // user borrows 600 more umee, utilization 800/1000 err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 600_000000)) @@ -77,7 +77,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { // Kink interest rate (80% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.NoError(err) - require.Equal(rate, sdk.MustNewDecFromStr("0.22")) + require.Equal(sdk.MustNewDecFromStr("0.22"), rate) // user borrows 100 more umee, utilization 900/1000 err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) @@ -86,7 +86,7 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { // Between kink interest and max (90% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.NoError(err) - require.Equal(rate, sdk.MustNewDecFromStr("0.87")) + require.Equal(sdk.MustNewDecFromStr("0.87"), rate) // user borrows 100 more umee, utilization 1000/1000 err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) @@ -95,12 +95,12 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { // Max interest rate (100% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.NoError(err) - require.Equal(rate, sdk.MustNewDecFromStr("1.52")) + require.Equal(sdk.MustNewDecFromStr("1.52"), rate) } func (s *IntegrationTestSuite) TestDynamicInterest_InvalidAsset() { app, ctx, require := s.app, s.ctx, s.Require() rate := app.LeverageKeeper.DeriveBorrowAPY(ctx, "uabc") - require.Equal(rate, sdk.ZeroDec()) + require.Equal(sdk.ZeroDec(), rate) } diff --git a/x/leverage/keeper/math_test.go b/x/leverage/keeper/math_test.go index b2351466a0..e357b9b37f 100644 --- a/x/leverage/keeper/math_test.go +++ b/x/leverage/keeper/math_test.go @@ -15,28 +15,28 @@ func TestInterpolate(t *testing.T) { y2 := sdk.MustNewDecFromStr("17.4") // Sloped line, endpoint checks - x := Interpolate(x1, x1, y1, x2, y2) - require.Equal(t, x, y1) - x = Interpolate(x2, x1, y1, x2, y2) - require.Equal(t, x, y2) + result := Interpolate(x1, x1, y1, x2, y2) + require.Equal(t, y1, result) + result = Interpolate(x2, x1, y1, x2, y2) + require.Equal(t, y2, result) // Sloped line, point on segment - x = Interpolate(sdk.MustNewDecFromStr("4.0"), x1, y1, x2, y2) - require.Equal(t, x, sdk.MustNewDecFromStr("13.2")) + result = Interpolate(sdk.MustNewDecFromStr("4.0"), x1, y1, x2, y2) + require.Equal(t, sdk.MustNewDecFromStr("13.2"), result) // Sloped line, point outside of segment - x = Interpolate(sdk.MustNewDecFromStr("2.0"), x1, y1, x2, y2) - require.Equal(t, x, sdk.MustNewDecFromStr("9.0")) + result = Interpolate(sdk.MustNewDecFromStr("2.0"), x1, y1, x2, y2) + require.Equal(t, sdk.MustNewDecFromStr("9.0"), result) // Vertical line: always return y1 - x = Interpolate(sdk.ZeroDec(), x1, y1, x1, y2) - require.Equal(t, x, y1) - x = Interpolate(x1, x1, y1, x1, y2) - require.Equal(t, x, y1) + result = Interpolate(sdk.ZeroDec(), x1, y1, x1, y2) + require.Equal(t, y1, result) + result = Interpolate(x1, x1, y1, x1, y2) + require.Equal(t, y1, result) // Undefined line (x1=x2, y1=y2): always return y1 - x = Interpolate(sdk.ZeroDec(), x1, y1, x1, y1) - require.Equal(t, x, y1) - x = Interpolate(x1, x1, y1, x1, y1) - require.Equal(t, x, y1) + result = Interpolate(sdk.ZeroDec(), x1, y1, x1, y1) + require.Equal(t, y1, result) + result = Interpolate(x1, x1, y1, x1, y1) + require.Equal(t, y1, result) } diff --git a/x/leverage/keeper/reserves_test.go b/x/leverage/keeper/reserves_test.go index afbb1b118b..f098bb3992 100644 --- a/x/leverage/keeper/reserves_test.go +++ b/x/leverage/keeper/reserves_test.go @@ -11,13 +11,13 @@ func (s *IntegrationTestSuite) TestSetReserves() { // get initial reserves amount := app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) - require.Equal(amount, sdk.ZeroInt()) + require.Equal(sdk.ZeroInt(), amount) // artifically reserve 200 umee s.setReserves(coin(umeeapp.BondDenom, 200_000000)) // get new reserves amount = app.LeverageKeeper.GetReserveAmount(ctx, umeeapp.BondDenom) - require.Equal(amount, sdk.NewInt(200_000000)) + require.Equal(sdk.NewInt(200_000000), amount) } func (s *IntegrationTestSuite) TestRepayBadDebt() { diff --git a/x/leverage/keeper/token_test.go b/x/leverage/keeper/token_test.go index 901f0ee1ef..e92e130641 100644 --- a/x/leverage/keeper/token_test.go +++ b/x/leverage/keeper/token_test.go @@ -12,14 +12,14 @@ func (s *IntegrationTestSuite) TestGetToken() { t, err := app.LeverageKeeper.GetTokenSettings(ctx, "uabc") require.NoError(err) - require.Equal(t.ReserveFactor, sdk.MustNewDecFromStr("0.2")) - require.Equal(t.CollateralWeight, sdk.MustNewDecFromStr("0.25")) - require.Equal(t.LiquidationThreshold, sdk.MustNewDecFromStr("0.25")) - require.Equal(t.BaseBorrowRate, sdk.MustNewDecFromStr("0.02")) - require.Equal(t.KinkBorrowRate, sdk.MustNewDecFromStr("0.22")) - require.Equal(t.MaxBorrowRate, sdk.MustNewDecFromStr("1.52")) - require.Equal(t.KinkUtilization, sdk.MustNewDecFromStr("0.8")) - require.Equal(t.LiquidationIncentive, sdk.MustNewDecFromStr("0.1")) + require.Equal(sdk.MustNewDecFromStr("0.2"), t.ReserveFactor) + require.Equal(sdk.MustNewDecFromStr("0.25"), t.CollateralWeight) + require.Equal(sdk.MustNewDecFromStr("0.25"), t.LiquidationThreshold) + require.Equal(sdk.MustNewDecFromStr("0.02"), t.BaseBorrowRate) + require.Equal(sdk.MustNewDecFromStr("0.22"), t.KinkBorrowRate) + require.Equal(sdk.MustNewDecFromStr("1.52"), t.MaxBorrowRate) + require.Equal(sdk.MustNewDecFromStr("0.8"), t.KinkUtilization) + require.Equal(sdk.MustNewDecFromStr("0.1"), t.LiquidationIncentive) require.NoError(t.AssertBorrowEnabled()) require.NoError(t.AssertSupplyEnabled()) From 95aa22b56c6c9709974d0e638d96ecd4a5c606a4 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:35:24 -0700 Subject: [PATCH 31/36] use borrow and repay helpers --- x/leverage/keeper/interest_test.go | 21 ++++++--------------- x/leverage/keeper/invariants_test.go | 8 +++----- x/leverage/keeper/iter_test.go | 3 +-- x/leverage/keeper/suite_test.go | 10 ++++++++++ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index a2fc06d6c0..6677653122 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -15,8 +15,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 40 umee - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 40_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 40_000000)) // verify user's loan amount (40 umee) loanBalance := app.LeverageKeeper.GetBorrow(ctx, addr, umeeapp.BondDenom) @@ -24,7 +23,7 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // Because no time has passed since genesis (due to test setup) this will not // increase borrowed amount. - err = app.LeverageKeeper.AccrueAllInterest(ctx) + err := app.LeverageKeeper.AccrueAllInterest(ctx) require.NoError(err) // verify user's loan amount (40 umee) @@ -40,7 +39,6 @@ func (s *IntegrationTestSuite) TestAccrueZeroInterest() { // and utilization is 4%, and reservefactor is 20%, and OracleRewardFactor is 1% // 0.03 * 0.04 * (1 - 0.21) = 0.000948 supplyAPY := app.LeverageKeeper.DeriveSupplyAPY(ctx, umeeapp.BondDenom) - require.NoError(err) require.Equal(sdk.MustNewDecFromStr("0.000948"), supplyAPY) } @@ -63,38 +61,31 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { require.Equal(sdk.MustNewDecFromStr("0.02"), rate) // user borrows 200 umee, utilization 200/1000 - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 200_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 200_000000)) // Between base interest and kink (20% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.Equal(sdk.MustNewDecFromStr("0.07"), rate) // user borrows 600 more umee, utilization 800/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 600_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 600_000000)) // Kink interest rate (80% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) - require.NoError(err) require.Equal(sdk.MustNewDecFromStr("0.22"), rate) // user borrows 100 more umee, utilization 900/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 100_000000)) // Between kink interest and max (90% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) - require.NoError(err) require.Equal(sdk.MustNewDecFromStr("0.87"), rate) // user borrows 100 more umee, utilization 1000/1000 - err = app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 100_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 100_000000)) // Max interest rate (100% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) - require.NoError(err) require.Equal(sdk.MustNewDecFromStr("1.52"), rate) } diff --git a/x/leverage/keeper/invariants_test.go b/x/leverage/keeper/invariants_test.go index 063b4da0c6..b398454ab5 100644 --- a/x/leverage/keeper/invariants_test.go +++ b/x/leverage/keeper/invariants_test.go @@ -32,8 +32,7 @@ func (s *IntegrationTestSuite) TestCollateralAmountInvariant() { uTokenDenom := types.ToUTokenDenom(umeeapp.BondDenom) // withdraw the supplied umee in the initBorrowScenario - _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin(uTokenDenom, 1000_000000)) - require.NoError(err) + s.withdraw(addr, coin(uTokenDenom, 1000_000000)) // check invariant _, broken = keeper.CollateralAmountInvariant(app.LeverageKeeper)(ctx) @@ -49,8 +48,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) // user borrows 20 umee - err := app.LeverageKeeper.Borrow(ctx, addr, coin(umeeapp.BondDenom, 20_000000)) - require.NoError(err) + s.borrow(addr, coin(umeeapp.BondDenom, 20_000000)) // check invariant _, broken := keeper.BorrowAmountInvariant(app.LeverageKeeper)(ctx) @@ -58,7 +56,7 @@ func (s *IntegrationTestSuite) TestBorrowAmountInvariant() { // user repays 30 umee, actually only 20 because is the min between // the amount borrowed and the amount repaid - _, err = app.LeverageKeeper.Repay(ctx, addr, coin(umeeapp.BondDenom, 30_000000)) + _, err := app.LeverageKeeper.Repay(ctx, addr, coin(umeeapp.BondDenom, 30_000000)) require.NoError(err) // check invariant diff --git a/x/leverage/keeper/iter_test.go b/x/leverage/keeper/iter_test.go index cbc8c756dd..7503e6b973 100644 --- a/x/leverage/keeper/iter_test.go +++ b/x/leverage/keeper/iter_test.go @@ -55,8 +55,7 @@ func (s *IntegrationTestSuite) TestGetEligibleLiquidationTargets_OneAddrTwoAsset // user borrows 4 atom (max current allowed - 1) user amount enabled as collateral * CollateralWeight // = (50 * 0.1) - 1 // = 4app. - err = app.LeverageKeeper.Borrow(s.ctx, addr, coin(atomDenom, 4_000000)) - require.NoError(err) + s.borrow(addr, coin(atomDenom, 4_000000)) // Note: Setting umee liquidation threshold to 0.05 to make the user eligible for liquidation umeeToken := newToken("uumee", "UMEE") diff --git a/x/leverage/keeper/suite_test.go b/x/leverage/keeper/suite_test.go index 8b012360fd..55e3f1f9ab 100644 --- a/x/leverage/keeper/suite_test.go +++ b/x/leverage/keeper/suite_test.go @@ -137,6 +137,16 @@ func (s *IntegrationTestSuite) supply(addr sdk.AccAddress, coins ...sdk.Coin) { } } +// withdraw utokens from an account and require no errors. Use when setting up leverage scenarios. +func (s *IntegrationTestSuite) withdraw(addr sdk.AccAddress, coins ...sdk.Coin) { + app, ctx, require := s.app, s.ctx, s.Require() + + for _, coin := range coins { + _, err := app.LeverageKeeper.Withdraw(ctx, addr, coin) + require.NoError(err, "withdraw") + } +} + // collateralize uTokens from an account and require no errors. Use when setting up leverage scenarios. func (s *IntegrationTestSuite) collateralize(addr sdk.AccAddress, uTokens ...sdk.Coin) { app, ctx, require := s.app, s.ctx, s.Require() From 28c4707565b58570d1f65998125fec56b872c50f Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:38:27 -0700 Subject: [PATCH 32/36] lint++ --- x/leverage/types/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/leverage/types/errors.go b/x/leverage/types/errors.go index 6627950f95..960b568d2c 100644 --- a/x/leverage/types/errors.go +++ b/x/leverage/types/errors.go @@ -47,4 +47,4 @@ var ( ErrInconsistentTotalBorrow = sdkerrors.Register(ModuleName, 605, "total adjusted borrow inconsistency") ErrInvalidInteresrScalar = sdkerrors.Register(ModuleName, 606, "interest scalar less than one") ErrExcessiveTimeElapsed = sdkerrors.Register(ModuleName, 607, "excessive time elapsed since last interest time") -) \ No newline at end of file +) From 2031109d654e7c43ab5fa9c5e688fe4aa50cb633 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:54:29 -0700 Subject: [PATCH 33/36] always require.ErrorIs --- x/leverage/keeper/borrows_test.go | 2 +- x/leverage/keeper/collateral_test.go | 2 +- x/leverage/keeper/grpc_query_test.go | 2 +- x/leverage/keeper/oracle_test.go | 9 +++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/x/leverage/keeper/borrows_test.go b/x/leverage/keeper/borrows_test.go index 3452c45222..2e5b3ea76a 100644 --- a/x/leverage/keeper/borrows_test.go +++ b/x/leverage/keeper/borrows_test.go @@ -35,7 +35,7 @@ func (s *IntegrationTestSuite) TestSetBorrow() { // empty account address err := s.tk.SetBorrow(ctx, sdk.AccAddress{}, coin(umeeDenom, 123)) - require.EqualError(err, "empty address") + require.ErrorIs(err, types.ErrEmptyAddress) addr := s.newAccount() diff --git a/x/leverage/keeper/collateral_test.go b/x/leverage/keeper/collateral_test.go index 245cdbddef..8ffdd438a6 100644 --- a/x/leverage/keeper/collateral_test.go +++ b/x/leverage/keeper/collateral_test.go @@ -59,7 +59,7 @@ func (s *IntegrationTestSuite) TestSetCollateralAmount() { // set u/umee collateral amount of empty account address (error) err := s.tk.SetCollateralAmount(ctx, sdk.AccAddress{}, coin(uDenom, 0)) - require.EqualError(err, "empty address") + require.ErrorIs(err, types.ErrEmptyAddress) addr := s.newAccount() diff --git a/x/leverage/keeper/grpc_query_test.go b/x/leverage/keeper/grpc_query_test.go index 069e5bcacc..49c01053e2 100644 --- a/x/leverage/keeper/grpc_query_test.go +++ b/x/leverage/keeper/grpc_query_test.go @@ -29,7 +29,7 @@ func (s *IntegrationTestSuite) TestQuerier_MarketSummary() { req := &types.QueryMarketSummary{} _, err := s.queryClient.MarketSummary(context.Background(), req) - require.Error(err) + require.ErrorContains(err, "empty denom") req = &types.QueryMarketSummary{Denom: "uumee"} resp, err := s.queryClient.MarketSummary(context.Background(), req) diff --git a/x/leverage/keeper/oracle_test.go b/x/leverage/keeper/oracle_test.go index 827e9df9b0..3c6bacfddc 100644 --- a/x/leverage/keeper/oracle_test.go +++ b/x/leverage/keeper/oracle_test.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" umeeapp "github.com/umee-network/umee/v3/app" + "github.com/umee-network/umee/v3/x/leverage/types" ) type mockOracleKeeper struct { @@ -59,7 +60,7 @@ func (s *IntegrationTestSuite) TestOracle_TokenPrice() { require.Equal(sdk.MustNewDecFromStr("0.00003938"), p) p, err = app.LeverageKeeper.TokenPrice(ctx, "foo") - require.Error(err) + require.ErrorIs(err, types.ErrNotRegisteredToken) require.Equal(sdk.ZeroDec(), p) } @@ -72,7 +73,7 @@ func (s *IntegrationTestSuite) TestOracle_TokenValue() { require.Equal(sdk.MustNewDecFromStr("10.104"), v) v, err = app.LeverageKeeper.TokenValue(ctx, coin("foo", 2_400000)) - require.Error(err) + require.ErrorIs(err, types.ErrNotRegisteredToken) require.Equal(sdk.ZeroDec(), v) } @@ -112,8 +113,8 @@ func (s *IntegrationTestSuite) TestOracle_PriceRatio() { require.Equal(sdk.MustNewDecFromStr("0.106907059421025901"), r) _, err = app.LeverageKeeper.PriceRatio(ctx, "foo", atomDenom) - require.Error(err) + require.ErrorIs(err, types.ErrNotRegisteredToken) _, err = app.LeverageKeeper.PriceRatio(ctx, umeeapp.BondDenom, "foo") - require.Error(err) + require.ErrorIs(err, types.ErrNotRegisteredToken) } From e4c6f67e5132fed774c4d81c564f0b28c562ee3d Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 10:55:58 -0700 Subject: [PATCH 34/36] use forceBorrow helper rather than SetTokenSettings in high utilization test --- x/leverage/keeper/interest_test.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/x/leverage/keeper/interest_test.go b/x/leverage/keeper/interest_test.go index 6677653122..a88fc6da9a 100644 --- a/x/leverage/keeper/interest_test.go +++ b/x/leverage/keeper/interest_test.go @@ -50,12 +50,6 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { s.supply(addr, coin(umeeDenom, 1000_000000)) s.collateralize(addr, coin("u/"+umeeDenom, 1000_000000)) - umeeToken := newToken("uumee", "UMEE") - umeeToken.CollateralWeight = sdk.MustNewDecFromStr("1.0") // to allow high utilization - umeeToken.LiquidationThreshold = sdk.MustNewDecFromStr("1.0") // to allow high utilization - - require.NoError(app.LeverageKeeper.SetTokenSettings(ctx, umeeToken)) - // Base interest rate (0% utilization) rate := app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.Equal(sdk.MustNewDecFromStr("0.02"), rate) @@ -67,22 +61,22 @@ func (s *IntegrationTestSuite) TestDynamicInterest() { rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.Equal(sdk.MustNewDecFromStr("0.07"), rate) - // user borrows 600 more umee, utilization 800/1000 - s.borrow(addr, coin(umeeapp.BondDenom, 600_000000)) + // user borrows 600 more umee (ignores collateral), utilization 800/1000 + s.forceBorrow(addr, coin(umeeapp.BondDenom, 600_000000)) // Kink interest rate (80% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.Equal(sdk.MustNewDecFromStr("0.22"), rate) - // user borrows 100 more umee, utilization 900/1000 - s.borrow(addr, coin(umeeapp.BondDenom, 100_000000)) + // user borrows 100 more umee (ignores collateral), utilization 900/1000 + s.forceBorrow(addr, coin(umeeapp.BondDenom, 100_000000)) // Between kink interest and max (90% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) require.Equal(sdk.MustNewDecFromStr("0.87"), rate) - // user borrows 100 more umee, utilization 1000/1000 - s.borrow(addr, coin(umeeapp.BondDenom, 100_000000)) + // user borrows 100 more umee (ignores collateral), utilization 1000/1000 + s.forceBorrow(addr, coin(umeeapp.BondDenom, 100_000000)) // Max interest rate (100% utilization) rate = app.LeverageKeeper.DeriveBorrowAPY(ctx, umeeapp.BondDenom) From 187c0fe0f7cc7f304800c0005a921686f2c82a32 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Tue, 30 Aug 2022 18:05:51 -0700 Subject: [PATCH 35/36] fix #1310 --- x/leverage/simulation/operations_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index e09238054b..cc7d8f9070 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -156,7 +156,8 @@ func (s *SimTestSuite) TestWeightedOperations() { } for i, w := range weightesOps { - operationMsg, _, _ := w.Op()(r, s.app.BaseApp, s.ctx, accs, "") + operationMsg, _, err := w.Op()(r, s.app.BaseApp, s.ctx, accs, "") + s.Require().NoError(err) // the following checks are very much dependent from the ordering of the output given // by WeightedOperations. if the ordering in WeightedOperations changes some tests // will fail From b3379b6afc3db4626566f2c4957b86cd8cbaaf89 Mon Sep 17 00:00:00 2001 From: toteki <63419657+toteki@users.noreply.github.com> Date: Wed, 31 Aug 2022 17:43:08 -0700 Subject: [PATCH 36/36] fix sim tests --- x/leverage/simulation/operations.go | 46 +++++--- x/leverage/simulation/operations_test.go | 130 ++++++----------------- 2 files changed, 62 insertions(+), 114 deletions(-) diff --git a/x/leverage/simulation/operations.go b/x/leverage/simulation/operations.go index 667956a722..f415dcfea5 100644 --- a/x/leverage/simulation/operations.go +++ b/x/leverage/simulation/operations.go @@ -19,7 +19,7 @@ const ( DefaultWeightMsgSupply int = 100 DefaultWeightMsgWithdraw int = 85 DefaultWeightMsgBorrow int = 80 - DefaultWeightMsgCollateralize int = 60 + DefaultWeightMsgCollateralize int = 65 DefaultWeightMsgDecollateralize int = 60 DefaultWeightMsgRepay int = 70 DefaultWeightMsgLiquidate int = 75 @@ -88,28 +88,28 @@ func WeightedOperations( SimulateMsgSupply(ak, bk), ), simulation.NewWeightedOperation( - weightMsgWithdraw, - SimulateMsgWithdraw(ak, bk, lk), + weightMsgCollateralize, + SimulateMsgCollateralize(ak, bk, lk), ), simulation.NewWeightedOperation( weightMsgBorrow, SimulateMsgBorrow(ak, bk, lk), ), simulation.NewWeightedOperation( - weightMsgCollateralize, - SimulateMsgCollateralize(ak, bk, lk), - ), - simulation.NewWeightedOperation( - weightMsgDecollateralize, - SimulateMsgDecollateralize(ak, bk, lk), + weightMsgLiquidate, + SimulateMsgLiquidate(ak, bk, lk), ), simulation.NewWeightedOperation( weightMsgRepay, SimulateMsgRepay(ak, bk, lk), ), simulation.NewWeightedOperation( - weightMsgLiquidate, - SimulateMsgLiquidate(ak, bk, lk), + weightMsgDecollateralize, + SimulateMsgDecollateralize(ak, bk, lk), + ), + simulation.NewWeightedOperation( + weightMsgWithdraw, + SimulateMsgWithdraw(ak, bk, lk), ), } } @@ -355,7 +355,7 @@ func randomDecollateralizeFields( } // randomBorrowFields returns a random account and an sdk.Coin from all -// the registered tokens with an random amount [0, 150]. +// the registered tokens with an random amount [0, 10^6]. // It returns skip=true if no registered token was found. func randomBorrowFields( r *rand.Rand, ctx sdk.Context, accs []simtypes.Account, lk keeper.Keeper, @@ -368,7 +368,7 @@ func randomBorrowFields( } registeredToken := allTokens[r.Int31n(int32(len(allTokens)))] - token = sdk.NewCoin(registeredToken.BaseDenom, simtypes.RandomAmount(r, sdk.NewInt(150))) + token = sdk.NewCoin(registeredToken.BaseDenom, simtypes.RandomAmount(r, sdk.NewInt(1_000000))) return acc, token, false } @@ -401,10 +401,9 @@ func randomLiquidateFields( rewardDenom string, skip bool, ) { - idxLiquidator := r.Intn(len(accs) - 1) - - liquidator = accs[idxLiquidator] - borrower = accs[idxLiquidator+1] + // note: liquidator and borrower might even be the same account + liquidator, _ = simtypes.RandomAcc(r, accs) + borrower, _ = simtypes.RandomAcc(r, accs) collateral := lk.GetBorrowerCollateral(ctx, borrower.Address) if collateral.Empty() { @@ -418,6 +417,19 @@ func randomLiquidateFields( return liquidator, borrower, sdk.Coin{}, "", true } + liquidationThreshold, err := lk.CalculateLiquidationThreshold(ctx, collateral) + if err != nil { + return liquidator, borrower, sdk.Coin{}, "", true + } + borrowedValue, err := lk.TotalTokenValue(ctx, borrowed) + if err != nil { + return liquidator, borrower, sdk.Coin{}, "", true + } + if borrowedValue.LTE(liquidationThreshold) { + // borrower not eligible for liquidation + return liquidator, borrower, sdk.Coin{}, "", true + } + rewardDenom = types.ToTokenDenom(randomCoin(r, collateral).Denom) return liquidator, borrower, randomCoin(r, borrowed), rewardDenom, false diff --git a/x/leverage/simulation/operations_test.go b/x/leverage/simulation/operations_test.go index 730afa3e98..5a16644ea3 100644 --- a/x/leverage/simulation/operations_test.go +++ b/x/leverage/simulation/operations_test.go @@ -14,6 +14,7 @@ import ( umeeapp "github.com/umee-network/umee/v3/app" "github.com/umee-network/umee/v3/x/leverage" + "github.com/umee-network/umee/v3/x/leverage/fixtures" "github.com/umee-network/umee/v3/x/leverage/simulation" "github.com/umee-network/umee/v3/x/leverage/types" ) @@ -32,73 +33,11 @@ func (s *SimTestSuite) SetupTest() { app := umeeapp.Setup(s.T(), checkTx, 1) ctx := app.NewContext(checkTx, tmproto.Header{}) - umeeToken := types.Token{ - BaseDenom: umeeapp.BondDenom, - ReserveFactor: sdk.MustNewDecFromStr("0.25"), - CollateralWeight: sdk.MustNewDecFromStr("0.5"), - LiquidationThreshold: sdk.MustNewDecFromStr("0.5"), - BaseBorrowRate: sdk.MustNewDecFromStr("0.02"), - KinkBorrowRate: sdk.MustNewDecFromStr("0.2"), - MaxBorrowRate: sdk.MustNewDecFromStr("1.0"), - KinkUtilization: sdk.MustNewDecFromStr("0.8"), - LiquidationIncentive: sdk.MustNewDecFromStr("0.1"), - SymbolDenom: umeeapp.DisplayDenom, - Exponent: 6, - EnableMsgSupply: true, - EnableMsgBorrow: true, - Blacklist: false, - MaxCollateralShare: sdk.MustNewDecFromStr("1"), - MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), - MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), - MaxSupply: sdk.NewInt(100000000000), - } - atomIBCToken := types.Token{ - BaseDenom: "ibc/CDC4587874B85BEA4FCEC3CEA5A1195139799A1FEE711A07D972537E18FDA39D", - ReserveFactor: sdk.MustNewDecFromStr("0.25"), - CollateralWeight: sdk.MustNewDecFromStr("0.8"), - LiquidationThreshold: sdk.MustNewDecFromStr("0.8"), - BaseBorrowRate: sdk.MustNewDecFromStr("0.05"), - KinkBorrowRate: sdk.MustNewDecFromStr("0.3"), - MaxBorrowRate: sdk.MustNewDecFromStr("0.9"), - KinkUtilization: sdk.MustNewDecFromStr("0.75"), - LiquidationIncentive: sdk.MustNewDecFromStr("0.11"), - SymbolDenom: "ATOM", - Exponent: 6, - EnableMsgSupply: true, - EnableMsgBorrow: true, - Blacklist: false, - MaxCollateralShare: sdk.MustNewDecFromStr("1"), - MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), - MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), - MaxSupply: sdk.NewInt(100000000000), - } - uabc := types.Token{ - BaseDenom: "uabc", - ReserveFactor: sdk.MustNewDecFromStr("0"), - CollateralWeight: sdk.MustNewDecFromStr("0.1"), - LiquidationThreshold: sdk.MustNewDecFromStr("0.1"), - BaseBorrowRate: sdk.MustNewDecFromStr("0.02"), - KinkBorrowRate: sdk.MustNewDecFromStr("0.22"), - MaxBorrowRate: sdk.MustNewDecFromStr("1.52"), - KinkUtilization: sdk.MustNewDecFromStr("0.87"), - LiquidationIncentive: sdk.MustNewDecFromStr("0.1"), - SymbolDenom: "ABC", - Exponent: 6, - EnableMsgSupply: true, - EnableMsgBorrow: true, - Blacklist: false, - MaxCollateralShare: sdk.MustNewDecFromStr("1"), - MaxSupplyUtilization: sdk.MustNewDecFromStr("0.9"), - MinCollateralLiquidity: sdk.MustNewDecFromStr("0"), - MaxSupply: sdk.NewInt(100000000000), - } - - tokens := []types.Token{umeeToken, atomIBCToken, uabc} leverage.InitGenesis(ctx, app.LeverageKeeper, *types.DefaultGenesis()) - for _, token := range tokens { - s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, token)) - app.OracleKeeper.SetExchangeRate(ctx, token.SymbolDenom, sdk.MustNewDecFromStr("100.0")) - } + + // Use default umee token for sim tests + s.Require().NoError(app.LeverageKeeper.SetTokenSettings(ctx, fixtures.Token("uumee", "UMEE"))) + app.OracleKeeper.SetExchangeRate(ctx, "UMEE", sdk.MustNewDecFromStr("100.0")) s.app = app s.ctx = ctx @@ -118,7 +57,7 @@ func (s *SimTestSuite) unmarshal(op *simtypes.OperationMsg, msg proto.Message) { func (s *SimTestSuite) getTestingAccounts(r *rand.Rand, n int, cb func(fundedAccount simtypes.Account)) []simtypes.Account { accounts := simtypes.RandomAccounts(r, n) - initAmt := sdk.NewInt(200000000) // 200 * 10^6 + initAmt := sdk.NewInt(200_000000) accCoins := sdk.NewCoins() tokens := s.app.LeverageKeeper.GetAllRegisteredTokens(s.ctx) @@ -146,30 +85,29 @@ func (s *SimTestSuite) TestWeightedOperations() { cdc := s.app.AppCodec() appParams := make(simtypes.AppParams) - weightesOps := simulation.WeightedOperations(appParams, cdc, s.app.AccountKeeper, s.app.BankKeeper, s.app.LeverageKeeper) + weightedOps := simulation.WeightedOperations(appParams, cdc, s.app.AccountKeeper, s.app.BankKeeper, s.app.LeverageKeeper) - // setup 3 accounts + // setup 1 account, which will test all 7 operations in order. the order is designed such that each + // transaction with prerequisites (e.g. must collateralize before borrow) has a chance to succeed. r := rand.New(rand.NewSource(1)) - accs := s.getTestingAccounts(r, 3, func(acc simtypes.Account) {}) + accs := s.getTestingAccounts(r, 1, func(acc simtypes.Account) {}) expected := []struct { weight int opMsgName string }{ + // the order of expected ops must match the order of weightedOps. {simulation.DefaultWeightMsgSupply, sdk.MsgTypeURL(new(types.MsgSupply))}, - {simulation.DefaultWeightMsgWithdraw, sdk.MsgTypeURL(new(types.MsgWithdraw))}, - {simulation.DefaultWeightMsgBorrow, sdk.MsgTypeURL(new(types.MsgBorrow))}, {simulation.DefaultWeightMsgCollateralize, sdk.MsgTypeURL(new(types.MsgCollateralize))}, - {simulation.DefaultWeightMsgDecollateralize, sdk.MsgTypeURL(new(types.MsgDecollateralize))}, - {simulation.DefaultWeightMsgRepay, sdk.MsgTypeURL(new(types.MsgRepay))}, + {simulation.DefaultWeightMsgBorrow, sdk.MsgTypeURL(new(types.MsgBorrow))}, {simulation.DefaultWeightMsgLiquidate, sdk.MsgTypeURL(new(types.MsgLiquidate))}, + {simulation.DefaultWeightMsgRepay, sdk.MsgTypeURL(new(types.MsgRepay))}, + {simulation.DefaultWeightMsgDecollateralize, sdk.MsgTypeURL(new(types.MsgDecollateralize))}, + {simulation.DefaultWeightMsgWithdraw, sdk.MsgTypeURL(new(types.MsgWithdraw))}, } - for i, w := range weightesOps { + for i, w := range weightedOps { operationMsg, _, err := w.Op()(r, s.app.BaseApp, s.ctx, accs, "") s.Require().NoError(err) - // the following checks are very much dependent from the ordering of the output given - // by WeightedOperations. if the ordering in WeightedOperations changes some tests - // will fail s.Require().Equal(expected[i].weight, w.Weight(), "weight should be the same") s.Require().Equal(expected[i].opMsgName, operationMsg.Name, "operation Msg name should be the same") } @@ -188,13 +126,13 @@ func (s *SimTestSuite) TestSimulateMsgSupply() { var msg types.MsgSupply s.unmarshal(&operationMsg, &msg) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) - s.Require().Equal("185121068uumee", msg.Asset.String()) + s.Require().Equal("4896096uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgWithdraw() { r := rand.New(rand.NewSource(1)) - supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(100)) + supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(10_000000)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { _, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -211,13 +149,13 @@ func (s *SimTestSuite) TestSimulateMsgWithdraw() { s.unmarshal(&operationMsg, &msg) s.Require().True(operationMsg.OK) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Supplier) - s.Require().Equal("73u/uumee", msg.Asset.String()) + s.Require().Equal("560969u/uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgBorrow() { r := rand.New(rand.NewSource(8)) - supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(1000)) + supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(10_000000)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { uToken, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -236,13 +174,13 @@ func (s *SimTestSuite) TestSimulateMsgBorrow() { var msg types.MsgBorrow s.unmarshal(&operationMsg, &msg) s.Require().Equal("umee1qnclgkcxtuledc8xhle4lqly2q0z96uqkks60s", msg.Borrower) - s.Require().Equal("67uumee", msg.Asset.String()) + s.Require().Equal("675395uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgCollateralize() { r := rand.New(rand.NewSource(1)) - supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 100) + supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 10_000000) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { _, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -258,13 +196,13 @@ func (s *SimTestSuite) TestSimulateMsgCollateralize() { var msg types.MsgCollateralize s.unmarshal(&operationMsg, &msg) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) - s.Require().Equal("73u/uumee", msg.Asset.String()) + s.Require().Equal("560969u/uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgDecollateralize() { r := rand.New(rand.NewSource(1)) - supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 100) + supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 10_000000) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { uToken, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -281,14 +219,14 @@ func (s *SimTestSuite) TestSimulateMsgDecollateralize() { var msg types.MsgDecollateralize s.unmarshal(&operationMsg, &msg) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) - s.Require().Equal("73u/uumee", msg.Asset.String()) + s.Require().Equal("560969u/uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgRepay() { r := rand.New(rand.NewSource(1)) - supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 100) - borrowToken := sdk.NewInt64Coin(umeeapp.BondDenom, 20) + supplyToken := sdk.NewInt64Coin(umeeapp.BondDenom, 10_000000) + borrowToken := sdk.NewInt64Coin(umeeapp.BondDenom, 1_000000) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { uToken, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -306,15 +244,15 @@ func (s *SimTestSuite) TestSimulateMsgRepay() { var msg types.MsgRepay s.unmarshal(&operationMsg, &msg) s.Require().Equal("umee1ghekyjucln7y67ntx7cf27m9dpuxxemn8w6h33", msg.Borrower) - s.Require().Equal("9uumee", msg.Asset.String()) + s.Require().Equal("560969uumee", msg.Asset.String()) s.Require().Len(futureOperations, 0) } func (s *SimTestSuite) TestSimulateMsgLiquidate() { r := rand.New(rand.NewSource(1)) - supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(100)) - uToken := sdk.NewCoin("u/"+umeeapp.BondDenom, sdk.NewInt(100)) - borrowToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(10)) + supplyToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(10_000000)) + uToken := sdk.NewCoin("u/"+umeeapp.BondDenom, sdk.NewInt(10_000000)) + borrowToken := sdk.NewCoin(umeeapp.BondDenom, sdk.NewInt(1_000000)) accs := s.getTestingAccounts(r, 3, func(fundedAccount simtypes.Account) { _, err := s.app.LeverageKeeper.Supply(s.ctx, fundedAccount.Address, supplyToken) @@ -327,12 +265,10 @@ func (s *SimTestSuite) TestSimulateMsgLiquidate() { op := simulation.SimulateMsgLiquidate(s.app.AccountKeeper, s.app.BankKeeper, s.app.LeverageKeeper) operationMsg, futureOperations, err := op(r, s.app.BaseApp, s.ctx, accs, "") - s.Require().EqualError(err, - "failed to execute message; message index: 0: borrower not eligible for liquidation", - ) + s.Require().NoError(err) // While it is no longer simple to create an eligible liquidation target using exported keeper methods here, - // we can still verify some properties of the resulting operation. + // we can still verify some properties of the resulting no-op. s.Require().Empty(operationMsg.Msg) s.Require().False(operationMsg.OK) s.Require().Len(futureOperations, 0)