mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 13:27:27 +00:00 
			
		
		
		
	* add additional tests that attempt to borrow funds from the insolvent market(should fail), and attempt to borrow funds from the not insolvent market (it will fail, but shouldn't). The not insolvent market should continue to processs borrows * remove unused code * make tests less specific for string contains * add new get total reserves for denoms functionality * start utilizing GetTotalReservesForDenoms in ValidateBorrow * update tests for Borrow to not fail when borrowing from an insolvent market * use get total reseves in GetTotalReservesForDenoms for reusability * refactor GetTotalReservesForDenoms to GetTotalReservesByCoinDenoms for more clarity * change the structure for new and old tests and add more verbosity for other tests * remove print * remove unneeded code * add paren * adjust structure again after initial PR * remove duplicate test case with invalid test name, and update to use error contains in places where it was validating if true for strings contains * no need for keeper method
		
			
				
	
	
		
			890 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			890 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package keeper_test
 | 
						|
 | 
						|
import (
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/kava-labs/kava/x/hard/keeper"
 | 
						|
 | 
						|
	sdkmath "cosmossdk.io/math"
 | 
						|
	"github.com/cometbft/cometbft/crypto"
 | 
						|
	tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
 | 
						|
	tmtime "github.com/cometbft/cometbft/types/time"
 | 
						|
	sdk "github.com/cosmos/cosmos-sdk/types"
 | 
						|
	"github.com/kava-labs/kava/app"
 | 
						|
	"github.com/kava-labs/kava/x/hard"
 | 
						|
	"github.com/kava-labs/kava/x/hard/types"
 | 
						|
	pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	USDX_CF = 1000000
 | 
						|
	KAVA_CF = 1000000
 | 
						|
	BTCB_CF = 100000000
 | 
						|
	BNB_CF  = 100000000
 | 
						|
	BUSD_CF = 100000000
 | 
						|
)
 | 
						|
 | 
						|
func (suite *KeeperTestSuite) TestBorrow() {
 | 
						|
	type setupArgs struct {
 | 
						|
		usdxBorrowLimit    sdk.Dec
 | 
						|
		priceKAVA          sdk.Dec
 | 
						|
		loanToValueKAVA    sdk.Dec
 | 
						|
		priceBTCB          sdk.Dec
 | 
						|
		loanToValueBTCB    sdk.Dec
 | 
						|
		priceBNB           sdk.Dec
 | 
						|
		loanToValueBNB     sdk.Dec
 | 
						|
		borrower           sdk.AccAddress
 | 
						|
		depositCoins       []sdk.Coin
 | 
						|
		initialBorrowCoins sdk.Coins
 | 
						|
	}
 | 
						|
 | 
						|
	type borrowArgs struct {
 | 
						|
		borrowCoins sdk.Coins
 | 
						|
		wantErr     string
 | 
						|
	}
 | 
						|
	type expected struct {
 | 
						|
		expectedAccountBalance    sdk.Coins
 | 
						|
		expectedModAccountBalance sdk.Coins
 | 
						|
 | 
						|
		expectPass bool
 | 
						|
		contains   string
 | 
						|
	}
 | 
						|
 | 
						|
	type borrowTest struct {
 | 
						|
		name     string
 | 
						|
		setup    setupArgs
 | 
						|
		borrows  []borrowArgs
 | 
						|
		expected []expected
 | 
						|
	}
 | 
						|
 | 
						|
	testCases := []borrowTest{
 | 
						|
		{
 | 
						|
			name: "valid",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.6"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       []sdk.Coin{sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF))},
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1080*KAVA_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: loan-to-value limited",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.6"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       []sdk.Coin{sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF))}, // 20 KAVA x $5.00 price = $100
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
 | 
						|
					expectPass: false,
 | 
						|
					contains:   "exceeds the allowable amount as determined by the collateralization ratio",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(61*USDX_CF))), // 61 USDX x $1 price = $61
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid: multiple deposits",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.80"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("10000.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.10"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(0.1*BTCB_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(99.9*BTCB_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(180*USDX_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1050*KAVA_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(20*USDX_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(0.1*BTCB_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
 | 
						|
					contains: "exceeds the allowable amount as determined by the collateralization ratio",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(180*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: multiple deposits",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.80"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("10000.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.10"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(0.1*BTCB_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
 | 
						|
					expectPass: false,
 | 
						|
					contains:   "exceeds the allowable amount as determined by the collateralization ratio",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(181*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid: multiple previous borrows",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.8"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("bnb", sdkmath.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))), // (50 KAVA x $2.00 price = $100) + (30 BNB x $5.00 price = $150) = $250
 | 
						|
				initialBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(70*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1050*KAVA_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(30*BUSD_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: over loan-to-value with multiple previous borrows",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.8"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("bnb", sdkmath.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))), // (50 KAVA x $2.00 price = $100) + (30 BNB x $5.00 price = $150) = $250
 | 
						|
				initialBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "exceeds the allowable amount as determined by the collateralization ratio",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: no price for asset",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.6"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1080*KAVA_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
 | 
						|
					expectPass: false,
 | 
						|
					contains:   "no price found for market",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: borrow exceed module account balance",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "exceeds borrowable module account balance",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("busd", sdkmath.NewInt(101*BUSD_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: over global asset borrow limit",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("20000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "fails global asset borrow limit validation",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: borrowing an individual coin type results in a borrow that's under the minimum USD borrow limit",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("20000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "below the minimum borrow limit",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(5*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "invalid: borrowing multiple coins results in a borrow that's under the minimum USD borrow limit",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("20000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "below the minimum borrow limit",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(5*USDX_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(2*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid borrow multiple blocks",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.6"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.01"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       []sdk.Coin{sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF))},
 | 
						|
				initialBorrowCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1080*KAVA_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(40*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1060*KAVA_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
					expectPass:                true,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid borrow followed by protocol reserves exceed available cash for busd when borrowing from ukava",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.8"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("bnb", sdkmath.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(70*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1050*KAVA_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(30*BUSD_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
				{
 | 
						|
					expectedAccountBalance: sdk.NewCoins(
 | 
						|
						sdk.NewCoin("ukava", sdkmath.NewInt(51*KAVA_CF)), // now should be 1 ukava more
 | 
						|
						sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)),
 | 
						|
						sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)),
 | 
						|
						sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)),
 | 
						|
						sdk.NewCoin("bnb", sdkmath.NewInt(70*BNB_CF)),
 | 
						|
						sdk.NewCoin("xyz", sdkmath.NewInt(1)),
 | 
						|
					),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(
 | 
						|
						sdk.NewCoin("ukava", sdkmath.NewInt(1049*KAVA_CF)), // now should be 1 ukava less
 | 
						|
						sdk.NewCoin("bnb", sdkmath.NewInt(30*BUSD_CF)),
 | 
						|
						sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)),
 | 
						|
					),
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1*KAVA_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "valid borrow followed by protocol reserves exceed available cash for busd when borrowing from busd",
 | 
						|
			setup: setupArgs{
 | 
						|
				usdxBorrowLimit:    sdk.MustNewDecFromStr("100000000000"),
 | 
						|
				priceKAVA:          sdk.MustNewDecFromStr("2.00"),
 | 
						|
				loanToValueKAVA:    sdk.MustNewDecFromStr("0.8"),
 | 
						|
				priceBTCB:          sdk.MustNewDecFromStr("0.00"),
 | 
						|
				loanToValueBTCB:    sdk.MustNewDecFromStr("0.01"),
 | 
						|
				priceBNB:           sdk.MustNewDecFromStr("5.00"),
 | 
						|
				loanToValueBNB:     sdk.MustNewDecFromStr("0.8"),
 | 
						|
				borrower:           sdk.AccAddress(crypto.AddressHash([]byte("test"))),
 | 
						|
				depositCoins:       sdk.NewCoins(sdk.NewCoin("bnb", sdkmath.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF))),
 | 
						|
				initialBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
			},
 | 
						|
			expected: []expected{
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(70*BNB_CF)), sdk.NewCoin("xyz", sdkmath.NewInt(1))),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(1050*KAVA_CF)), sdk.NewCoin("bnb", sdkmath.NewInt(30*BUSD_CF)), sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF))),
 | 
						|
 | 
						|
					expectPass: true,
 | 
						|
				},
 | 
						|
				{
 | 
						|
					expectedAccountBalance:    sdk.NewCoins(),
 | 
						|
					expectedModAccountBalance: sdk.NewCoins(),
 | 
						|
					expectPass:                false,
 | 
						|
					contains:                  "insolvency - protocol reserves exceed available cash",
 | 
						|
				},
 | 
						|
			},
 | 
						|
			borrows: []borrowArgs{
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
				{
 | 
						|
					borrowCoins: sdk.NewCoins(sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))),
 | 
						|
					wantErr:     "",
 | 
						|
				},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tc := range testCases {
 | 
						|
		suite.Run(tc.name, func() {
 | 
						|
			// Initialize test app and set context
 | 
						|
			tApp := app.NewTestApp()
 | 
						|
			ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
 | 
						|
 | 
						|
			// Auth module genesis state
 | 
						|
			authGS := app.NewFundedGenStateWithCoins(
 | 
						|
				tApp.AppCodec(),
 | 
						|
				[]sdk.Coins{
 | 
						|
					sdk.NewCoins(
 | 
						|
						sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF)),
 | 
						|
						sdk.NewCoin("btcb", sdkmath.NewInt(100*BTCB_CF)),
 | 
						|
						sdk.NewCoin("bnb", sdkmath.NewInt(100*BNB_CF)),
 | 
						|
						sdk.NewCoin("xyz", sdkmath.NewInt(1)),
 | 
						|
					),
 | 
						|
				},
 | 
						|
				[]sdk.AccAddress{tc.setup.borrower},
 | 
						|
			)
 | 
						|
 | 
						|
			// hard module genesis state
 | 
						|
			hardGS := types.NewGenesisState(types.NewParams(
 | 
						|
				types.MoneyMarkets{
 | 
						|
					types.NewMoneyMarket("usdx", types.NewBorrowLimit(true, tc.setup.usdxBorrowLimit, sdk.MustNewDecFromStr("1")), "usdx:usd", sdkmath.NewInt(USDX_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
 | 
						|
					types.NewMoneyMarket("busd", types.NewBorrowLimit(false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1")), "busd:usd", sdkmath.NewInt(BUSD_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
 | 
						|
					types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), tc.setup.loanToValueKAVA), "kava:usd", sdkmath.NewInt(KAVA_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.99"), sdk.ZeroDec()),
 | 
						|
					types.NewMoneyMarket("btcb", types.NewBorrowLimit(false, sdk.NewDec(100000000*BTCB_CF), tc.setup.loanToValueBTCB), "btcb:usd", sdkmath.NewInt(BTCB_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
 | 
						|
					types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(100000000*BNB_CF), tc.setup.loanToValueBNB), "bnb:usd", sdkmath.NewInt(BNB_CF), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
 | 
						|
					types.NewMoneyMarket("xyz", types.NewBorrowLimit(false, sdk.NewDec(1), tc.setup.loanToValueBNB), "xyz:usd", sdkmath.NewInt(1), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
 | 
						|
				},
 | 
						|
				sdk.NewDec(10),
 | 
						|
			), types.DefaultAccumulationTimes, types.DefaultDeposits, types.DefaultBorrows,
 | 
						|
				types.DefaultTotalSupplied, types.DefaultTotalBorrowed, types.DefaultTotalReserves,
 | 
						|
			)
 | 
						|
 | 
						|
			// Pricefeed module genesis state
 | 
						|
			pricefeedGS := pricefeedtypes.GenesisState{
 | 
						|
				Params: pricefeedtypes.Params{
 | 
						|
					Markets: []pricefeedtypes.Market{
 | 
						|
						{MarketID: "usdx:usd", BaseAsset: "usdx", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "busd:usd", BaseAsset: "busd", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "btcb:usd", BaseAsset: "btcb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
						{MarketID: "xyz:usd", BaseAsset: "xyz", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
 | 
						|
					},
 | 
						|
				},
 | 
						|
				PostedPrices: []pricefeedtypes.PostedPrice{
 | 
						|
					{
 | 
						|
						MarketID:      "usdx:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         sdk.MustNewDecFromStr("1.00"),
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
					{
 | 
						|
						MarketID:      "busd:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         sdk.MustNewDecFromStr("1.00"),
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
					{
 | 
						|
						MarketID:      "kava:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         tc.setup.priceKAVA,
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
					{
 | 
						|
						MarketID:      "btcb:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         tc.setup.priceBTCB,
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
					{
 | 
						|
						MarketID:      "bnb:usd",
 | 
						|
						OracleAddress: sdk.AccAddress{},
 | 
						|
						Price:         tc.setup.priceBNB,
 | 
						|
						Expiry:        time.Now().Add(1 * time.Hour),
 | 
						|
					},
 | 
						|
				},
 | 
						|
			}
 | 
						|
 | 
						|
			// Initialize test application
 | 
						|
			tApp.InitializeFromGenesisStates(authGS,
 | 
						|
				app.GenesisState{pricefeedtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&pricefeedGS)},
 | 
						|
				app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&hardGS)})
 | 
						|
			// Mint coins to hard module account
 | 
						|
			bankKeeper := tApp.GetBankKeeper()
 | 
						|
			hardMaccCoins := sdk.NewCoins(
 | 
						|
				sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
				sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)),
 | 
						|
				sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)),
 | 
						|
			)
 | 
						|
			err := bankKeeper.MintCoins(ctx, types.ModuleAccountName, hardMaccCoins)
 | 
						|
			suite.Require().NoError(err)
 | 
						|
 | 
						|
			keeper := tApp.GetHardKeeper()
 | 
						|
			suite.app = tApp
 | 
						|
			suite.ctx = ctx
 | 
						|
			suite.keeper = keeper
 | 
						|
			// Run BeginBlocker once to transition MoneyMarkets
 | 
						|
			hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
			suite.Require().NoError(suite.keeper.Deposit(suite.ctx, tc.setup.borrower, tc.setup.depositCoins))
 | 
						|
			// Execute user's previous borrows
 | 
						|
			if err = suite.keeper.Borrow(suite.ctx, tc.setup.borrower, tc.setup.initialBorrowCoins); tc.setup.initialBorrowCoins.IsZero() {
 | 
						|
				suite.Require().ErrorContains(err, "cannot borrow zero coins")
 | 
						|
			} else {
 | 
						|
				suite.Require().NoError(err)
 | 
						|
			}
 | 
						|
 | 
						|
			for i, borrow := range tc.borrows {
 | 
						|
				// Now that our state is properly set up, execute the last borrow
 | 
						|
				err = suite.keeper.Borrow(suite.ctx, tc.setup.borrower, borrow.borrowCoins)
 | 
						|
				if tc.expected[i].expectPass {
 | 
						|
					suite.Require().NoError(err)
 | 
						|
 | 
						|
					// Check borrower balance
 | 
						|
					acc := suite.getAccount(tc.setup.borrower)
 | 
						|
					suite.Require().Equal(tc.expected[i].expectedAccountBalance, suite.getAccountCoins(acc))
 | 
						|
 | 
						|
					// Check module account balance
 | 
						|
					mAcc := suite.getModuleAccount(types.ModuleAccountName)
 | 
						|
					suite.Require().Equal(tc.expected[i].expectedModAccountBalance, suite.getAccountCoins(mAcc))
 | 
						|
 | 
						|
					// Check that borrow struct is in store
 | 
						|
					_, f := suite.keeper.GetBorrow(suite.ctx, tc.setup.borrower)
 | 
						|
					suite.Require().True(f)
 | 
						|
				} else {
 | 
						|
					suite.Require().Error(err)
 | 
						|
					suite.Require().ErrorContains(err, tc.expected[i].contains)
 | 
						|
				}
 | 
						|
				blockDuration := time.Second * 3600 * 30 // long blocks to accumulate larger interest
 | 
						|
				runAtTime := suite.ctx.BlockTime().Add(blockDuration)
 | 
						|
				suite.ctx = suite.ctx.WithBlockTime(runAtTime)
 | 
						|
				// Run BeginBlocker once to transition MoneyMarkets
 | 
						|
				hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (suite *KeeperTestSuite) TestValidateBorrow() {
 | 
						|
	blockDuration := time.Second * 3600 * 24 // long blocks to accumulate larger interest
 | 
						|
 | 
						|
	_, addrs := app.GeneratePrivKeyAddressPairs(5)
 | 
						|
	borrower := addrs[0]
 | 
						|
	initialBorrowerBalance := sdk.NewCoins(
 | 
						|
		sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
		sdk.NewCoin("usdx", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
	)
 | 
						|
 | 
						|
	model := types.NewInterestRateModel(sdk.MustNewDecFromStr("1.0"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))
 | 
						|
 | 
						|
	// Initialize test app and set context
 | 
						|
	tApp := app.NewTestApp()
 | 
						|
	ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
 | 
						|
 | 
						|
	// Auth module genesis state
 | 
						|
	authGS := app.NewFundedGenStateWithSameCoins(
 | 
						|
		tApp.AppCodec(),
 | 
						|
		initialBorrowerBalance,
 | 
						|
		[]sdk.AccAddress{borrower},
 | 
						|
	)
 | 
						|
 | 
						|
	// 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
 | 
						|
					sdkmath.NewInt(USDX_CF),        // Conversion Factor
 | 
						|
					model,                          // Interest Rate Model
 | 
						|
					sdk.MustNewDecFromStr("1.0"),   // Reserve Factor (high)
 | 
						|
					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
 | 
						|
					sdkmath.NewInt(KAVA_CF),        // Conversion Factor
 | 
						|
					model,                          // Interest Rate Model
 | 
						|
					sdk.MustNewDecFromStr("1.0"),   // Reserve Factor (high)
 | 
						|
					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 := pricefeedtypes.GenesisState{
 | 
						|
		Params: pricefeedtypes.Params{
 | 
						|
			Markets: []pricefeedtypes.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: []pricefeedtypes.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{pricefeedtypes.ModuleName: tApp.AppCodec().MustMarshalJSON(&pricefeedGS)},
 | 
						|
		app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&hardGS)},
 | 
						|
	)
 | 
						|
 | 
						|
	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)
 | 
						|
 | 
						|
	// Setup borrower with some collateral to borrow against, and some reserve in the protocol.
 | 
						|
	depositCoins := sdk.NewCoins(
 | 
						|
		sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF)),
 | 
						|
		sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)),
 | 
						|
	)
 | 
						|
	err = suite.keeper.Deposit(suite.ctx, borrower, depositCoins)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
 | 
						|
	initialBorrowCoins := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(70*KAVA_CF)))
 | 
						|
	err = suite.keeper.Borrow(suite.ctx, borrower, initialBorrowCoins)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
 | 
						|
	runAtTime := suite.ctx.BlockTime().Add(blockDuration)
 | 
						|
	suite.ctx = suite.ctx.WithBlockTime(runAtTime)
 | 
						|
	hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
 | 
						|
	repayCoins := sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(100*KAVA_CF))) // repay everything including accumulated interest
 | 
						|
	err = suite.keeper.Repay(suite.ctx, borrower, borrower, repayCoins)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
 | 
						|
	// Get the total borrowable amount from the protocol, taking into account the reserves.
 | 
						|
	modAccBalance := suite.getAccountCoins(suite.getModuleAccountAtCtx(types.ModuleAccountName, suite.ctx))
 | 
						|
	reserves, found := suite.keeper.GetTotalReserves(suite.ctx)
 | 
						|
	suite.Require().True(found)
 | 
						|
	availableToBorrow := modAccBalance.Sub(reserves...)
 | 
						|
 | 
						|
	// Test borrowing one over the available amount (try to borrow from the reserves)
 | 
						|
	err = suite.keeper.Borrow(
 | 
						|
		suite.ctx,
 | 
						|
		borrower,
 | 
						|
		sdk.NewCoins(sdk.NewCoin("ukava", availableToBorrow.AmountOf("ukava").Add(sdk.OneInt()))),
 | 
						|
	)
 | 
						|
	suite.Require().Error(err)
 | 
						|
 | 
						|
	// Test borrowing exactly the limit
 | 
						|
	err = suite.keeper.Borrow(
 | 
						|
		suite.ctx,
 | 
						|
		borrower,
 | 
						|
		sdk.NewCoins(sdk.NewCoin("ukava", availableToBorrow.AmountOf("ukava"))),
 | 
						|
	)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
 | 
						|
	// now that it's all that you can borrow, we shouldn't be able to borrow anything
 | 
						|
	err = suite.keeper.Borrow(
 | 
						|
		suite.ctx,
 | 
						|
		borrower,
 | 
						|
		sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(2*USDX_CF))),
 | 
						|
	)
 | 
						|
	suite.Require().Error(err)
 | 
						|
	suite.Require().ErrorContains(err, "available to borrow: exceeds module account balance")
 | 
						|
 | 
						|
	suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(blockDuration))
 | 
						|
	hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
 | 
						|
	// Should error since ukava has become insolvent at this point
 | 
						|
	err = suite.keeper.Borrow(
 | 
						|
		suite.ctx,
 | 
						|
		borrower,
 | 
						|
		sdk.NewCoins(sdk.NewCoin("ukava", availableToBorrow.AmountOf("ukava"))),
 | 
						|
	)
 | 
						|
	suite.Require().Error(err)
 | 
						|
	suite.Require().ErrorContains(err, "protocol reserves exceed available cash")
 | 
						|
 | 
						|
	suite.ctx = suite.ctx.WithBlockTime(suite.ctx.BlockTime().Add(blockDuration))
 | 
						|
	hard.BeginBlocker(suite.ctx, suite.keeper)
 | 
						|
 | 
						|
	err = suite.keeper.Borrow(
 | 
						|
		suite.ctx,
 | 
						|
		borrower,
 | 
						|
		sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF))),
 | 
						|
	)
 | 
						|
	suite.Require().NoError(err)
 | 
						|
}
 | 
						|
 | 
						|
