mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-23 23: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
13
app/app.go
13
app/app.go
@ -102,9 +102,9 @@ var (
|
||||
bep3.ModuleName: {supply.Minter, supply.Burner},
|
||||
kavadist.ModuleName: {supply.Minter},
|
||||
issuance.ModuleAccountName: {supply.Minter, supply.Burner},
|
||||
harvest.LPAccount: {supply.Minter, supply.Burner},
|
||||
harvest.DelegatorAccount: {supply.Minter, supply.Burner},
|
||||
harvest.ModuleAccountName: {supply.Minter, supply.Burner},
|
||||
harvest.LPAccount: {supply.Minter, supply.Burner},
|
||||
harvest.DelegatorAccount: {supply.Minter, supply.Burner},
|
||||
harvest.ModuleAccountName: {supply.Minter, supply.Burner},
|
||||
}
|
||||
|
||||
// module accounts that are allowed to receive tokens
|
||||
@ -376,7 +376,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool,
|
||||
harvestSubspace,
|
||||
app.accountKeeper,
|
||||
app.supplyKeeper,
|
||||
&stakingKeeper)
|
||||
&stakingKeeper,
|
||||
app.pricefeedKeeper)
|
||||
|
||||
// register the staking 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),
|
||||
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
||||
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
|
||||
@ -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),
|
||||
committee.NewAppModule(app.committeeKeeper, app.accountKeeper),
|
||||
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()
|
||||
|
@ -129,7 +129,7 @@ func MigrateAppState(v0_9AppState v39_genutil.AppMap) v39_genutil.AppMap {
|
||||
delete(v0_9AppState, v0_9pricefeed.ModuleName)
|
||||
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())
|
||||
return v0_11AppState
|
||||
}
|
||||
@ -633,29 +633,34 @@ func MigrateGov(oldGenState v39_1gov.GenesisState) v39_1gov.GenesisState {
|
||||
return oldGenState
|
||||
}
|
||||
|
||||
// MigrateHarvest initializes the harvest genesis state for kava-4
|
||||
func MigrateHarvest() v0_11harvest.GenesisState {
|
||||
// total HARD per second for lps (week one): 633761
|
||||
// HARD per second for delegators (week one): 1267522
|
||||
incentiveGoLiveDate := time.Date(2020, 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)
|
||||
harvestGS := v0_11harvest.NewGenesisState(v0_11harvest.NewParams(
|
||||
true,
|
||||
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, "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, "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.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,
|
||||
),
|
||||
},
|
||||
), v0_11harvest.DefaultPreviousBlockTime, v0_11harvest.DefaultDistributionTimes)
|
||||
return harvestGS
|
||||
}
|
||||
// // MigrateHarvest initializes the harvest genesis state for kava-4
|
||||
// func MigrateHarvest() v0_11harvest.GenesisState {
|
||||
// // total HARD per second for lps (week one): 633761
|
||||
// // HARD per second for delegators (week one): 1267522
|
||||
// incentiveGoLiveDate := time.Date(2020, 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)
|
||||
// harvestGS := v0_11harvest.NewGenesisState(v0_11harvest.NewParams(
|
||||
// true,
|
||||
// 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, "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, "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.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,
|
||||
// ),
|
||||
// },
|
||||
// v0_11harvest.BlockLimits{
|
||||
// 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
|
||||
func MigrateCDP(oldGenState v0_9cdp.GenesisState) v0_11cdp.GenesisState {
|
||||
|
@ -74,6 +74,7 @@ var (
|
||||
RegisterCodec = types.RegisterCodec
|
||||
|
||||
// variable aliases
|
||||
BorrowsKeyPrefix = types.BorrowsKeyPrefix
|
||||
ClaimsKeyPrefix = types.ClaimsKeyPrefix
|
||||
DefaultActive = types.DefaultActive
|
||||
DefaultDelegatorSchedules = types.DefaultDelegatorSchedules
|
||||
@ -109,7 +110,9 @@ var (
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
AccountKeeper = types.AccountKeeper
|
||||
Claim = types.Claim
|
||||
Borrow = types.Borrow
|
||||
MoneyMarket = types.MoneyMarket
|
||||
MoneyMarkets = types.MoneyMarkets
|
||||
DelegatorDistributionSchedule = types.DelegatorDistributionSchedule
|
||||
DelegatorDistributionSchedules = types.DelegatorDistributionSchedules
|
||||
Deposit = types.Deposit
|
||||
|
@ -40,6 +40,7 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
queryModAccountsCmd(queryRoute, cdc),
|
||||
queryDepositsCmd(queryRoute, cdc),
|
||||
queryClaimsCmd(queryRoute, cdc),
|
||||
queryBorrowsCmd(queryRoute, cdc),
|
||||
)...)
|
||||
|
||||
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)")
|
||||
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),
|
||||
getCmdWithdraw(cdc),
|
||||
getCmdClaimReward(cdc),
|
||||
getCmdBorrow(cdc),
|
||||
)...)
|
||||
|
||||
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)
|
||||
case types.MsgWithdraw:
|
||||
return handleMsgWithdraw(ctx, k, msg)
|
||||
case types.MsgBorrow:
|
||||
return handleMsgBorrow(ctx, k, msg)
|
||||
default:
|
||||
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(),
|
||||
}, 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))),
|
||||
})
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
@ -263,6 +264,10 @@ func (suite *KeeperTestSuite) TestClaim() {
|
||||
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)})
|
||||
if tc.args.validatorVesting {
|
||||
|
@ -116,6 +116,7 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
||||
tApp := app.NewTestApp()
|
||||
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)))})
|
||||
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
@ -126,6 +127,10 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
||||
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()
|
||||
@ -283,6 +288,7 @@ func (suite *KeeperTestSuite) TestWithdraw() {
|
||||
tApp := app.NewTestApp()
|
||||
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)))})
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
@ -293,6 +299,10 @@ func (suite *KeeperTestSuite) TestWithdraw() {
|
||||
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()
|
||||
|
@ -13,27 +13,31 @@ import (
|
||||
|
||||
// Keeper keeper for the harvest module
|
||||
type Keeper struct {
|
||||
key sdk.StoreKey
|
||||
cdc *codec.Codec
|
||||
paramSubspace subspace.Subspace
|
||||
accountKeeper types.AccountKeeper
|
||||
supplyKeeper types.SupplyKeeper
|
||||
stakingKeeper types.StakingKeeper
|
||||
key sdk.StoreKey
|
||||
cdc *codec.Codec
|
||||
paramSubspace subspace.Subspace
|
||||
accountKeeper types.AccountKeeper
|
||||
supplyKeeper types.SupplyKeeper
|
||||
stakingKeeper types.StakingKeeper
|
||||
pricefeedKeeper types.PricefeedKeeper
|
||||
}
|
||||
|
||||
// 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() {
|
||||
paramstore = paramstore.WithKeyTable(types.ParamKeyTable())
|
||||
}
|
||||
|
||||
return Keeper{
|
||||
key: key,
|
||||
cdc: cdc,
|
||||
paramSubspace: paramstore,
|
||||
accountKeeper: ak,
|
||||
supplyKeeper: sk,
|
||||
stakingKeeper: stk,
|
||||
key: key,
|
||||
cdc: cdc,
|
||||
paramSubspace: paramstore,
|
||||
accountKeeper: ak,
|
||||
supplyKeeper: sk,
|
||||
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
|
||||
func (k Keeper) BondDenom(ctx sdk.Context) string {
|
||||
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)
|
||||
}
|
||||
|
||||
// GetLPSchedule gets the LP's schedule
|
||||
func (k Keeper) GetLPSchedule(ctx sdk.Context, denom string) (types.DistributionSchedule, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, lps := range params.LiquidityProviderSchedules {
|
||||
@ -28,6 +29,7 @@ func (k Keeper) GetLPSchedule(ctx sdk.Context, denom string) (types.Distribution
|
||||
return types.DistributionSchedule{}, false
|
||||
}
|
||||
|
||||
// GetDelegatorSchedule gets the Delgator's schedule
|
||||
func (k Keeper) GetDelegatorSchedule(ctx sdk.Context, denom string) (types.DelegatorDistributionSchedule, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, dds := range params.DelegatorDistributionSchedules {
|
||||
@ -37,3 +39,14 @@ func (k Keeper) GetDelegatorSchedule(ctx sdk.Context, denom string) (types.Deleg
|
||||
}
|
||||
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)
|
||||
case types.QueryGetClaims:
|
||||
return queryGetClaims(ctx, req, k)
|
||||
case types.QueryGetBorrows:
|
||||
return queryGetBorrows(ctx, req, k)
|
||||
default:
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tc.args.blockTime})
|
||||
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
@ -73,6 +74,10 @@ func (suite *KeeperTestSuite) TestApplyDepositRewards() {
|
||||
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)
|
||||
tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(harvestGS)})
|
||||
supplyKeeper := tApp.GetSupplyKeeper()
|
||||
@ -400,6 +405,7 @@ func (suite *DelegatorRewardsTestSuite) kavaClaimExists(ctx sdk.Context, owner s
|
||||
}
|
||||
|
||||
func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
genState := types.NewGenesisState(
|
||||
types.NewParams(
|
||||
true,
|
||||
@ -436,6 +442,10 @@ func harvestGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
||||
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,
|
||||
|
@ -279,6 +279,7 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
|
||||
tApp := app.NewTestApp()
|
||||
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})
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
@ -289,6 +290,10 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
|
||||
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)})
|
||||
if tc.args.accArgs.vestingAccountBefore {
|
||||
|
@ -77,16 +77,18 @@ func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
|
||||
type AppModule struct {
|
||||
AppModuleBasic
|
||||
|
||||
keeper Keeper
|
||||
supplyKeeper types.SupplyKeeper
|
||||
keeper Keeper
|
||||
supplyKeeper types.SupplyKeeper
|
||||
pricefeedKeeper types.PricefeedKeeper
|
||||
}
|
||||
|
||||
// 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{
|
||||
AppModuleBasic: AppModuleBasic{},
|
||||
keeper: keeper,
|
||||
supplyKeeper: supplyKeeper,
|
||||
AppModuleBasic: AppModuleBasic{},
|
||||
keeper: keeper,
|
||||
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(MsgDeposit{}, "harvest/MsgDeposit", nil)
|
||||
cdc.RegisterConcrete(MsgWithdraw{}, "harvest/MsgWithdraw", nil)
|
||||
cdc.RegisterConcrete(MsgBorrow{}, "harvest/MsgBorrow", nil)
|
||||
cdc.RegisterConcrete(DistributionSchedule{}, "harvest/DistributionSchedule", nil)
|
||||
}
|
||||
|
@ -35,4 +35,14 @@ var (
|
||||
ErrClaimExpired = sdkerrors.Register(ModuleName, 14, "claim period expired")
|
||||
// ErrInvalidReceiver error for when sending and receiving accounts don't match
|
||||
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"
|
||||
EventTypeHarvestWithdrawal = "harvest_withdrawal"
|
||||
EventTypeClaimHarvestReward = "claim_harvest_reward"
|
||||
EventTypeHarvestBorrow = "harvest_borrow"
|
||||
AttributeValueCategory = ModuleName
|
||||
AttributeKeyBlockHeight = "block_height"
|
||||
AttributeKeyRewardsDistribution = "rewards_distributed"
|
||||
@ -18,4 +19,7 @@ const (
|
||||
AttributeKeyClaimHolder = "claim_holder"
|
||||
AttributeKeyClaimAmount = "claim_amount"
|
||||
AttributeKeyClaimMultiplier = "claim_multiplier"
|
||||
AttributeKeyBorrow = "borrow"
|
||||
AttributeKeyBorrower = "borrower"
|
||||
AttributeKeyBorrowCoins = "borrow_coins"
|
||||
)
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||
|
||||
pftypes "github.com/kava-labs/kava/x/pricefeed/types"
|
||||
)
|
||||
|
||||
// SupplyKeeper defines the expected supply keeper
|
||||
@ -33,3 +35,8 @@ type StakingKeeper interface {
|
||||
GetBondedPool(ctx sdk.Context) (bondedPool exported.ModuleAccountI)
|
||||
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}
|
||||
DepositsKeyPrefix = []byte{0x03}
|
||||
ClaimsKeyPrefix = []byte{0x04}
|
||||
BorrowsKeyPrefix = []byte{0x05}
|
||||
sep = []byte(":")
|
||||
)
|
||||
|
||||
|
@ -53,6 +53,7 @@ var (
|
||||
_ sdk.Msg = &MsgClaimReward{}
|
||||
_ sdk.Msg = &MsgDeposit{}
|
||||
_ sdk.Msg = &MsgWithdraw{}
|
||||
_ sdk.Msg = &MsgBorrow{}
|
||||
)
|
||||
|
||||
// MsgDeposit deposit collateral to the harvest module.
|
||||
@ -214,3 +215,55 @@ func (msg MsgClaimReward) GetSignBytes() []byte {
|
||||
func (msg MsgClaimReward) GetSigners() []sdk.AccAddress {
|
||||
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")
|
||||
KeyLPSchedules = []byte("LPSchedules")
|
||||
KeyDelegatorSchedule = []byte("DelegatorSchedule")
|
||||
KeyMoneyMarkets = []byte("MoneyMarkets")
|
||||
DefaultActive = true
|
||||
DefaultGovSchedules = DistributionSchedules{}
|
||||
DefaultLPSchedules = DistributionSchedules{}
|
||||
DefaultDelegatorSchedules = DelegatorDistributionSchedules{}
|
||||
DefaultMoneyMarkets = MoneyMarkets{}
|
||||
GovDenom = cdptypes.DefaultGovDenom
|
||||
)
|
||||
|
||||
@ -28,6 +30,7 @@ type Params struct {
|
||||
Active bool `json:"active" yaml:"active"`
|
||||
LiquidityProviderSchedules DistributionSchedules `json:"liquidity_provider_schedules" yaml:"liquidity_provider_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
|
||||
@ -218,18 +221,91 @@ func (ds DistributionSchedule) GetMultiplier(name MultiplierName) (Multiplier, b
|
||||
// Multipliers slice of 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
|
||||
func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules) Params {
|
||||
func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules, moneyMarkets MoneyMarkets) Params {
|
||||
return Params{
|
||||
Active: active,
|
||||
LiquidityProviderSchedules: lps,
|
||||
DelegatorDistributionSchedules: dds,
|
||||
MoneyMarkets: moneyMarkets,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultParams returns default params for harvest module
|
||||
func DefaultParams() Params {
|
||||
return NewParams(DefaultActive, DefaultLPSchedules, DefaultDelegatorSchedules)
|
||||
return NewParams(DefaultActive, DefaultLPSchedules, DefaultDelegatorSchedules, DefaultMoneyMarkets)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
@ -237,7 +313,8 @@ func (p Params) String() string {
|
||||
return fmt.Sprintf(`Params:
|
||||
Active: %t
|
||||
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
|
||||
@ -251,6 +328,7 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||
params.NewParamSetPair(KeyActive, &p.Active, validateActiveParam),
|
||||
params.NewParamSetPair(KeyLPSchedules, &p.LiquidityProviderSchedules, validateLPParams),
|
||||
params.NewParamSetPair(KeyDelegatorSchedule, &p.DelegatorDistributionSchedules, validateDelegatorParams),
|
||||
params.NewParamSetPair(KeyMoneyMarkets, &p.MoneyMarkets, validateMoneyMarketParams),
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,3 +378,12 @@ func validateDelegatorParams(i interface{}) error {
|
||||
|
||||
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"
|
||||
QueryGetDeposits = "deposits"
|
||||
QueryGetClaims = "claims"
|
||||
QueryGetBorrows = "borrows"
|
||||
)
|
||||
|
||||
// QueryDepositParams is the params for a filtered deposit query
|
||||
@ -67,3 +68,21 @@ func NewQueryAccountParams(page, limit int, name string) QueryAccountParams {
|
||||
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
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
|
||||
// Market an asset in the pricefeed
|
||||
type Market struct {
|
||||
// TODO: rename to ID
|
||||
MarketID string `json:"market_id" yaml:"market_id"`
|
||||
BaseAsset string `json:"base_asset" yaml:"base_asset"`
|
||||
QuoteAsset string `json:"quote_asset" yaml:"quote_asset"`
|
||||
|
Loading…
Reference in New Issue
Block a user