Harvest: deposit multiple assets supplying multiple assets (#712)

* introduce local cache

* apply LTV for borrowable amount calculation

* add multiple previous borrow tests

* remove unused functions

* address revisions
This commit is contained in:
Denali Marsh 2020-11-09 22:52:08 +01:00 committed by GitHub
parent 3ea3148129
commit 33cbe34991
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 158 additions and 58 deletions

View File

@ -9,8 +9,7 @@ import (
// Borrow funds // Borrow funds
func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins) error { func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins) error {
// TODO: Here we assume borrower only has one coin. To be addressed in future card. err := k.ValidateBorrow(ctx, borrower, coins)
err := k.ValidateBorrow(ctx, borrower, coins[0])
if err != nil { if err != nil {
return err return err
} }
@ -40,66 +39,88 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
} }
// ValidateBorrow validates a borrow request against borrower and protocol requirements // ValidateBorrow validates a borrow request against borrower and protocol requirements
func (k Keeper) ValidateBorrow(ctx sdk.Context, borrower sdk.AccAddress, amount sdk.Coin) error { func (k Keeper) ValidateBorrow(ctx sdk.Context, borrower sdk.AccAddress, amount sdk.Coins) error {
proprosedBorrowUSDValue, err := k.calculateUSDValue(ctx, amount.Amount, amount.Denom) // Get the proposed borrow USD value
if err != nil { moneyMarketCache := map[string]types.MoneyMarket{}
return err proprosedBorrowUSDValue := sdk.ZeroDec()
for _, coin := range amount {
moneyMarket, ok := moneyMarketCache[coin.Denom]
// Fetch money market and store in local cache
if !ok {
newMoneyMarket, found := k.GetMoneyMarket(ctx, coin.Denom)
if !found {
return sdkerrors.Wrapf(types.ErrMarketNotFound, "no market found for denom %s", coin.Denom)
}
moneyMarketCache[coin.Denom] = newMoneyMarket
moneyMarket = newMoneyMarket
}
// Calculate this coin's USD value and add it borrow's total USD value
assetPriceInfo, err := k.pricefeedKeeper.GetCurrentPrice(ctx, moneyMarket.SpotMarketID)
if err != nil {
return sdkerrors.Wrapf(types.ErrPriceNotFound, "no price found for market %s", moneyMarket.SpotMarketID)
}
coinUSDValue := sdk.NewDecFromInt(coin.Amount).Quo(sdk.NewDecFromInt(moneyMarket.ConversionFactor)).Mul(assetPriceInfo.Price)
proprosedBorrowUSDValue = proprosedBorrowUSDValue.Add(coinUSDValue)
} }
// Get the total value of the user's deposits // Get the total borrowable USD amount at user's existing deposits
deposits := k.GetDepositsByUser(ctx, borrower) deposits := k.GetDepositsByUser(ctx, borrower)
if len(deposits) == 0 { if len(deposits) == 0 {
return sdkerrors.Wrapf(types.ErrDepositsNotFound, "no deposits found for %s", borrower) return sdkerrors.Wrapf(types.ErrDepositsNotFound, "no deposits found for %s", borrower)
} }
totalBorrowableAmount := sdk.ZeroDec() totalBorrowableAmount := sdk.ZeroDec()
for _, deposit := range deposits { for _, deposit := range deposits {
borrowableAmountForDeposit, err := k.getBorrowableAmountForDeposit(ctx, deposit) moneyMarket, ok := moneyMarketCache[deposit.Amount.Denom]
if err != nil { // Fetch money market and store in local cache
return err if !ok {
newMoneyMarket, found := k.GetMoneyMarket(ctx, deposit.Amount.Denom)
if !found {
return sdkerrors.Wrapf(types.ErrMarketNotFound, "no market found for denom %s", deposit.Amount.Denom)
}
moneyMarketCache[deposit.Amount.Denom] = newMoneyMarket
moneyMarket = newMoneyMarket
} }
// Calculate the borrowable amount and add it to the user's total borrowable amount
assetPriceInfo, err := k.pricefeedKeeper.GetCurrentPrice(ctx, moneyMarket.SpotMarketID)
if err != nil {
sdkerrors.Wrapf(types.ErrPriceNotFound, "no price found for market %s", moneyMarket.SpotMarketID)
}
depositUSDValue := sdk.NewDecFromInt(deposit.Amount.Amount).Quo(sdk.NewDecFromInt(moneyMarket.ConversionFactor)).Mul(assetPriceInfo.Price)
borrowableAmountForDeposit := depositUSDValue.Mul(moneyMarket.BorrowLimit.LoanToValue)
totalBorrowableAmount = totalBorrowableAmount.Add(borrowableAmountForDeposit) totalBorrowableAmount = totalBorrowableAmount.Add(borrowableAmountForDeposit)
} }
previousBorrowsUSDValue := sdk.ZeroDec() // Get the total USD value of user's existing borrows
previousBorrows, found := k.GetBorrow(ctx, borrower) existingBorrowUSDValue := sdk.ZeroDec()
existingBorrow, found := k.GetBorrow(ctx, borrower)
if found { if found {
// TODO: here we're assuming that the user only has 1 previous borrow. To be addressed in future cards. for _, borrowedCoin := range existingBorrow.Amount {
previousBorrow := previousBorrows.Amount[0] moneyMarket, ok := moneyMarketCache[borrowedCoin.Denom]
previousBorrowUSDValue, err := k.calculateUSDValue(ctx, previousBorrow.Amount, previousBorrow.Denom) // Fetch money market and store in local cache
if err != nil { if !ok {
return err newMoneyMarket, found := k.GetMoneyMarket(ctx, borrowedCoin.Denom)
if !found {
return sdkerrors.Wrapf(types.ErrMarketNotFound, "no market found for denom %s", borrowedCoin.Denom)
}
moneyMarketCache[borrowedCoin.Denom] = newMoneyMarket
moneyMarket = newMoneyMarket
}
// Calculate this borrow coin's USD value and add it to the total previous borrowed USD value
assetPriceInfo, err := k.pricefeedKeeper.GetCurrentPrice(ctx, moneyMarket.SpotMarketID)
if err != nil {
return sdkerrors.Wrapf(types.ErrPriceNotFound, "no price found for market %s", moneyMarket.SpotMarketID)
}
coinUSDValue := sdk.NewDecFromInt(borrowedCoin.Amount).Quo(sdk.NewDecFromInt(moneyMarket.ConversionFactor)).Mul(assetPriceInfo.Price)
existingBorrowUSDValue = existingBorrowUSDValue.Add(coinUSDValue)
} }
previousBorrowsUSDValue = previousBorrowsUSDValue.Add(previousBorrowUSDValue)
} }
// Validate that the proposed borrow's USD value is within user's borrowable limit // Validate that the proposed borrow's USD value is within user's borrowable limit
if proprosedBorrowUSDValue.GT(totalBorrowableAmount.Sub(previousBorrowsUSDValue)) { if proprosedBorrowUSDValue.GT(totalBorrowableAmount.Sub(existingBorrowUSDValue)) {
return sdkerrors.Wrapf(types.ErrInsufficientLoanToValue, "requested borrow %s is greater than maximum valid borrow", amount) return sdkerrors.Wrapf(types.ErrInsufficientLoanToValue, "requested borrow %s is greater than maximum valid borrow", amount)
} }
return nil return nil
} }
func (k Keeper) calculateUSDValue(ctx sdk.Context, amount sdk.Int, denom string) (sdk.Dec, error) {
moneyMarket, found := k.GetMoneyMarket(ctx, denom)
if !found {
return sdk.ZeroDec(), sdkerrors.Wrapf(types.ErrMarketNotFound, "no market found for denom %s", denom)
}
assetPriceInfo, err := k.pricefeedKeeper.GetCurrentPrice(ctx, moneyMarket.SpotMarketID)
if err != nil {
return sdk.ZeroDec(), sdkerrors.Wrapf(types.ErrPriceNotFound, "no price found for market %s", moneyMarket.SpotMarketID)
}
return sdk.NewDecFromInt(amount).Quo(sdk.NewDecFromInt(moneyMarket.ConversionFactor)).Mul(assetPriceInfo.Price), nil
}
func (k Keeper) getBorrowableAmountForDeposit(ctx sdk.Context, deposit types.Deposit) (sdk.Dec, error) {
moneyMarket, found := k.GetMoneyMarket(ctx, deposit.Amount.Denom)
if !found {
return sdk.ZeroDec(), sdkerrors.Wrapf(types.ErrMarketNotFound, "no market found for denom %s", deposit.Amount.Denom)
}
assetPriceInfo, err := k.pricefeedKeeper.GetCurrentPrice(ctx, moneyMarket.SpotMarketID)
if err != nil {
return sdk.ZeroDec(), sdkerrors.Wrapf(types.ErrPriceNotFound, "no price found for market %s", moneyMarket.SpotMarketID)
}
usdValue := sdk.NewDecFromInt(deposit.Amount.Amount).Quo(sdk.NewDecFromInt(moneyMarket.ConversionFactor)).Mul(assetPriceInfo.Price)
return usdValue.Mul(moneyMarket.BorrowLimit.LoanToValue), nil
}

