From 916ec6d30cc71d2023c2b89217719b2fbc2f59c3 Mon Sep 17 00:00:00 2001 From: sesheffield Date: Mon, 29 Jul 2024 20:51:08 -0400 Subject: [PATCH] test(insolvency): add tests for Kava lend insolvency check (#1981) * 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 * change the structure for new and old tests and add more verbosity for other tests * remove print * remove unneeded code * add paren * 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 --------- Co-authored-by: Sam Sheffield --- x/hard/keeper/borrow.go | 1 - x/hard/keeper/borrow_test.go | 744 +++++++++++++++++++++++------------ 2 files changed, 494 insertions(+), 251 deletions(-) diff --git a/x/hard/keeper/borrow.go b/x/hard/keeper/borrow.go index 6c198d45..5f02361e 100644 --- a/x/hard/keeper/borrow.go +++ b/x/hard/keeper/borrow.go @@ -7,7 +7,6 @@ import ( sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/kava-labs/kava/x/hard/types" ) diff --git a/x/hard/keeper/borrow_test.go b/x/hard/keeper/borrow_test.go index 3d254cc2..348cdb47 100644 --- a/x/hard/keeper/borrow_test.go +++ b/x/hard/keeper/borrow_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "strings" "time" sdkmath "cosmossdk.io/math" @@ -9,7 +8,6 @@ import ( 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" @@ -25,268 +23,477 @@ const ( ) func (suite *KeeperTestSuite) TestBorrow() { - type args 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 - previousBorrowCoins sdk.Coins - borrowCoins sdk.Coins + 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 - } - type errArgs struct { + expectPass bool contains string } + type borrowTest struct { - name string - args args - errArgs errArgs + name string + setup setupArgs + borrows []borrowArgs + expected []expected } + testCases := []borrowTest{ { - "valid", - args{ - 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))}, - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdkmath.NewInt(20*KAVA_CF))), - 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))), + 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(), }, - errArgs{ - expectPass: true, - contains: "", + 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: "", + }, }, }, { - "invalid: loan-to-value limited", - args{ - 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 - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(61*USDX_CF))), // 61 USDX x $1 price = $61 - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "exceeds the allowable amount as determined by the collateralization ratio", + 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: "", + }, }, }, { - "valid: multiple deposits", - args{ - 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))), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(180*USDX_CF))), - 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))), + 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(), }, - errArgs{ - expectPass: true, - contains: "", + 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: "", + }, }, }, { - "invalid: multiple deposits", - args{ - 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))), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(181*USDX_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "exceeds the allowable amount as determined by the collateralization ratio", + 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: "", + }, }, }, { - "valid: multiple previous borrows", - args{ - 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 - previousBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(99*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))), - 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))), + 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))), }, - errArgs{ - expectPass: true, - contains: "", + 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: "", + }, }, }, { - "invalid: over loan-to-value with multiple previous borrows", - args{ - 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 - previousBorrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdkmath.NewInt(100*BUSD_CF))), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(1*USDX_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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))), }, - errArgs{ - expectPass: false, - contains: "exceeds the allowable amount as determined by the collateralization ratio", + 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: "", + }, }, }, { - "invalid: no price for asset", - args{ - 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))), - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("xyz", sdkmath.NewInt(1))), - 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))), + 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(), }, - errArgs{ - expectPass: false, - contains: "no price found for market", + 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: "", + }, }, }, { - "invalid: borrow exceed module account balance", - args{ - 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))), - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("busd", sdkmath.NewInt(101*BUSD_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "exceeds borrowable module account balance", + 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: "", + }, }, }, { - "invalid: over global asset borrow limit", - args{ - 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))), - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "fails global asset borrow limit validation", + 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: "", + }, }, }, { - "invalid: borrowing an individual coin type results in a borrow that's under the minimum USD borrow limit", - args{ - 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))), - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(5*USDX_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "below the minimum borrow limit", + 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: "", + }, }, }, { - "invalid: borrowing multiple coins results in a borrow that's under the minimum USD borrow limit", - args{ - 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))), - previousBorrowCoins: sdk.NewCoins(), - borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(5*USDX_CF)), sdk.NewCoin("ukava", sdkmath.NewInt(2*USDX_CF))), - expectedAccountBalance: sdk.NewCoins(), - expectedModAccountBalance: sdk.NewCoins(), + 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(), }, - errArgs{ - expectPass: false, - contains: "below the minimum borrow limit", + 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(incorrect)", + 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(), + 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("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: "", + }, }, }, } @@ -307,18 +514,18 @@ func (suite *KeeperTestSuite) TestBorrow() { sdk.NewCoin("xyz", sdkmath.NewInt(1)), ), }, - []sdk.AccAddress{tc.args.borrower}, + []sdk.AccAddress{tc.setup.borrower}, ) // hard module genesis state hardGS := types.NewGenesisState(types.NewParams( types.MoneyMarkets{ - types.NewMoneyMarket("usdx", types.NewBorrowLimit(true, tc.args.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("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.args.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.05"), sdk.ZeroDec()), - types.NewMoneyMarket("btcb", types.NewBorrowLimit(false, sdk.NewDec(100000000*BTCB_CF), tc.args.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.args.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.args.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()), + 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, @@ -353,19 +560,19 @@ func (suite *KeeperTestSuite) TestBorrow() { { MarketID: "kava:usd", OracleAddress: sdk.AccAddress{}, - Price: tc.args.priceKAVA, + Price: tc.setup.priceKAVA, Expiry: time.Now().Add(1 * time.Hour), }, { MarketID: "btcb:usd", OracleAddress: sdk.AccAddress{}, - Price: tc.args.priceBTCB, + Price: tc.setup.priceBTCB, Expiry: time.Now().Add(1 * time.Hour), }, { MarketID: "bnb:usd", OracleAddress: sdk.AccAddress{}, - Price: tc.args.priceBNB, + Price: tc.setup.priceBNB, Expiry: time.Now().Add(1 * time.Hour), }, }, @@ -375,11 +582,13 @@ func (suite *KeeperTestSuite) TestBorrow() { 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))) + 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) @@ -387,41 +596,43 @@ func (suite *KeeperTestSuite) TestBorrow() { suite.app = tApp suite.ctx = ctx suite.keeper = keeper - // Run BeginBlocker once to transition MoneyMarkets hard.BeginBlocker(suite.ctx, suite.keeper) - - err = suite.keeper.Deposit(suite.ctx, tc.args.borrower, tc.args.depositCoins) - suite.Require().NoError(err) - + suite.Require().NoError(suite.keeper.Deposit(suite.ctx, tc.setup.borrower, tc.setup.depositCoins)) // Execute user's previous borrows - err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.previousBorrowCoins) - if tc.args.previousBorrowCoins.IsZero() { - suite.Require().True(strings.Contains(err.Error(), "cannot borrow zero coins")) + 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) } - // Now that our state is properly set up, execute the last borrow - err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.borrowCoins) + 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) - if tc.errArgs.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 borrower balance - acc := suite.getAccount(tc.args.borrower) - suite.Require().Equal(tc.args.expectedAccountBalance, suite.getAccountCoins(acc)) + // Check module account balance + mAcc := suite.getModuleAccount(types.ModuleAccountName) + suite.Require().Equal(tc.expected[i].expectedModAccountBalance, suite.getAccountCoins(mAcc)) - // Check module account balance - mAcc := suite.getModuleAccount(types.ModuleAccountName) - suite.Require().Equal(tc.args.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) - // Check that borrow struct is in store - _, f := suite.keeper.GetBorrow(suite.ctx, tc.args.borrower) - suite.Require().True(f) - } else { - suite.Require().Error(err) - suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains)) } }) } @@ -561,4 +772,37 @@ func (suite *KeeperTestSuite) TestValidateBorrow() { 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) + + // TODO: this is wrong, since usdk is not insolvent, ukava is. + err = suite.keeper.Borrow( + suite.ctx, + borrower, + sdk.NewCoins(sdk.NewCoin("usdx", sdkmath.NewInt(25*USDX_CF))), + ) + suite.Require().Error(err) + suite.Require().ErrorContains(err, "protocol reserves exceed available cash") }