mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +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