func (suite *KeeperTestSuite) TestFilterCoinsByDenoms() {
 | 
						|
	type args struct {
 | 
						|
		coins         sdk.Coins
 | 
						|
		filterByCoins sdk.Coins
 | 
						|
	}
 | 
						|
	tests := []struct {
 | 
						|
		name string
 | 
						|
		args args
 | 
						|
		want sdk.Coins
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "more coins than filtered coins",
 | 
						|
			args: args{
 | 
						|
				coins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
					sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)),
 | 
						|
					sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF)),
 | 
						|
				),
 | 
						|
				filterByCoins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF)),
 | 
						|
					sdk.NewCoin("ukava", sdkmath.NewInt(25*KAVA_CF)),
 | 
						|
				),
 | 
						|
			},
 | 
						|
			want: sdk.NewCoins(
 | 
						|
				sdk.NewCoin("usdx", sdkmath.NewInt(200*USDX_CF)),
 | 
						|
				sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
			),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "less coins than filtered coins",
 | 
						|
			args: args{
 | 
						|
				coins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
				),
 | 
						|
				filterByCoins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF)),
 | 
						|
					sdk.NewCoin("ukava", sdkmath.NewInt(25*KAVA_CF)),
 | 
						|
				),
 | 
						|
			},
 | 
						|
			want: sdk.NewCoins(
 | 
						|
				sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
			),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "no filter coins ",
 | 
						|
			args: args{
 | 
						|
				coins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("ukava", sdkmath.NewInt(1000*KAVA_CF)),
 | 
						|
				),
 | 
						|
				filterByCoins: sdk.NewCoins(),
 | 
						|
			},
 | 
						|
			want: sdk.NewCoins(),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "no coins ",
 | 
						|
			args: args{
 | 
						|
				coins: sdk.NewCoins(),
 | 
						|
				filterByCoins: sdk.NewCoins(
 | 
						|
					sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF))),
 | 
						|
			},
 | 
						|
			want: sdk.NewCoins(),
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		suite.Run(tt.name, func() {
 | 
						|
			got := keeper.FilterCoinsByDenoms(tt.args.coins, tt.args.filterByCoins)
 | 
						|
			suite.Require().Equal(tt.want, got)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |