mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
Harvest basic borrow functionality (#702)
* basic borrow types * borrow keeper scaffolding * borrow limits param * integrate pricefeed keeper * msg handling and querier * borrow user validation * update migration scripts for compile * borrows querier, fixes * add money market param * add spot market ID to params, refactor pricefeed * working bnb -> ukava borrows * refactor to getAssetPrice * conversion_factor param, refactor validateBorrow() * address misc revisions * remove validation code * add borrow test * update test params * single borrow with sdk.Coins per user * fix harvest test * removed legacy commented out code * address minor revisions
This commit is contained in:
parent
35a82acbd0
commit
1442deb3dc
@ -376,7 +376,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
|||||||
harvestSubspace,
|
harvestSubspace,
|
||||||
app.accountKeeper,
|
app.accountKeeper,
|
||||||
app.supplyKeeper,
|
app.supplyKeeper,
|
||||||
&stakingKeeper)
|
&stakingKeeper,
|
||||||
|
app.pricefeedKeeper)
|
||||||
|
|
||||||
// register the staking hooks
|
// register the staking hooks
|
||||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
||||||
@ -407,7 +408,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
|||||||
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
||||||
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
||||||
issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper),
|
issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper),
|
||||||
harvest.NewAppModule(app.harvestKeeper, app.supplyKeeper),
|
harvest.NewAppModule(app.harvestKeeper, app.supplyKeeper, app.pricefeedKeeper),
|
||||||
)
|
)
|
||||||
|
|
||||||
// During begin block slashing happens after distr.BeginBlocker so that
|
// During begin block slashing happens after distr.BeginBlocker so that
|
||||||
@ -460,7 +461,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
|||||||
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
incentive.NewAppModule(app.incentiveKeeper, app.accountKeeper, app.supplyKeeper),
|
||||||
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
||||||
issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper),
|
issuance.NewAppModule(app.issuanceKeeper, app.accountKeeper, app.supplyKeeper),
|
||||||
harvest.NewAppModule(app.harvestKeeper, app.supplyKeeper),
|
harvest.NewAppModule(app.harvestKeeper, app.supplyKeeper, app.pricefeedKeeper),
|
||||||
)
|
)
|
||||||
|
|
||||||
app.sm.RegisterStoreDecoders()
|
app.sm.RegisterStoreDecoders()
|
||||||
|
@ -129,7 +129,7 @@ func MigrateAppState(v0_9AppState v39_genutil.AppMap) v39_genutil.AppMap {
|
|||||||
delete(v0_9AppState, v0_9pricefeed.ModuleName)
|
delete(v0_9AppState, v0_9pricefeed.ModuleName)
|
||||||
v0_11AppState[v0_9pricefeed.ModuleName] = v0_11Codec.MustMarshalJSON(MigratePricefeed(pricefeedGenState))
|
v0_11AppState[v0_9pricefeed.ModuleName] = v0_11Codec.MustMarshalJSON(MigratePricefeed(pricefeedGenState))
|
||||||
}
|
}
|
||||||
v0_11AppState[v0_11harvest.ModuleName] = v0_11Codec.MustMarshalJSON(MigrateHarvest())
|
// v0_11AppState[v0_11harvest.ModuleName] = v0_11Codec.MustMarshalJSON(MigrateHarvest())
|
||||||
v0_11AppState[v0_11issuance.ModuleName] = v0_11Codec.MustMarshalJSON(v0_11issuance.DefaultGenesisState())
|
v0_11AppState[v0_11issuance.ModuleName] = v0_11Codec.MustMarshalJSON(v0_11issuance.DefaultGenesisState())
|
||||||
return v0_11AppState
|
return v0_11AppState
|
||||||
}
|
}
|
||||||
@ -633,29 +633,34 @@ func MigrateGov(oldGenState v39_1gov.GenesisState) v39_1gov.GenesisState {
|
|||||||
return oldGenState
|
return oldGenState
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateHarvest initializes the harvest genesis state for kava-4
|
// // MigrateHarvest initializes the harvest genesis state for kava-4
|
||||||
func MigrateHarvest() v0_11harvest.GenesisState {
|
// func MigrateHarvest() v0_11harvest.GenesisState {
|
||||||
// total HARD per second for lps (week one): 633761
|
// // total HARD per second for lps (week one): 633761
|
||||||
// HARD per second for delegators (week one): 1267522
|
// // HARD per second for delegators (week one): 1267522
|
||||||
incentiveGoLiveDate := time.Date(2020, 10, 16, 14, 0, 0, 0, time.UTC)
|
// incentiveGoLiveDate := time.Date(2020, 10, 16, 14, 0, 0, 0, time.UTC)
|
||||||
incentiveEndDate := time.Date(2024, 10, 16, 14, 0, 0, 0, time.UTC)
|
// incentiveEndDate := time.Date(2024, 10, 16, 14, 0, 0, 0, time.UTC)
|
||||||
claimEndDate := time.Date(2025, 10, 16, 14, 0, 0, 0, time.UTC)
|
// claimEndDate := time.Date(2025, 10, 16, 14, 0, 0, 0, time.UTC)
|
||||||
harvestGS := v0_11harvest.NewGenesisState(v0_11harvest.NewParams(
|
// harvestGS := v0_11harvest.NewGenesisState(v0_11harvest.NewParams(
|
||||||
true,
|
// true,
|
||||||
v0_11harvest.DistributionSchedules{
|
// v0_11harvest.DistributionSchedules{
|
||||||
v0_11harvest.NewDistributionSchedule(true, "usdx", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(310543)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
// v0_11harvest.NewDistributionSchedule(true, "usdx", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(310543)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
||||||
v0_11harvest.NewDistributionSchedule(true, "hard", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(285193)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
// v0_11harvest.NewDistributionSchedule(true, "hard", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(285193)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
||||||
v0_11harvest.NewDistributionSchedule(true, "bnb", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(12675)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
// v0_11harvest.NewDistributionSchedule(true, "bnb", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(12675)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
||||||
v0_11harvest.NewDistributionSchedule(true, "ukava", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(25350)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
// v0_11harvest.NewDistributionSchedule(true, "ukava", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(25350)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
||||||
},
|
// },
|
||||||
v0_11harvest.DelegatorDistributionSchedules{v0_11harvest.NewDelegatorDistributionSchedule(
|
// v0_11harvest.DelegatorDistributionSchedules{v0_11harvest.NewDelegatorDistributionSchedule(
|
||||||
v0_11harvest.NewDistributionSchedule(true, "ukava", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(1267522)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
// v0_11harvest.NewDistributionSchedule(true, "ukava", incentiveGoLiveDate, incentiveEndDate, sdk.NewCoin("hard", sdk.NewInt(1267522)), claimEndDate, v0_11harvest.Multipliers{v0_11harvest.NewMultiplier(v0_11harvest.Small, 1, sdk.MustNewDecFromStr("0.33")), v0_11harvest.NewMultiplier(v0_11harvest.Large, 12, sdk.OneDec())}),
|
||||||
time.Hour*24,
|
// time.Hour*24,
|
||||||
),
|
// ),
|
||||||
},
|
// },
|
||||||
), v0_11harvest.DefaultPreviousBlockTime, v0_11harvest.DefaultDistributionTimes)
|
// v0_11harvest.BlockLimits{
|
||||||
return harvestGS
|
// v0_11harvest.NewBlockLimit("usdx", sdk.Dec(0.9)),
|
||||||
}
|
// v0_11harvest.NewBlockLimit("ukava", sdk.Dec(0.6)),
|
||||||
|
// v0_11harvest.NewBlockLimit("bnb", sdk.Dec(0.9)),
|
||||||
|
// },
|
||||||
|
// ), v0_11harvest.DefaultPreviousBlockTime, v0_11harvest.DefaultDistributionTimes)
|
||||||
|
// return harvestGS
|
||||||
|
// }
|
||||||
|
|
||||||
// MigrateCDP migrates from a v0.9 (or v0.10) cdp genesis state to a v0.11 cdp genesis state
|
// MigrateCDP migrates from a v0.9 (or v0.10) cdp genesis state to a v0.11 cdp genesis state
|
||||||
func MigrateCDP(oldGenState v0_9cdp.GenesisState) v0_11cdp.GenesisState {
|
func MigrateCDP(oldGenState v0_9cdp.GenesisState) v0_11cdp.GenesisState {
|
||||||
|
@ -74,6 +74,7 @@ var (
|
|||||||
RegisterCodec = types.RegisterCodec
|
RegisterCodec = types.RegisterCodec
|
||||||
|
|
||||||
// variable aliases
|
// variable aliases
|
||||||
|
BorrowsKeyPrefix = types.BorrowsKeyPrefix
|
||||||
ClaimsKeyPrefix = types.ClaimsKeyPrefix
|
ClaimsKeyPrefix = types.ClaimsKeyPrefix
|
||||||
DefaultActive = types.DefaultActive
|
DefaultActive = types.DefaultActive
|
||||||
DefaultDelegatorSchedules = types.DefaultDelegatorSchedules
|
DefaultDelegatorSchedules = types.DefaultDelegatorSchedules
|
||||||
@ -109,7 +110,9 @@ var (
|
|||||||
type (
|
type (
|
||||||
Keeper = keeper.Keeper
|
Keeper = keeper.Keeper
|
||||||
AccountKeeper = types.AccountKeeper
|
AccountKeeper = types.AccountKeeper
|
||||||
Claim = types.Claim
|
Borrow = types.Borrow
|
||||||
|
MoneyMarket = types.MoneyMarket
|
||||||
|
MoneyMarkets = types.MoneyMarkets
|
||||||
DelegatorDistributionSchedule = types.DelegatorDistributionSchedule
|
DelegatorDistributionSchedule = types.DelegatorDistributionSchedule
|
||||||
DelegatorDistributionSchedules = types.DelegatorDistributionSchedules
|
DelegatorDistributionSchedules = types.DelegatorDistributionSchedules
|
||||||
Deposit = types.Deposit
|
Deposit = types.Deposit
|
||||||
|
@ -40,6 +40,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
|||||||
queryModAccountsCmd(queryRoute, cdc),
|
queryModAccountsCmd(queryRoute, cdc),
|
||||||
queryDepositsCmd(queryRoute, cdc),
|
queryDepositsCmd(queryRoute, cdc),
|
||||||
queryClaimsCmd(queryRoute, cdc),
|
queryClaimsCmd(queryRoute, cdc),
|
||||||
|
queryBorrowsCmd(queryRoute, cdc),
|
||||||
)...)
|
)...)
|
||||||
|
|
||||||
return harvestQueryCmd
|
return harvestQueryCmd
|
||||||
@ -250,3 +251,60 @@ func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
|||||||
cmd.Flags().String(flagDepositType, "", "(optional) filter for claims by type (lp or staking)")
|
cmd.Flags().String(flagDepositType, "", "(optional) filter for claims by type (lp or staking)")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryBorrowsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: "borrows",
|
||||||
|
Short: "query harvest module borrows with optional filters",
|
||||||
|
Long: strings.TrimSpace(`query for all harvest module borrows or a specific borrow using flags:
|
||||||
|
|
||||||
|
Example:
|
||||||
|
$ kvcli q harvest borrows
|
||||||
|
$ kvcli q harvest borrows --borrower kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny
|
||||||
|
$ kvcli q harvest borrows --borrow-denom bnb`,
|
||||||
|
),
|
||||||
|
Args: cobra.NoArgs,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
var owner sdk.AccAddress
|
||||||
|
|
||||||
|
ownerBech := viper.GetString(flagOwner)
|
||||||
|
depositDenom := viper.GetString(flagDepositDenom)
|
||||||
|
|
||||||
|
if len(ownerBech) != 0 {
|
||||||
|
borrowOwner, err := sdk.AccAddressFromBech32(ownerBech)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
owner = borrowOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
page := viper.GetInt(flags.FlagPage)
|
||||||
|
limit := viper.GetInt(flags.FlagLimit)
|
||||||
|
|
||||||
|
params := types.NewQueryBorrowParams(page, limit, owner, depositDenom)
|
||||||
|
bz, err := cdc.MarshalJSON(params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetBorrows)
|
||||||
|
res, height, err := cliCtx.QueryWithData(route, bz)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cliCtx = cliCtx.WithHeight(height)
|
||||||
|
|
||||||
|
var borrows []types.Borrow
|
||||||
|
if err := cdc.UnmarshalJSON(res, &borrows); err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal borrows: %w", err)
|
||||||
|
}
|
||||||
|
return cliCtx.PrintOutput(borrows)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cmd.Flags().Int(flags.FlagPage, 1, "pagination page to query for")
|
||||||
|
cmd.Flags().Int(flags.FlagLimit, 100, "pagination limit (max 100)")
|
||||||
|
cmd.Flags().String(flagOwner, "", "(optional) filter for borrows by owner address")
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
|||||||
getCmdDeposit(cdc),
|
getCmdDeposit(cdc),
|
||||||
getCmdWithdraw(cdc),
|
getCmdWithdraw(cdc),
|
||||||
getCmdClaimReward(cdc),
|
getCmdClaimReward(cdc),
|
||||||
|
getCmdBorrow(cdc),
|
||||||
)...)
|
)...)
|
||||||
|
|
||||||
return harvestTxCmd
|
return harvestTxCmd
|
||||||
@ -116,3 +117,31 @@ func getCmdClaimReward(cdc *codec.Codec) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCmdBorrow(cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "borrow [1000000000ukava]",
|
||||||
|
Short: "borrow tokens from the harvest protocol",
|
||||||
|
Long: strings.TrimSpace(`borrows tokens from the harvest protocol`),
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Example: fmt.Sprintf(
|
||||||
|
`%s tx %s borrow 1000000000ukava --from <key>`, version.ClientName, types.ModuleName,
|
||||||
|
),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
inBuf := bufio.NewReader(cmd.InOrStdin())
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc))
|
||||||
|
|
||||||
|
coins, err := sdk.ParseCoins(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := types.NewMsgBorrow(cliCtx.GetFromAddress(), coins)
|
||||||
|
if err := msg.ValidateBasic(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,8 @@ func NewHandler(k Keeper) sdk.Handler {
|
|||||||
return handleMsgDeposit(ctx, k, msg)
|
return handleMsgDeposit(ctx, k, msg)
|
||||||
case types.MsgWithdraw:
|
case types.MsgWithdraw:
|
||||||
return handleMsgWithdraw(ctx, k, msg)
|
return handleMsgWithdraw(ctx, k, msg)
|
||||||
|
case types.MsgBorrow:
|
||||||
|
return handleMsgBorrow(ctx, k, msg)
|
||||||
default:
|
default:
|
||||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg)
|
||||||
}
|
}
|
||||||
@ -80,3 +82,21 @@ func handleMsgWithdraw(ctx sdk.Context, k keeper.Keeper, msg types.MsgWithdraw)
|
|||||||
Events: ctx.EventManager().Events(),
|
Events: ctx.EventManager().Events(),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMsgBorrow(ctx sdk.Context, k keeper.Keeper, msg types.MsgBorrow) (*sdk.Result, error) {
|
||||||
|
err := k.Borrow(ctx, msg.Borrower, msg.Amount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
sdk.NewEvent(
|
||||||
|
sdk.EventTypeMessage,
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||||
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.Borrower.String()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
return &sdk.Result{
|
||||||
|
Events: ctx.EventManager().Events(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
34
x/harvest/keeper/borrow.go
Normal file
34
x/harvest/keeper/borrow.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package keeper
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/harvest/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Borrow funds
|
||||||
|
func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins) error {
|
||||||
|
err := k.supplyKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleAccountName, borrower, coins)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
borrow, found := k.GetBorrow(ctx, borrower)
|
||||||
|
if !found {
|
||||||
|
borrow = types.NewBorrow(borrower, coins)
|
||||||
|
} else {
|
||||||
|
borrow.Amount = borrow.Amount.Add(coins...)
|
||||||
|
}
|
||||||
|
|
||||||
|
k.SetBorrow(ctx, borrow)
|
||||||
|
|
||||||
|
ctx.EventManager().EmitEvent(
|
||||||
|
sdk.NewEvent(
|
||||||
|
types.EventTypeHarvestBorrow,
|
||||||
|
sdk.NewAttribute(types.AttributeKeyBorrower, borrow.Borrower.String()),
|
||||||
|
sdk.NewAttribute(types.AttributeKeyBorrowCoins, coins.String()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
101
x/harvest/keeper/borrow_test.go
Normal file
101
x/harvest/keeper/borrow_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package keeper_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
|
"github.com/kava-labs/kava/x/harvest/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (suite *KeeperTestSuite) TestBorrow() {
|
||||||
|
type args struct {
|
||||||
|
borrower sdk.AccAddress
|
||||||
|
coins sdk.Coins
|
||||||
|
expectedAccountBalance sdk.Coins
|
||||||
|
expectedModAccountBalance sdk.Coins
|
||||||
|
}
|
||||||
|
type errArgs struct {
|
||||||
|
expectPass bool
|
||||||
|
contains string
|
||||||
|
}
|
||||||
|
type borrowTest struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
errArgs errArgs
|
||||||
|
}
|
||||||
|
testCases := []borrowTest{
|
||||||
|
{
|
||||||
|
"valid",
|
||||||
|
args{
|
||||||
|
borrower: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||||
|
coins: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(50))),
|
||||||
|
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(150))),
|
||||||
|
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(950))),
|
||||||
|
},
|
||||||
|
errArgs{
|
||||||
|
expectPass: true,
|
||||||
|
contains: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
suite.Run(tc.name, func() {
|
||||||
|
// create new app with one funded account
|
||||||
|
|
||||||
|
// Initialize test app and set context
|
||||||
|
tApp := app.NewTestApp()
|
||||||
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||||
|
authGS := app.NewAuthGenState(
|
||||||
|
[]sdk.AccAddress{tc.args.borrower},
|
||||||
|
[]sdk.Coins{sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(100)))})
|
||||||
|
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||||
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
|
true,
|
||||||
|
types.DistributionSchedules{
|
||||||
|
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.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())}),
|
||||||
|
time.Hour*24,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
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.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||||
|
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
|
keeper := tApp.GetHarvestKeeper()
|
||||||
|
supplyKeeper := tApp.GetSupplyKeeper()
|
||||||
|
supplyKeeper.MintCoins(ctx, types.ModuleAccountName, sdk.NewCoins(sdk.NewCoin("ukava", sdk.NewInt(1000))))
|
||||||
|
suite.app = tApp
|
||||||
|
suite.ctx = ctx
|
||||||
|
suite.keeper = keeper
|
||||||
|
|
||||||
|
// run the test
|
||||||
|
var err error
|
||||||
|
err = suite.keeper.Borrow(suite.ctx, tc.args.borrower, tc.args.coins)
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
if tc.errArgs.expectPass {
|
||||||
|
suite.Require().NoError(err)
|
||||||
|
acc := suite.getAccount(tc.args.borrower)
|
||||||
|
suite.Require().Equal(tc.args.expectedAccountBalance, acc.GetCoins())
|
||||||
|
mAcc := suite.getModuleAccount(types.ModuleAccountName)
|
||||||
|
suite.Require().Equal(tc.args.expectedModAccountBalance, mAcc.GetCoins())
|
||||||
|
_, 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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -253,6 +253,7 @@ func (suite *KeeperTestSuite) TestClaim() {
|
|||||||
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||||
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||||
})
|
})
|
||||||
|
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||||
harvestGS := types.NewGenesisState(types.NewParams(
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
true,
|
true,
|
||||||
types.DistributionSchedules{
|
types.DistributionSchedules{
|
||||||
@ -263,6 +264,10 @@ func (suite *KeeperTestSuite) TestClaim() {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
if tc.args.validatorVesting {
|
if tc.args.validatorVesting {
|
||||||
|
@ -116,6 +116,7 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
|||||||
tApp := app.NewTestApp()
|
tApp := app.NewTestApp()
|
||||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||||
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.depositor}, []sdk.Coins{sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000)))})
|
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.depositor}, []sdk.Coins{sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000)))})
|
||||||
|
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||||
harvestGS := types.NewGenesisState(types.NewParams(
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
true,
|
true,
|
||||||
types.DistributionSchedules{
|
types.DistributionSchedules{
|
||||||
@ -126,6 +127,10 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
keeper := tApp.GetHarvestKeeper()
|
keeper := tApp.GetHarvestKeeper()
|
||||||
@ -283,6 +288,7 @@ func (suite *KeeperTestSuite) TestWithdraw() {
|
|||||||
tApp := app.NewTestApp()
|
tApp := app.NewTestApp()
|
||||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||||
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.depositor}, []sdk.Coins{sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000)))})
|
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.depositor}, []sdk.Coins{sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000)))})
|
||||||
|
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||||
harvestGS := types.NewGenesisState(types.NewParams(
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
true,
|
true,
|
||||||
types.DistributionSchedules{
|
types.DistributionSchedules{
|
||||||
@ -293,6 +299,10 @@ func (suite *KeeperTestSuite) TestWithdraw() {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
keeper := tApp.GetHarvestKeeper()
|
keeper := tApp.GetHarvestKeeper()
|
||||||
|
@ -19,10 +19,13 @@ type Keeper struct {
|
|||||||
accountKeeper types.AccountKeeper
|
accountKeeper types.AccountKeeper
|
||||||
supplyKeeper types.SupplyKeeper
|
supplyKeeper types.SupplyKeeper
|
||||||
stakingKeeper types.StakingKeeper
|
stakingKeeper types.StakingKeeper
|
||||||
|
pricefeedKeeper types.PricefeedKeeper
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeeper creates a new keeper
|
// NewKeeper creates a new keeper
|
||||||
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, ak types.AccountKeeper, sk types.SupplyKeeper, stk types.StakingKeeper) Keeper {
|
func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace,
|
||||||
|
ak types.AccountKeeper, sk types.SupplyKeeper, stk types.StakingKeeper,
|
||||||
|
pfk types.PricefeedKeeper) Keeper {
|
||||||
if !paramstore.HasKeyTable() {
|
if !paramstore.HasKeyTable() {
|
||||||
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
||||||
}
|
}
|
||||||
@ -34,6 +37,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace,
|
|||||||
accountKeeper: ak,
|
accountKeeper: ak,
|
||||||
supplyKeeper: sk,
|
supplyKeeper: sk,
|
||||||
stakingKeeper: stk,
|
stakingKeeper: stk,
|
||||||
|
pricefeedKeeper: pfk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +182,58 @@ func (k Keeper) IterateClaimsByTypeAndDenom(ctx sdk.Context, depositType types.D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDepositsByUser gets all deposits for an individual user
|
||||||
|
func (k Keeper) GetDepositsByUser(ctx sdk.Context, user sdk.AccAddress) []types.Deposit {
|
||||||
|
var deposits []types.Deposit
|
||||||
|
k.IterateDeposits(ctx, func(deposit types.Deposit) (stop bool) {
|
||||||
|
if deposit.Depositor.Equals(user) {
|
||||||
|
deposits = append(deposits, deposit)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
return deposits
|
||||||
|
}
|
||||||
|
|
||||||
// BondDenom returns the bond denom from the staking keeper
|
// BondDenom returns the bond denom from the staking keeper
|
||||||
func (k Keeper) BondDenom(ctx sdk.Context) string {
|
func (k Keeper) BondDenom(ctx sdk.Context) string {
|
||||||
return k.stakingKeeper.BondDenom(ctx)
|
return k.stakingKeeper.BondDenom(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBorrow returns a Borrow from the store for a particular borrower address and borrow denom
|
||||||
|
func (k Keeper) GetBorrow(ctx sdk.Context, borrower sdk.AccAddress) (types.Borrow, bool) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowsKeyPrefix)
|
||||||
|
bz := store.Get(borrower)
|
||||||
|
if bz == nil {
|
||||||
|
return types.Borrow{}, false
|
||||||
|
}
|
||||||
|
var borrow types.Borrow
|
||||||
|
k.cdc.MustUnmarshalBinaryBare(bz, &borrow)
|
||||||
|
return borrow, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBorrow sets the input borrow in the store, prefixed by the borrower address and borrow denom
|
||||||
|
func (k Keeper) SetBorrow(ctx sdk.Context, borrow types.Borrow) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowsKeyPrefix)
|
||||||
|
bz := k.cdc.MustMarshalBinaryBare(borrow)
|
||||||
|
store.Set(borrow.Borrower, bz)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteBorrow deletes a borrow from the store
|
||||||
|
func (k Keeper) DeleteBorrow(ctx sdk.Context, borrow types.Borrow) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowsKeyPrefix)
|
||||||
|
store.Delete(borrow.Borrower)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IterateBorrows iterates over all borrow objects in the store and performs a callback function
|
||||||
|
func (k Keeper) IterateBorrows(ctx sdk.Context, cb func(borrow types.Borrow) (stop bool)) {
|
||||||
|
store := prefix.NewStore(ctx.KVStore(k.key), types.BorrowsKeyPrefix)
|
||||||
|
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||||
|
defer iterator.Close()
|
||||||
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
|
var borrow types.Borrow
|
||||||
|
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &borrow)
|
||||||
|
if cb(borrow) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
|||||||
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLPSchedule gets the LP's schedule
|
||||||
func (k Keeper) GetLPSchedule(ctx sdk.Context, denom string) (types.DistributionSchedule, bool) {
|
func (k Keeper) GetLPSchedule(ctx sdk.Context, denom string) (types.DistributionSchedule, bool) {
|
||||||
params := k.GetParams(ctx)
|
params := k.GetParams(ctx)
|
||||||
for _, lps := range params.LiquidityProviderSchedules {
|
for _, lps := range params.LiquidityProviderSchedules {
|
||||||
@ -28,6 +29,7 @@ func (k Keeper) GetLPSchedule(ctx sdk.Context, denom string) (types.Distribution
|
|||||||
return types.DistributionSchedule{}, false
|
return types.DistributionSchedule{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDelegatorSchedule gets the Delgator's schedule
|
||||||
func (k Keeper) GetDelegatorSchedule(ctx sdk.Context, denom string) (types.DelegatorDistributionSchedule, bool) {
|
func (k Keeper) GetDelegatorSchedule(ctx sdk.Context, denom string) (types.DelegatorDistributionSchedule, bool) {
|
||||||
params := k.GetParams(ctx)
|
params := k.GetParams(ctx)
|
||||||
for _, dds := range params.DelegatorDistributionSchedules {
|
for _, dds := range params.DelegatorDistributionSchedules {
|
||||||
@ -37,3 +39,14 @@ func (k Keeper) GetDelegatorSchedule(ctx sdk.Context, denom string) (types.Deleg
|
|||||||
}
|
}
|
||||||
return types.DelegatorDistributionSchedule{}, false
|
return types.DelegatorDistributionSchedule{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMoneyMarket returns the corresponding Money Market param for a specific denom
|
||||||
|
func (k Keeper) GetMoneyMarket(ctx sdk.Context, denom string) (types.MoneyMarket, bool) {
|
||||||
|
params := k.GetParams(ctx)
|
||||||
|
for _, mm := range params.MoneyMarkets {
|
||||||
|
if mm.Denom == denom {
|
||||||
|
return mm, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.MoneyMarket{}, false
|
||||||
|
}
|
||||||
|
@ -24,6 +24,8 @@ func NewQuerier(k Keeper) sdk.Querier {
|
|||||||
return queryGetDeposits(ctx, req, k)
|
return queryGetDeposits(ctx, req, k)
|
||||||
case types.QueryGetClaims:
|
case types.QueryGetClaims:
|
||||||
return queryGetClaims(ctx, req, k)
|
return queryGetClaims(ctx, req, k)
|
||||||
|
case types.QueryGetBorrows:
|
||||||
|
return queryGetBorrows(ctx, req, k)
|
||||||
default:
|
default:
|
||||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unknown %s query endpoint", types.ModuleName)
|
||||||
}
|
}
|
||||||
@ -260,3 +262,36 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
|
|||||||
|
|
||||||
return bz, nil
|
return bz, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryGetBorrows(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
|
||||||
|
|
||||||
|
var params types.QueryBorrowParams
|
||||||
|
err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: filter query results
|
||||||
|
// depositDenom := len(params.BorrowDenom) > 0
|
||||||
|
// owner := len(params.Owner) > 0
|
||||||
|
|
||||||
|
var borrows []types.Borrow
|
||||||
|
k.IterateBorrows(ctx, func(borrow types.Borrow) (stop bool) {
|
||||||
|
borrows = append(borrows, borrow)
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
start, end := client.Paginate(len(borrows), params.Page, params.Limit, 100)
|
||||||
|
if start < 0 || end < 0 {
|
||||||
|
borrows = []types.Borrow{}
|
||||||
|
} else {
|
||||||
|
borrows = borrows[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, borrows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return bz, nil
|
||||||
|
}
|
||||||
|
@ -63,6 +63,7 @@ func (suite *KeeperTestSuite) TestApplyDepositRewards() {
|
|||||||
// Initialize test app and set context
|
// Initialize test app and set context
|
||||||
tApp := app.NewTestApp()
|
tApp := app.NewTestApp()
|
||||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tc.args.blockTime})
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tc.args.blockTime})
|
||||||
|
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||||
harvestGS := types.NewGenesisState(types.NewParams(
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
true,
|
true,
|
||||||
types.DistributionSchedules{
|
types.DistributionSchedules{
|
||||||
@ -73,6 +74,10 @@ func (suite *KeeperTestSuite) TestApplyDepositRewards() {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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)),
|
||||||
|
},
|
||||||
), tc.args.previousBlockTime, types.DefaultDistributionTimes)
|
), tc.args.previousBlockTime, types.DefaultDistributionTimes)
|
||||||
tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
supplyKeeper := tApp.GetSupplyKeeper()
|
supplyKeeper := tApp.GetSupplyKeeper()
|
||||||
@ -400,6 +405,7 @@ func (suite *DelegatorRewardsTestSuite) kavaClaimExists(ctx sdk.Context, owner s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
||||||
|
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||||
genState := types.NewGenesisState(
|
genState := types.NewGenesisState(
|
||||||
types.NewParams(
|
types.NewParams(
|
||||||
true,
|
true,
|
||||||
@ -436,6 +442,10 @@ func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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.DefaultPreviousBlockTime,
|
types.DefaultPreviousBlockTime,
|
||||||
types.DefaultDistributionTimes,
|
types.DefaultDistributionTimes,
|
||||||
|
@ -279,6 +279,7 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
|
|||||||
tApp := app.NewTestApp()
|
tApp := app.NewTestApp()
|
||||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tc.args.blockTime})
|
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tc.args.blockTime})
|
||||||
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.accArgs.addr}, []sdk.Coins{tc.args.accArgs.coins})
|
authGS := app.NewAuthGenState([]sdk.AccAddress{tc.args.accArgs.addr}, []sdk.Coins{tc.args.accArgs.coins})
|
||||||
|
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||||
harvestGS := types.NewGenesisState(types.NewParams(
|
harvestGS := types.NewGenesisState(types.NewParams(
|
||||||
true,
|
true,
|
||||||
types.DistributionSchedules{
|
types.DistributionSchedules{
|
||||||
@ -289,6 +290,10 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
|
|||||||
time.Hour*24,
|
time.Hour*24,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
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.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||||
if tc.args.accArgs.vestingAccountBefore {
|
if tc.args.accArgs.vestingAccountBefore {
|
||||||
|
@ -79,14 +79,16 @@ type AppModule struct {
|
|||||||
|
|
||||||
keeper Keeper
|
keeper Keeper
|
||||||
supplyKeeper types.SupplyKeeper
|
supplyKeeper types.SupplyKeeper
|
||||||
|
pricefeedKeeper types.PricefeedKeeper
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAppModule creates a new AppModule object
|
// NewAppModule creates a new AppModule object
|
||||||
func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper) AppModule {
|
func NewAppModule(keeper Keeper, supplyKeeper types.SupplyKeeper, pricefeedKeeper types.PricefeedKeeper) AppModule {
|
||||||
return AppModule{
|
return AppModule{
|
||||||
AppModuleBasic: AppModuleBasic{},
|
AppModuleBasic: AppModuleBasic{},
|
||||||
keeper: keeper,
|
keeper: keeper,
|
||||||
supplyKeeper: supplyKeeper,
|
supplyKeeper: supplyKeeper,
|
||||||
|
pricefeedKeeper: pricefeedKeeper,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
19
x/harvest/types/borrow.go
Normal file
19
x/harvest/types/borrow.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Borrow defines an amount of coins borrowed from a harvest module account
|
||||||
|
type Borrow struct {
|
||||||
|
Borrower sdk.AccAddress `json:"borrower" yaml:"borrower"`
|
||||||
|
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBorrow returns a new Borrow instance
|
||||||
|
func NewBorrow(borrower sdk.AccAddress, amount sdk.Coins) Borrow {
|
||||||
|
return Borrow{
|
||||||
|
Borrower: borrower,
|
||||||
|
Amount: amount,
|
||||||
|
}
|
||||||
|
}
|
@ -17,5 +17,6 @@ func RegisterCodec(cdc *codec.Codec) {
|
|||||||
cdc.RegisterConcrete(MsgClaimReward{}, "harvest/MsgClaimReward", nil)
|
cdc.RegisterConcrete(MsgClaimReward{}, "harvest/MsgClaimReward", nil)
|
||||||
cdc.RegisterConcrete(MsgDeposit{}, "harvest/MsgDeposit", nil)
|
cdc.RegisterConcrete(MsgDeposit{}, "harvest/MsgDeposit", nil)
|
||||||
cdc.RegisterConcrete(MsgWithdraw{}, "harvest/MsgWithdraw", nil)
|
cdc.RegisterConcrete(MsgWithdraw{}, "harvest/MsgWithdraw", nil)
|
||||||
|
cdc.RegisterConcrete(MsgBorrow{}, "harvest/MsgBorrow", nil)
|
||||||
cdc.RegisterConcrete(DistributionSchedule{}, "harvest/DistributionSchedule", nil)
|
cdc.RegisterConcrete(DistributionSchedule{}, "harvest/DistributionSchedule", nil)
|
||||||
}
|
}
|
||||||
|
@ -35,4 +35,14 @@ var (
|
|||||||
ErrClaimExpired = sdkerrors.Register(ModuleName, 14, "claim period expired")
|
ErrClaimExpired = sdkerrors.Register(ModuleName, 14, "claim period expired")
|
||||||
// ErrInvalidReceiver error for when sending and receiving accounts don't match
|
// ErrInvalidReceiver error for when sending and receiving accounts don't match
|
||||||
ErrInvalidReceiver = sdkerrors.Register(ModuleName, 15, "receiver account must match sender account")
|
ErrInvalidReceiver = sdkerrors.Register(ModuleName, 15, "receiver account must match sender account")
|
||||||
|
// ErrMoneyMarketNotFound error for money market param not found
|
||||||
|
ErrMoneyMarketNotFound = sdkerrors.Register(ModuleName, 16, "no money market found")
|
||||||
|
// ErrDepositsNotFound error for no deposits found
|
||||||
|
ErrDepositsNotFound = sdkerrors.Register(ModuleName, 17, "no deposits found")
|
||||||
|
// ErrInsufficientLoanToValue error for when an attempted borrow exceeds maximum loan-to-value
|
||||||
|
ErrInsufficientLoanToValue = sdkerrors.Register(ModuleName, 18, "total deposited value is insufficient for borrow request")
|
||||||
|
// ErrMarketNotFound error for when a market for the input denom is not found
|
||||||
|
ErrMarketNotFound = sdkerrors.Register(ModuleName, 19, "no market found for denom")
|
||||||
|
// ErrPriceNotFound error for when a price for the input market is not found
|
||||||
|
ErrPriceNotFound = sdkerrors.Register(ModuleName, 20, "no price found for market")
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,7 @@ const (
|
|||||||
EventTypeDeleteHarvestDeposit = "delete_harvest_deposit"
|
EventTypeDeleteHarvestDeposit = "delete_harvest_deposit"
|
||||||
EventTypeHarvestWithdrawal = "harvest_withdrawal"
|
EventTypeHarvestWithdrawal = "harvest_withdrawal"
|
||||||
EventTypeClaimHarvestReward = "claim_harvest_reward"
|
EventTypeClaimHarvestReward = "claim_harvest_reward"
|
||||||
|
EventTypeHarvestBorrow = "harvest_borrow"
|
||||||
AttributeValueCategory = ModuleName
|
AttributeValueCategory = ModuleName
|
||||||
AttributeKeyBlockHeight = "block_height"
|
AttributeKeyBlockHeight = "block_height"
|
||||||
AttributeKeyRewardsDistribution = "rewards_distributed"
|
AttributeKeyRewardsDistribution = "rewards_distributed"
|
||||||
@ -18,4 +19,7 @@ const (
|
|||||||
AttributeKeyClaimHolder = "claim_holder"
|
AttributeKeyClaimHolder = "claim_holder"
|
||||||
AttributeKeyClaimAmount = "claim_amount"
|
AttributeKeyClaimAmount = "claim_amount"
|
||||||
AttributeKeyClaimMultiplier = "claim_multiplier"
|
AttributeKeyClaimMultiplier = "claim_multiplier"
|
||||||
|
AttributeKeyBorrow = "borrow"
|
||||||
|
AttributeKeyBorrower = "borrower"
|
||||||
|
AttributeKeyBorrowCoins = "borrow_coins"
|
||||||
)
|
)
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||||
|
|
||||||
|
pftypes "github.com/kava-labs/kava/x/pricefeed/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SupplyKeeper defines the expected supply keeper
|
// SupplyKeeper defines the expected supply keeper
|
||||||
@ -33,3 +35,8 @@ type StakingKeeper interface {
|
|||||||
GetBondedPool(ctx sdk.Context) (bondedPool exported.ModuleAccountI)
|
GetBondedPool(ctx sdk.Context) (bondedPool exported.ModuleAccountI)
|
||||||
BondDenom(ctx sdk.Context) (res string)
|
BondDenom(ctx sdk.Context) (res string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PricefeedKeeper defines the expected interface for the pricefeed
|
||||||
|
type PricefeedKeeper interface {
|
||||||
|
GetCurrentPrice(sdk.Context, string) (pftypes.CurrentPrice, error)
|
||||||
|
}
|
||||||
|
@ -35,6 +35,7 @@ var (
|
|||||||
PreviousDelegationDistributionKey = []byte{0x02}
|
PreviousDelegationDistributionKey = []byte{0x02}
|
||||||
DepositsKeyPrefix = []byte{0x03}
|
DepositsKeyPrefix = []byte{0x03}
|
||||||
ClaimsKeyPrefix = []byte{0x04}
|
ClaimsKeyPrefix = []byte{0x04}
|
||||||
|
BorrowsKeyPrefix = []byte{0x05}
|
||||||
sep = []byte(":")
|
sep = []byte(":")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ var (
|
|||||||
_ sdk.Msg = &MsgClaimReward{}
|
_ sdk.Msg = &MsgClaimReward{}
|
||||||
_ sdk.Msg = &MsgDeposit{}
|
_ sdk.Msg = &MsgDeposit{}
|
||||||
_ sdk.Msg = &MsgWithdraw{}
|
_ sdk.Msg = &MsgWithdraw{}
|
||||||
|
_ sdk.Msg = &MsgBorrow{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgDeposit deposit collateral to the harvest module.
|
// MsgDeposit deposit collateral to the harvest module.
|
||||||
@ -214,3 +215,55 @@ func (msg MsgClaimReward) GetSignBytes() []byte {
|
|||||||
func (msg MsgClaimReward) GetSigners() []sdk.AccAddress {
|
func (msg MsgClaimReward) GetSigners() []sdk.AccAddress {
|
||||||
return []sdk.AccAddress{msg.Sender}
|
return []sdk.AccAddress{msg.Sender}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------
|
||||||
|
|
||||||
|
// MsgBorrow borrows funds from the harvest module.
|
||||||
|
type MsgBorrow struct {
|
||||||
|
Borrower sdk.AccAddress `json:"borrower" yaml:"borrower"`
|
||||||
|
Amount sdk.Coins `json:"amount" yaml:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgBorrow returns a new MsgBorrow
|
||||||
|
func NewMsgBorrow(borrower sdk.AccAddress, amount sdk.Coins) MsgBorrow {
|
||||||
|
return MsgBorrow{
|
||||||
|
Borrower: borrower,
|
||||||
|
Amount: amount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Route return the message type used for routing the message.
|
||||||
|
func (msg MsgBorrow) Route() string { return RouterKey }
|
||||||
|
|
||||||
|
// Type returns a human-readable string for the message, intended for utilization within tags.
|
||||||
|
func (msg MsgBorrow) Type() string { return "harvest_borrow" } // TODO: or just 'borrow'
|
||||||
|
|
||||||
|
// ValidateBasic does a simple validation check that doesn't require access to any other information.
|
||||||
|
func (msg MsgBorrow) ValidateBasic() error {
|
||||||
|
if msg.Borrower.Empty() {
|
||||||
|
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address cannot be empty")
|
||||||
|
}
|
||||||
|
if !msg.Amount.IsValid() || msg.Amount.IsZero() {
|
||||||
|
return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "borrow amount %s", msg.Amount)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSignBytes gets the canonical byte representation of the Msg.
|
||||||
|
func (msg MsgBorrow) GetSignBytes() []byte {
|
||||||
|
bz := ModuleCdc.MustMarshalJSON(msg)
|
||||||
|
return sdk.MustSortJSON(bz)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSigners returns the addresses of signers that must sign.
|
||||||
|
func (msg MsgBorrow) GetSigners() []sdk.AccAddress {
|
||||||
|
return []sdk.AccAddress{msg.Borrower}
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements the Stringer interface
|
||||||
|
func (msg MsgBorrow) String() string {
|
||||||
|
return fmt.Sprintf(`Borrow Message:
|
||||||
|
Borrower: %s
|
||||||
|
Amount: %s
|
||||||
|
`, msg.Borrower, msg.Amount)
|
||||||
|
}
|
||||||
|
@ -16,10 +16,12 @@ var (
|
|||||||
KeyActive = []byte("Active")
|
KeyActive = []byte("Active")
|
||||||
KeyLPSchedules = []byte("LPSchedules")
|
KeyLPSchedules = []byte("LPSchedules")
|
||||||
KeyDelegatorSchedule = []byte("DelegatorSchedule")
|
KeyDelegatorSchedule = []byte("DelegatorSchedule")
|
||||||
|
KeyMoneyMarkets = []byte("MoneyMarkets")
|
||||||
DefaultActive = true
|
DefaultActive = true
|
||||||
DefaultGovSchedules = DistributionSchedules{}
|
DefaultGovSchedules = DistributionSchedules{}
|
||||||
DefaultLPSchedules = DistributionSchedules{}
|
DefaultLPSchedules = DistributionSchedules{}
|
||||||
DefaultDelegatorSchedules = DelegatorDistributionSchedules{}
|
DefaultDelegatorSchedules = DelegatorDistributionSchedules{}
|
||||||
|
DefaultMoneyMarkets = MoneyMarkets{}
|
||||||
GovDenom = cdptypes.DefaultGovDenom
|
GovDenom = cdptypes.DefaultGovDenom
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,6 +30,7 @@ type Params struct {
|
|||||||
Active bool `json:"active" yaml:"active"`
|
Active bool `json:"active" yaml:"active"`
|
||||||
LiquidityProviderSchedules DistributionSchedules `json:"liquidity_provider_schedules" yaml:"liquidity_provider_schedules"`
|
LiquidityProviderSchedules DistributionSchedules `json:"liquidity_provider_schedules" yaml:"liquidity_provider_schedules"`
|
||||||
DelegatorDistributionSchedules DelegatorDistributionSchedules `json:"delegator_distribution_schedules" yaml:"delegator_distribution_schedules"`
|
DelegatorDistributionSchedules DelegatorDistributionSchedules `json:"delegator_distribution_schedules" yaml:"delegator_distribution_schedules"`
|
||||||
|
MoneyMarkets MoneyMarkets `json:"money_markets" yaml:"money_markets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DistributionSchedule distribution schedule for liquidity providers
|
// DistributionSchedule distribution schedule for liquidity providers
|
||||||
@ -218,18 +221,91 @@ func (ds DistributionSchedule) GetMultiplier(name MultiplierName) (Multiplier, b
|
|||||||
// Multipliers slice of Multiplier
|
// Multipliers slice of Multiplier
|
||||||
type Multipliers []Multiplier
|
type Multipliers []Multiplier
|
||||||
|
|
||||||
|
// BorrowLimit enforces restrictions on a money market
|
||||||
|
type BorrowLimit struct {
|
||||||
|
MaximumLimit sdk.Int `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 {
|
||||||
|
return BorrowLimit{
|
||||||
|
MaximumLimit: maximumLimit,
|
||||||
|
LoanToValue: loanToValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate BorrowLimit
|
||||||
|
func (bl BorrowLimit) Validate() error {
|
||||||
|
if bl.MaximumLimit.IsNegative() {
|
||||||
|
return fmt.Errorf("maximum limit cannot be negative: %s", bl.MaximumLimit)
|
||||||
|
}
|
||||||
|
if !bl.LoanToValue.IsPositive() {
|
||||||
|
return fmt.Errorf("loan-to-value must be a positive integer: %s", bl.LoanToValue)
|
||||||
|
}
|
||||||
|
if bl.LoanToValue.GT(sdk.OneDec()) {
|
||||||
|
return fmt.Errorf("loan-to-value cannot be greater than 1.0: %s", bl.LoanToValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoneyMarket is a money market for an individual asset
|
||||||
|
type MoneyMarket struct {
|
||||||
|
Denom string `json:"denom" yaml:"denom"`
|
||||||
|
BorrowLimit BorrowLimit `json:"borrow_limit" yaml:"borrow_limit"`
|
||||||
|
SpotMarketID string `json:"spot_market_id" yaml:"spot_market_id"`
|
||||||
|
ConversionFactor sdk.Int `json:"conversion_factor" yaml:"conversion_factor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMoneyMarket returns a new MoneyMarket
|
||||||
|
func NewMoneyMarket(denom string, maximumLimit sdk.Int, loanToValue sdk.Dec,
|
||||||
|
spotMarketID string, conversionFactor sdk.Int) MoneyMarket {
|
||||||
|
return MoneyMarket{
|
||||||
|
Denom: denom,
|
||||||
|
BorrowLimit: NewBorrowLimit(maximumLimit, loanToValue),
|
||||||
|
SpotMarketID: spotMarketID,
|
||||||
|
ConversionFactor: conversionFactor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate MoneyMarket param
|
||||||
|
func (mm MoneyMarket) Validate() error {
|
||||||
|
if err := sdk.ValidateDenom(mm.Denom); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mm.BorrowLimit.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MoneyMarkets slice of MoneyMarket
|
||||||
|
type MoneyMarkets []MoneyMarket
|
||||||
|
|
||||||
|
// Validate borrow limits
|
||||||
|
func (mms MoneyMarkets) Validate() error {
|
||||||
|
for _, moneyMarket := range mms {
|
||||||
|
if err := moneyMarket.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// NewParams returns a new params object
|
// NewParams returns a new params object
|
||||||
func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules) Params {
|
func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules, moneyMarkets MoneyMarkets) Params {
|
||||||
return Params{
|
return Params{
|
||||||
Active: active,
|
Active: active,
|
||||||
LiquidityProviderSchedules: lps,
|
LiquidityProviderSchedules: lps,
|
||||||
DelegatorDistributionSchedules: dds,
|
DelegatorDistributionSchedules: dds,
|
||||||
|
MoneyMarkets: moneyMarkets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultParams returns default params for harvest module
|
// DefaultParams returns default params for harvest module
|
||||||
func DefaultParams() Params {
|
func DefaultParams() Params {
|
||||||
return NewParams(DefaultActive, DefaultLPSchedules, DefaultDelegatorSchedules)
|
return NewParams(DefaultActive, DefaultLPSchedules, DefaultDelegatorSchedules, DefaultMoneyMarkets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements fmt.Stringer
|
// String implements fmt.Stringer
|
||||||
@ -237,7 +313,8 @@ func (p Params) String() string {
|
|||||||
return fmt.Sprintf(`Params:
|
return fmt.Sprintf(`Params:
|
||||||
Active: %t
|
Active: %t
|
||||||
Liquidity Provider Distribution Schedules %s
|
Liquidity Provider Distribution Schedules %s
|
||||||
Delegator Distribution Schedule %s`, p.Active, p.LiquidityProviderSchedules, p.DelegatorDistributionSchedules)
|
Delegator Distribution Schedule %s
|
||||||
|
Money Markets %s`, p.Active, p.LiquidityProviderSchedules, p.DelegatorDistributionSchedules, p.MoneyMarkets)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParamKeyTable Key declaration for parameters
|
// ParamKeyTable Key declaration for parameters
|
||||||
@ -251,6 +328,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
|||||||
params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam),
|
params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam),
|
||||||
params.NewParamSetPair(KeyLPSchedules, &p.LiquidityProviderSchedules, validateLPParams),
|
params.NewParamSetPair(KeyLPSchedules, &p.LiquidityProviderSchedules, validateLPParams),
|
||||||
params.NewParamSetPair(KeyDelegatorSchedule, &p.DelegatorDistributionSchedules, validateDelegatorParams),
|
params.NewParamSetPair(KeyDelegatorSchedule, &p.DelegatorDistributionSchedules, validateDelegatorParams),
|
||||||
|
params.NewParamSetPair(KeyMoneyMarkets, &p.MoneyMarkets, validateMoneyMarketParams),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,3 +378,12 @@ func validateDelegatorParams(i interface{}) error {
|
|||||||
|
|
||||||
return dds.Validate()
|
return dds.Validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateMoneyMarketParams(i interface{}) error {
|
||||||
|
mm, ok := i.(MoneyMarkets)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid parameter type: %T", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mm.Validate()
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ const (
|
|||||||
QueryGetModuleAccounts = "accounts"
|
QueryGetModuleAccounts = "accounts"
|
||||||
QueryGetDeposits = "deposits"
|
QueryGetDeposits = "deposits"
|
||||||
QueryGetClaims = "claims"
|
QueryGetClaims = "claims"
|
||||||
|
QueryGetBorrows = "borrows"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryDepositParams is the params for a filtered deposit query
|
// QueryDepositParams is the params for a filtered deposit query
|
||||||
@ -67,3 +68,21 @@ func NewQueryAccountParams(page, limit int, name string) QueryAccountParams {
|
|||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryBorrowParams is the params for a filtered borrow query
|
||||||
|
type QueryBorrowParams struct {
|
||||||
|
Page int `json:"page" yaml:"page"`
|
||||||
|
Limit int `json:"limit" yaml:"limit"`
|
||||||
|
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
|
||||||
|
BorrowDenom string `json:"borrow_denom" yaml:"borrow_denom"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQueryBorrowParams creates a new QueryBorrowParams
|
||||||
|
func NewQueryBorrowParams(page, limit int, owner sdk.AccAddress, depositDenom string) QueryBorrowParams {
|
||||||
|
return QueryBorrowParams{
|
||||||
|
Page: page,
|
||||||
|
Limit: limit,
|
||||||
|
Owner: owner,
|
||||||
|
BorrowDenom: depositDenom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -58,5 +58,4 @@ func (k Keeper) GetMarket(ctx sdk.Context, marketID string) (types.Market, bool)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.Market{}, false
|
return types.Market{}, false
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
|
|
||||||
// Market an asset in the pricefeed
|
// Market an asset in the pricefeed
|
||||||
type Market struct {
|
type Market struct {
|
||||||
// TODO: rename to ID
|
|
||||||
MarketID string `json:"market_id" yaml:"market_id"`
|
MarketID string `json:"market_id" yaml:"market_id"`
|
||||||
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
||||||
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
||||||
|
Loading…
Reference in New Issue
Block a user