View File

@ -18,6 +18,8 @@ const (
USDX_CF = 1000000 USDX_CF = 1000000
KAVA_CF = 1000000 KAVA_CF = 1000000
BTCB_CF = 100000000 BTCB_CF = 100000000
BNB_CF = 100000000
BUSD_CF = 100000000
) )
func (suite *KeeperTestSuite) TestBorrow() { func (suite *KeeperTestSuite) TestBorrow() {
@ -27,8 +29,11 @@ func (suite *KeeperTestSuite) TestBorrow() {
loanToValueKAVA sdk.Dec loanToValueKAVA sdk.Dec
priceBTCB sdk.Dec priceBTCB sdk.Dec
loanToValueBTCB sdk.Dec loanToValueBTCB sdk.Dec
priceBNB sdk.Dec
loanToValueBNB sdk.Dec
borrower sdk.AccAddress borrower sdk.AccAddress
depositCoins []sdk.Coin depositCoins []sdk.Coin
previousBorrowCoins sdk.Coins
borrowCoins sdk.Coins borrowCoins sdk.Coins
expectedAccountBalance sdk.Coins expectedAccountBalance sdk.Coins
expectedModAccountBalance sdk.Coins expectedModAccountBalance sdk.Coins
@ -50,11 +55,14 @@ func (suite *KeeperTestSuite) TestBorrow() {
loanToValueKAVA: sdk.MustNewDecFromStr("0.6"), loanToValueKAVA: sdk.MustNewDecFromStr("0.6"),
priceBTCB: sdk.MustNewDecFromStr("0.00"), priceBTCB: sdk.MustNewDecFromStr("0.00"),
loanToValueBTCB: sdk.MustNewDecFromStr("0.01"), loanToValueBTCB: sdk.MustNewDecFromStr("0.01"),
priceBNB: sdk.MustNewDecFromStr("0.00"),
loanToValueBNB: sdk.MustNewDecFromStr("0.01"),
borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))), borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))}, depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF))},
previousBorrowCoins: sdk.NewCoins(),
borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF))), borrowCoins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF))),
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF))), expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdk.NewInt(100*BNB_CF))),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1080*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(200*USDX_CF))), expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1080*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF))),
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -68,11 +76,13 @@ func (suite *KeeperTestSuite) TestBorrow() {
loanToValueKAVA: sdk.MustNewDecFromStr("0.6"), loanToValueKAVA: sdk.MustNewDecFromStr("0.6"),
priceBTCB: sdk.MustNewDecFromStr("0.00"), priceBTCB: sdk.MustNewDecFromStr("0.00"),
loanToValueBTCB: sdk.MustNewDecFromStr("0.01"), loanToValueBTCB: sdk.MustNewDecFromStr("0.01"),
priceBNB: sdk.MustNewDecFromStr("0.00"),
loanToValueBNB: sdk.MustNewDecFromStr("0.01"),
borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))), borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF))}, // 20 KAVA x $5.00 price = $100 depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(20*KAVA_CF))}, // 20 KAVA x $5.00 price = $100
borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(61*USDX_CF))), // 61 USDX x $1 price = $61 borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(61*USDX_CF))), // 61 USDX x $1 price = $61
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(80*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF))), expectedAccountBalance: sdk.NewCoins(),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1020*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(261*USDX_CF))), expectedModAccountBalance: sdk.NewCoins(),
}, },
errArgs{ errArgs{
expectPass: false, expectPass: false,
@ -86,11 +96,13 @@ func (suite *KeeperTestSuite) TestBorrow() {
loanToValueKAVA: sdk.MustNewDecFromStr("0.80"), loanToValueKAVA: sdk.MustNewDecFromStr("0.80"),
priceBTCB: sdk.MustNewDecFromStr("10000.00"), priceBTCB: sdk.MustNewDecFromStr("10000.00"),
loanToValueBTCB: sdk.MustNewDecFromStr("0.10"), loanToValueBTCB: sdk.MustNewDecFromStr("0.10"),
priceBNB: sdk.MustNewDecFromStr("0.00"),
loanToValueBNB: sdk.MustNewDecFromStr("0.01"),
borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))), borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))}, depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))},
borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(180*USDX_CF))), borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(180*USDX_CF))),
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(99.9*BTCB_CF)), sdk.NewCoin("usdx", sdk.NewInt(180*USDX_CF))), expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(99.9*BTCB_CF)), sdk.NewCoin("usdx", sdk.NewInt(180*USDX_CF)), sdk.NewCoin("bnb", sdk.NewInt(100*BNB_CF))),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1050*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(20*USDX_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))), expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1050*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(20*USDX_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF))),
}, },
errArgs{ errArgs{
expectPass: true, expectPass: true,
@ -104,17 +116,61 @@ func (suite *KeeperTestSuite) TestBorrow() {
loanToValueKAVA: sdk.MustNewDecFromStr("0.80"), loanToValueKAVA: sdk.MustNewDecFromStr("0.80"),
priceBTCB: sdk.MustNewDecFromStr("10000.00"), priceBTCB: sdk.MustNewDecFromStr("10000.00"),
loanToValueBTCB: sdk.MustNewDecFromStr("0.10"), loanToValueBTCB: sdk.MustNewDecFromStr("0.10"),
priceBNB: sdk.MustNewDecFromStr("0.00"),
loanToValueBNB: sdk.MustNewDecFromStr("0.01"),
borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))), borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))}, depositCoins: []sdk.Coin{sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))},
borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(181*USDX_CF))), borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(181*USDX_CF))),
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(99.9*BTCB_CF)), sdk.NewCoin("usdx", sdk.NewInt(180*USDX_CF))), expectedAccountBalance: sdk.NewCoins(),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1050*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(20*USDX_CF)), sdk.NewCoin("btcb", sdk.NewInt(0.1*BTCB_CF))), expectedModAccountBalance: sdk.NewCoins(),
}, },
errArgs{ errArgs{
expectPass: false, expectPass: false,
contains: "total deposited value is insufficient for borrow request", contains: "total deposited value is insufficient for borrow request",
}, },
}, },
{
"valid: multiple previous borrows",
args{
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.Coin{sdk.NewCoin("bnb", sdk.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdk.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", sdk.NewInt(99*USDX_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF))),
borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(1*USDX_CF))),
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF)), sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF)), sdk.NewCoin("bnb", sdk.NewInt(70*BNB_CF))),
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1050*KAVA_CF)), sdk.NewCoin("bnb", sdk.NewInt(30*BUSD_CF)), sdk.NewCoin("usdx", sdk.NewInt(100*USDX_CF))),
},
errArgs{
expectPass: true,
contains: "",
},
},
{
"invalid: over loan-to-value with multiple previous borrows",
args{
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.Coin{sdk.NewCoin("bnb", sdk.NewInt(30*BNB_CF)), sdk.NewCoin("ukava", sdk.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", sdk.NewInt(100*USDX_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF))),
borrowCoins: sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(1*USDX_CF))),
expectedAccountBalance: sdk.NewCoins(),
expectedModAccountBalance: sdk.NewCoins(),
},
errArgs{
expectPass: false,
contains: "requested borrow 1000000usdx is greater than maximum valid borrow",
},
},
} }
for _, tc := range testCases { for _, tc := range testCases {
suite.Run(tc.name, func() { suite.Run(tc.name, func() {
@ -125,7 +181,8 @@ func (suite *KeeperTestSuite) TestBorrow() {
// Auth module genesis state // Auth module genesis state
authGS := app.NewAuthGenState( authGS := app.NewAuthGenState(
[]sdk.AccAddress{tc.args.borrower}, []sdk.AccAddress{tc.args.borrower},
[]sdk.Coins{sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF)), sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF)))}) []sdk.Coins{sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100*KAVA_CF)),
sdk.NewCoin("btcb", sdk.NewInt(100*BTCB_CF)), sdk.NewCoin("bnb", sdk.NewInt(100*BNB_CF)))})
// Harvest module genesis state // Harvest module genesis state
harvestGS := types.NewGenesisState(types.NewParams( harvestGS := types.NewGenesisState(types.NewParams(
@ -134,6 +191,8 @@ func (suite *KeeperTestSuite) TestBorrow() {
types.NewDistributionSchedule(true, "usdx", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}), types.NewDistributionSchedule(true, "usdx", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}), types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
types.NewDistributionSchedule(true, "btcb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}), types.NewDistributionSchedule(true, "btcb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
types.NewDistributionSchedule(true, "busd", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
}, },
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule( types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
types.NewDistributionSchedule(true, "usdx", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2025, 10, 8, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(500)), time.Date(2026, 10, 8, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}), types.NewDistributionSchedule(true, "usdx", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2025, 10, 8, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(500)), time.Date(2026, 10, 8, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
@ -141,9 +200,11 @@ func (suite *KeeperTestSuite) TestBorrow() {
), ),
}, },
types.MoneyMarkets{ types.MoneyMarkets{
types.NewMoneyMarket("usdx", sdk.NewInt(100000000*USDX_CF), sdk.MustNewDecFromStr("0.01"), "usdx:usd", sdk.NewInt(USDX_CF)), types.NewMoneyMarket("usdx", sdk.NewInt(100000000*USDX_CF), sdk.MustNewDecFromStr("1"), "usdx:usd", sdk.NewInt(USDX_CF)),
types.NewMoneyMarket("busd", sdk.NewInt(100000000*BUSD_CF), sdk.MustNewDecFromStr("1"), "busd:usd", sdk.NewInt(BUSD_CF)),
types.NewMoneyMarket("ukava", sdk.NewInt(100000000*KAVA_CF), tc.args.loanToValueKAVA, "kava:usd", sdk.NewInt(KAVA_CF)), types.NewMoneyMarket("ukava", sdk.NewInt(100000000*KAVA_CF), tc.args.loanToValueKAVA, "kava:usd", sdk.NewInt(KAVA_CF)),
types.NewMoneyMarket("btcb", sdk.NewInt(100000000*BTCB_CF), tc.args.loanToValueBTCB, "btcb:usd", sdk.NewInt(BTCB_CF)), types.NewMoneyMarket("btcb", sdk.NewInt(100000000*BTCB_CF), tc.args.loanToValueBTCB, "btcb:usd", sdk.NewInt(BTCB_CF)),
types.NewMoneyMarket("bnb", sdk.NewInt(100000000*BNB_CF), tc.args.loanToValueBNB, "bnb:usd", sdk.NewInt(BNB_CF)),
}, },
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes) ), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
@ -151,9 +212,11 @@ func (suite *KeeperTestSuite) TestBorrow() {
pricefeedGS := pricefeed.GenesisState{ pricefeedGS := pricefeed.GenesisState{
Params: pricefeed.Params{ Params: pricefeed.Params{
Markets: []pricefeed.Market{ Markets: []pricefeed.Market{
{MarketID: "usdx:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true}, {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: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
{MarketID: "btcb:usd", BaseAsset: "btcb", 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},
}, },
}, },
PostedPrices: []pricefeed.PostedPrice{ PostedPrices: []pricefeed.PostedPrice{
@ -163,6 +226,12 @@ func (suite *KeeperTestSuite) TestBorrow() {
Price: sdk.MustNewDecFromStr("1.00"), Price: sdk.MustNewDecFromStr("1.00"),
Expiry: time.Now().Add(1 * time.Hour), 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", MarketID: "kava:usd",
OracleAddress: sdk.AccAddress{}, OracleAddress: sdk.AccAddress{},
@ -175,6 +244,12 @@ func (suite *KeeperTestSuite) TestBorrow() {
Price: tc.args.priceBTCB, Price: tc.args.priceBTCB,
Expiry: time.Now().Add(1 * time.Hour), Expiry: time.Now().Add(1 * time.Hour),
}, },
{
MarketID: "bnb:usd",
OracleAddress: sdk.AccAddress{},
Price: tc.args.priceBNB,
Expiry: time.Now().Add(1 * time.Hour),
},
}, },
} }
@ -185,7 +260,8 @@ func (suite *KeeperTestSuite) TestBorrow() {
// Mint coins to Harvest module account // Mint coins to Harvest module account
supplyKeeper := tApp.GetSupplyKeeper() supplyKeeper := tApp.GetSupplyKeeper()
harvestMaccCoins := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)), sdk.NewCoin("usdx", sdk.NewInt(200*USDX_CF))) harvestMaccCoins := sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000*KAVA_CF)),
sdk.NewCoin("usdx", sdk.NewInt(200*USDX_CF)), sdk.NewCoin("busd", sdk.NewInt(100*BUSD_CF)))
supplyKeeper.MintCoins(ctx, types.ModuleAccountName, harvestMaccCoins) supplyKeeper.MintCoins(ctx, types.ModuleAccountName, harvestMaccCoins)
keeper := tApp.GetHarvestKeeper() keeper := tApp.GetHarvestKeeper()
@ -203,10 +279,13 @@ func (suite *KeeperTestSuite) TestBorrow() {
depositedCoins.Add(depositCoin) depositedCoins.Add(depositCoin)
} }
// run the test // Execute user's previous borrows
err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.previousBorrowCoins)
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) err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.borrowCoins)
// verify results
if tc.errArgs.expectPass { if tc.errArgs.expectPass {
suite.Require().NoError(err) suite.Require().NoError(err)