mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 02:57:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			362 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package keeper_test
 | 
						|
 | 
						|
import (
 | 
						|
	"time"
 | 
						|
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	abci "github.com/tendermint/tendermint/abci/types"
 | 
						|
	"github.com/tendermint/tendermint/crypto"
 | 
						|
	tmtime "github.com/tendermint/tendermint/types/time"
 | 
						|
 | 
						|
	"github.com/kava-labs/kava/app"
 | 
						|
	"github.com/kava-labs/kava/x/hard"
 | 
						|
	"github.com/kava-labs/kava/x/hard/types"
 | 
						|
	"github.com/kava-labs/kava/x/pricefeed"
 | 
						|
)
 | 
						|
 | 
						|
func (suite *KeeperTestSuite) TestRepay() {
 | 
						|
	type args struct {
 | 
						|
		borrower             sdk.AccAddress
 | 
						|
		repayer              sdk.AccAddress
 | 
						|
		initialBorrowerCoins sdk.Coins
 | 
						|
		initialRepayerCoins  sdk.Coins
 | 
						|
		initialModuleCoins   sdk.Coins
 | 
						|
		depositCoins         []sdk.Coin
 | 
						|
		borrowCoins          sdk.Coins
 | 
						|
		repayCoins           sdk.Coins
 | 
						|
	}
 | 
						|
 | 
						|
	type errArgs struct {
 | 
						|
		expectPass   bool
 | 
						|
		expectDelete bool
 | 
						|
		contains     string
 | 
						|
	}
 | 
						|
 | 
						|
	type borrowTest struct {
 | 
						|
		name    string
 | 
						|
		args    args
 | 
						|
		errArgs errArgs
 | 
						|
	}
 | 
						|
 | 
						|
	model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))
 | 
						|
 | 
						|
	testCases := []borrowTest{
 | 
						|
		{
 | 
						|
			"valid: partial repay",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))),
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   true,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"valid: partial repay by non borrower",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("repayer"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))),
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   true,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"valid: repay in full",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   true,
 | 
						|
				expectDelete: true,
 | 
						|
				contains:     "",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"valid: overpayment is adjusted",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(80*KAVA_CF))), // Deposit less so user still has some KAVA
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(60*KAVA_CF))), // Exceeds borrowed coins but not user's balance
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   true,
 | 
						|
				expectDelete: true,
 | 
						|
				contains:     "",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid: attempt to repay non-supplied coin",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF)), sdk.NewCoin("bnb", sdk.NewInt(10*KAVA_CF))),
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   false,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "no coins of this type borrowed",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid: insufficient balance for repay",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("repayer"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(49*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))), // Exceeds repayer's balance, but not borrow amount
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   false,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "account can only repay up to 49000000ukava",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid: repaying a single coin type results in borrow position below the minimum USD value",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(50*USDX_CF))),
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(45*USDX_CF))),
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   false,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "proposed borrow's USD value $5.000000000000000000 is below the minimum borrow limit",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid: repaying multiple coin types results in borrow position below the minimum USD value",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(50*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))), // (50*$1)+(10*$2) = $70
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(45*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(8*KAVA_CF))),  // (45*$1)+(8*$2) = $61
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   false,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "proposed borrow's USD value $9.000000000000000000 is below the minimum borrow limit",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			"invalid: overpaying multiple coin types results in borrow position below the minimum USD value",
 | 
						|
			args{
 | 
						|
				borrower:             sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				repayer:              sdk.AccAddress(crypto.AddressHash([]byte("borrower"))),
 | 
						|
				initialBorrowerCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				initialRepayerCoins:  sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))),
 | 
						|
				initialModuleCoins:   sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(1000*USDX_CF))),
 | 
						|
				depositCoins:         sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
 | 
						|
				borrowCoins:          sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(50*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(10*KAVA_CF))), // (50*$1)+(10*$2) = $70
 | 
						|
				repayCoins:           sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(500*USDX_CF)), sdk.NewCoin("ukava", sdk.NewInt(8*KAVA_CF))), // (500*$1)+(8*$2) = $516, or capping to borrowed amount, (50*$1)+(8*$2) = $66
 | 
						|
			},
 | 
						|
			errArgs{
 | 
						|
				expectPass:   false,
 | 
						|
				expectDelete: false,
 | 
						|
				contains:     "proposed borrow's USD value $4.000000000000000000 is below the minimum borrow limit",
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range testCases {
 | 
						|
		suite.Run(tc.name, func() {
 | 
						|
			// Initialize test app and set context
 | 
						|
			tApp := app.NewTestApp()
 | 
						|
			ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
 | 
						|
 | 
						|
			// Auth module genesis state
 | 
						|
			addrs, coinses := uniqueAddressCoins(
 | 
						|
				[]sdk.AccAddress{tc.args.borrower, tc.args.repayer},
 | 
						|
				[]sdk.Coins{tc.args.initialBorrowerCoins, tc.args.initialRepayerCoins},
 | 
						|
			)
 | 
						|
			authGS := app.NewAuthGenState(addrs, coinses)
 | 
						|
 | 
						|
			// Hard module genesis state
 | 
						|
			hardGS := types.NewGenesisState(types.NewParams(
 | 
						|
				types.MoneyMarkets{
 | 
						|
					types.NewMoneyMarket("usdx",
 | 
						|
						types.NewBorrowLimit(false, sdk.NewDec(100000000*USDX_CF), sdk.MustNewDecFromStr("1")), // Borrow Limit
 | 
						|
						"usdx:usd",                     // Market ID
 | 
						|
						sdk.NewInt(USDX_CF),            // Conversion Factor
 | 
						|
						model,                          // Interest Rate Model
 | 
						|
						sdk.MustNewDecFromStr("0.05"),  // Reserve Factor
 | 
						|
						sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
 | 
						|
					types.NewMoneyMarket("ukava",
 | 
						|
						types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit
 | 
						|
						"kava:usd",                     // Market ID
 | 
						|
						sdk.NewInt(KAVA_CF),            // Conversion Factor
 | 
						|
						model,                          // Interest Rate Model
 | 
						|
						sdk.MustNewDecFromStr("0.05"),  // Reserve Factor
 | 
						|
						sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
 | 
						|
				},
 | 
						|
				sdk.NewDec(10),
 | 
						|
			), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
 | 
						|
				types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
 | 
						|
			)
 | 
						|
 | 
						|
			// Pricefeed module genesis state
 | 
						|
			pricefeedGS := pricefeed.GenesisState{
 | 
						|
				Params: pricefeed.Params{
 | 
						|
					Markets: []pricefeed.Market{
 | 
						|
						{MarketID: "usdx:usd", BaseAsset: "usdx", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				PostedPrices: []pricefeed.PostedPrice{
 | 
						|
					{
 | 
						|
						MarketID:      "usdx:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         sdk.MustNewDecFromStr("1.00"),
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
					{
 | 
						|
						MarketID:      "kava:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         sdk.MustNewDecFromStr("2.00"),
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
				},
 | 
						|
			}
 | 
						|
 | 
						|
			// Initialize test application
 | 
						|
			tApp.InitializeFromGenesisStates(authGS,
 | 
						|
				app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pricefeedGS)},
 | 
						|
				app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(hardGS)},
 | 
						|
			)
 | 
						|
 | 
						|
			// Mint coins to Hard module account
 | 
						|
			supplyKeeper := tApp.GetSupplyKeeper()
 | 
						|
			supplyKeeper.MintCoins(ctx, types.ModuleAccountName, tc.args.initialModuleCoins)
 | 
						|
 | 
						|
			keeper := tApp.GetHardKeeper()
 | 
						|
			suite.app = tApp
 | 
						|
			suite.ctx = ctx
 | 
						|
			suite.keeper = keeper
 | 
						|
 | 
						|
			var err error
 | 
						|
 | 
						|
			// Run BeginBlocker once to transition MoneyMarkets
 | 
						|
			hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
 | 
						|
			// Deposit coins to hard
 | 
						|
			err = suite.keeper.Deposit(suite.ctx, tc.args.borrower, tc.args.depositCoins)
 | 
						|
			suite.Require().NoError(err)
 | 
						|
 | 
						|
			// Borrow coins from hard
 | 
						|
			err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.borrowCoins)
 | 
						|
			suite.Require().NoError(err)
 | 
						|
 | 
						|
			previousRepayerCoins := suite.getAccount(tc.args.repayer).GetCoins()
 | 
						|
 | 
						|
			err = suite.keeper.Repay(suite.ctx, tc.args.repayer, tc.args.borrower, tc.args.repayCoins)
 | 
						|
			if tc.errArgs.expectPass {
 | 
						|
				suite.Require().NoError(err)
 | 
						|
				// If we overpaid expect an adjustment
 | 
						|
				repaymentCoins, err := suite.keeper.CalculatePaymentAmount(tc.args.borrowCoins, tc.args.repayCoins)
 | 
						|
				suite.Require().NoError(err)
 | 
						|
 | 
						|
				// Check repayer balance
 | 
						|
				expectedRepayerCoins := previousRepayerCoins.Sub(repaymentCoins)
 | 
						|
				acc := suite.getAccount(tc.args.repayer)
 | 
						|
				suite.Require().Equal(expectedRepayerCoins, acc.GetCoins())
 | 
						|
 | 
						|
				// Check module account balance
 | 
						|
				expectedModuleCoins := tc.args.initialModuleCoins.Add(tc.args.depositCoins...).Sub(tc.args.borrowCoins).Add(repaymentCoins...)
 | 
						|
				mAcc := suite.getModuleAccount(types.ModuleAccountName)
 | 
						|
				suite.Require().Equal(expectedModuleCoins, mAcc.GetCoins())
 | 
						|
 | 
						|
				// Check user's borrow object
 | 
						|
				borrow, foundBorrow := suite.keeper.GetBorrow(suite.ctx, tc.args.borrower)
 | 
						|
				expectedBorrowCoins := tc.args.borrowCoins.Sub(repaymentCoins)
 | 
						|
 | 
						|
				if tc.errArgs.expectDelete {
 | 
						|
					suite.Require().False(foundBorrow)
 | 
						|
				} else {
 | 
						|
					suite.Require().True(foundBorrow)
 | 
						|
					suite.Require().Equal(expectedBorrowCoins, borrow.Amount)
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				suite.Require().Error(err)
 | 
						|
				suite.Require().Contains(err.Error(), tc.errArgs.contains)
 | 
						|
 | 
						|
				// Check repayer balance (no repay coins)
 | 
						|
				acc := suite.getAccount(tc.args.repayer)
 | 
						|
				suite.Require().Equal(previousRepayerCoins, acc.GetCoins())
 | 
						|
 | 
						|
				// Check module account balance (no repay coins)
 | 
						|
				expectedModuleCoins := tc.args.initialModuleCoins.Add(tc.args.depositCoins...).Sub(tc.args.borrowCoins)
 | 
						|
				mAcc := suite.getModuleAccount(types.ModuleAccountName)
 | 
						|
				suite.Require().Equal(expectedModuleCoins, mAcc.GetCoins())
 | 
						|
 | 
						|
				// Check user's borrow object (no repay coins)
 | 
						|
				borrow, foundBorrow := suite.keeper.GetBorrow(suite.ctx, tc.args.borrower)
 | 
						|
				suite.Require().True(foundBorrow)
 | 
						|
				suite.Require().Equal(tc.args.borrowCoins, borrow.Amount)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// uniqueAddressCoins removes duplicate addresses, and the corresponding elements in a list of coins.
 | 
						|
func uniqueAddressCoins(addresses []sdk.AccAddress, coinses []sdk.Coins) ([]sdk.AccAddress, []sdk.Coins) {
 | 
						|
	uniqueAddresses := []sdk.AccAddress{}
 | 
						|
	filteredCoins := []sdk.Coins{}
 | 
						|
 | 
						|
	addrMap := map[string]bool{}
 | 
						|
	for i, a := range addresses {
 | 
						|
		if !addrMap[a.String()] {
 | 
						|
			uniqueAddresses = append(uniqueAddresses, a)
 | 
						|
			filteredCoins = append(filteredCoins, coinses[i])
 | 
						|
		}
 | 
						|
		addrMap[a.String()] = true
 | 
						|
	}
 | 
						|
	return uniqueAddresses, filteredCoins
 | 
						|
}
 |