mirror of
				https://github.com/0glabs/0g-chain.git
				synced 2025-11-04 04:37:26 +00:00 
			
		
		
		
	Harvest: borrows limited by global asset borrow limit (#715)
* update MaximumLimit param to MaximumLimitUSD * track total borrowed coins in the store * implement total borrowed coins querier * add maximum value usd check * update test suite, add zero coins check * add test case, update error msg * max limit in native amount * remove debug logging * prepare for master rebase * master rebase * fix build
This commit is contained in:
		
							parent
							
								
									cfb1905ad3
								
							
						
					
					
						commit
						510b7e7c04
					
				@ -308,3 +308,30 @@ func queryBorrowsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	cmd.Flags().String(flagOwner, "", "(optional) filter for borrows by owner address")
 | 
			
		||||
	return cmd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryBorrowedCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
 | 
			
		||||
	return &cobra.Command{
 | 
			
		||||
		Use:   "borrowed",
 | 
			
		||||
		Short: "get total current borrowed amount",
 | 
			
		||||
		Long:  "get the total amount of coins currently borrowed for the Harvest protocol",
 | 
			
		||||
		Args:  cobra.NoArgs,
 | 
			
		||||
		RunE: func(cmd *cobra.Command, args []string) error {
 | 
			
		||||
			cliCtx := context.NewCLIContext().WithCodec(cdc)
 | 
			
		||||
 | 
			
		||||
			// Query
 | 
			
		||||
			route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetBorrowed)
 | 
			
		||||
			res, height, err := cliCtx.QueryWithData(route, nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			cliCtx = cliCtx.WithHeight(height)
 | 
			
		||||
 | 
			
		||||
			// Decode and print results
 | 
			
		||||
			var borrowedCoins sdk.Coins
 | 
			
		||||
			if err := cdc.UnmarshalJSON(res, &borrowedCoins); err != nil {
 | 
			
		||||
				return fmt.Errorf("failed to unmarshal borrowed coins: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			return cliCtx.PrintOutput(borrowedCoins)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,11 +11,13 @@ import (
 | 
			
		||||
 | 
			
		||||
// Borrow funds
 | 
			
		||||
func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins) error {
 | 
			
		||||
	// Validate borrow amount within user and protocol limits
 | 
			
		||||
	err := k.ValidateBorrow(ctx, borrower, coins)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Sends coins from Harvest module account to user
 | 
			
		||||
	err = k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleAccountName, borrower, coins)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if strings.Contains(err.Error(), "insufficient account funds") {
 | 
			
		||||
@ -32,6 +34,7 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Update user's borrow in store
 | 
			
		||||
	borrow, found := k.GetBorrow(ctx, borrower)
 | 
			
		||||
	if !found {
 | 
			
		||||
		borrow = types.NewBorrow(borrower, coins)
 | 
			
		||||
@ -40,6 +43,9 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
 | 
			
		||||
	}
 | 
			
		||||
	k.SetBorrow(ctx, borrow)
 | 
			
		||||
 | 
			
		||||
	// Update total borrowed amount
 | 
			
		||||
	k.IncrementBorrowedCoins(ctx, coins)
 | 
			
		||||
 | 
			
		||||
	ctx.EventManager().EmitEvent(
 | 
			
		||||
		sdk.NewEvent(
 | 
			
		||||
			types.EventTypeHarvestBorrow,
 | 
			
		||||
@ -53,6 +59,10 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
 | 
			
		||||
 | 
			
		||||
// ValidateBorrow validates a borrow request against borrower and protocol requirements
 | 
			
		||||
func (k Keeper) ValidateBorrow(ctx sdk.Context, borrower sdk.AccAddress, amount sdk.Coins) error {
 | 
			
		||||
	if amount.IsZero() {
 | 
			
		||||
		return types.ErrBorrowEmptyCoins
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get the proposed borrow USD value
 | 
			
		||||
	moneyMarketCache := map[string]types.MoneyMarket{}
 | 
			
		||||
	proprosedBorrowUSDValue := sdk.ZeroDec()
 | 
			
		||||
@ -74,6 +84,23 @@ func (k Keeper) ValidateBorrow(ctx sdk.Context, borrower sdk.AccAddress, amount
 | 
			
		||||
			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)
 | 
			
		||||
 | 
			
		||||
		// Validate the requested borrow value for the asset against the money market's global borrow limit
 | 
			
		||||
		if moneyMarket.BorrowLimit.HasMaxLimit {
 | 
			
		||||
			var assetTotalBorrowedAmount sdk.Int
 | 
			
		||||
			totalBorrowedCoins, found := k.GetBorrowedCoins(ctx)
 | 
			
		||||
			if !found {
 | 
			
		||||
				assetTotalBorrowedAmount = sdk.ZeroInt()
 | 
			
		||||
			} else {
 | 
			
		||||
				assetTotalBorrowedAmount = totalBorrowedCoins.AmountOf(coin.Denom)
 | 
			
		||||
			}
 | 
			
		||||
			newProposedAssetTotalBorrowedAmount := sdk.NewDecFromInt(assetTotalBorrowedAmount.Add(coin.Amount))
 | 
			
		||||
			if newProposedAssetTotalBorrowedAmount.GT(moneyMarket.BorrowLimit.MaximumLimit) {
 | 
			
		||||
				return sdkerrors.Wrapf(types.ErrGreaterThanAssetBorrowLimit,
 | 
			
		||||
					"proposed borrow would result in %s borrowed, but the maximum global asset borrow limit is %s",
 | 
			
		||||
					newProposedAssetTotalBorrowedAmount, moneyMarket.BorrowLimit.MaximumLimit)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		proprosedBorrowUSDValue = proprosedBorrowUSDValue.Add(coinUSDValue)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -137,3 +164,29 @@ func (k Keeper) ValidateBorrow(ctx sdk.Context, borrower sdk.AccAddress, amount
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IncrementBorrowedCoins increments the amount of borrowed coins by the newCoins parameter
 | 
			
		||||
func (k Keeper) IncrementBorrowedCoins(ctx sdk.Context, newCoins sdk.Coins) {
 | 
			
		||||
	borrowedCoins, found := k.GetBorrowedCoins(ctx)
 | 
			
		||||
	if !found {
 | 
			
		||||
		k.SetBorrowedCoins(ctx, newCoins)
 | 
			
		||||
	} else {
 | 
			
		||||
		k.SetBorrowedCoins(ctx, borrowedCoins.Add(newCoins...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DecrementBorrowedCoins decrements the amount of borrowed coins by the coins parameter
 | 
			
		||||
func (k Keeper) DecrementBorrowedCoins(ctx sdk.Context, coins sdk.Coins) error {
 | 
			
		||||
	borrowedCoins, found := k.GetBorrowedCoins(ctx)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return sdkerrors.Wrapf(types.ErrBorrowedCoinsNotFound, "cannot repay coins if no coins are currently borrowed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updatedBorrowedCoins, isAnyNegative := borrowedCoins.SafeSub(coins)
 | 
			
		||||
	if isAnyNegative {
 | 
			
		||||
		return types.ErrNegativeBorrowedCoins
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	k.SetBorrowedCoins(ctx, updatedBorrowedCoins)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,7 @@ const (
 | 
			
		||||
func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
 | 
			
		||||
	type args struct {
 | 
			
		||||
		usdxBorrowLimit           sdk.Dec
 | 
			
		||||
		priceKAVA                 sdk.Dec
 | 
			
		||||
		loanToValueKAVA           sdk.Dec
 | 
			
		||||
		priceBTCB                 sdk.Dec
 | 
			
		||||
@ -51,6 +52,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"valid",
 | 
			
		||||
			args{
 | 
			
		||||
				usdxBorrowLimit:           sdk.MustNewDecFromStr("100000000000"),
 | 
			
		||||
				priceKAVA:                 sdk.MustNewDecFromStr("5.00"),
 | 
			
		||||
				loanToValueKAVA:           sdk.MustNewDecFromStr("0.6"),
 | 
			
		||||
				priceBTCB:                 sdk.MustNewDecFromStr("0.00"),
 | 
			
		||||
@ -72,6 +74,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"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"),
 | 
			
		||||
@ -92,6 +95,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"valid: multiple deposits",
 | 
			
		||||
			args{
 | 
			
		||||
				usdxBorrowLimit:           sdk.MustNewDecFromStr("100000000000"),
 | 
			
		||||
				priceKAVA:                 sdk.MustNewDecFromStr("2.00"),
 | 
			
		||||
				loanToValueKAVA:           sdk.MustNewDecFromStr("0.80"),
 | 
			
		||||
				priceBTCB:                 sdk.MustNewDecFromStr("10000.00"),
 | 
			
		||||
@ -112,6 +116,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"invalid: multiple deposits",
 | 
			
		||||
			args{
 | 
			
		||||
				usdxBorrowLimit:           sdk.MustNewDecFromStr("100000000000"),
 | 
			
		||||
				priceKAVA:                 sdk.MustNewDecFromStr("2.00"),
 | 
			
		||||
				loanToValueKAVA:           sdk.MustNewDecFromStr("0.80"),
 | 
			
		||||
				priceBTCB:                 sdk.MustNewDecFromStr("10000.00"),
 | 
			
		||||
@ -132,6 +137,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"valid: multiple previous borrows",
 | 
			
		||||
			args{
 | 
			
		||||
				usdxBorrowLimit:           sdk.MustNewDecFromStr("100000000000"),
 | 
			
		||||
				priceKAVA:                 sdk.MustNewDecFromStr("2.00"),
 | 
			
		||||
				loanToValueKAVA:           sdk.MustNewDecFromStr("0.8"),
 | 
			
		||||
				priceBTCB:                 sdk.MustNewDecFromStr("0.00"),
 | 
			
		||||
@ -153,6 +159,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"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"),
 | 
			
		||||
@ -174,6 +181,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"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"),
 | 
			
		||||
@ -195,6 +203,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
		{
 | 
			
		||||
			"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"),
 | 
			
		||||
@ -213,6 +222,28 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
				contains:   "exceeds module account balance:",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"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.Coin{sdk.NewCoin("ukava", sdk.NewInt(50*KAVA_CF))},
 | 
			
		||||
				previousBorrowCoins:       sdk.NewCoins(),
 | 
			
		||||
				borrowCoins:               sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(25*USDX_CF))),
 | 
			
		||||
				expectedAccountBalance:    sdk.NewCoins(),
 | 
			
		||||
				expectedModAccountBalance: sdk.NewCoins(),
 | 
			
		||||
			},
 | 
			
		||||
			errArgs{
 | 
			
		||||
				expectPass: false,
 | 
			
		||||
				contains:   "fails global asset borrow limit validation",
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, tc := range testCases {
 | 
			
		||||
		suite.Run(tc.name, func() {
 | 
			
		||||
@ -244,12 +275,12 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					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("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.NewMoneyMarket("xyz", sdk.NewInt(1), tc.args.loanToValueBNB, "xyz:usd", sdk.NewInt(1)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", true, tc.args.usdxBorrowLimit, sdk.MustNewDecFromStr("1"), "usdx:usd", sdk.NewInt(USDX_CF)),
 | 
			
		||||
					types.NewMoneyMarket("busd", false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1"), "busd:usd", sdk.NewInt(BUSD_CF)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(100000000*KAVA_CF), tc.args.loanToValueKAVA, "kava:usd", sdk.NewInt(KAVA_CF)),
 | 
			
		||||
					types.NewMoneyMarket("btcb", false, sdk.NewDec(100000000*BTCB_CF), tc.args.loanToValueBTCB, "btcb:usd", sdk.NewInt(BTCB_CF)),
 | 
			
		||||
					types.NewMoneyMarket("bnb", false, sdk.NewDec(100000000*BNB_CF), tc.args.loanToValueBNB, "bnb:usd", sdk.NewInt(BNB_CF)),
 | 
			
		||||
					types.NewMoneyMarket("xyz", false, sdk.NewDec(1), tc.args.loanToValueBNB, "xyz:usd", sdk.NewInt(1)),
 | 
			
		||||
				},
 | 
			
		||||
			), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
 | 
			
		||||
@ -327,7 +358,11 @@ func (suite *KeeperTestSuite) TestBorrow() {
 | 
			
		||||
 | 
			
		||||
			// Execute user's previous borrows
 | 
			
		||||
			err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.previousBorrowCoins)
 | 
			
		||||
			suite.Require().NoError(err)
 | 
			
		||||
			if tc.args.previousBorrowCoins.IsZero() {
 | 
			
		||||
				suite.Require().True(strings.Contains(err.Error(), "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)
 | 
			
		||||
 | 
			
		||||
@ -265,8 +265,8 @@ func (suite *KeeperTestSuite) TestClaim() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				},
 | 
			
		||||
			), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
			tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
 | 
			
		||||
 | 
			
		||||
@ -128,8 +128,8 @@ func (suite *KeeperTestSuite) TestDeposit() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				},
 | 
			
		||||
			), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
			tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
 | 
			
		||||
@ -300,8 +300,8 @@ func (suite *KeeperTestSuite) TestWithdraw() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				},
 | 
			
		||||
			), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
			tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
 | 
			
		||||
 | 
			
		||||
@ -237,3 +237,22 @@ func (k Keeper) IterateBorrows(ctx sdk.Context, cb func(borrow types.Borrow) (st
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBorrowedCoins sets the total amount of coins currently borrowed in the store
 | 
			
		||||
func (k Keeper) SetBorrowedCoins(ctx sdk.Context, borrowedCoins sdk.Coins) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowedCoinsPrefix)
 | 
			
		||||
	bz := k.cdc.MustMarshalBinaryBare(borrowedCoins)
 | 
			
		||||
	store.Set([]byte{}, bz)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetBorrowedCoins returns an sdk.Coins object from the store representing all currently borrowed coins
 | 
			
		||||
func (k Keeper) GetBorrowedCoins(ctx sdk.Context) (sdk.Coins, bool) {
 | 
			
		||||
	store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowedCoinsPrefix)
 | 
			
		||||
	bz := store.Get([]byte{})
 | 
			
		||||
	if bz == nil {
 | 
			
		||||
		return sdk.Coins{}, false
 | 
			
		||||
	}
 | 
			
		||||
	var borrowedCoins sdk.Coins
 | 
			
		||||
	k.cdc.MustUnmarshalBinaryBare(bz, &borrowedCoins)
 | 
			
		||||
	return borrowedCoins, true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,8 @@ func NewQuerier(k Keeper) sdk.Querier {
 | 
			
		||||
			return queryGetClaims(ctx, req, k)
 | 
			
		||||
		case types.QueryGetBorrows:
 | 
			
		||||
			return queryGetBorrows(ctx, req, k)
 | 
			
		||||
		case types.QueryGetBorrowed:
 | 
			
		||||
			return queryGetBorrowed(ctx, req, k)
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
 | 
			
		||||
		}
 | 
			
		||||
@ -295,3 +297,28 @@ func queryGetBorrows(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
 | 
			
		||||
 | 
			
		||||
	return bz, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryGetBorrowed(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
 | 
			
		||||
	var params types.QueryBorrowedParams
 | 
			
		||||
	err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	borrowedCoins, found := k.GetBorrowedCoins(ctx)
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil, types.ErrBorrowedCoinsNotFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// If user specified a denom only return coins of that denom type
 | 
			
		||||
	if len(params.Denom) > 0 {
 | 
			
		||||
		borrowedCoins = sdk.NewCoins(sdk.NewCoin(params.Denom, borrowedCoins.AmountOf(params.Denom)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bz, err := codec.MarshalJSONIndent(types.ModuleCdc, borrowedCoins)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bz, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -75,8 +75,8 @@ func (suite *KeeperTestSuite) TestApplyDepositRewards() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				},
 | 
			
		||||
			), tc.args.previousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
			tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
 | 
			
		||||
@ -443,8 +443,8 @@ func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
 | 
			
		||||
				),
 | 
			
		||||
			},
 | 
			
		||||
			types.MoneyMarkets{
 | 
			
		||||
				types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
			},
 | 
			
		||||
		),
 | 
			
		||||
		types.DefaultPreviousBlockTime,
 | 
			
		||||
 | 
			
		||||
@ -291,8 +291,8 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
 | 
			
		||||
				),
 | 
			
		||||
				},
 | 
			
		||||
				types.MoneyMarkets{
 | 
			
		||||
					types.NewMoneyMarket("usdx", sdk.NewInt(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", sdk.NewInt(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("usdx", false, sdk.NewDec(1000000000000000), loanToValue, "usdx:usd", sdk.NewInt(1000000)),
 | 
			
		||||
					types.NewMoneyMarket("ukava", false, sdk.NewDec(1000000000000000), loanToValue, "kava:usd", sdk.NewInt(1000000)),
 | 
			
		||||
				},
 | 
			
		||||
			), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
 | 
			
		||||
			tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
 | 
			
		||||
 | 
			
		||||
@ -47,4 +47,12 @@ var (
 | 
			
		||||
	ErrPriceNotFound = sdkerrors.Register(ModuleName, 20, "no price found for market")
 | 
			
		||||
	// ErrBorrowExceedsAvailableBalance for when a requested borrow exceeds available module acc balances
 | 
			
		||||
	ErrBorrowExceedsAvailableBalance = sdkerrors.Register(ModuleName, 21, "exceeds module account balance")
 | 
			
		||||
	// ErrBorrowedCoinsNotFound error for when the total amount of borrowed coins cannot be found
 | 
			
		||||
	ErrBorrowedCoinsNotFound = sdkerrors.Register(ModuleName, 22, "no borrowed coins found")
 | 
			
		||||
	// ErrNegativeBorrowedCoins error for when substracting coins from the total borrowed balance results in a negative amount
 | 
			
		||||
	ErrNegativeBorrowedCoins = sdkerrors.Register(ModuleName, 23, "subtraction results in negative borrow amount")
 | 
			
		||||
	// ErrGreaterThanAssetBorrowLimit error for when a proposed borrow would increase borrowed amount over the asset's global borrow limit
 | 
			
		||||
	ErrGreaterThanAssetBorrowLimit = sdkerrors.Register(ModuleName, 24, "fails global asset borrow limit validation")
 | 
			
		||||
	// ErrBorrowEmptyCoins error for when you cannot borrow empty coins
 | 
			
		||||
	ErrBorrowEmptyCoins = sdkerrors.Register(ModuleName, 25, "cannot borrow zero coins")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,7 @@ var (
 | 
			
		||||
	DepositsKeyPrefix                 = []byte{0x03}
 | 
			
		||||
	ClaimsKeyPrefix                   = []byte{0x04}
 | 
			
		||||
	BorrowsKeyPrefix                  = []byte{0x05}
 | 
			
		||||
	BorrowedCoinsPrefix               = []byte{0x06}
 | 
			
		||||
	sep                               = []byte(":")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -223,13 +223,15 @@ type Multipliers []Multiplier
 | 
			
		||||
 | 
			
		||||
// BorrowLimit enforces restrictions on a money market
 | 
			
		||||
type BorrowLimit struct {
 | 
			
		||||
	MaximumLimit sdk.Int `json:"maximum_limit" yaml:"maximum_limit"`
 | 
			
		||||
	HasMaxLimit  bool    `json:"has_max_limit" yaml:"has_max_limit"`
 | 
			
		||||
	MaximumLimit sdk.Dec `json:"maximum_limit" yaml:"maximum_limit"`
 | 
			
		||||
	LoanToValue  sdk.Dec `json:"loan_to_value" yaml:"loan_to_value"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBorrowLimit returns a new BorrowLimit
 | 
			
		||||
func NewBorrowLimit(maximumLimit sdk.Int, loanToValue sdk.Dec) BorrowLimit {
 | 
			
		||||
func NewBorrowLimit(hasMaxLimit bool, maximumLimit, loanToValue sdk.Dec) BorrowLimit {
 | 
			
		||||
	return BorrowLimit{
 | 
			
		||||
		HasMaxLimit:  hasMaxLimit,
 | 
			
		||||
		MaximumLimit: maximumLimit,
 | 
			
		||||
		LoanToValue:  loanToValue,
 | 
			
		||||
	}
 | 
			
		||||
@ -238,7 +240,7 @@ func NewBorrowLimit(maximumLimit sdk.Int, loanToValue sdk.Dec) BorrowLimit {
 | 
			
		||||
// Validate BorrowLimit
 | 
			
		||||
func (bl BorrowLimit) Validate() error {
 | 
			
		||||
	if bl.MaximumLimit.IsNegative() {
 | 
			
		||||
		return fmt.Errorf("maximum limit cannot be negative: %s", bl.MaximumLimit)
 | 
			
		||||
		return fmt.Errorf("maximum limit USD cannot be negative: %s", bl.MaximumLimit)
 | 
			
		||||
	}
 | 
			
		||||
	if !bl.LoanToValue.IsPositive() {
 | 
			
		||||
		return fmt.Errorf("loan-to-value must be a positive integer: %s", bl.LoanToValue)
 | 
			
		||||
@ -258,11 +260,11 @@ type MoneyMarket struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMoneyMarket returns a new MoneyMarket
 | 
			
		||||
func NewMoneyMarket(denom string, maximumLimit sdk.Int, loanToValue sdk.Dec,
 | 
			
		||||
func NewMoneyMarket(denom string, hasMaxLimit bool, maximumLimit, loanToValue sdk.Dec,
 | 
			
		||||
	spotMarketID string, conversionFactor sdk.Int) MoneyMarket {
 | 
			
		||||
	return MoneyMarket{
 | 
			
		||||
		Denom:            denom,
 | 
			
		||||
		BorrowLimit:      NewBorrowLimit(maximumLimit, loanToValue),
 | 
			
		||||
		BorrowLimit:      NewBorrowLimit(hasMaxLimit, maximumLimit, loanToValue),
 | 
			
		||||
		SpotMarketID:     spotMarketID,
 | 
			
		||||
		ConversionFactor: conversionFactor,
 | 
			
		||||
	}
 | 
			
		||||
@ -314,7 +316,7 @@ func (p Params) String() string {
 | 
			
		||||
	Active: %t
 | 
			
		||||
	Liquidity Provider Distribution Schedules %s
 | 
			
		||||
	Delegator Distribution Schedule %s
 | 
			
		||||
	Money Markets %s`, p.Active, p.LiquidityProviderSchedules, p.DelegatorDistributionSchedules, p.MoneyMarkets)
 | 
			
		||||
	Money Markets %v`, p.Active, p.LiquidityProviderSchedules, p.DelegatorDistributionSchedules, p.MoneyMarkets)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParamKeyTable Key declaration for parameters
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ const (
 | 
			
		||||
	QueryGetDeposits       = "deposits"
 | 
			
		||||
	QueryGetClaims         = "claims"
 | 
			
		||||
	QueryGetBorrows        = "borrows"
 | 
			
		||||
	QueryGetBorrowed       = "borrowed"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// QueryDepositParams is the params for a filtered deposit query
 | 
			
		||||
@ -86,3 +87,15 @@ func NewQueryBorrowParams(page, limit int, owner sdk.AccAddress, depositDenom st
 | 
			
		||||
		BorrowDenom: depositDenom,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// QueryBorrowedParams is the params for a filtered borrowed coins query
 | 
			
		||||
type QueryBorrowedParams struct {
 | 
			
		||||
	Denom string `json:"denom" yaml:"denom"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewQueryBorrowedParams creates a new QueryBorrowedParams
 | 
			
		||||
func NewQueryBorrowedParams(denom string) QueryBorrowedParams {
 | 
			
		||||
	return QueryBorrowedParams{
 | 
			
		||||
		Denom: denom,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user