mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-23 23:05:18 +00:00
Incentive/Hard rebase to master (#773)
* spike: incentive/types * spike: incentive/types tests * spike: incentive/types/expected_keepers.go * spike: incentive/keeper * spike: incentive/keeper tests * spike: incentive/sims and incentive/sims tests * spike: incentive/module * spike: incentive/module tests * spike: hard/types * spike: hard/types hooks * spike: hard/types * spike: hard/keeper basics * spike: hard/keeper hooks * integrate hard/keeper/borrow.go * integrate hard/keeper/deposit.go * integrate hard/keeper/liquidation.go * integrate hard/keeper/withdraw.go * integrate hard/keeper/repay.go * spike: hard/sims * spike: hard/sims tests * spike: hard/client * spike: hard/module * integrate app.go * spike: x/hard/keeper compile tests * incentive/keeper test clean up * validate usdx incentive types in genesis * refactoring & fix deposit test * fix liquidaton tests * fix incentive tests for hard supply rewards * fix hard genesis tests * update incentive genesis state and params * update cdp rewards accumulation * update app init order and begin blocker order Co-authored-by: karzak <kjydavis3@gmail.com>
This commit is contained in:
parent
67b58f555b
commit
4e6f6d1e9c
29
app/app.go
29
app/app.go
@ -358,6 +358,16 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
bep3Subspace,
|
||||
app.ModuleAccountAddrs(),
|
||||
)
|
||||
hardKeeper := hard.NewKeeper(
|
||||
app.cdc,
|
||||
keys[hard.StoreKey],
|
||||
hardSubspace,
|
||||
app.accountKeeper,
|
||||
app.supplyKeeper,
|
||||
&stakingKeeper,
|
||||
app.pricefeedKeeper,
|
||||
app.auctionKeeper,
|
||||
)
|
||||
app.kavadistKeeper = kavadist.NewKeeper(
|
||||
app.cdc,
|
||||
keys[kavadist.StoreKey],
|
||||
@ -370,6 +380,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
incentiveSubspace,
|
||||
app.supplyKeeper,
|
||||
&cdpKeeper,
|
||||
&hardKeeper,
|
||||
app.accountKeeper,
|
||||
)
|
||||
app.issuanceKeeper = issuance.NewKeeper(
|
||||
@ -379,16 +390,6 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
app.accountKeeper,
|
||||
app.supplyKeeper,
|
||||
)
|
||||
app.hardKeeper = hard.NewKeeper(
|
||||
app.cdc,
|
||||
keys[hard.StoreKey],
|
||||
hardSubspace,
|
||||
app.accountKeeper,
|
||||
app.supplyKeeper,
|
||||
&stakingKeeper,
|
||||
app.pricefeedKeeper,
|
||||
app.auctionKeeper,
|
||||
)
|
||||
|
||||
// register the staking hooks
|
||||
// NOTE: stakingKeeper above is passed by reference, so that it will contain these hooks
|
||||
@ -397,6 +398,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
|
||||
app.cdpKeeper = *cdpKeeper.SetHooks(cdp.NewMultiCDPHooks(app.incentiveKeeper.Hooks()))
|
||||
|
||||
app.hardKeeper = *hardKeeper.SetHooks(hard.NewMultiHARDHooks(app.incentiveKeeper.Hooks()))
|
||||
|
||||
// create the module manager (Note: Any module instantiated in the module manager that is later modified
|
||||
// must be passed by reference here.)
|
||||
app.mm = module.NewManager(
|
||||
@ -432,7 +435,7 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
app.mm.SetOrderBeginBlockers(
|
||||
upgrade.ModuleName, mint.ModuleName, distr.ModuleName, slashing.ModuleName,
|
||||
validatorvesting.ModuleName, kavadist.ModuleName, auction.ModuleName, cdp.ModuleName,
|
||||
bep3.ModuleName, incentive.ModuleName, committee.ModuleName, issuance.ModuleName, hard.ModuleName,
|
||||
bep3.ModuleName, hard.ModuleName, committee.ModuleName, issuance.ModuleName, incentive.ModuleName,
|
||||
)
|
||||
|
||||
app.mm.SetOrderEndBlockers(crisis.ModuleName, gov.ModuleName, staking.ModuleName, pricefeed.ModuleName)
|
||||
@ -442,8 +445,8 @@ func NewApp(logger log.Logger, db dbm.DB, traceStore io.Writer, appOpts AppOptio
|
||||
validatorvesting.ModuleName, distr.ModuleName,
|
||||
staking.ModuleName, bank.ModuleName, slashing.ModuleName,
|
||||
gov.ModuleName, mint.ModuleName, evidence.ModuleName,
|
||||
pricefeed.ModuleName, cdp.ModuleName, auction.ModuleName,
|
||||
bep3.ModuleName, kavadist.ModuleName, incentive.ModuleName, committee.ModuleName, issuance.ModuleName, hard.ModuleName,
|
||||
pricefeed.ModuleName, cdp.ModuleName, hard.ModuleName, auction.ModuleName,
|
||||
bep3.ModuleName, kavadist.ModuleName, incentive.ModuleName, committee.ModuleName, issuance.ModuleName,
|
||||
supply.ModuleName, // calculates the total supply from account - should run after modules that modify accounts in genesis
|
||||
crisis.ModuleName, // runs the invariants at genesis - should run after other modules
|
||||
genutil.ModuleName, // genutils must occur after staking so that pools are properly initialized with tokens from genesis accounts.
|
||||
|
@ -4,13 +4,8 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// BeginBlocker applies rewards to liquidity providers and delegators according to params
|
||||
// BeginBlocker updates interest rates and attempts liquidations
|
||||
func BeginBlocker(ctx sdk.Context, k Keeper) {
|
||||
k.ApplyDepositRewards(ctx)
|
||||
if k.ShouldDistributeValidatorRewards(ctx, k.BondDenom(ctx)) {
|
||||
k.ApplyDelegationRewards(ctx, k.BondDenom(ctx))
|
||||
k.SetPreviousDelegationDistribution(ctx, ctx.BlockTime(), k.BondDenom(ctx))
|
||||
}
|
||||
k.ApplyInterestRateUpdates(ctx)
|
||||
k.AttemptIndexLiquidations(ctx)
|
||||
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||
|
153
x/hard/alias.go
153
x/hard/alias.go
@ -8,9 +8,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
BeginningOfMonth = keeper.BeginningOfMonth
|
||||
MidMonth = keeper.MidMonth
|
||||
PaymentHour = keeper.PaymentHour
|
||||
AttributeKeyBlockHeight = types.AttributeKeyBlockHeight
|
||||
AttributeKeyClaimAmount = types.AttributeKeyClaimAmount
|
||||
AttributeKeyClaimHolder = types.AttributeKeyClaimHolder
|
||||
@ -49,91 +46,77 @@ const (
|
||||
|
||||
var (
|
||||
// function aliases
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
CalculateUtilizationRatio = keeper.CalculateUtilizationRatio
|
||||
CalculateBorrowRate = keeper.CalculateBorrowRate
|
||||
CalculateBorrowInterestFactor = keeper.CalculateBorrowInterestFactor
|
||||
CalculateSupplyInterestFactor = keeper.CalculateSupplyInterestFactor
|
||||
APYToSPY = keeper.APYToSPY
|
||||
ClaimKey = types.ClaimKey
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
DefaultParams = types.DefaultParams
|
||||
DepositTypeIteratorKey = types.DepositTypeIteratorKey
|
||||
GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength
|
||||
NewClaim = types.NewClaim
|
||||
NewDelegatorDistributionSchedule = types.NewDelegatorDistributionSchedule
|
||||
NewDeposit = types.NewDeposit
|
||||
NewDistributionSchedule = types.NewDistributionSchedule
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewMsgClaimReward = types.NewMsgClaimReward
|
||||
NewMsgDeposit = types.NewMsgDeposit
|
||||
NewMsgWithdraw = types.NewMsgWithdraw
|
||||
NewMultiplier = types.NewMultiplier
|
||||
NewParams = types.NewParams
|
||||
NewPeriod = types.NewPeriod
|
||||
NewQueryAccountParams = types.NewQueryAccountParams
|
||||
NewQueryClaimParams = types.NewQueryClaimParams
|
||||
ParamKeyTable = types.ParamKeyTable
|
||||
RegisterCodec = types.RegisterCodec
|
||||
NewKeeper = keeper.NewKeeper
|
||||
NewQuerier = keeper.NewQuerier
|
||||
CalculateUtilizationRatio = keeper.CalculateUtilizationRatio
|
||||
CalculateBorrowRate = keeper.CalculateBorrowRate
|
||||
CalculateBorrowInterestFactor = keeper.CalculateBorrowInterestFactor
|
||||
CalculateSupplyInterestFactor = keeper.CalculateSupplyInterestFactor
|
||||
APYToSPY = keeper.APYToSPY
|
||||
DefaultGenesisState = types.DefaultGenesisState
|
||||
DefaultParams = types.DefaultParams
|
||||
DepositTypeIteratorKey = types.DepositTypeIteratorKey
|
||||
GetTotalVestingPeriodLength = types.GetTotalVestingPeriodLength
|
||||
NewBorrowLimit = types.NewBorrowLimit
|
||||
NewInterestRateModel = types.NewInterestRateModel
|
||||
NewDeposit = types.NewDeposit
|
||||
NewGenesisState = types.NewGenesisState
|
||||
NewMsgClaimReward = types.NewMsgClaimReward
|
||||
NewMsgDeposit = types.NewMsgDeposit
|
||||
NewMsgWithdraw = types.NewMsgWithdraw
|
||||
NewMultiHARDHooks = types.NewMultiHARDHooks
|
||||
NewMultiplier = types.NewMultiplier
|
||||
NewParams = types.NewParams
|
||||
NewPeriod = types.NewPeriod
|
||||
NewMoneyMarket = types.NewMoneyMarket
|
||||
NewQueryAccountParams = types.NewQueryAccountParams
|
||||
NewQueryClaimParams = types.NewQueryClaimParams
|
||||
ParamKeyTable = types.ParamKeyTable
|
||||
RegisterCodec = types.RegisterCodec
|
||||
|
||||
// variable aliases
|
||||
BorrowsKeyPrefix = types.BorrowsKeyPrefix
|
||||
ClaimsKeyPrefix = types.ClaimsKeyPrefix
|
||||
DefaultActive = types.DefaultActive
|
||||
DefaultDelegatorSchedules = types.DefaultDelegatorSchedules
|
||||
DefaultDistributionTimes = types.DefaultDistributionTimes
|
||||
DefaultGovSchedules = types.DefaultGovSchedules
|
||||
DefaultLPSchedules = types.DefaultLPSchedules
|
||||
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
||||
ClaimTypesClaimQuery = types.ClaimTypesClaimQuery
|
||||
DepositsKeyPrefix = types.DepositsKeyPrefix
|
||||
ErrAccountNotFound = types.ErrAccountNotFound
|
||||
ErrClaimExpired = types.ErrClaimExpired
|
||||
ErrClaimNotFound = types.ErrClaimNotFound
|
||||
ErrDepositNotFound = types.ErrDepositNotFound
|
||||
ErrGovScheduleNotFound = types.ErrGovScheduleNotFound
|
||||
ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance
|
||||
ErrInvaliWithdrawAmount = types.ErrInvalidWithdrawAmount
|
||||
ErrInvalidAccountType = types.ErrInvalidAccountType
|
||||
ErrInvalidDepositDenom = types.ErrInvalidDepositDenom
|
||||
ErrInvalidClaimType = types.ErrInvalidClaimType
|
||||
ErrInvalidMultiplier = types.ErrInvalidMultiplier
|
||||
ErrLPScheduleNotFound = types.ErrLPScheduleNotFound
|
||||
ErrZeroClaim = types.ErrZeroClaim
|
||||
GovDenom = types.GovDenom
|
||||
KeyActive = types.KeyActive
|
||||
KeyDelegatorSchedule = types.KeyDelegatorSchedule
|
||||
KeyLPSchedules = types.KeyLPSchedules
|
||||
ModuleCdc = types.ModuleCdc
|
||||
PreviousBlockTimeKey = types.PreviousBlockTimeKey
|
||||
PreviousDelegationDistributionKey = types.PreviousDelegationDistributionKey
|
||||
BorrowsKeyPrefix = types.BorrowsKeyPrefix
|
||||
DefaultActive = types.DefaultActive
|
||||
DefaultPreviousBlockTime = types.DefaultPreviousBlockTime
|
||||
ClaimTypesClaimQuery = types.ClaimTypesClaimQuery
|
||||
DepositsKeyPrefix = types.DepositsKeyPrefix
|
||||
ErrAccountNotFound = types.ErrAccountNotFound
|
||||
ErrClaimExpired = types.ErrClaimExpired
|
||||
ErrClaimNotFound = types.ErrClaimNotFound
|
||||
ErrDepositNotFound = types.ErrDepositNotFound
|
||||
ErrGovScheduleNotFound = types.ErrGovScheduleNotFound
|
||||
ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance
|
||||
ErrInvaliWithdrawAmount = types.ErrInvalidWithdrawAmount
|
||||
ErrInvalidAccountType = types.ErrInvalidAccountType
|
||||
ErrInvalidDepositDenom = types.ErrInvalidDepositDenom
|
||||
ErrInvalidClaimType = types.ErrInvalidClaimType
|
||||
ErrInvalidMultiplier = types.ErrInvalidMultiplier
|
||||
ErrLPScheduleNotFound = types.ErrLPScheduleNotFound
|
||||
ErrZeroClaim = types.ErrZeroClaim
|
||||
GovDenom = types.GovDenom
|
||||
KeyActive = types.KeyActive
|
||||
ModuleCdc = types.ModuleCdc
|
||||
PreviousBlockTimeKey = types.PreviousBlockTimeKey
|
||||
)
|
||||
|
||||
type (
|
||||
Keeper = keeper.Keeper
|
||||
AccountKeeper = types.AccountKeeper
|
||||
Borrow = types.Borrow
|
||||
MoneyMarket = types.MoneyMarket
|
||||
MoneyMarkets = types.MoneyMarkets
|
||||
DelegatorDistributionSchedule = types.DelegatorDistributionSchedule
|
||||
DelegatorDistributionSchedules = types.DelegatorDistributionSchedules
|
||||
Deposit = types.Deposit
|
||||
ClaimType = types.ClaimType
|
||||
DistributionSchedule = types.DistributionSchedule
|
||||
DistributionSchedules = types.DistributionSchedules
|
||||
GenesisDistributionTime = types.GenesisDistributionTime
|
||||
GenesisDistributionTimes = types.GenesisDistributionTimes
|
||||
GenesisState = types.GenesisState
|
||||
MsgClaimReward = types.MsgClaimReward
|
||||
MsgDeposit = types.MsgDeposit
|
||||
MsgWithdraw = types.MsgWithdraw
|
||||
Multiplier = types.Multiplier
|
||||
MultiplierName = types.MultiplierName
|
||||
Multipliers = types.Multipliers
|
||||
Params = types.Params
|
||||
QueryAccountParams = types.QueryAccountParams
|
||||
QueryClaimParams = types.QueryClaimParams
|
||||
StakingKeeper = types.StakingKeeper
|
||||
SupplyKeeper = types.SupplyKeeper
|
||||
Keeper = keeper.Keeper
|
||||
AccountKeeper = types.AccountKeeper
|
||||
Borrow = types.Borrow
|
||||
MoneyMarket = types.MoneyMarket
|
||||
MoneyMarkets = types.MoneyMarkets
|
||||
Deposit = types.Deposit
|
||||
ClaimType = types.ClaimType
|
||||
GenesisState = types.GenesisState
|
||||
MsgClaimReward = types.MsgClaimReward
|
||||
MsgDeposit = types.MsgDeposit
|
||||
MsgWithdraw = types.MsgWithdraw
|
||||
Multiplier = types.Multiplier
|
||||
MultiplierName = types.MultiplierName
|
||||
Multipliers = types.Multipliers
|
||||
Params = types.Params
|
||||
QueryAccountParams = types.QueryAccountParams
|
||||
QueryClaimParams = types.QueryClaimParams
|
||||
StakingKeeper = types.StakingKeeper
|
||||
SupplyKeeper = types.SupplyKeeper
|
||||
)
|
||||
|
@ -19,10 +19,9 @@ import (
|
||||
|
||||
// flags for cli queries
|
||||
const (
|
||||
flagName = "name"
|
||||
flagDenom = "denom"
|
||||
flagOwner = "owner"
|
||||
flagClaimType = "claim-type"
|
||||
flagName = "name"
|
||||
flagDenom = "denom"
|
||||
flagOwner = "owner"
|
||||
)
|
||||
|
||||
// GetQueryCmd returns the cli query commands for the module
|
||||
@ -40,7 +39,6 @@ func GetQueryCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
queryModAccountsCmd(queryRoute, cdc),
|
||||
queryDepositsCmd(queryRoute, cdc),
|
||||
queryTotalDepositedCmd(queryRoute, cdc),
|
||||
queryClaimsCmd(queryRoute, cdc),
|
||||
queryBorrowsCmd(queryRoute, cdc),
|
||||
queryTotalBorrowedCmd(queryRoute, cdc),
|
||||
)...)
|
||||
@ -177,76 +175,6 @@ func queryDepositsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
return cmd
|
||||
}
|
||||
|
||||
func queryClaimsCmd(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "claims",
|
||||
Short: "query hard module claims with optional filters",
|
||||
Long: strings.TrimSpace(`query for all hard module claims or a specific claim using flags:
|
||||
|
||||
Example:
|
||||
$ kvcli q hard claims
|
||||
$ kvcli q hard claims --owner kava1l0xsq2z7gqd7yly0g40y5836g0appumark77ny --claim-type lp --denom bnb
|
||||
$ kvcli q hard claims --claim-type stake --denom ukava
|
||||
$ kvcli q hard claims --denom btcb`,
|
||||
),
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||
|
||||
var owner sdk.AccAddress
|
||||
var claimType types.ClaimType
|
||||
|
||||
ownerBech := viper.GetString(flagOwner)
|
||||
denom := viper.GetString(flagDenom)
|
||||
claimTypeStr := viper.GetString(flagClaimType)
|
||||
|
||||
if len(ownerBech) != 0 {
|
||||
claimOwner, err := sdk.AccAddressFromBech32(ownerBech)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
owner = claimOwner
|
||||
}
|
||||
|
||||
if len(claimTypeStr) != 0 {
|
||||
if err := types.ClaimType(claimTypeStr).IsValid(); err != nil {
|
||||
return err
|
||||
}
|
||||
claimType = types.ClaimType(claimTypeStr)
|
||||
}
|
||||
|
||||
page := viper.GetInt(flags.FlagPage)
|
||||
limit := viper.GetInt(flags.FlagLimit)
|
||||
|
||||
params := types.NewQueryClaimParams(page, limit, denom, owner, claimType)
|
||||
bz, err := cdc.MarshalJSON(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
route := fmt.Sprintf("custom/%s/%s", queryRoute, types.QueryGetClaims)
|
||||
res, height, err := cliCtx.QueryWithData(route, bz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cliCtx = cliCtx.WithHeight(height)
|
||||
|
||||
var claims []types.Claim
|
||||
if err := cdc.UnmarshalJSON(res, &claims); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal claims: %w", err)
|
||||
}
|
||||
return cliCtx.PrintOutput(claims)
|
||||
},
|
||||
}
|
||||
|
||||
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 claims by owner address")
|
||||
cmd.Flags().String(flagDenom, "", "(optional) filter for claims by denom")
|
||||
cmd.Flags().String(flagClaimType, "", "(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",
|
||||
|
@ -32,7 +32,6 @@ func GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||
hardTxCmd.AddCommand(flags.PostCommands(
|
||||
getCmdDeposit(cdc),
|
||||
getCmdWithdraw(cdc),
|
||||
getCmdClaimReward(cdc),
|
||||
getCmdBorrow(cdc),
|
||||
getCmdLiquidate(cdc),
|
||||
getCmdRepay(cdc),
|
||||
@ -90,35 +89,6 @@ func getCmdWithdraw(cdc *codec.Codec) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func getCmdClaimReward(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "claim [receiver-addr] [deposit-denom] [deposit-type] [multiplier]",
|
||||
Short: "claim HARD tokens to receiver address",
|
||||
Long: strings.TrimSpace(
|
||||
`sends accumulated HARD tokens from the hard module account to the receiver address.
|
||||
Note that receiver address should match the sender address,
|
||||
unless the sender is a validator-vesting account`),
|
||||
Args: cobra.ExactArgs(4),
|
||||
Example: fmt.Sprintf(
|
||||
`%s tx %s claim kava1hgcfsuwc889wtdmt8pjy7qffua9dd2tralu64j bnb lp large --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))
|
||||
receiver, err := sdk.AccAddressFromBech32(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg := types.NewMsgClaimReward(cliCtx.GetFromAddress(), receiver, args[1], args[2], args[3])
|
||||
if err := msg.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getCmdBorrow(cdc *codec.Codec) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "borrow [1000000000ukava]",
|
||||
|
@ -21,12 +21,6 @@ func InitGenesis(ctx sdk.Context, k Keeper, supplyKeeper types.SupplyKeeper, gs
|
||||
k.SetPreviousBlockTime(ctx, gs.PreviousBlockTime)
|
||||
}
|
||||
|
||||
for _, pdt := range gs.PreviousDistributionTimes {
|
||||
if !pdt.PreviousDistributionTime.Equal(DefaultPreviousBlockTime) {
|
||||
k.SetPreviousDelegationDistribution(ctx, pdt.PreviousDistributionTime, pdt.Denom)
|
||||
}
|
||||
}
|
||||
|
||||
for _, mm := range gs.Params.MoneyMarkets {
|
||||
k.SetMoneyMarket(ctx, mm.Denom, mm)
|
||||
}
|
||||
@ -64,12 +58,5 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||
if !found {
|
||||
previousBlockTime = DefaultPreviousBlockTime
|
||||
}
|
||||
previousDistTimes := GenesisDistributionTimes{}
|
||||
for _, dds := range params.DelegatorDistributionSchedules {
|
||||
previousDistTime, found := k.GetPreviousDelegatorDistribution(ctx, dds.DistributionSchedule.DepositDenom)
|
||||
if found {
|
||||
previousDistTimes = append(previousDistTimes, GenesisDistributionTime{PreviousDistributionTime: previousDistTime, Denom: dds.DistributionSchedule.DepositDenom})
|
||||
}
|
||||
}
|
||||
return NewGenesisState(params, previousBlockTime, previousDistTimes)
|
||||
return NewGenesisState(params, previousBlockTime)
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package hard
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
@ -15,8 +13,6 @@ func NewHandler(k Keeper) sdk.Handler {
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
switch msg := msg.(type) {
|
||||
case types.MsgClaimReward:
|
||||
return handleMsgClaimReward(ctx, k, msg)
|
||||
case types.MsgDeposit:
|
||||
return handleMsgDeposit(ctx, k, msg)
|
||||
case types.MsgWithdraw:
|
||||
@ -33,24 +29,6 @@ func NewHandler(k Keeper) sdk.Handler {
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgClaimReward(ctx sdk.Context, k keeper.Keeper, msg types.MsgClaimReward) (*sdk.Result, error) {
|
||||
err := k.ClaimReward(ctx, msg.Sender, msg.Receiver, msg.DepositDenom, types.ClaimType(strings.ToLower(msg.ClaimType)), types.MultiplierName(strings.ToLower(msg.MultiplierName)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()),
|
||||
),
|
||||
)
|
||||
return &sdk.Result{
|
||||
Events: ctx.EventManager().Events(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func handleMsgDeposit(ctx sdk.Context, k keeper.Keeper, msg types.MsgDeposit) (*sdk.Result, error) {
|
||||
err := k.Deposit(ctx, msg.Depositor, msg.Amount)
|
||||
if err != nil {
|
||||
|
@ -28,7 +28,12 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
|
||||
return err
|
||||
}
|
||||
|
||||
// Sync any outstanding interest
|
||||
// Call incentive hook
|
||||
existingBorrow, hasExistingBorrow := k.GetBorrow(ctx, borrower)
|
||||
if hasExistingBorrow {
|
||||
k.BeforeBorrowModified(ctx, existingBorrow)
|
||||
}
|
||||
|
||||
k.SyncBorrowInterest(ctx, borrower)
|
||||
|
||||
// Validate borrow amount within user and protocol limits
|
||||
@ -84,7 +89,6 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
|
||||
} else {
|
||||
amount = coins
|
||||
}
|
||||
|
||||
// Construct the user's new/updated borrow with amount and interest factors
|
||||
borrow := types.NewBorrow(borrower, amount, borrowInterestFactors)
|
||||
|
||||
@ -104,6 +108,12 @@ func (k Keeper) Borrow(ctx sdk.Context, borrower sdk.AccAddress, coins sdk.Coins
|
||||
// it has already been included in the total borrowed coins by the BeginBlocker.
|
||||
k.IncrementBorrowedCoins(ctx, coins)
|
||||
|
||||
if !hasExistingBorrow {
|
||||
k.AfterBorrowCreated(ctx, borrow)
|
||||
} else {
|
||||
k.AfterBorrowModified(ctx, borrow)
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeHardBorrow,
|
||||
|
@ -262,19 +262,6 @@ func (suite *KeeperTestSuite) TestBorrow() {
|
||||
// hard module genesis state
|
||||
hardGS := 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.NewDistributionSchedule(true, "btcb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "busd", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "xyz", 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", types.NewBorrowLimit(true, tc.args.usdxBorrowLimit, sdk.MustNewDecFromStr("1")), "usdx:usd", sdk.NewInt(USDX_CF), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("busd", types.NewBorrowLimit(false, sdk.NewDec(100000000*BUSD_CF), sdk.MustNewDecFromStr("1")), "busd:usd", sdk.NewInt(BUSD_CF), sdk.NewInt(BUSD_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
@ -284,7 +271,7 @@ func (suite *KeeperTestSuite) TestBorrow() {
|
||||
types.NewMoneyMarket("xyz", types.NewBorrowLimit(false, sdk.NewDec(1), tc.args.loanToValueBNB), "xyz:usd", sdk.NewInt(1), sdk.NewInt(1), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
@ -1,149 +0,0 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
|
||||
)
|
||||
|
||||
const (
|
||||
// BeginningOfMonth hard rewards that are claimed after the 15th at 14:00UTC of the month always vest on the first of the month
|
||||
BeginningOfMonth = 1
|
||||
// MidMonth hard rewards that are claimed before the 15th at 14:00UTC of the month always vest on the 15 of the month
|
||||
MidMonth = 15
|
||||
// PaymentHour hard rewards always vest at 14:00UTC
|
||||
PaymentHour = 14
|
||||
)
|
||||
|
||||
// ClaimReward sends the reward amount to the reward owner and deletes the claim from the store
|
||||
func (k Keeper) ClaimReward(ctx sdk.Context, claimHolder sdk.AccAddress, receiver sdk.AccAddress, depositDenom string, claimType types.ClaimType, multiplier types.MultiplierName) error {
|
||||
|
||||
claim, found := k.GetClaim(ctx, claimHolder, depositDenom, claimType)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrClaimNotFound, "no %s %s claim found for %s", depositDenom, claimType, claimHolder)
|
||||
}
|
||||
|
||||
err := k.validateSenderReceiver(ctx, claimHolder, receiver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch claimType {
|
||||
case types.LP:
|
||||
err = k.claimLPReward(ctx, claim, receiver, multiplier)
|
||||
case types.Stake:
|
||||
err = k.claimDelegatorReward(ctx, claim, receiver, multiplier)
|
||||
default:
|
||||
return sdkerrors.Wrap(types.ErrInvalidClaimType, string(claimType))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeClaimHardReward,
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, claim.Amount.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyClaimHolder, claimHolder.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyDepositDenom, depositDenom),
|
||||
sdk.NewAttribute(types.AttributeKeyClaimType, string(claimType)),
|
||||
sdk.NewAttribute(types.AttributeKeyClaimMultiplier, string(multiplier)),
|
||||
),
|
||||
)
|
||||
k.DeleteClaim(ctx, claim)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPeriodLength returns the length of the period based on the input blocktime and multiplier
|
||||
// note that pay dates are always the 1st or 15th of the month at 14:00UTC.
|
||||
func (k Keeper) GetPeriodLength(ctx sdk.Context, multiplier types.Multiplier) (int64, error) {
|
||||
|
||||
if multiplier.MonthsLockup == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
switch multiplier.Name {
|
||||
case types.Small, types.Medium, types.Large:
|
||||
currentDay := ctx.BlockTime().Day()
|
||||
payDay := BeginningOfMonth
|
||||
monthOffset := int64(1)
|
||||
if currentDay < MidMonth || (currentDay == MidMonth && ctx.BlockTime().Hour() < PaymentHour) {
|
||||
payDay = MidMonth
|
||||
monthOffset = int64(0)
|
||||
}
|
||||
periodEndDate := time.Date(ctx.BlockTime().Year(), ctx.BlockTime().Month(), payDay, PaymentHour, 0, 0, 0, time.UTC).AddDate(0, int(multiplier.MonthsLockup+monthOffset), 0)
|
||||
return periodEndDate.Unix() - ctx.BlockTime().Unix(), nil
|
||||
}
|
||||
return 0, types.ErrInvalidMultiplier
|
||||
}
|
||||
|
||||
func (k Keeper) claimLPReward(ctx sdk.Context, claim types.Claim, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
|
||||
lps, found := k.GetLPSchedule(ctx, claim.DepositDenom)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrLPScheduleNotFound, claim.DepositDenom)
|
||||
}
|
||||
multiplier, found := lps.GetMultiplier(multiplierName)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
|
||||
}
|
||||
if ctx.BlockTime().After(lps.ClaimEnd) {
|
||||
return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), lps.ClaimEnd)
|
||||
}
|
||||
rewardAmount := sdk.NewDecFromInt(claim.Amount.Amount).Mul(multiplier.Factor).RoundInt()
|
||||
if rewardAmount.IsZero() {
|
||||
return types.ErrZeroClaim
|
||||
}
|
||||
rewardCoin := sdk.NewCoin(claim.Amount.Denom, rewardAmount)
|
||||
length, err := k.GetPeriodLength(ctx, multiplier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.SendTimeLockedCoinsToAccount(ctx, types.LPAccount, receiver, sdk.NewCoins(rewardCoin), length)
|
||||
}
|
||||
|
||||
func (k Keeper) claimDelegatorReward(ctx sdk.Context, claim types.Claim, receiver sdk.AccAddress, multiplierName types.MultiplierName) error {
|
||||
dss, found := k.GetDelegatorSchedule(ctx, claim.DepositDenom)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrLPScheduleNotFound, claim.DepositDenom)
|
||||
}
|
||||
multiplier, found := dss.DistributionSchedule.GetMultiplier(multiplierName)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidMultiplier, string(multiplierName))
|
||||
}
|
||||
if ctx.BlockTime().After(dss.DistributionSchedule.ClaimEnd) {
|
||||
return sdkerrors.Wrapf(types.ErrClaimExpired, "block time %s > claim end time %s", ctx.BlockTime(), dss.DistributionSchedule.ClaimEnd)
|
||||
}
|
||||
rewardAmount := sdk.NewDecFromInt(claim.Amount.Amount).Mul(multiplier.Factor).RoundInt()
|
||||
if rewardAmount.IsZero() {
|
||||
return types.ErrZeroClaim
|
||||
}
|
||||
rewardCoin := sdk.NewCoin(claim.Amount.Denom, rewardAmount)
|
||||
|
||||
length, err := k.GetPeriodLength(ctx, multiplier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.SendTimeLockedCoinsToAccount(ctx, types.DelegatorAccount, receiver, sdk.NewCoins(rewardCoin), length)
|
||||
}
|
||||
|
||||
func (k Keeper) validateSenderReceiver(ctx sdk.Context, sender, receiver sdk.AccAddress) error {
|
||||
senderAcc := k.accountKeeper.GetAccount(ctx, sender)
|
||||
if senderAcc == nil {
|
||||
return sdkerrors.Wrapf(types.ErrAccountNotFound, sender.String())
|
||||
}
|
||||
switch senderAcc.(type) {
|
||||
case *validatorvesting.ValidatorVestingAccount:
|
||||
if sender.Equals(receiver) {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidAccountType, "%T", senderAcc)
|
||||
}
|
||||
default:
|
||||
if !sender.Equals(receiver) {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidReceiver, "%s", sender)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,475 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestClaim() {
|
||||
type args struct {
|
||||
claimOwner sdk.AccAddress
|
||||
receiver sdk.AccAddress
|
||||
denom string
|
||||
claimType types.ClaimType
|
||||
multiplier types.MultiplierName
|
||||
blockTime time.Time
|
||||
createClaim bool
|
||||
claimAmount sdk.Coin
|
||||
validatorVesting bool
|
||||
expectedAccountBalance sdk.Coins
|
||||
expectedModAccountBalance sdk.Coins
|
||||
expectedVestingAccount bool
|
||||
expectedVestingLength int64
|
||||
}
|
||||
type errArgs struct {
|
||||
expectPass bool
|
||||
contains string
|
||||
}
|
||||
type claimTest struct {
|
||||
name string
|
||||
args args
|
||||
errArgs errArgs
|
||||
}
|
||||
testCases := []claimTest{
|
||||
{
|
||||
"valid liquid claim",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(33)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(967))),
|
||||
expectedVestingAccount: false,
|
||||
expectedVestingLength: 0,
|
||||
multiplier: types.Small,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"valid liquid delegator claim",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.Stake,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(33)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(967))),
|
||||
expectedVestingAccount: false,
|
||||
expectedVestingLength: 0,
|
||||
multiplier: types.Small,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"valid medium vesting claim",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(50)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(950))),
|
||||
expectedVestingAccount: true,
|
||||
expectedVestingLength: 16848000,
|
||||
multiplier: types.Medium,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"valid large vesting claim",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(100)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(900))),
|
||||
expectedVestingAccount: true,
|
||||
expectedVestingLength: 64281600,
|
||||
multiplier: types.Large,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"valid validator vesting",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test2"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: true,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(100)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(900))),
|
||||
expectedVestingAccount: true,
|
||||
expectedVestingLength: 64281600,
|
||||
multiplier: types.Large,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid validator vesting",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: true,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(100)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(900))),
|
||||
expectedVestingAccount: true,
|
||||
expectedVestingLength: 64281600,
|
||||
multiplier: types.Large,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: false,
|
||||
contains: "receiver account type not supported",
|
||||
},
|
||||
},
|
||||
{
|
||||
"claim not found",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: false,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.Coins{},
|
||||
expectedModAccountBalance: sdk.Coins{},
|
||||
expectedVestingAccount: false,
|
||||
expectedVestingLength: 0,
|
||||
multiplier: types.Small,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: false,
|
||||
contains: "claim not found",
|
||||
},
|
||||
},
|
||||
{
|
||||
"claim expired",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2022, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.Coins{},
|
||||
expectedModAccountBalance: sdk.Coins{},
|
||||
expectedVestingAccount: false,
|
||||
expectedVestingLength: 0,
|
||||
multiplier: types.Small,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: false,
|
||||
contains: "claim period expired",
|
||||
},
|
||||
},
|
||||
{
|
||||
"different receiver address",
|
||||
args{
|
||||
claimOwner: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
receiver: sdk.AccAddress(crypto.AddressHash([]byte("test2"))),
|
||||
denom: "bnb",
|
||||
claimType: types.LP,
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
createClaim: true,
|
||||
claimAmount: sdk.NewCoin("hard", sdk.NewInt(100)),
|
||||
validatorVesting: false,
|
||||
expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(100)), sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))),
|
||||
expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(900))),
|
||||
expectedVestingAccount: true,
|
||||
expectedVestingLength: 64281600,
|
||||
multiplier: types.Large,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: false,
|
||||
contains: "receiver account must match sender account",
|
||||
},
|
||||
},
|
||||
}
|
||||
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: tc.args.blockTime})
|
||||
authGS := app.NewAuthGenState(
|
||||
[]sdk.AccAddress{tc.args.claimOwner, tc.args.receiver},
|
||||
[]sdk.Coins{
|
||||
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")
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Large, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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.Large, 24, sdk.OneDec())}),
|
||||
time.Hour*24,
|
||||
),
|
||||
},
|
||||
types.MoneyMarkets{
|
||||
types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(hardGS)})
|
||||
if tc.args.validatorVesting {
|
||||
ak := tApp.GetAccountKeeper()
|
||||
acc := ak.GetAccount(ctx, tc.args.claimOwner)
|
||||
bacc := auth.NewBaseAccount(acc.GetAddress(), acc.GetCoins(), acc.GetPubKey(), acc.GetAccountNumber(), acc.GetSequence())
|
||||
bva, err := vesting.NewBaseVestingAccount(
|
||||
bacc,
|
||||
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(20))), time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC).Unix()+100)
|
||||
suite.Require().NoError(err)
|
||||
vva := validatorvesting.NewValidatorVestingAccountRaw(
|
||||
bva,
|
||||
time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC).Unix(),
|
||||
vesting.Periods{
|
||||
vesting.Period{Length: 25, Amount: cs(c("bnb", 5))},
|
||||
vesting.Period{Length: 25, Amount: cs(c("bnb", 5))},
|
||||
vesting.Period{Length: 25, Amount: cs(c("bnb", 5))},
|
||||
vesting.Period{Length: 25, Amount: cs(c("bnb", 5))}},
|
||||
sdk.ConsAddress(crypto.AddressHash([]byte("test"))),
|
||||
sdk.AccAddress{},
|
||||
95,
|
||||
)
|
||||
err = vva.Validate()
|
||||
suite.Require().NoError(err)
|
||||
ak.SetAccount(ctx, vva)
|
||||
}
|
||||
supplyKeeper := tApp.GetSupplyKeeper()
|
||||
supplyKeeper.MintCoins(ctx, types.LPAccount, sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(1000))))
|
||||
supplyKeeper.MintCoins(ctx, types.DelegatorAccount, sdk.NewCoins(sdk.NewCoin("hard", sdk.NewInt(1000))))
|
||||
keeper := tApp.GetHardKeeper()
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.keeper = keeper
|
||||
|
||||
if tc.args.createClaim {
|
||||
claim := types.NewClaim(tc.args.claimOwner, tc.args.denom, tc.args.claimAmount, tc.args.claimType)
|
||||
suite.Require().NotPanics(func() { suite.keeper.SetClaim(suite.ctx, claim) })
|
||||
}
|
||||
|
||||
err := suite.keeper.ClaimReward(suite.ctx, tc.args.claimOwner, tc.args.receiver, tc.args.denom, tc.args.claimType, tc.args.multiplier)
|
||||
if tc.errArgs.expectPass {
|
||||
suite.Require().NoError(err)
|
||||
acc := suite.getAccount(tc.args.receiver)
|
||||
suite.Require().Equal(tc.args.expectedAccountBalance, acc.GetCoins())
|
||||
mAcc := suite.getModuleAccount(types.LPAccount)
|
||||
if tc.args.claimType == types.Stake {
|
||||
mAcc = suite.getModuleAccount(types.DelegatorAccount)
|
||||
}
|
||||
suite.Require().Equal(tc.args.expectedModAccountBalance, mAcc.GetCoins())
|
||||
vacc, ok := acc.(*vesting.PeriodicVestingAccount)
|
||||
if tc.args.expectedVestingAccount {
|
||||
suite.Require().True(ok)
|
||||
suite.Require().Equal(tc.args.expectedVestingLength, vacc.VestingPeriods[0].Length)
|
||||
} else {
|
||||
suite.Require().False(ok)
|
||||
}
|
||||
_, f := suite.keeper.GetClaim(ctx, tc.args.claimOwner, tc.args.denom, tc.args.claimType)
|
||||
suite.Require().False(f)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetPeriodLength() {
|
||||
type args struct {
|
||||
blockTime time.Time
|
||||
multiplier types.Multiplier
|
||||
expectedLength int64
|
||||
}
|
||||
type errArgs struct {
|
||||
expectPass bool
|
||||
contains string
|
||||
}
|
||||
type periodTest struct {
|
||||
name string
|
||||
args args
|
||||
errArgs errArgs
|
||||
}
|
||||
testCases := []periodTest{
|
||||
{
|
||||
name: "first half of month",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 11, 2, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2021, 5, 15, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 11, 2, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "first half of month long lockup",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 11, 2, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 24, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2022, 11, 15, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 11, 2, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "second half of month",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 12, 31, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2021, 7, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 12, 31, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "second half of month long lockup",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 12, 31, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Large, 24, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2023, 1, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 12, 31, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "end of feb",
|
||||
args: args{
|
||||
blockTime: time.Date(2021, 2, 28, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2021, 9, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2021, 2, 28, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "leap year",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 2, 29, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2020, 9, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 2, 29, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "leap year long lockup",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 2, 29, 15, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Large, 24, sdk.MustNewDecFromStr("1")),
|
||||
expectedLength: time.Date(2022, 3, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 2, 29, 15, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "exactly half of month",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2021, 7, 1, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "just before half of month",
|
||||
args: args{
|
||||
blockTime: time.Date(2020, 12, 15, 13, 59, 59, 0, time.UTC),
|
||||
multiplier: types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.333333")),
|
||||
expectedLength: time.Date(2021, 6, 15, 14, 0, 0, 0, time.UTC).Unix() - time.Date(2020, 12, 15, 13, 59, 59, 0, time.UTC).Unix(),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
ctx := suite.ctx.WithBlockTime(tc.args.blockTime)
|
||||
length, err := suite.keeper.GetPeriodLength(ctx, tc.args.multiplier)
|
||||
if tc.errArgs.expectPass {
|
||||
suite.Require().NoError(err)
|
||||
suite.Require().Equal(tc.args.expectedLength, length)
|
||||
} else {
|
||||
suite.Require().Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -29,6 +29,12 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
|
||||
return err
|
||||
}
|
||||
|
||||
// Call incentive hook
|
||||
existingDeposit, hasExistingDeposit := k.GetDeposit(ctx, depositor)
|
||||
if hasExistingDeposit {
|
||||
k.BeforeDepositModified(ctx, existingDeposit)
|
||||
}
|
||||
|
||||
// Sync any outstanding interest
|
||||
k.SyncBorrowInterest(ctx, depositor)
|
||||
k.SyncSupplyInterest(ctx, depositor)
|
||||
@ -98,10 +104,12 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
|
||||
}
|
||||
|
||||
k.UpdateDepositAndLtvIndex(ctx, deposit, newLtv, prevLtv)
|
||||
|
||||
// Update total supplied amount by newly supplied coins. Don't add user's pending interest as
|
||||
// it has already been included in the total supplied coins by the BeginBlocker.
|
||||
k.IncrementSuppliedCoins(ctx, coins)
|
||||
if !foundDeposit { // User's first deposit
|
||||
k.AfterDepositCreated(ctx, deposit)
|
||||
} else {
|
||||
k.AfterDepositModified(ctx, deposit)
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
@ -116,16 +124,10 @@ func (k Keeper) Deposit(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coi
|
||||
|
||||
// ValidateDeposit validates a deposit
|
||||
func (k Keeper) ValidateDeposit(ctx sdk.Context, coins sdk.Coins) error {
|
||||
params := k.GetParams(ctx)
|
||||
for _, depCoin := range coins {
|
||||
found := false
|
||||
for _, lps := range params.LiquidityProviderSchedules {
|
||||
if lps.DepositDenom == depCoin.Denom {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidDepositDenom, "liquidity provider denom %s not found", depCoin.Denom)
|
||||
_, foundMm := k.GetMoneyMarket(ctx, depCoin.Denom)
|
||||
if !foundMm {
|
||||
return sdkerrors.Wrapf(types.ErrInvalidDepositDenom, "money market denom %s not found", depCoin.Denom)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
||||
"invalid deposit denom",
|
||||
args{
|
||||
depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
amount: sdk.NewCoins(sdk.NewCoin("btcb", sdk.NewInt(100))),
|
||||
amount: sdk.NewCoins(sdk.NewCoin("fake", sdk.NewInt(100))),
|
||||
numberDeposits: 1,
|
||||
expectedAccountBalance: sdk.Coins{},
|
||||
expectedModAccountBalance: sdk.Coins{},
|
||||
@ -106,14 +106,6 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
||||
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
@ -121,7 +113,7 @@ func (suite *KeeperTestSuite) TestDeposit() {
|
||||
types.NewMoneyMarket("btcb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "btcb:usd", sdk.NewInt(1000000), sdk.NewInt(BTCB_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
52
x/hard/keeper/hooks.go
Normal file
52
x/hard/keeper/hooks.go
Normal file
@ -0,0 +1,52 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
// Implements StakingHooks interface
|
||||
var _ types.HARDHooks = Keeper{}
|
||||
|
||||
// AfterDepositCreated - call hook if registered
|
||||
func (k Keeper) AfterDepositCreated(ctx sdk.Context, deposit types.Deposit) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.AfterDepositCreated(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeDepositModified - call hook if registered
|
||||
func (k Keeper) BeforeDepositModified(ctx sdk.Context, deposit types.Deposit) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.BeforeDepositModified(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterDepositModified - call hook if registered
|
||||
func (k Keeper) AfterDepositModified(ctx sdk.Context, deposit types.Deposit) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.AfterDepositModified(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterBorrowCreated - call hook if registered
|
||||
func (k Keeper) AfterBorrowCreated(ctx sdk.Context, borrow types.Borrow) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.AfterBorrowCreated(ctx, borrow)
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeBorrowModified - call hook if registered
|
||||
func (k Keeper) BeforeBorrowModified(ctx sdk.Context, borrow types.Borrow) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.BeforeBorrowModified(ctx, borrow)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterBorrowModified - call hook if registered
|
||||
func (k Keeper) AfterBorrowModified(ctx sdk.Context, borrow types.Borrow) {
|
||||
if k.hooks != nil {
|
||||
k.hooks.AfterBorrowModified(ctx, borrow)
|
||||
}
|
||||
}
|
@ -135,7 +135,6 @@ func (k Keeper) AccrueInterest(ctx sdk.Context, denom string) error {
|
||||
k.IncrementBorrowedCoins(ctx, totalBorrowInterestAccumulated)
|
||||
k.IncrementSuppliedCoins(ctx, sdk.NewCoins(sdk.NewCoin(denom, supplyInterestNew)))
|
||||
k.SetTotalReserves(ctx, denom, reservesPrior.Add(sdk.NewCoin(mm.Denom, reservesNew)))
|
||||
k.SetSupplyInterestFactor(ctx, denom, supplyInterestFactorNew)
|
||||
k.SetPreviousAccrualTime(ctx, denom, ctx.BlockTime())
|
||||
|
||||
return nil
|
||||
|
@ -712,14 +712,6 @@ func (suite *KeeperTestSuite) TestBorrowInterest() {
|
||||
// Hard module genesis state
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
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("ukava",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit
|
||||
@ -731,7 +723,7 @@ func (suite *KeeperTestSuite) TestBorrowInterest() {
|
||||
sdk.ZeroDec()), // Keeper Reward Percentage
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
@ -1127,15 +1119,6 @@ func (suite *KeeperTestSuite) TestSupplyInterest() {
|
||||
// Hard module genesis state
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.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("ukava",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit
|
||||
@ -1155,7 +1138,7 @@ func (suite *KeeperTestSuite) TestSupplyInterest() {
|
||||
sdk.ZeroDec()), // Keeper Reward Percentage
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
@ -21,6 +21,7 @@ type Keeper struct {
|
||||
stakingKeeper types.StakingKeeper
|
||||
pricefeedKeeper types.PricefeedKeeper
|
||||
auctionKeeper types.AuctionKeeper
|
||||
hooks types.HARDHooks
|
||||
}
|
||||
|
||||
// NewKeeper creates a new keeper
|
||||
@ -40,9 +41,19 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace,
|
||||
stakingKeeper: stk,
|
||||
pricefeedKeeper: pfk,
|
||||
auctionKeeper: auk,
|
||||
hooks: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// SetHooks sets the cdp keeper hooks
|
||||
func (k *Keeper) SetHooks(hooks types.HARDHooks) *Keeper {
|
||||
if k.hooks != nil {
|
||||
panic("cannot set validator hooks twice")
|
||||
}
|
||||
k.hooks = hooks
|
||||
return k
|
||||
}
|
||||
|
||||
// GetPreviousBlockTime get the blocktime for the previous block
|
||||
func (k Keeper) GetPreviousBlockTime(ctx sdk.Context) (blockTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousBlockTimeKey)
|
||||
@ -60,23 +71,6 @@ func (k Keeper) SetPreviousBlockTime(ctx sdk.Context, blockTime time.Time) {
|
||||
store.Set([]byte{}, k.cdc.MustMarshalBinaryBare(blockTime))
|
||||
}
|
||||
|
||||
// GetPreviousDelegatorDistribution get the time of the previous delegator distribution
|
||||
func (k Keeper) GetPreviousDelegatorDistribution(ctx sdk.Context, denom string) (distTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousDelegationDistributionKey)
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &distTime)
|
||||
return distTime, true
|
||||
}
|
||||
|
||||
// SetPreviousDelegationDistribution set the time of the previous delegator distribution
|
||||
func (k Keeper) SetPreviousDelegationDistribution(ctx sdk.Context, distTime time.Time, denom string) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousDelegationDistributionKey)
|
||||
store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(distTime))
|
||||
}
|
||||
|
||||
// GetDeposit returns a deposit from the store for a particular depositor address, deposit denom
|
||||
func (k Keeper) GetDeposit(ctx sdk.Context, depositor sdk.AccAddress) (types.Deposit, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.DepositsKeyPrefix)
|
||||
@ -116,59 +110,6 @@ func (k Keeper) IterateDeposits(ctx sdk.Context, cb func(deposit types.Deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// GetClaim returns a claim from the store for a particular claim owner, deposit denom, and claim type
|
||||
func (k Keeper) GetClaim(ctx sdk.Context, owner sdk.AccAddress, depositDenom string, claimType types.ClaimType) (types.Claim, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimsKeyPrefix)
|
||||
bz := store.Get(types.ClaimKey(claimType, depositDenom, owner))
|
||||
if bz == nil {
|
||||
return types.Claim{}, false
|
||||
}
|
||||
var claim types.Claim
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &claim)
|
||||
return claim, true
|
||||
}
|
||||
|
||||
// SetClaim stores the input claim in the store, prefixed by the deposit type, deposit denom, and owner address, in that order
|
||||
func (k Keeper) SetClaim(ctx sdk.Context, claim types.Claim) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimsKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryBare(claim)
|
||||
store.Set(types.ClaimKey(claim.Type, claim.DepositDenom, claim.Owner), bz)
|
||||
}
|
||||
|
||||
// DeleteClaim deletes a claim from the store
|
||||
func (k Keeper) DeleteClaim(ctx sdk.Context, claim types.Claim) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimsKeyPrefix)
|
||||
store.Delete(types.ClaimKey(claim.Type, claim.DepositDenom, claim.Owner))
|
||||
}
|
||||
|
||||
// IterateClaims iterates over all claim objects in the store and performs a callback function
|
||||
func (k Keeper) IterateClaims(ctx sdk.Context, cb func(claim types.Claim) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimsKeyPrefix)
|
||||
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var claim types.Claim
|
||||
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &claim)
|
||||
if cb(claim) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IterateClaimsByTypeAndDenom iterates over all claim objects in the store with the matching claim type and deposit denom and performs a callback function
|
||||
func (k Keeper) IterateClaimsByTypeAndDenom(ctx sdk.Context, claimType types.ClaimType, depositDenom string, cb func(claim types.Claim) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimsKeyPrefix)
|
||||
iterator := sdk.KVStorePrefixIterator(store, types.ClaimTypeIteratorKey(claimType, depositDenom))
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var claim types.Claim
|
||||
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &claim)
|
||||
if cb(claim) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetDepositsByUser gets all deposits for an individual user
|
||||
func (k Keeper) GetDepositsByUser(ctx sdk.Context, user sdk.AccAddress) []types.Deposit {
|
||||
var deposits []types.Deposit
|
||||
|
@ -60,21 +60,6 @@ func (suite *KeeperTestSuite) TestGetSetPreviousBlockTime() {
|
||||
suite.Equal(now, pbt)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetPreviousDelegatorDistribution() {
|
||||
now := tmtime.Now()
|
||||
|
||||
_, f := suite.keeper.GetPreviousDelegatorDistribution(suite.ctx, suite.keeper.BondDenom(suite.ctx))
|
||||
suite.Require().False(f)
|
||||
|
||||
suite.NotPanics(func() {
|
||||
suite.keeper.SetPreviousDelegationDistribution(suite.ctx, now, suite.keeper.BondDenom(suite.ctx))
|
||||
})
|
||||
|
||||
pdt, f := suite.keeper.GetPreviousDelegatorDistribution(suite.ctx, suite.keeper.BondDenom(suite.ctx))
|
||||
suite.True(f)
|
||||
suite.Equal(now, pdt)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetDeleteDeposit() {
|
||||
dep := types.NewDeposit(sdk.AccAddress("test"), sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(100))),
|
||||
types.SupplyInterestFactors{types.NewSupplyInterestFactor("", sdk.MustNewDecFromStr("0"))})
|
||||
@ -108,21 +93,6 @@ func (suite *KeeperTestSuite) TestIterateDeposits() {
|
||||
suite.Require().Equal(5, len(deposits))
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetDeleteClaim() {
|
||||
claim := types.NewClaim(sdk.AccAddress("test"), "bnb", sdk.NewCoin("hard", sdk.NewInt(100)), "lp")
|
||||
_, f := suite.keeper.GetClaim(suite.ctx, sdk.AccAddress("test"), "bnb", "lp")
|
||||
suite.Require().False(f)
|
||||
|
||||
suite.Require().NotPanics(func() { suite.keeper.SetClaim(suite.ctx, claim) })
|
||||
testClaim, f := suite.keeper.GetClaim(suite.ctx, sdk.AccAddress("test"), "bnb", "lp")
|
||||
suite.Require().True(f)
|
||||
suite.Require().Equal(claim, testClaim)
|
||||
|
||||
suite.Require().NotPanics(func() { suite.keeper.DeleteClaim(suite.ctx, claim) })
|
||||
_, f = suite.keeper.GetClaim(suite.ctx, sdk.AccAddress("test"), "bnb", "lp")
|
||||
suite.Require().False(f)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetDeleteInterestRateModel() {
|
||||
denom := "test"
|
||||
model := types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10"))
|
||||
|
@ -39,9 +39,6 @@ func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress,
|
||||
return err
|
||||
}
|
||||
|
||||
k.SyncBorrowInterest(ctx, borrower)
|
||||
k.SyncSupplyInterest(ctx, borrower)
|
||||
|
||||
deposit, found := k.GetDeposit(ctx, borrower)
|
||||
if !found {
|
||||
return types.ErrDepositNotFound
|
||||
@ -52,6 +49,23 @@ func (k Keeper) AttemptKeeperLiquidation(ctx sdk.Context, keeper sdk.AccAddress,
|
||||
return types.ErrBorrowNotFound
|
||||
}
|
||||
|
||||
// Call incentive hooks
|
||||
k.BeforeDepositModified(ctx, deposit)
|
||||
k.BeforeBorrowModified(ctx, borrow)
|
||||
|
||||
k.SyncBorrowInterest(ctx, borrower)
|
||||
k.SyncSupplyInterest(ctx, borrower)
|
||||
|
||||
deposit, found = k.GetDeposit(ctx, borrower)
|
||||
if !found {
|
||||
return types.ErrDepositNotFound
|
||||
}
|
||||
|
||||
borrow, found = k.GetBorrow(ctx, borrower)
|
||||
if !found {
|
||||
return types.ErrBorrowNotFound
|
||||
}
|
||||
|
||||
isWithinRange, err := k.IsWithinValidLtvRange(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -123,20 +123,6 @@ func (suite *KeeperTestSuite) TestIndexLiquidation() {
|
||||
// Hard module genesis state
|
||||
hardGS := 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, "usdc", 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, "usdt", 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, "dai", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "btc", 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",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit
|
||||
@ -196,7 +182,7 @@ func (suite *KeeperTestSuite) TestIndexLiquidation() {
|
||||
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
|
||||
},
|
||||
tc.args.ltvIndexCount, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
@ -549,15 +535,6 @@ func (suite *KeeperTestSuite) TestFullIndexLiquidation() {
|
||||
// Hard module genesis state
|
||||
hardGS := 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",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit
|
||||
@ -577,7 +554,7 @@ func (suite *KeeperTestSuite) TestFullIndexLiquidation() {
|
||||
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
|
||||
},
|
||||
tc.args.ltvIndexCount, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
@ -1181,20 +1158,6 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
||||
// Hard module genesis state
|
||||
hardGS := 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, "usdc", 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, "usdt", 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, "dai", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "btc", 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",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.9")), // Borrow Limit
|
||||
@ -1254,7 +1217,7 @@ func (suite *KeeperTestSuite) TestKeeperLiquidation() {
|
||||
tc.args.keeperRewardPercent), // Keeper Reward Percent
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
@ -18,28 +18,6 @@ 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 {
|
||||
if lps.DepositDenom == denom {
|
||||
return lps, true
|
||||
}
|
||||
}
|
||||
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 {
|
||||
if dds.DistributionSchedule.DepositDenom == denom {
|
||||
return dds, true
|
||||
}
|
||||
}
|
||||
return types.DelegatorDistributionSchedule{}, false
|
||||
}
|
||||
|
||||
// GetMoneyMarketParam returns the corresponding Money Market param for a specific denom
|
||||
func (k Keeper) GetMoneyMarketParam(ctx sdk.Context, denom string) (types.MoneyMarket, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
|
@ -24,8 +24,6 @@ func NewQuerier(k Keeper) sdk.Querier {
|
||||
return queryGetDeposits(ctx, req, k)
|
||||
case types.QueryGetTotalDeposited:
|
||||
return queryGetTotalDeposited(ctx, req, k)
|
||||
case types.QueryGetClaims:
|
||||
return queryGetClaims(ctx, req, k)
|
||||
case types.QueryGetBorrows:
|
||||
return queryGetBorrows(ctx, req, k)
|
||||
case types.QueryGetTotalBorrowed:
|
||||
@ -149,111 +147,6 @@ func queryGetDeposits(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte,
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
|
||||
|
||||
var params types.QueryClaimParams
|
||||
err := types.ModuleCdc.UnmarshalJSON(req.Data, ¶ms)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error())
|
||||
}
|
||||
depositDenom := len(params.Denom) > 0
|
||||
owner := len(params.Owner) > 0
|
||||
claimType := len(params.ClaimType) > 0
|
||||
|
||||
var claims []types.Claim
|
||||
switch {
|
||||
case depositDenom && owner && claimType:
|
||||
claim, found := k.GetClaim(ctx, params.Owner, params.Denom, params.ClaimType)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
case depositDenom && owner:
|
||||
for _, dt := range types.ClaimTypesClaimQuery {
|
||||
claim, found := k.GetClaim(ctx, params.Owner, params.Denom, dt)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
}
|
||||
case depositDenom && claimType:
|
||||
k.IterateClaimsByTypeAndDenom(ctx, params.ClaimType, params.Denom, func(claim types.Claim) (stop bool) {
|
||||
claims = append(claims, claim)
|
||||
return false
|
||||
})
|
||||
case owner && claimType:
|
||||
hardParams := k.GetParams(ctx)
|
||||
switch {
|
||||
case params.ClaimType == types.LP:
|
||||
for _, lps := range hardParams.LiquidityProviderSchedules {
|
||||
claim, found := k.GetClaim(ctx, params.Owner, lps.DepositDenom, params.ClaimType)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
}
|
||||
case params.ClaimType == types.Stake:
|
||||
for _, dss := range hardParams.DelegatorDistributionSchedules {
|
||||
claim, found := k.GetClaim(ctx, params.Owner, dss.DistributionSchedule.DepositDenom, params.ClaimType)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
}
|
||||
}
|
||||
case depositDenom:
|
||||
for _, dt := range types.ClaimTypesClaimQuery {
|
||||
k.IterateClaimsByTypeAndDenom(ctx, dt, params.Denom, func(claim types.Claim) (stop bool) {
|
||||
claims = append(claims, claim)
|
||||
return false
|
||||
})
|
||||
}
|
||||
case owner:
|
||||
hardParams := k.GetParams(ctx)
|
||||
for _, lps := range hardParams.LiquidityProviderSchedules {
|
||||
claim, found := k.GetClaim(ctx, params.Owner, lps.DepositDenom, types.LP)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
}
|
||||
for _, dds := range hardParams.DelegatorDistributionSchedules {
|
||||
claim, found := k.GetClaim(ctx, params.Owner, dds.DistributionSchedule.DepositDenom, types.Stake)
|
||||
if found {
|
||||
claims = append(claims, claim)
|
||||
}
|
||||
}
|
||||
case claimType:
|
||||
hardParams := k.GetParams(ctx)
|
||||
for _, lps := range hardParams.LiquidityProviderSchedules {
|
||||
k.IterateClaimsByTypeAndDenom(ctx, params.ClaimType, lps.DepositDenom, func(claim types.Claim) (stop bool) {
|
||||
claims = append(claims, claim)
|
||||
return false
|
||||
})
|
||||
}
|
||||
for _, dds := range hardParams.DelegatorDistributionSchedules {
|
||||
k.IterateClaimsByTypeAndDenom(ctx, params.ClaimType, dds.DistributionSchedule.DepositDenom, func(claim types.Claim) (stop bool) {
|
||||
claims = append(claims, claim)
|
||||
return false
|
||||
})
|
||||
}
|
||||
default:
|
||||
k.IterateClaims(ctx, func(claim types.Claim) (stop bool) {
|
||||
claims = append(claims, claim)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
start, end := client.Paginate(len(claims), params.Page, params.Limit, 100)
|
||||
if start < 0 || end < 0 {
|
||||
claims = []types.Claim{}
|
||||
} else {
|
||||
claims = claims[start:end]
|
||||
}
|
||||
|
||||
bz, err := codec.MarshalJSONIndent(types.ModuleCdc, claims)
|
||||
if err != nil {
|
||||
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||
}
|
||||
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
func queryGetBorrows(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, error) {
|
||||
|
||||
var params types.QueryBorrowsParams
|
||||
|
@ -15,6 +15,14 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
|
||||
return err
|
||||
}
|
||||
|
||||
// Check borrow exists here to avoid duplicating store read in ValidateRepay
|
||||
borrow, found := k.GetBorrow(ctx, sender)
|
||||
if !found {
|
||||
return types.ErrBorrowNotFound
|
||||
}
|
||||
// Call incentive hook
|
||||
k.BeforeBorrowModified(ctx, borrow)
|
||||
|
||||
// Sync borrow interest so loan is up-to-date
|
||||
k.SyncBorrowInterest(ctx, sender)
|
||||
|
||||
@ -24,12 +32,6 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
|
||||
return err
|
||||
}
|
||||
|
||||
// Check borrow exists here to avoid duplicating store read in ValidateRepay
|
||||
borrow, found := k.GetBorrow(ctx, sender)
|
||||
if !found {
|
||||
return types.ErrBorrowNotFound
|
||||
}
|
||||
|
||||
payment, err := k.CalculatePaymentAmount(borrow.Amount, coins)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -59,6 +61,11 @@ func (k Keeper) Repay(ctx sdk.Context, sender sdk.AccAddress, coins sdk.Coins) e
|
||||
// Update total borrowed amount
|
||||
k.DecrementBorrowedCoins(ctx, payment)
|
||||
|
||||
// Call incentive hook
|
||||
if !borrow.Amount.Empty() {
|
||||
k.AfterBorrowModified(ctx, borrow)
|
||||
}
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeHardRepay,
|
||||
|
@ -138,15 +138,6 @@ func (suite *KeeperTestSuite) TestRepay() {
|
||||
// Hard module genesis state
|
||||
hardGS := 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",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*USDX_CF), sdk.MustNewDecFromStr("1")), // Borrow Limit
|
||||
@ -166,7 +157,7 @@ func (suite *KeeperTestSuite) TestRepay() {
|
||||
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
@ -1,175 +0,0 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
// ApplyDepositRewards iterates over lp and gov deposits and updates the amount of rewards for each depositor
|
||||
func (k Keeper) ApplyDepositRewards(ctx sdk.Context) {
|
||||
previousBlockTime, found := k.GetPreviousBlockTime(ctx)
|
||||
if !found {
|
||||
previousBlockTime = ctx.BlockTime()
|
||||
k.SetPreviousBlockTime(ctx, previousBlockTime)
|
||||
return
|
||||
}
|
||||
params := k.GetParams(ctx)
|
||||
if !params.Active {
|
||||
return
|
||||
}
|
||||
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousBlockTime.Unix())
|
||||
|
||||
for _, lps := range params.LiquidityProviderSchedules {
|
||||
if !lps.Active {
|
||||
continue
|
||||
}
|
||||
if lps.End.Before(ctx.BlockTime()) {
|
||||
continue
|
||||
}
|
||||
if lps.Start.After(ctx.BlockTime()) {
|
||||
continue
|
||||
}
|
||||
totalDeposited := k.GetTotalDeposited(ctx, lps.DepositDenom)
|
||||
if totalDeposited.IsZero() {
|
||||
continue
|
||||
}
|
||||
rewardsToDistribute := lps.RewardsPerSecond.Amount.Mul(timeElapsed)
|
||||
if rewardsToDistribute.IsZero() {
|
||||
continue
|
||||
}
|
||||
rewardsDistributed := sdk.ZeroInt()
|
||||
k.IterateDeposits(ctx, func(dep types.Deposit) (stop bool) {
|
||||
rewardsShare := sdk.NewDecFromInt(dep.Amount.AmountOf(lps.DepositDenom)).Quo(sdk.NewDecFromInt(totalDeposited))
|
||||
if rewardsShare.IsZero() {
|
||||
return false
|
||||
}
|
||||
rewardsEarned := rewardsShare.Mul(sdk.NewDecFromInt(rewardsToDistribute)).RoundInt()
|
||||
if rewardsEarned.IsZero() {
|
||||
return false
|
||||
}
|
||||
k.AddToClaim(ctx, dep.Depositor, lps.DepositDenom, types.LP, sdk.NewCoin(lps.RewardsPerSecond.Denom, rewardsEarned))
|
||||
rewardsDistributed = rewardsDistributed.Add(rewardsEarned)
|
||||
return false
|
||||
})
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeHardLPDistribution,
|
||||
sdk.NewAttribute(types.AttributeKeyBlockHeight, fmt.Sprintf("%d", ctx.BlockHeight())),
|
||||
sdk.NewAttribute(types.AttributeKeyRewardsDistribution, rewardsDistributed.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyDepositDenom, lps.DepositDenom),
|
||||
),
|
||||
)
|
||||
}
|
||||
k.SetPreviousBlockTime(ctx, ctx.BlockTime())
|
||||
}
|
||||
|
||||
// ShouldDistributeValidatorRewards returns true if enough time has elapsed such that rewards should be distributed to delegators
|
||||
func (k Keeper) ShouldDistributeValidatorRewards(ctx sdk.Context, denom string) bool {
|
||||
previousDistributionTime, found := k.GetPreviousDelegatorDistribution(ctx, denom)
|
||||
if !found {
|
||||
k.SetPreviousDelegationDistribution(ctx, ctx.BlockTime(), denom)
|
||||
return false
|
||||
}
|
||||
params := k.GetParams(ctx)
|
||||
if !params.Active {
|
||||
return false
|
||||
}
|
||||
for _, dds := range params.DelegatorDistributionSchedules {
|
||||
if denom != dds.DistributionSchedule.DepositDenom {
|
||||
continue
|
||||
}
|
||||
if dds.DistributionSchedule.End.Before(ctx.BlockTime()) {
|
||||
continue
|
||||
}
|
||||
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousDistributionTime.Unix())
|
||||
if timeElapsed.GTE(sdk.NewInt(int64(dds.DistributionFrequency.Seconds()))) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ApplyDelegationRewards iterates over each delegation object in the staking store and applies rewards according to the input delegation distribution schedule
|
||||
func (k Keeper) ApplyDelegationRewards(ctx sdk.Context, denom string) {
|
||||
dds, found := k.GetDelegatorSchedule(ctx, denom)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
if !dds.DistributionSchedule.Active {
|
||||
return
|
||||
}
|
||||
if dds.DistributionSchedule.Start.After(ctx.BlockTime()) {
|
||||
return
|
||||
}
|
||||
bondMacc := k.stakingKeeper.GetBondedPool(ctx)
|
||||
bondedCoinAmount := bondMacc.GetCoins().AmountOf(dds.DistributionSchedule.DepositDenom)
|
||||
if bondedCoinAmount.IsZero() {
|
||||
return
|
||||
}
|
||||
previousDistributionTime, found := k.GetPreviousDelegatorDistribution(ctx, dds.DistributionSchedule.DepositDenom)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
timeElapsed := sdk.NewInt(ctx.BlockTime().Unix() - previousDistributionTime.Unix())
|
||||
rewardsToDistribute := dds.DistributionSchedule.RewardsPerSecond.Amount.Mul(timeElapsed)
|
||||
|
||||
// create a map that has each validator address (sdk.ValAddress) as a key and the coversion factor for going from delegator shares to tokens for delegations to that validator.
|
||||
// If a validator has never been slashed, the conversion factor will be 1.0, if they have been, it will be < 1.0
|
||||
sharesToTokens := make(map[string]sdk.Dec)
|
||||
k.stakingKeeper.IterateValidators(ctx, func(index int64, validator stakingexported.ValidatorI) (stop bool) {
|
||||
if validator.GetTokens().IsZero() {
|
||||
return false
|
||||
}
|
||||
// don't include a validator if it's unbonded or unbonding- ie delegators don't accumulate rewards when delegated to an unbonded/slashed validator
|
||||
if validator.GetStatus() != sdk.Bonded {
|
||||
return false
|
||||
}
|
||||
sharesToTokens[validator.GetOperator().String()] = sdk.NewDecFromInt(validator.GetTokens()).Quo(validator.GetDelegatorShares())
|
||||
return false
|
||||
})
|
||||
|
||||
rewardsDistributed := sdk.ZeroInt()
|
||||
|
||||
k.stakingKeeper.IterateAllDelegations(ctx, func(delegation stakingtypes.Delegation) (stop bool) {
|
||||
conversionFactor, ok := sharesToTokens[delegation.ValidatorAddress.String()]
|
||||
if ok {
|
||||
delegationTokens := conversionFactor.Mul(delegation.Shares)
|
||||
delegationShare := delegationTokens.Quo(sdk.NewDecFromInt(bondedCoinAmount))
|
||||
rewardsEarned := delegationShare.Mul(sdk.NewDecFromInt(rewardsToDistribute)).RoundInt()
|
||||
if rewardsEarned.IsZero() {
|
||||
return false
|
||||
}
|
||||
k.AddToClaim(
|
||||
ctx, delegation.DelegatorAddress, dds.DistributionSchedule.DepositDenom,
|
||||
types.Stake, sdk.NewCoin(dds.DistributionSchedule.RewardsPerSecond.Denom, rewardsEarned))
|
||||
rewardsDistributed = rewardsDistributed.Add(rewardsEarned)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeHardDelegatorDistribution,
|
||||
sdk.NewAttribute(types.AttributeKeyBlockHeight, fmt.Sprintf("%d", ctx.BlockHeight())),
|
||||
sdk.NewAttribute(types.AttributeKeyRewardsDistribution, rewardsDistributed.String()),
|
||||
sdk.NewAttribute(types.AttributeKeyDepositDenom, denom),
|
||||
),
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// AddToClaim adds the input amount to an existing claim or creates a new one
|
||||
func (k Keeper) AddToClaim(ctx sdk.Context, owner sdk.AccAddress, depositDenom string, claimType types.ClaimType, amountToAdd sdk.Coin) {
|
||||
claim, found := k.GetClaim(ctx, owner, depositDenom, claimType)
|
||||
if !found {
|
||||
claim = types.NewClaim(owner, depositDenom, amountToAdd, claimType)
|
||||
} else {
|
||||
claim.Amount = claim.Amount.Add(amountToAdd)
|
||||
}
|
||||
k.SetClaim(ctx, claim)
|
||||
}
|
@ -1,476 +0,0 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/stretchr/testify/suite"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/hard/keeper"
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestApplyDepositRewards() {
|
||||
type args struct {
|
||||
depositor sdk.AccAddress
|
||||
denom string
|
||||
depositAmount sdk.Coins
|
||||
totalDeposits sdk.Coin
|
||||
rewardRate sdk.Coin
|
||||
claimType types.ClaimType
|
||||
previousBlockTime time.Time
|
||||
blockTime time.Time
|
||||
expectedClaimBalance sdk.Coin
|
||||
}
|
||||
type errArgs struct {
|
||||
expectPanic bool
|
||||
contains string
|
||||
}
|
||||
type testCase struct {
|
||||
name string
|
||||
args args
|
||||
errArgs errArgs
|
||||
}
|
||||
testCases := []testCase{
|
||||
{
|
||||
name: "distribute rewards",
|
||||
args: args{
|
||||
depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))),
|
||||
denom: "bnb",
|
||||
rewardRate: c("hard", 500),
|
||||
depositAmount: cs(c("bnb", 100)),
|
||||
totalDeposits: c("bnb", 1000),
|
||||
claimType: types.LP,
|
||||
previousBlockTime: time.Date(2020, 11, 1, 13, 59, 50, 0, time.UTC),
|
||||
blockTime: time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC),
|
||||
expectedClaimBalance: c("hard", 500),
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPanic: false,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
// 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")
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), tc.args.rewardRate, 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.Large, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2025, 10, 8, 14, 0, 0, 0, time.UTC), tc.args.rewardRate, 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.Large, 24, sdk.OneDec())}),
|
||||
time.Hour*24,
|
||||
),
|
||||
},
|
||||
types.MoneyMarkets{
|
||||
types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), tc.args.previousBlockTime, types.DefaultDistributionTimes)
|
||||
tApp.InitializeFromGenesisStates(app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(hardGS)})
|
||||
supplyKeeper := tApp.GetSupplyKeeper()
|
||||
supplyKeeper.MintCoins(ctx, types.ModuleAccountName, cs(tc.args.totalDeposits))
|
||||
keeper := tApp.GetHardKeeper()
|
||||
deposit := types.NewDeposit(tc.args.depositor, tc.args.depositAmount, types.SupplyInterestFactors{})
|
||||
keeper.SetDeposit(ctx, deposit)
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.keeper = keeper
|
||||
|
||||
if tc.errArgs.expectPanic {
|
||||
suite.Require().Panics(func() { suite.keeper.ApplyDepositRewards(suite.ctx) })
|
||||
} else {
|
||||
suite.Require().NotPanics(func() { suite.keeper.ApplyDepositRewards(suite.ctx) })
|
||||
claim, f := suite.keeper.GetClaim(suite.ctx, tc.args.depositor, tc.args.denom, tc.args.claimType)
|
||||
suite.Require().True(f)
|
||||
suite.Require().Equal(tc.args.expectedClaimBalance, claim.Amount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyDelegatorRewardsTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(DelegatorRewardsTestSuite))
|
||||
}
|
||||
|
||||
type DelegatorRewardsTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
validatorAddrs []sdk.ValAddress
|
||||
delegatorAddrs []sdk.AccAddress
|
||||
|
||||
keeper keeper.Keeper
|
||||
stakingKeeper staking.Keeper
|
||||
app app.TestApp
|
||||
rewardRate int64
|
||||
}
|
||||
|
||||
// The default state used by each test
|
||||
func (suite *DelegatorRewardsTestSuite) SetupTest() {
|
||||
config := sdk.GetConfig()
|
||||
app.SetBech32AddressPrefixes(config)
|
||||
|
||||
_, allAddrs := app.GeneratePrivKeyAddressPairs(10)
|
||||
suite.delegatorAddrs = allAddrs[:5]
|
||||
for _, a := range allAddrs[5:] {
|
||||
suite.validatorAddrs = append(suite.validatorAddrs, sdk.ValAddress(a))
|
||||
}
|
||||
|
||||
suite.app = app.NewTestApp()
|
||||
|
||||
suite.rewardRate = 500
|
||||
|
||||
suite.app.InitializeFromGenesisStates(
|
||||
equalCoinsAuthGenState(allAddrs, cs(c("ukava", 5_000_000))),
|
||||
stakingGenesisState(),
|
||||
hardGenesisState(c("hard", suite.rewardRate)),
|
||||
)
|
||||
|
||||
suite.keeper = suite.app.GetHardKeeper()
|
||||
suite.stakingKeeper = suite.app.GetStakingKeeper()
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) TestSlash() {
|
||||
|
||||
blockTime := time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC)
|
||||
ctx := suite.app.NewContext(true, abci.Header{Height: 1, Time: blockTime})
|
||||
const rewardDuration = 5
|
||||
suite.keeper.SetPreviousDelegationDistribution(ctx, blockTime.Add(-1*rewardDuration*time.Second), "ukava")
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[0], c("ukava", 5_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.slashValidator(ctx, suite.validatorAddrs[0], "0.05"),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
// Run function under test
|
||||
suite.keeper.ApplyDelegationRewards(ctx, "ukava")
|
||||
|
||||
// Check claim amounts
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, sdk.AccAddress(suite.validatorAddrs[0]), c("hard", suite.rewardRate*rewardDuration)),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) TestUndelegation() {
|
||||
|
||||
blockTime := time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC)
|
||||
ctx := suite.app.NewContext(true, abci.Header{Height: 1, Time: blockTime})
|
||||
const rewardDuration = 5
|
||||
suite.keeper.SetPreviousDelegationDistribution(ctx, blockTime.Add(-1*rewardDuration*time.Second), "ukava")
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgUndelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
// Run function under test
|
||||
suite.keeper.ApplyDelegationRewards(ctx, "ukava")
|
||||
|
||||
// Check claim amounts
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, sdk.AccAddress(suite.validatorAddrs[0]), c("hard", suite.rewardRate*rewardDuration)),
|
||||
)
|
||||
suite.Require().False(
|
||||
suite.kavaClaimExists(ctx, suite.delegatorAddrs[0]),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) TestUnevenNumberDelegations() {
|
||||
|
||||
// Setup a context
|
||||
blockTime := time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC)
|
||||
ctx := suite.app.NewContext(true, abci.Header{Height: 1, Time: blockTime})
|
||||
const rewardDuration = 5
|
||||
suite.keeper.SetPreviousDelegationDistribution(ctx, blockTime.Add(-1*rewardDuration*time.Second), "ukava")
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[1], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
// Run function under test
|
||||
suite.keeper.ApplyDelegationRewards(ctx, "ukava")
|
||||
|
||||
// Check claim amounts
|
||||
expectedReward := suite.rewardRate * rewardDuration / 3 // floor division
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, sdk.AccAddress(suite.validatorAddrs[0]), c("hard", expectedReward)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, suite.delegatorAddrs[0], c("hard", expectedReward)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, suite.delegatorAddrs[1], c("hard", expectedReward)),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) TestSlashWithUndelegated() {
|
||||
|
||||
// Setup a context
|
||||
blockTime := time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC)
|
||||
ctx := suite.app.NewContext(true, abci.Header{Height: 1, Time: blockTime})
|
||||
const rewardDuration = 5
|
||||
suite.keeper.SetPreviousDelegationDistribution(ctx, blockTime.Add(-1*rewardDuration*time.Second), "ukava")
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[1], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgUndelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.slashValidator(ctx, suite.validatorAddrs[0], "0.05"),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
// Run function under test
|
||||
suite.keeper.ApplyDelegationRewards(ctx, "ukava")
|
||||
|
||||
// Check claim amounts
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, sdk.AccAddress(suite.validatorAddrs[0]), c("hard", suite.rewardRate*rewardDuration/2)),
|
||||
)
|
||||
suite.Require().False(
|
||||
suite.kavaClaimExists(ctx, suite.delegatorAddrs[0]),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, suite.delegatorAddrs[1], c("hard", suite.rewardRate*rewardDuration/2)),
|
||||
)
|
||||
}
|
||||
func (suite *DelegatorRewardsTestSuite) TestUnbondingValidator() {
|
||||
|
||||
// Setup a context
|
||||
blockTime := time.Date(2020, 11, 1, 14, 0, 0, 0, time.UTC)
|
||||
ctx := suite.app.NewContext(true, abci.Header{Height: 1, Time: blockTime})
|
||||
const rewardDuration = 5
|
||||
suite.keeper.SetPreviousDelegationDistribution(ctx, blockTime.Add(-1*rewardDuration*time.Second), "ukava")
|
||||
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgCreateValidator(ctx, suite.validatorAddrs[1], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[0], suite.validatorAddrs[0], c("ukava", 1_000_000)),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.deliverMsgDelegate(ctx, suite.delegatorAddrs[1], suite.validatorAddrs[1], c("ukava", 1_000_000)),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
suite.Require().NoError(
|
||||
// jail the validator to put it into an unbonding state
|
||||
suite.jailValidator(ctx, suite.validatorAddrs[0]),
|
||||
)
|
||||
staking.EndBlocker(ctx, suite.stakingKeeper)
|
||||
|
||||
// Run function under test
|
||||
suite.keeper.ApplyDelegationRewards(ctx, "ukava")
|
||||
|
||||
// Check claim amounts
|
||||
suite.Require().False(
|
||||
// validator 0 will be unbonding and should not receive rewards
|
||||
suite.kavaClaimExists(ctx, sdk.AccAddress(suite.validatorAddrs[0])),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, sdk.AccAddress(suite.validatorAddrs[1]), c("hard", suite.rewardRate*rewardDuration/2)),
|
||||
)
|
||||
suite.Require().False(
|
||||
// delegations to unbonding validators and should not receive rewards
|
||||
suite.kavaClaimExists(ctx, suite.delegatorAddrs[0]),
|
||||
)
|
||||
suite.Require().NoError(
|
||||
suite.verifyKavaClaimAmount(ctx, suite.delegatorAddrs[1], c("hard", suite.rewardRate*rewardDuration/2)),
|
||||
)
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error {
|
||||
msg := staking.NewMsgCreateValidator(
|
||||
address,
|
||||
ed25519.GenPrivKey().PubKey(),
|
||||
selfDelegation,
|
||||
staking.Description{},
|
||||
staking.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
|
||||
sdk.NewInt(1_000_000),
|
||||
)
|
||||
handleStakingMsg := staking.NewHandler(suite.stakingKeeper)
|
||||
_, err := handleStakingMsg(ctx, msg)
|
||||
return err
|
||||
}
|
||||
func (suite *DelegatorRewardsTestSuite) deliverMsgDelegate(ctx sdk.Context, delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error {
|
||||
msg := staking.NewMsgDelegate(
|
||||
delegator,
|
||||
validator,
|
||||
amount,
|
||||
)
|
||||
handleStakingMsg := staking.NewHandler(suite.stakingKeeper)
|
||||
_, err := handleStakingMsg(ctx, msg)
|
||||
return err
|
||||
}
|
||||
func (suite *DelegatorRewardsTestSuite) deliverMsgUndelegate(ctx sdk.Context, delegator sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) error {
|
||||
msg := staking.NewMsgUndelegate(
|
||||
delegator,
|
||||
validator,
|
||||
amount,
|
||||
)
|
||||
handleStakingMsg := staking.NewHandler(suite.stakingKeeper)
|
||||
_, err := handleStakingMsg(ctx, msg)
|
||||
return err
|
||||
}
|
||||
|
||||
func (suite *DelegatorRewardsTestSuite) slashValidator(ctx sdk.Context, validator sdk.ValAddress, slashPercent string) error {
|
||||
// Assume slashable offence occurred at block 1. Note this might cause problems if tests are running at a block height higher than the unbonding period (default 3 weeks)
|
||||
const infractionHeight int64 = 1
|
||||
|
||||
val, found := suite.stakingKeeper.GetValidator(ctx, validator)
|
||||
if !found {
|
||||
return fmt.Errorf("can't find validator in state")
|
||||
}
|
||||
suite.stakingKeeper.Slash(
|
||||
ctx,
|
||||
sdk.GetConsAddress(val.ConsPubKey),
|
||||
infractionHeight,
|
||||
val.GetConsensusPower(),
|
||||
sdk.MustNewDecFromStr(slashPercent),
|
||||
)
|
||||
return nil
|
||||
}
|
||||
func (suite *DelegatorRewardsTestSuite) jailValidator(ctx sdk.Context, validator sdk.ValAddress) error {
|
||||
val, found := suite.stakingKeeper.GetValidator(ctx, validator)
|
||||
if !found {
|
||||
return fmt.Errorf("can't find validator in state")
|
||||
}
|
||||
suite.stakingKeeper.Jail(ctx, sdk.GetConsAddress(val.ConsPubKey))
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyKavaClaimAmount looks up a ukava claim and checks the claim amount is equal to an expected value
|
||||
func (suite *DelegatorRewardsTestSuite) verifyKavaClaimAmount(ctx sdk.Context, owner sdk.AccAddress, expectedAmount sdk.Coin) error {
|
||||
claim, found := suite.keeper.GetClaim(ctx, owner, "ukava", types.Stake)
|
||||
if !found {
|
||||
return fmt.Errorf("could not find claim")
|
||||
}
|
||||
if !expectedAmount.IsEqual(claim.Amount) {
|
||||
return fmt.Errorf("expected claim amount (%s) != actual claim amount (%s)", expectedAmount, claim.Amount)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// kavaClaimExists checks the store for a ukava claim
|
||||
func (suite *DelegatorRewardsTestSuite) kavaClaimExists(ctx sdk.Context, owner sdk.AccAddress) bool {
|
||||
_, found := suite.keeper.GetClaim(ctx, owner, "ukava", types.Stake)
|
||||
return found
|
||||
}
|
||||
|
||||
func hardGenesisState(rewardRate sdk.Coin) app.GenesisState {
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
genState := types.NewGenesisState(
|
||||
types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(
|
||||
true,
|
||||
"bnb",
|
||||
time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC),
|
||||
time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC),
|
||||
rewardRate,
|
||||
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.Large, 24, sdk.OneDec()),
|
||||
},
|
||||
),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{
|
||||
types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(
|
||||
true,
|
||||
"ukava",
|
||||
time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC),
|
||||
time.Date(2025, 10, 8, 14, 0, 0, 0, time.UTC),
|
||||
rewardRate,
|
||||
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.Large, 24, sdk.OneDec()),
|
||||
},
|
||||
),
|
||||
time.Hour*24,
|
||||
),
|
||||
},
|
||||
types.MoneyMarkets{
|
||||
types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
),
|
||||
types.DefaultPreviousBlockTime,
|
||||
types.DefaultDistributionTimes,
|
||||
)
|
||||
return app.GenesisState{
|
||||
types.ModuleName: types.ModuleCdc.MustMarshalJSON(genState),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func stakingGenesisState() app.GenesisState {
|
||||
genState := staking.DefaultGenesisState()
|
||||
genState.Params.BondDenom = "ukava"
|
||||
return app.GenesisState{
|
||||
staking.ModuleName: staking.ModuleCdc.MustMarshalJSON(genState),
|
||||
}
|
||||
}
|
||||
|
||||
// equalCoinsAuthGenState returns an auth genesis state with the same coins for each account
|
||||
func equalCoinsAuthGenState(addresses []sdk.AccAddress, coins sdk.Coins) app.GenesisState {
|
||||
coinsList := []sdk.Coins{}
|
||||
for range addresses {
|
||||
coinsList = append(coinsList, coins)
|
||||
}
|
||||
return app.NewAuthGenState(addresses, coinsList)
|
||||
}
|
@ -282,20 +282,12 @@ func (suite *KeeperTestSuite) TestSendTimeLockedCoinsToAccount() {
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Large, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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.Large, 24, sdk.OneDec())}),
|
||||
time.Hour*24,
|
||||
),
|
||||
},
|
||||
types.MoneyMarkets{
|
||||
types.NewMoneyMarket("usdx", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
tApp.InitializeFromGenesisStates(authGS, app.GenesisState{types.ModuleName: types.ModuleCdc.MustMarshalJSON(hardGS)})
|
||||
if tc.args.accArgs.vestingAccountBefore {
|
||||
ak := tApp.GetAccountKeeper()
|
||||
|
@ -15,13 +15,15 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
return err
|
||||
}
|
||||
|
||||
k.SyncBorrowInterest(ctx, depositor)
|
||||
k.SyncSupplyInterest(ctx, depositor)
|
||||
|
||||
deposit, found := k.GetDeposit(ctx, depositor)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrDepositNotFound, "no deposit found for %s", depositor)
|
||||
}
|
||||
// Call incentive hook
|
||||
k.BeforeDepositModified(ctx, deposit)
|
||||
|
||||
k.SyncBorrowInterest(ctx, depositor)
|
||||
k.SyncSupplyInterest(ctx, depositor)
|
||||
|
||||
amount, err := k.CalculateWithdrawAmount(deposit.Amount, coins)
|
||||
if err != nil {
|
||||
@ -47,19 +49,7 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
return err
|
||||
}
|
||||
|
||||
if deposit.Amount.IsEqual(amount) {
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeDeleteHardDeposit,
|
||||
sdk.NewAttribute(types.AttributeKeyDepositor, depositor.String()),
|
||||
),
|
||||
)
|
||||
k.DeleteDeposit(ctx, deposit)
|
||||
return nil
|
||||
}
|
||||
|
||||
deposit.Amount = deposit.Amount.Sub(amount)
|
||||
|
||||
newLtv, err := k.CalculateLtv(ctx, deposit, borrow)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -69,6 +59,9 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
// Update total supplied amount
|
||||
k.DecrementBorrowedCoins(ctx, amount)
|
||||
|
||||
// Call incentive hook
|
||||
k.AfterDepositModified(ctx, deposit)
|
||||
|
||||
ctx.EventManager().EmitEvent(
|
||||
sdk.NewEvent(
|
||||
types.EventTypeHardWithdrawal,
|
||||
@ -76,7 +69,6 @@ func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Co
|
||||
sdk.NewAttribute(types.AttributeKeyDepositor, depositor.String()),
|
||||
),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -125,21 +125,13 @@ func (suite *KeeperTestSuite) TestWithdraw() {
|
||||
loanToValue := sdk.MustNewDecFromStr("0.6")
|
||||
hardGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("ukava", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
types.NewMoneyMarket("bnb", types.NewBorrowLimit(false, sdk.NewDec(1000000000000000), loanToValue), "bnb:usd", sdk.NewInt(100000000), sdk.NewInt(BNB_CF*1000), types.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
@ -273,15 +265,6 @@ func (suite *KeeperTestSuite) TestLtvWithdraw() {
|
||||
// Harvest module genesis state
|
||||
harvestGS := types.NewGenesisState(types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "ukava", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
types.NewDistributionSchedule(true, "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.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("ukava",
|
||||
types.NewBorrowLimit(false, sdk.NewDec(100000000*KAVA_CF), sdk.MustNewDecFromStr("0.8")), // Borrow Limit
|
||||
@ -301,7 +284,7 @@ func (suite *KeeperTestSuite) TestLtvWithdraw() {
|
||||
sdk.MustNewDecFromStr("0.05")), // Keeper Reward Percent
|
||||
},
|
||||
0, // LTV counter
|
||||
), types.DefaultPreviousBlockTime, types.DefaultDistributionTimes)
|
||||
), types.DefaultPreviousBlockTime)
|
||||
|
||||
// Pricefeed module genesis state
|
||||
pricefeedGS := pricefeed.GenesisState{
|
||||
|
@ -15,21 +15,16 @@ import (
|
||||
// DecodeStore unmarshals the KVPair's Value to the corresponding hard type
|
||||
func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
|
||||
switch {
|
||||
case bytes.Equal(kvA.Key[:1], types.PreviousBlockTimeKey), bytes.Equal(kvA.Key[:1], types.PreviousDelegationDistributionKey):
|
||||
var timeA, timeB time.Time
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
case bytes.Equal(kvA.Key[:1], types.DepositsKeyPrefix):
|
||||
var depA, depB types.Deposit
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &depA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &depB)
|
||||
return fmt.Sprintf("%s\n%s", depA, depB)
|
||||
case bytes.Equal(kvA.Key[:1], types.ClaimsKeyPrefix):
|
||||
var claimA, claimB types.Claim
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &claimA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &claimB)
|
||||
return fmt.Sprintf("%s\n%s", claimA, claimB)
|
||||
case bytes.Equal(kvA.Key[:1], types.PreviousBlockTimeKey):
|
||||
var timeA, timeB time.Time
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1]))
|
||||
}
|
||||
|
@ -27,16 +27,11 @@ func TestDecodeDistributionStore(t *testing.T) {
|
||||
cdc := makeTestCodec()
|
||||
|
||||
prevBlockTime := time.Now().UTC()
|
||||
deposit := types.NewDeposit(sdk.AccAddress("test"),
|
||||
sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1))),
|
||||
types.SupplyInterestFactors{types.NewSupplyInterestFactor("bnb", sdk.OneDec())})
|
||||
claim := types.NewClaim(sdk.AccAddress("test"), "bnb", sdk.NewCoin("hard", sdk.NewInt(100)), "stake")
|
||||
deposit := types.NewDeposit(sdk.AccAddress("test"), sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1))), types.SupplyInterestFactors{})
|
||||
|
||||
kvPairs := kv.Pairs{
|
||||
kv.Pair{Key: []byte(types.PreviousBlockTimeKey), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
kv.Pair{Key: []byte(types.PreviousDelegationDistributionKey), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
kv.Pair{Key: []byte(types.DepositsKeyPrefix), Value: cdc.MustMarshalBinaryBare(deposit)},
|
||||
kv.Pair{Key: []byte(types.ClaimsKeyPrefix), Value: cdc.MustMarshalBinaryBare(claim)},
|
||||
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},
|
||||
}
|
||||
|
||||
@ -45,9 +40,7 @@ func TestDecodeDistributionStore(t *testing.T) {
|
||||
expectedLog string
|
||||
}{
|
||||
{"PreviousBlockTime", fmt.Sprintf("%s\n%s", prevBlockTime, prevBlockTime)},
|
||||
{"PreviousDistributionTime", fmt.Sprintf("%s\n%s", prevBlockTime, prevBlockTime)},
|
||||
{"Deposit", fmt.Sprintf("%s\n%s", deposit, deposit)},
|
||||
{"Claim", fmt.Sprintf("%s\n%s", claim, claim)},
|
||||
{"other", ""},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
|
@ -1,23 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// Claim defines an amount of coins that the owner can claim
|
||||
type Claim struct {
|
||||
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
|
||||
DepositDenom string `json:"deposit_denom" yaml:"deposit_denom"`
|
||||
Amount sdk.Coin `json:"amount" yaml:"amount"`
|
||||
Type ClaimType `json:"claim_type" yaml:"claim_type"`
|
||||
}
|
||||
|
||||
// NewClaim returns a new claim
|
||||
func NewClaim(owner sdk.AccAddress, denom string, amount sdk.Coin, claimType ClaimType) Claim {
|
||||
return Claim{
|
||||
Owner: owner,
|
||||
DepositDenom: denom,
|
||||
Amount: amount,
|
||||
Type: claimType,
|
||||
}
|
||||
}
|
@ -20,5 +20,4 @@ func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterConcrete(MsgBorrow{}, "hard/MsgBorrow", nil)
|
||||
cdc.RegisterConcrete(MsgLiquidate{}, "hard/MsgLiquidate", nil)
|
||||
cdc.RegisterConcrete(MsgRepay{}, "hard/MsgRepay", nil)
|
||||
cdc.RegisterConcrete(DistributionSchedule{}, "hard/DistributionSchedule", nil)
|
||||
}
|
||||
|
@ -45,3 +45,13 @@ type PricefeedKeeper interface {
|
||||
type AuctionKeeper interface {
|
||||
StartCollateralAuction(ctx sdk.Context, seller string, lot sdk.Coin, maxBid sdk.Coin, lotReturnAddrs []sdk.AccAddress, lotReturnWeights []sdk.Int, debt sdk.Coin) (uint64, error)
|
||||
}
|
||||
|
||||
// HARDHooks event hooks for other keepers to run code in response to HARD modifications
|
||||
type HARDHooks interface {
|
||||
AfterDepositCreated(ctx sdk.Context, deposit Deposit)
|
||||
BeforeDepositModified(ctx sdk.Context, deposit Deposit)
|
||||
AfterDepositModified(ctx sdk.Context, deposit Deposit)
|
||||
AfterBorrowCreated(ctx sdk.Context, borrow Borrow)
|
||||
BeforeBorrowModified(ctx sdk.Context, borrow Borrow)
|
||||
AfterBorrowModified(ctx sdk.Context, borrow Borrow)
|
||||
}
|
||||
|
@ -5,39 +5,33 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
// GenesisState default values
|
||||
var (
|
||||
DefaultPreviousBlockTime = tmtime.Canonical(time.Unix(0, 0))
|
||||
DefaultDistributionTimes = GenesisDistributionTimes{}
|
||||
)
|
||||
|
||||
// GenesisState is the state that must be provided at genesis.
|
||||
type GenesisState struct {
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||
PreviousDistributionTimes GenesisDistributionTimes `json:"previous_distribution_times" yaml:"previous_distribution_times"`
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
PreviousBlockTime time.Time `json:"previous_block_time" yaml:"previous_block_time"`
|
||||
}
|
||||
|
||||
// NewGenesisState returns a new genesis state
|
||||
func NewGenesisState(params Params, previousBlockTime time.Time, previousDistTimes GenesisDistributionTimes) GenesisState {
|
||||
func NewGenesisState(params Params, previousBlockTime time.Time) GenesisState {
|
||||
return GenesisState{
|
||||
Params: params,
|
||||
PreviousBlockTime: previousBlockTime,
|
||||
PreviousDistributionTimes: previousDistTimes,
|
||||
Params: params,
|
||||
PreviousBlockTime: previousBlockTime,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGenesisState returns a default genesis state
|
||||
func DefaultGenesisState() GenesisState {
|
||||
return GenesisState{
|
||||
Params: DefaultParams(),
|
||||
PreviousBlockTime: DefaultPreviousBlockTime,
|
||||
PreviousDistributionTimes: DefaultDistributionTimes,
|
||||
Params: DefaultParams(),
|
||||
PreviousBlockTime: DefaultPreviousBlockTime,
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,14 +45,6 @@ func (gs GenesisState) Validate() error {
|
||||
if gs.PreviousBlockTime.Equal(time.Time{}) {
|
||||
return fmt.Errorf("previous block time not set")
|
||||
}
|
||||
for _, gdt := range gs.PreviousDistributionTimes {
|
||||
if gdt.PreviousDistributionTime.Equal(time.Time{}) {
|
||||
return fmt.Errorf("previous distribution time not set for %s", gdt.Denom)
|
||||
}
|
||||
if err := sdk.ValidateDenom(gdt.Denom); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -73,12 +59,3 @@ func (gs GenesisState) Equal(gs2 GenesisState) bool {
|
||||
func (gs GenesisState) IsEmpty() bool {
|
||||
return gs.Equal(GenesisState{})
|
||||
}
|
||||
|
||||
// GenesisDistributionTime stores the previous distribution time and its corresponding denom
|
||||
type GenesisDistributionTime struct {
|
||||
Denom string `json:"denom" yaml:"denom"`
|
||||
PreviousDistributionTime time.Time `json:"previous_distribution_time" yaml:"previous_distribution_time"`
|
||||
}
|
||||
|
||||
// GenesisDistributionTimes slice of GenesisDistributionTime
|
||||
type GenesisDistributionTimes []GenesisDistributionTime
|
||||
|
@ -5,8 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
@ -20,7 +18,6 @@ func (suite *GenesisTestSuite) TestGenesisValidation() {
|
||||
type args struct {
|
||||
params types.Params
|
||||
pbt time.Time
|
||||
pdts types.GenesisDistributionTimes
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -33,7 +30,6 @@ func (suite *GenesisTestSuite) TestGenesisValidation() {
|
||||
args: args{
|
||||
params: types.DefaultParams(),
|
||||
pbt: types.DefaultPreviousBlockTime,
|
||||
pdts: types.DefaultDistributionTimes,
|
||||
},
|
||||
expectPass: true,
|
||||
expectedErr: "",
|
||||
@ -41,23 +37,8 @@ func (suite *GenesisTestSuite) TestGenesisValidation() {
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
params: types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.OneDec()), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("1.5")), types.NewMultiplier(types.Medium, 24, sdk.MustNewDecFromStr("3"))}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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.DefaultMoneyMarkets,
|
||||
types.DefaultCheckLtvIndexCount,
|
||||
),
|
||||
pbt: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC),
|
||||
pdts: types.GenesisDistributionTimes{
|
||||
{PreviousDistributionTime: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC), Denom: "bnb"},
|
||||
},
|
||||
params: types.NewParams(true, types.DefaultMoneyMarkets, types.DefaultCheckLtvIndexCount),
|
||||
pbt: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC),
|
||||
},
|
||||
expectPass: true,
|
||||
expectedErr: "",
|
||||
@ -65,55 +46,16 @@ func (suite *GenesisTestSuite) TestGenesisValidation() {
|
||||
{
|
||||
name: "invalid previous blocktime",
|
||||
args: args{
|
||||
params: types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.OneDec()), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("1.5")), types.NewMultiplier(types.Medium, 24, sdk.MustNewDecFromStr("3"))}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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.DefaultMoneyMarkets,
|
||||
types.DefaultCheckLtvIndexCount,
|
||||
),
|
||||
pbt: time.Time{},
|
||||
pdts: types.GenesisDistributionTimes{
|
||||
{PreviousDistributionTime: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC), Denom: "bnb"},
|
||||
},
|
||||
params: types.NewParams(true, types.DefaultMoneyMarkets, types.DefaultCheckLtvIndexCount),
|
||||
pbt: time.Time{},
|
||||
},
|
||||
expectPass: false,
|
||||
expectedErr: "previous block time not set",
|
||||
},
|
||||
{
|
||||
name: "invalid previous distribution time",
|
||||
args: args{
|
||||
params: types.NewParams(
|
||||
true,
|
||||
types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.OneDec()), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("1.5")), types.NewMultiplier(types.Medium, 24, sdk.MustNewDecFromStr("3"))}),
|
||||
},
|
||||
types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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.DefaultMoneyMarkets,
|
||||
types.DefaultCheckLtvIndexCount,
|
||||
),
|
||||
pbt: time.Date(2020, 10, 8, 12, 0, 0, 0, time.UTC),
|
||||
pdts: types.GenesisDistributionTimes{
|
||||
{PreviousDistributionTime: time.Time{}, Denom: "bnb"},
|
||||
},
|
||||
},
|
||||
expectPass: false,
|
||||
expectedErr: "previous distribution time not set",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
gs := types.NewGenesisState(tc.args.params, tc.args.pbt, tc.args.pdts)
|
||||
gs := types.NewGenesisState(tc.args.params, tc.args.pbt)
|
||||
err := gs.Validate()
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
|
53
x/hard/types/hooks.go
Normal file
53
x/hard/types/hooks.go
Normal file
@ -0,0 +1,53 @@
|
||||
package types
|
||||
|
||||
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
// MultiHARDHooks combine multiple HARD hooks, all hook functions are run in array sequence
|
||||
type MultiHARDHooks []HARDHooks
|
||||
|
||||
// NewMultiHARDHooks returns a new MultiHARDHooks
|
||||
func NewMultiHARDHooks(hooks ...HARDHooks) MultiHARDHooks {
|
||||
return hooks
|
||||
}
|
||||
|
||||
// AfterDepositCreated runs after a deposit is created
|
||||
func (h MultiHARDHooks) AfterDepositCreated(ctx sdk.Context, deposit Deposit) {
|
||||
for i := range h {
|
||||
h[i].AfterDepositCreated(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeDepositModified runs before a deposit is modified
|
||||
func (h MultiHARDHooks) BeforeDepositModified(ctx sdk.Context, deposit Deposit) {
|
||||
for i := range h {
|
||||
h[i].BeforeDepositModified(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterDepositModified runs after a deposit is modified
|
||||
func (h MultiHARDHooks) AfterDepositModified(ctx sdk.Context, deposit Deposit) {
|
||||
for i := range h {
|
||||
h[i].AfterDepositModified(ctx, deposit)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterBorrowCreated runs after a borrow is created
|
||||
func (h MultiHARDHooks) AfterBorrowCreated(ctx sdk.Context, borrow Borrow) {
|
||||
for i := range h {
|
||||
h[i].AfterBorrowCreated(ctx, borrow)
|
||||
}
|
||||
}
|
||||
|
||||
// BeforeBorrowModified runs before a borrow is modified
|
||||
func (h MultiHARDHooks) BeforeBorrowModified(ctx sdk.Context, borrow Borrow) {
|
||||
for i := range h {
|
||||
h[i].BeforeBorrowModified(ctx, borrow)
|
||||
}
|
||||
}
|
||||
|
||||
// AfterBorrowModified runs after a borrow is modified
|
||||
func (h MultiHARDHooks) AfterBorrowModified(ctx sdk.Context, borrow Borrow) {
|
||||
for i := range h {
|
||||
h[i].AfterBorrowModified(ctx, borrow)
|
||||
}
|
||||
}
|
@ -34,20 +34,18 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
PreviousBlockTimeKey = []byte{0x01}
|
||||
PreviousDelegationDistributionKey = []byte{0x02}
|
||||
DepositsKeyPrefix = []byte{0x03}
|
||||
ClaimsKeyPrefix = []byte{0x04}
|
||||
BorrowsKeyPrefix = []byte{0x05}
|
||||
BorrowedCoinsPrefix = []byte{0x06}
|
||||
SuppliedCoinsPrefix = []byte{0x07}
|
||||
MoneyMarketsPrefix = []byte{0x08}
|
||||
PreviousAccrualTimePrefix = []byte{0x09} // denom -> time
|
||||
TotalReservesPrefix = []byte{0x10} // denom -> sdk.Coin
|
||||
BorrowInterestFactorPrefix = []byte{0x11} // denom -> sdk.Dec
|
||||
SupplyInterestFactorPrefix = []byte{0x12} // denom -> sdk.Dec
|
||||
LtvIndexPrefix = []byte{0x13}
|
||||
sep = []byte(":")
|
||||
PreviousBlockTimeKey = []byte{0x01}
|
||||
DepositsKeyPrefix = []byte{0x03}
|
||||
BorrowsKeyPrefix = []byte{0x05}
|
||||
BorrowedCoinsPrefix = []byte{0x06}
|
||||
SuppliedCoinsPrefix = []byte{0x07}
|
||||
MoneyMarketsPrefix = []byte{0x08}
|
||||
PreviousAccrualTimePrefix = []byte{0x09} // denom -> time
|
||||
TotalReservesPrefix = []byte{0x10} // denom -> sdk.Coin
|
||||
BorrowInterestFactorPrefix = []byte{0x11} // denom -> sdk.Dec
|
||||
SupplyInterestFactorPrefix = []byte{0x12} // denom -> sdk.Dec
|
||||
LtvIndexPrefix = []byte{0x13}
|
||||
sep = []byte(":")
|
||||
)
|
||||
|
||||
// DepositTypeIteratorKey returns an interator prefix for interating over deposits by deposit denom
|
||||
@ -55,16 +53,6 @@ func DepositTypeIteratorKey(denom string) []byte {
|
||||
return createKey([]byte(denom))
|
||||
}
|
||||
|
||||
// ClaimKey key of a specific deposit in the store
|
||||
func ClaimKey(depositType ClaimType, denom string, owner sdk.AccAddress) []byte {
|
||||
return createKey([]byte(depositType), sep, []byte(denom), sep, owner)
|
||||
}
|
||||
|
||||
// ClaimTypeIteratorKey returns an interator prefix for interating over claims by deposit type and denom
|
||||
func ClaimTypeIteratorKey(depositType ClaimType, denom string) []byte {
|
||||
return createKey([]byte(depositType), sep, []byte(denom))
|
||||
}
|
||||
|
||||
// GetBorrowByLtvKey is used by the LTV index
|
||||
func GetBorrowByLtvKey(ltv sdk.Dec, borrower sdk.AccAddress) []byte {
|
||||
return append(ltv.Bytes(), borrower...)
|
||||
|
@ -1,9 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/params"
|
||||
@ -14,14 +12,9 @@ import (
|
||||
// Parameter keys and default values
|
||||
var (
|
||||
KeyActive = []byte("Active")
|
||||
KeyLPSchedules = []byte("LPSchedules")
|
||||
KeyDelegatorSchedule = []byte("DelegatorSchedule")
|
||||
KeyMoneyMarkets = []byte("MoneyMarkets")
|
||||
KeyCheckLtvIndexCount = []byte("CheckLtvIndexCount")
|
||||
DefaultActive = true
|
||||
DefaultGovSchedules = DistributionSchedules{}
|
||||
DefaultLPSchedules = DistributionSchedules{}
|
||||
DefaultDelegatorSchedules = DelegatorDistributionSchedules{}
|
||||
DefaultMoneyMarkets = MoneyMarkets{}
|
||||
DefaultCheckLtvIndexCount = 10
|
||||
GovDenom = cdptypes.DefaultGovDenom
|
||||
@ -29,155 +22,9 @@ var (
|
||||
|
||||
// Params governance parameters for hard module
|
||||
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"`
|
||||
CheckLtvIndexCount int `json:"check_ltv_index_count" yaml:"check_ltv_index_count"`
|
||||
}
|
||||
|
||||
// DistributionSchedule distribution schedule for liquidity providers
|
||||
type DistributionSchedule struct {
|
||||
Active bool `json:"active" yaml:"active"`
|
||||
DepositDenom string `json:"deposit_denom" yaml:"deposit_denom"`
|
||||
Start time.Time `json:"start" yaml:"start"`
|
||||
End time.Time `json:"end" yaml:"end"`
|
||||
RewardsPerSecond sdk.Coin `json:"rewards_per_second" yaml:"rewards_per_second"`
|
||||
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
|
||||
ClaimMultipliers Multipliers `json:"claim_multipliers" yaml:"claim_multipliers"`
|
||||
}
|
||||
|
||||
// NewDistributionSchedule returns a new DistributionSchedule
|
||||
func NewDistributionSchedule(active bool, denom string, start, end time.Time, reward sdk.Coin, claimEnd time.Time, multipliers Multipliers) DistributionSchedule {
|
||||
return DistributionSchedule{
|
||||
Active: active,
|
||||
DepositDenom: denom,
|
||||
Start: start,
|
||||
End: end,
|
||||
RewardsPerSecond: reward,
|
||||
ClaimEnd: claimEnd,
|
||||
ClaimMultipliers: multipliers,
|
||||
}
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (ds DistributionSchedule) String() string {
|
||||
return fmt.Sprintf(`Liquidity Provider Distribution Schedule:
|
||||
Deposit Denom: %s,
|
||||
Start: %s,
|
||||
End: %s,
|
||||
Rewards Per Second: %s,
|
||||
Claim End: %s,
|
||||
Active: %t
|
||||
`, ds.DepositDenom, ds.Start, ds.End, ds.RewardsPerSecond, ds.ClaimEnd, ds.Active)
|
||||
}
|
||||
|
||||
// Validate performs a basic check of a distribution schedule.
|
||||
func (ds DistributionSchedule) Validate() error {
|
||||
if !ds.RewardsPerSecond.IsValid() {
|
||||
return fmt.Errorf("invalid reward coins %s for %s", ds.RewardsPerSecond, ds.DepositDenom)
|
||||
}
|
||||
if !ds.RewardsPerSecond.IsPositive() {
|
||||
return fmt.Errorf("reward amount must be positive, is %s for %s", ds.RewardsPerSecond, ds.DepositDenom)
|
||||
}
|
||||
if ds.RewardsPerSecond.Denom != "hard" {
|
||||
return fmt.Errorf("reward denom should be hard, is %s", ds.RewardsPerSecond.Denom)
|
||||
}
|
||||
if ds.Start.IsZero() {
|
||||
return errors.New("reward period start time cannot be 0")
|
||||
}
|
||||
if ds.End.IsZero() {
|
||||
return errors.New("reward period end time cannot be 0")
|
||||
}
|
||||
if ds.Start.After(ds.End) {
|
||||
return fmt.Errorf("end period time %s cannot be before start time %s", ds.End, ds.Start)
|
||||
}
|
||||
if ds.ClaimEnd.Before(ds.End) {
|
||||
return fmt.Errorf("claim end time %s cannot be before end time %s", ds.ClaimEnd, ds.End)
|
||||
}
|
||||
for _, multiplier := range ds.ClaimMultipliers {
|
||||
if err := multiplier.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DistributionSchedules slice of DistributionSchedule
|
||||
type DistributionSchedules []DistributionSchedule
|
||||
|
||||
// Validate checks if all the LiquidityProviderSchedules are valid and there are no duplicated
|
||||
// entries.
|
||||
func (dss DistributionSchedules) Validate() error {
|
||||
seenPeriods := make(map[string]bool)
|
||||
for _, ds := range dss {
|
||||
if seenPeriods[ds.DepositDenom] {
|
||||
return fmt.Errorf("duplicated distribution provider schedule with deposit denom %s", ds.DepositDenom)
|
||||
}
|
||||
|
||||
if err := ds.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
seenPeriods[ds.DepositDenom] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (dss DistributionSchedules) String() string {
|
||||
out := "Distribution Schedules\n"
|
||||
for _, ds := range dss {
|
||||
out += fmt.Sprintf("%s\n", ds)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// DelegatorDistributionSchedule distribution schedule for delegators
|
||||
type DelegatorDistributionSchedule struct {
|
||||
DistributionSchedule DistributionSchedule `json:"distribution_schedule" yaml:"distribution_schedule"`
|
||||
|
||||
DistributionFrequency time.Duration `json:"distribution_frequency" yaml:"distribution_frequency"`
|
||||
}
|
||||
|
||||
// NewDelegatorDistributionSchedule returns a new DelegatorDistributionSchedule
|
||||
func NewDelegatorDistributionSchedule(ds DistributionSchedule, frequency time.Duration) DelegatorDistributionSchedule {
|
||||
return DelegatorDistributionSchedule{
|
||||
DistributionSchedule: ds,
|
||||
DistributionFrequency: frequency,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate performs a basic check of a reward fields.
|
||||
func (dds DelegatorDistributionSchedule) Validate() error {
|
||||
if err := dds.DistributionSchedule.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dds.DistributionFrequency <= 0 {
|
||||
return fmt.Errorf("distribution frequency should be positive, got %d", dds.DistributionFrequency)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelegatorDistributionSchedules slice of DelegatorDistributionSchedule
|
||||
type DelegatorDistributionSchedules []DelegatorDistributionSchedule
|
||||
|
||||
// Validate checks if all the LiquidityProviderSchedules are valid and there are no duplicated
|
||||
// entries.
|
||||
func (dds DelegatorDistributionSchedules) Validate() error {
|
||||
seenPeriods := make(map[string]bool)
|
||||
for _, ds := range dds {
|
||||
if seenPeriods[ds.DistributionSchedule.DepositDenom] {
|
||||
return fmt.Errorf("duplicated liquidity provider schedule with deposit denom %s", ds.DistributionSchedule.DepositDenom)
|
||||
}
|
||||
|
||||
if err := ds.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
seenPeriods[ds.DistributionSchedule.DepositDenom] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
Active bool `json:"active" yaml:"active"`
|
||||
MoneyMarkets MoneyMarkets `json:"money_markets" yaml:"money_markets"`
|
||||
CheckLtvIndexCount int `json:"check_ltv_index_count" yaml:"check_ltv_index_count"`
|
||||
}
|
||||
|
||||
// Multiplier amount the claim rewards get increased by, along with how long the claim rewards are locked
|
||||
@ -211,16 +58,6 @@ func (m Multiplier) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMultiplier returns the named multiplier from the input distribution schedule
|
||||
func (ds DistributionSchedule) GetMultiplier(name MultiplierName) (Multiplier, bool) {
|
||||
for _, multiplier := range ds.ClaimMultipliers {
|
||||
if multiplier.Name == name {
|
||||
return multiplier, true
|
||||
}
|
||||
}
|
||||
return Multiplier{}, false
|
||||
}
|
||||
|
||||
// Multipliers slice of Multiplier
|
||||
type Multipliers []Multiplier
|
||||
|
||||
@ -426,33 +263,26 @@ func (irm InterestRateModel) Equal(irmCompareTo InterestRateModel) bool {
|
||||
type InterestRateModels []InterestRateModel
|
||||
|
||||
// NewParams returns a new params object
|
||||
func NewParams(active bool, lps DistributionSchedules, dds DelegatorDistributionSchedules,
|
||||
moneyMarkets MoneyMarkets, checkLtvIndexCount int) Params {
|
||||
func NewParams(active bool, moneyMarkets MoneyMarkets, checkLtvIndexCount int) Params {
|
||||
return Params{
|
||||
Active: active,
|
||||
LiquidityProviderSchedules: lps,
|
||||
DelegatorDistributionSchedules: dds,
|
||||
MoneyMarkets: moneyMarkets,
|
||||
CheckLtvIndexCount: checkLtvIndexCount,
|
||||
Active: active,
|
||||
MoneyMarkets: moneyMarkets,
|
||||
CheckLtvIndexCount: checkLtvIndexCount,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultParams returns default params for hard module
|
||||
func DefaultParams() Params {
|
||||
return NewParams(DefaultActive, DefaultLPSchedules, DefaultDelegatorSchedules,
|
||||
DefaultMoneyMarkets, DefaultCheckLtvIndexCount)
|
||||
return NewParams(DefaultActive, DefaultMoneyMarkets, DefaultCheckLtvIndexCount)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (p Params) String() string {
|
||||
return fmt.Sprintf(`Params:
|
||||
Active: %t
|
||||
Liquidity Provider Distribution Schedules %s
|
||||
Delegator Distribution Schedule %s
|
||||
Money Markets %v
|
||||
Check LTV Index Count: %v`,
|
||||
p.Active, p.LiquidityProviderSchedules, p.DelegatorDistributionSchedules,
|
||||
p.MoneyMarkets, p.CheckLtvIndexCount)
|
||||
p.Active, p.MoneyMarkets, p.CheckLtvIndexCount)
|
||||
}
|
||||
|
||||
// ParamKeyTable Key declaration for parameters
|
||||
@ -464,8 +294,6 @@ func ParamKeyTable() params.KeyTable {
|
||||
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||
return 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),
|
||||
params.NewParamSetPair(KeyCheckLtvIndexCount, &p.CheckLtvIndexCount, validateCheckLtvIndexCount),
|
||||
}
|
||||
@ -477,14 +305,6 @@ func (p Params) Validate() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateDelegatorParams(p.DelegatorDistributionSchedules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateLPParams(p.LiquidityProviderSchedules); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateMoneyMarketParams(p.MoneyMarkets); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -501,31 +321,6 @@ func validateActiveParam(i interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateLPParams(i interface{}) error {
|
||||
dss, ok := i.(DistributionSchedules)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid parameter type: %T", i)
|
||||
}
|
||||
|
||||
for _, ds := range dss {
|
||||
err := ds.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDelegatorParams(i interface{}) error {
|
||||
dds, ok := i.(DelegatorDistributionSchedules)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid parameter type: %T", i)
|
||||
}
|
||||
|
||||
return dds.Validate()
|
||||
}
|
||||
|
||||
func validateMoneyMarketParams(i interface{}) error {
|
||||
mm, ok := i.(MoneyMarkets)
|
||||
if !ok {
|
||||
|
@ -3,12 +3,9 @@ package types_test
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
"github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
@ -18,9 +15,6 @@ type ParamTestSuite struct {
|
||||
|
||||
func (suite *ParamTestSuite) TestParamValidation() {
|
||||
type args struct {
|
||||
lps types.DistributionSchedules
|
||||
gds types.DistributionSchedules
|
||||
dds types.DelegatorDistributionSchedules
|
||||
mms types.MoneyMarkets
|
||||
ltvCounter int
|
||||
active bool
|
||||
@ -34,8 +28,6 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
{
|
||||
name: "default",
|
||||
args: args{
|
||||
lps: types.DefaultLPSchedules,
|
||||
dds: types.DefaultDelegatorSchedules,
|
||||
active: types.DefaultActive,
|
||||
},
|
||||
expectPass: true,
|
||||
@ -44,14 +36,6 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
lps: types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("hard", sdk.NewInt(5000)), time.Date(2021, 11, 22, 14, 0, 0, 0, time.UTC), types.Multipliers{types.NewMultiplier(types.Small, 0, sdk.MustNewDecFromStr("0.33")), types.NewMultiplier(types.Medium, 6, sdk.MustNewDecFromStr("0.5")), types.NewMultiplier(types.Medium, 24, sdk.OneDec())}),
|
||||
},
|
||||
dds: types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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,
|
||||
),
|
||||
},
|
||||
mms: types.DefaultMoneyMarkets,
|
||||
ltvCounter: 10,
|
||||
active: true,
|
||||
@ -59,28 +43,10 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
expectPass: true,
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "invalid rewards",
|
||||
args: args{
|
||||
lps: types.DistributionSchedules{
|
||||
types.NewDistributionSchedule(true, "bnb", time.Date(2020, 10, 8, 14, 0, 0, 0, time.UTC), time.Date(2020, 11, 22, 14, 0, 0, 0, time.UTC), sdk.NewCoin("busd", 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())}),
|
||||
},
|
||||
dds: types.DelegatorDistributionSchedules{types.NewDelegatorDistributionSchedule(
|
||||
types.NewDistributionSchedule(true, "bnb", 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,
|
||||
),
|
||||
},
|
||||
mms: types.DefaultMoneyMarkets,
|
||||
ltvCounter: 10,
|
||||
active: true,
|
||||
},
|
||||
expectPass: false,
|
||||
expectedErr: "reward denom should be hard",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
params := types.NewParams(tc.args.active, tc.args.lps, tc.args.dds, tc.args.mms, tc.args.ltvCounter)
|
||||
params := types.NewParams(tc.args.active, tc.args.mms, tc.args.ltvCounter)
|
||||
err := params.Validate()
|
||||
if tc.expectPass {
|
||||
suite.NoError(err)
|
||||
|
@ -8,8 +8,21 @@ import (
|
||||
|
||||
// BeginBlocker runs at the start of every block
|
||||
func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
|
||||
for _, rp := range k.GetParams(ctx).RewardPeriods {
|
||||
err := k.AccumulateRewards(ctx, rp)
|
||||
params := k.GetParams(ctx)
|
||||
for _, rp := range params.USDXMintingRewardPeriods {
|
||||
err := k.AccumulateUSDXMintingRewards(ctx, rp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, rp := range params.HardSupplyRewardPeriods {
|
||||
err := k.AccumulateHardSupplyRewards(ctx, rp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, rp := range params.HardBorrowRewardPeriods {
|
||||
err := k.AccumulateHardBorrowRewards(ctx, rp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -55,33 +55,32 @@ var (
|
||||
RegisterCodec = types.RegisterCodec
|
||||
|
||||
// variable aliases
|
||||
BlockTimeKey = types.BlockTimeKey
|
||||
ClaimKeyPrefix = types.ClaimKeyPrefix
|
||||
DefaultActive = types.DefaultActive
|
||||
DefaultClaimEnd = types.DefaultClaimEnd
|
||||
DefaultClaims = types.DefaultClaims
|
||||
DefaultGenesisAccumulationTimes = types.DefaultGenesisAccumulationTimes
|
||||
DefaultMultipliers = types.DefaultMultipliers
|
||||
DefaultRewardPeriods = types.DefaultRewardPeriods
|
||||
ErrAccountNotFound = types.ErrAccountNotFound
|
||||
ErrClaimExpired = types.ErrClaimExpired
|
||||
ErrClaimNotFound = types.ErrClaimNotFound
|
||||
ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance
|
||||
ErrInvalidAccountType = types.ErrInvalidAccountType
|
||||
ErrInvalidMultiplier = types.ErrInvalidMultiplier
|
||||
ErrNoClaimsFound = types.ErrNoClaimsFound
|
||||
ErrRewardPeriodNotFound = types.ErrRewardPeriodNotFound
|
||||
ErrZeroClaim = types.ErrZeroClaim
|
||||
GovDenom = types.GovDenom
|
||||
IncentiveMacc = types.IncentiveMacc
|
||||
KeyActive = types.KeyActive
|
||||
KeyClaimEnd = types.KeyClaimEnd
|
||||
KeyMultipliers = types.KeyMultipliers
|
||||
KeyRewards = types.KeyRewards
|
||||
ModuleCdc = types.ModuleCdc
|
||||
PrincipalDenom = types.PrincipalDenom
|
||||
RewardFactorKey = types.RewardFactorKey
|
||||
USDXMintingRewardDenom = types.USDXMintingRewardDenom
|
||||
PreviousUSDXMintingRewardAccrualTimeKeyPrefix = types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix
|
||||
USDXMintingClaimKeyPrefix = types.USDXMintingClaimKeyPrefix
|
||||
DefaultActive = types.DefaultActive
|
||||
DefaultClaimEnd = types.DefaultClaimEnd
|
||||
DefaultClaims = types.DefaultClaims
|
||||
DefaultGenesisAccumulationTimes = types.DefaultGenesisAccumulationTimes
|
||||
DefaultMultipliers = types.DefaultMultipliers
|
||||
DefaultRewardPeriods = types.DefaultRewardPeriods
|
||||
ErrAccountNotFound = types.ErrAccountNotFound
|
||||
ErrClaimExpired = types.ErrClaimExpired
|
||||
ErrClaimNotFound = types.ErrClaimNotFound
|
||||
ErrInsufficientModAccountBalance = types.ErrInsufficientModAccountBalance
|
||||
ErrInvalidAccountType = types.ErrInvalidAccountType
|
||||
ErrInvalidMultiplier = types.ErrInvalidMultiplier
|
||||
ErrNoClaimsFound = types.ErrNoClaimsFound
|
||||
ErrRewardPeriodNotFound = types.ErrRewardPeriodNotFound
|
||||
ErrZeroClaim = types.ErrZeroClaim
|
||||
GovDenom = types.GovDenom
|
||||
IncentiveMacc = types.IncentiveMacc
|
||||
KeyClaimEnd = types.KeyClaimEnd
|
||||
KeyMultipliers = types.KeyMultipliers
|
||||
KeyUSDXMintingRewardPeriods = types.KeyUSDXMintingRewardPeriods
|
||||
ModuleCdc = types.ModuleCdc
|
||||
PrincipalDenom = types.PrincipalDenom
|
||||
USDXMintingRewardFactorKeyPrefix = types.USDXMintingRewardFactorKeyPrefix
|
||||
USDXMintingRewardDenom = types.USDXMintingRewardDenom
|
||||
)
|
||||
|
||||
type (
|
||||
@ -89,6 +88,7 @@ type (
|
||||
Keeper = keeper.Keeper
|
||||
AccountKeeper = types.AccountKeeper
|
||||
CDPHooks = types.CDPHooks
|
||||
HARDHooks = types.HARDHooks
|
||||
CdpKeeper = types.CdpKeeper
|
||||
GenesisAccumulationTime = types.GenesisAccumulationTime
|
||||
GenesisAccumulationTimes = types.GenesisAccumulationTimes
|
||||
|
@ -22,7 +22,7 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
|
||||
panic(fmt.Sprintf("failed to validate %s genesis state: %s", types.ModuleName, err))
|
||||
}
|
||||
|
||||
for _, rp := range gs.Params.RewardPeriods {
|
||||
for _, rp := range gs.Params.USDXMintingRewardPeriods {
|
||||
_, found := cdpKeeper.GetCollateral(ctx, rp.CollateralType)
|
||||
if !found {
|
||||
panic(fmt.Sprintf("usdx minting collateral type %s not found in cdp collateral types", rp.CollateralType))
|
||||
@ -31,13 +31,15 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
|
||||
|
||||
k.SetParams(ctx, gs.Params)
|
||||
|
||||
// TODO: previous hard module accrual times/indexes should be set here
|
||||
|
||||
for _, gat := range gs.PreviousAccumulationTimes {
|
||||
k.SetPreviousAccrualTime(ctx, gat.CollateralType, gat.PreviousAccumulationTime)
|
||||
k.SetRewardFactor(ctx, gat.CollateralType, gat.RewardFactor)
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, gat.CollateralType, gat.PreviousAccumulationTime)
|
||||
k.SetUSDXMintingRewardFactor(ctx, gat.CollateralType, gat.RewardFactor)
|
||||
}
|
||||
|
||||
for _, claim := range gs.USDXMintingClaims {
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
}
|
||||
|
||||
}
|
||||
@ -46,16 +48,16 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, supplyKeeper types.SupplyKeep
|
||||
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) types.GenesisState {
|
||||
params := k.GetParams(ctx)
|
||||
|
||||
claims := k.GetAllClaims(ctx)
|
||||
claims := k.GetAllUSDXMintingClaims(ctx)
|
||||
|
||||
var gats GenesisAccumulationTimes
|
||||
|
||||
for _, rp := range params.RewardPeriods {
|
||||
pat, found := k.GetPreviousAccrualTime(ctx, rp.CollateralType)
|
||||
for _, rp := range params.USDXMintingRewardPeriods {
|
||||
pat, found := k.GetPreviousUSDXMintingAccrualTime(ctx, rp.CollateralType)
|
||||
if !found {
|
||||
pat = ctx.BlockTime()
|
||||
}
|
||||
factor, found := k.GetRewardFactor(ctx, rp.CollateralType)
|
||||
factor, found := k.GetUSDXMintingRewardFactor(ctx, rp.CollateralType)
|
||||
if !found {
|
||||
factor = sdk.ZeroDec()
|
||||
}
|
||||
|
@ -44,6 +44,9 @@ func (suite *HandlerTestSuite) SetupTest() {
|
||||
authGS := app.NewAuthGenState(addrs, coins)
|
||||
incentiveGS := incentive.NewGenesisState(
|
||||
incentive.NewParams(
|
||||
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
|
||||
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
|
||||
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
|
||||
incentive.RewardPeriods{incentive.NewRewardPeriod(true, "bnb-a", time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 12, 15, 14, 0, 0, 0, time.UTC), c("ukava", 122354))},
|
||||
incentive.Multipliers{incentive.NewMultiplier(incentive.MultiplierName("small"), 1, d("0.25")), incentive.NewMultiplier(incentive.MultiplierName("large"), 12, d("1.0"))},
|
||||
time.Date(2025, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
@ -66,7 +69,7 @@ func (suite *HandlerTestSuite) addClaim() {
|
||||
suite.Require().NoError(err)
|
||||
c1 := incentive.NewUSDXMintingClaim(suite.addrs[0], c("ukava", 1000000), types.RewardIndexes{types.NewRewardIndex("bnb-s", sdk.ZeroDec())})
|
||||
suite.NotPanics(func() {
|
||||
suite.keeper.SetClaim(suite.ctx, c1)
|
||||
suite.keeper.SetUSDXMintingClaim(suite.ctx, c1)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
"github.com/kava-labs/kava/x/incentive"
|
||||
"github.com/kava-labs/kava/x/incentive/types"
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
)
|
||||
|
||||
@ -155,6 +156,9 @@ func NewIncentiveGenState(previousAccumTime, endTime time.Time, rewardPeriods ..
|
||||
genesis := incentive.NewGenesisState(
|
||||
incentive.NewParams(
|
||||
rewardPeriods,
|
||||
types.RewardPeriods{},
|
||||
types.RewardPeriods{},
|
||||
types.RewardPeriods{},
|
||||
incentive.Multipliers{
|
||||
incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
|
||||
incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
|
||||
|
@ -3,6 +3,7 @@ package keeper
|
||||
import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
||||
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
// Hooks wrapper struct for hooks
|
||||
@ -11,18 +12,49 @@ type Hooks struct {
|
||||
}
|
||||
|
||||
var _ cdptypes.CDPHooks = Hooks{}
|
||||
var _ hardtypes.HARDHooks = Hooks{}
|
||||
|
||||
// Hooks create new incentive hooks
|
||||
func (k Keeper) Hooks() Hooks { return Hooks{k} }
|
||||
|
||||
// AfterCDPCreated function that runs after a cdp is created
|
||||
func (h Hooks) AfterCDPCreated(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
h.k.InitializeClaim(ctx, cdp)
|
||||
h.k.InitializeUSDXMintingClaim(ctx, cdp)
|
||||
}
|
||||
|
||||
// BeforeCDPModified function that runs before a cdp is modified
|
||||
// note that this is called immediately after interest is synchronized, and so could potentially
|
||||
// be called AfterCDPInterestUpdated or something like that, if we we're to expand the scope of cdp hooks
|
||||
func (h Hooks) BeforeCDPModified(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
h.k.SynchronizeReward(ctx, cdp)
|
||||
h.k.SynchronizeUSDXMintingReward(ctx, cdp)
|
||||
}
|
||||
|
||||
// AfterDepositCreated function that runs after a deposit is created
|
||||
func (h Hooks) AfterDepositCreated(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
h.k.InitializeHardSupplyReward(ctx, deposit)
|
||||
}
|
||||
|
||||
// BeforeDepositModified function that runs before a deposit is modified
|
||||
func (h Hooks) BeforeDepositModified(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
h.k.SynchronizeHardSupplyReward(ctx, deposit)
|
||||
}
|
||||
|
||||
// AfterDepositModified function that runs after a deposit is modified
|
||||
func (h Hooks) AfterDepositModified(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
h.k.UpdateHardSupplyIndexDenoms(ctx, deposit)
|
||||
}
|
||||
|
||||
// AfterBorrowCreated function that runs after a borrow is created
|
||||
func (h Hooks) AfterBorrowCreated(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
h.k.InitializeHardBorrowReward(ctx, borrow)
|
||||
}
|
||||
|
||||
// BeforeBorrowModified function that runs before a borrow is modified
|
||||
func (h Hooks) BeforeBorrowModified(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
h.k.SynchronizeHardBorrowReward(ctx, borrow)
|
||||
}
|
||||
|
||||
// AfterBorrowModified function that runs after a borrow is modified
|
||||
func (h Hooks) AfterBorrowModified(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
h.k.UpdateHardBorrowIndexDenoms(ctx, borrow)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
"github.com/kava-labs/kava/x/cdp"
|
||||
"github.com/kava-labs/kava/x/incentive"
|
||||
"github.com/kava-labs/kava/x/hard"
|
||||
"github.com/kava-labs/kava/x/pricefeed"
|
||||
)
|
||||
|
||||
@ -104,6 +104,7 @@ func NewPricefeedGenStateMulti() app.GenesisState {
|
||||
pfGenesis := pricefeed.GenesisState{
|
||||
Params: pricefeed.Params{
|
||||
Markets: []pricefeed.Market{
|
||||
{MarketID: "kava:usd", BaseAsset: "kava", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||
{MarketID: "btc:usd", BaseAsset: "btc", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||
{MarketID: "xrp:usd", BaseAsset: "xrp", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||
{MarketID: "bnb:usd", BaseAsset: "bnb", QuoteAsset: "usd", Oracles: []sdk.AccAddress{}, Active: true},
|
||||
@ -111,6 +112,12 @@ func NewPricefeedGenStateMulti() app.GenesisState {
|
||||
},
|
||||
},
|
||||
PostedPrices: []pricefeed.PostedPrice{
|
||||
{
|
||||
MarketID: "kava:usd",
|
||||
OracleAddress: sdk.AccAddress{},
|
||||
Price: sdk.MustNewDecFromStr("2.00"),
|
||||
Expiry: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
{
|
||||
MarketID: "btc:usd",
|
||||
OracleAddress: sdk.AccAddress{},
|
||||
@ -140,73 +147,26 @@ func NewPricefeedGenStateMulti() app.GenesisState {
|
||||
return app.GenesisState{pricefeed.ModuleName: pricefeed.ModuleCdc.MustMarshalJSON(pfGenesis)}
|
||||
}
|
||||
|
||||
func NewIncentiveGenState(previousAccumTime, endTime time.Time, rewardPeriods ...incentive.RewardPeriod) app.GenesisState {
|
||||
var accumulationTimes incentive.GenesisAccumulationTimes
|
||||
for _, rp := range rewardPeriods {
|
||||
accumulationTimes = append(
|
||||
accumulationTimes,
|
||||
incentive.NewGenesisAccumulationTime(
|
||||
rp.CollateralType,
|
||||
previousAccumTime,
|
||||
sdk.ZeroDec(),
|
||||
),
|
||||
)
|
||||
}
|
||||
genesis := incentive.NewGenesisState(
|
||||
incentive.NewParams(
|
||||
rewardPeriods,
|
||||
incentive.Multipliers{
|
||||
incentive.NewMultiplier(incentive.Small, 1, d("0.25")),
|
||||
incentive.NewMultiplier(incentive.Large, 12, d("1.0")),
|
||||
},
|
||||
endTime,
|
||||
),
|
||||
accumulationTimes,
|
||||
incentive.USDXMintingClaims{},
|
||||
)
|
||||
return app.GenesisState{incentive.ModuleName: incentive.ModuleCdc.MustMarshalJSON(genesis)}
|
||||
}
|
||||
func NewHardGenStateMulti() app.GenesisState {
|
||||
KAVA_CF := int64(1000000)
|
||||
USDX_CF := int64(1000000)
|
||||
BNB_CF := int64(100000000)
|
||||
BTCB_CF := int64(100000000)
|
||||
|
||||
func NewCDPGenStateHighInterest() app.GenesisState {
|
||||
cdpGenesis := cdp.GenesisState{
|
||||
Params: cdp.Params{
|
||||
GlobalDebtLimit: sdk.NewInt64Coin("usdx", 2000000000000),
|
||||
SurplusAuctionThreshold: cdp.DefaultSurplusThreshold,
|
||||
SurplusAuctionLot: cdp.DefaultSurplusLot,
|
||||
DebtAuctionThreshold: cdp.DefaultDebtThreshold,
|
||||
DebtAuctionLot: cdp.DefaultDebtLot,
|
||||
CollateralParams: cdp.CollateralParams{
|
||||
{
|
||||
Denom: "bnb",
|
||||
Type: "bnb-a",
|
||||
LiquidationRatio: sdk.MustNewDecFromStr("1.5"),
|
||||
DebtLimit: sdk.NewInt64Coin("usdx", 500000000000),
|
||||
StabilityFee: sdk.MustNewDecFromStr("1.000000051034942716"), // 500% APR
|
||||
LiquidationPenalty: d("0.05"),
|
||||
AuctionSize: i(50000000000),
|
||||
Prefix: 0x22,
|
||||
SpotMarketID: "bnb:usd",
|
||||
LiquidationMarketID: "bnb:usd",
|
||||
ConversionFactor: i(8),
|
||||
},
|
||||
},
|
||||
DebtParam: cdp.DebtParam{
|
||||
Denom: "usdx",
|
||||
ReferenceAsset: "usd",
|
||||
ConversionFactor: i(6),
|
||||
DebtFloor: i(10000000),
|
||||
},
|
||||
loanToValue, _ := sdk.NewDecFromStr("0.6")
|
||||
borrowLimit := sdk.NewDec(1000000000000000)
|
||||
|
||||
hardGS := hard.NewGenesisState(hard.NewParams(
|
||||
true,
|
||||
hard.MoneyMarkets{
|
||||
hard.NewMoneyMarket("usdx", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "usdx:usd", sdk.NewInt(1000000), sdk.NewInt(USDX_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
hard.NewMoneyMarket("ukava", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "kava:usd", sdk.NewInt(1000000), sdk.NewInt(KAVA_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
hard.NewMoneyMarket("bnb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "bnb:usd", sdk.NewInt(1000000), sdk.NewInt(BNB_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
hard.NewMoneyMarket("btcb", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "btc:usd", sdk.NewInt(1000000), sdk.NewInt(BTCB_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
hard.NewMoneyMarket("xrp", hard.NewBorrowLimit(false, borrowLimit, loanToValue), "xrp:usd", sdk.NewInt(1000000), sdk.NewInt(BTCB_CF*1000), hard.NewInterestRateModel(sdk.MustNewDecFromStr("0.05"), sdk.MustNewDecFromStr("2"), sdk.MustNewDecFromStr("0.8"), sdk.MustNewDecFromStr("10")), sdk.MustNewDecFromStr("0.05"), sdk.ZeroDec()),
|
||||
},
|
||||
StartingCdpID: cdp.DefaultCdpStartingID,
|
||||
DebtDenom: cdp.DefaultDebtDenom,
|
||||
GovDenom: cdp.DefaultGovDenom,
|
||||
CDPs: cdp.CDPs{},
|
||||
PreviousAccumulationTimes: cdp.GenesisAccumulationTimes{
|
||||
cdp.NewGenesisAccumulationTime("bnb-a", time.Time{}, sdk.OneDec()),
|
||||
},
|
||||
TotalPrincipals: cdp.GenesisTotalPrincipals{
|
||||
cdp.NewGenesisTotalPrincipal("bnb-a", sdk.ZeroInt()),
|
||||
},
|
||||
}
|
||||
return app.GenesisState{cdp.ModuleName: cdp.ModuleCdc.MustMarshalJSON(cdpGenesis)}
|
||||
0, // LTV counter
|
||||
), hard.DefaultPreviousBlockTime)
|
||||
|
||||
return app.GenesisState{hard.ModuleName: hard.ModuleCdc.MustMarshalJSON(hardGS)}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ type Keeper struct {
|
||||
accountKeeper types.AccountKeeper
|
||||
cdc *codec.Codec
|
||||
cdpKeeper types.CdpKeeper
|
||||
hardKeeper types.HardKeeper
|
||||
key sdk.StoreKey
|
||||
paramSubspace subspace.Subspace
|
||||
supplyKeeper types.SupplyKeeper
|
||||
@ -24,22 +25,23 @@ type Keeper struct {
|
||||
// NewKeeper creates a new keeper
|
||||
func NewKeeper(
|
||||
cdc *codec.Codec, key sdk.StoreKey, paramstore subspace.Subspace, sk types.SupplyKeeper,
|
||||
cdpk types.CdpKeeper, ak types.AccountKeeper,
|
||||
cdpk types.CdpKeeper, hk types.HardKeeper, ak types.AccountKeeper,
|
||||
) Keeper {
|
||||
|
||||
return Keeper{
|
||||
accountKeeper: ak,
|
||||
cdc: cdc,
|
||||
cdpKeeper: cdpk,
|
||||
hardKeeper: hk,
|
||||
key: key,
|
||||
paramSubspace: paramstore.WithKeyTable(types.ParamKeyTable()),
|
||||
supplyKeeper: sk,
|
||||
}
|
||||
}
|
||||
|
||||
// GetClaim returns the claim in the store corresponding the the input address collateral type and id and a boolean for if the claim was found
|
||||
func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress) (types.USDXMintingClaim, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
|
||||
// GetUSDXMintingClaim returns the claim in the store corresponding the the input address collateral type and id and a boolean for if the claim was found
|
||||
func (k Keeper) GetUSDXMintingClaim(ctx sdk.Context, addr sdk.AccAddress) (types.USDXMintingClaim, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingClaimKeyPrefix)
|
||||
bz := store.Get(addr)
|
||||
if bz == nil {
|
||||
return types.USDXMintingClaim{}, false
|
||||
@ -49,23 +51,23 @@ func (k Keeper) GetClaim(ctx sdk.Context, addr sdk.AccAddress) (types.USDXMintin
|
||||
return c, true
|
||||
}
|
||||
|
||||
// SetClaim sets the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) SetClaim(ctx sdk.Context, c types.USDXMintingClaim) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
|
||||
// SetUSDXMintingClaim sets the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) SetUSDXMintingClaim(ctx sdk.Context, c types.USDXMintingClaim) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingClaimKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryBare(c)
|
||||
store.Set(c.Owner, bz)
|
||||
|
||||
}
|
||||
|
||||
// DeleteClaim deletes the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) DeleteClaim(ctx sdk.Context, owner sdk.AccAddress) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
|
||||
// DeleteUSDXMintingClaim deletes the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) DeleteUSDXMintingClaim(ctx sdk.Context, owner sdk.AccAddress) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingClaimKeyPrefix)
|
||||
store.Delete(owner)
|
||||
}
|
||||
|
||||
// IterateClaims iterates over all claim objects in the store and preforms a callback function
|
||||
func (k Keeper) IterateClaims(ctx sdk.Context, cb func(c types.USDXMintingClaim) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.ClaimKeyPrefix)
|
||||
// IterateUSDXMintingClaims iterates over all claim objects in the store and preforms a callback function
|
||||
func (k Keeper) IterateUSDXMintingClaims(ctx sdk.Context, cb func(c types.USDXMintingClaim) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingClaimKeyPrefix)
|
||||
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
@ -77,19 +79,19 @@ func (k Keeper) IterateClaims(ctx sdk.Context, cb func(c types.USDXMintingClaim)
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllClaims returns all Claim objects in the store
|
||||
func (k Keeper) GetAllClaims(ctx sdk.Context) types.USDXMintingClaims {
|
||||
// GetAllUSDXMintingClaims returns all Claim objects in the store
|
||||
func (k Keeper) GetAllUSDXMintingClaims(ctx sdk.Context) types.USDXMintingClaims {
|
||||
cs := types.USDXMintingClaims{}
|
||||
k.IterateClaims(ctx, func(c types.USDXMintingClaim) (stop bool) {
|
||||
k.IterateUSDXMintingClaims(ctx, func(c types.USDXMintingClaim) (stop bool) {
|
||||
cs = append(cs, c)
|
||||
return false
|
||||
})
|
||||
return cs
|
||||
}
|
||||
|
||||
// GetPreviousAccrualTime returns the last time a collateral type accrued rewards
|
||||
func (k Keeper) GetPreviousAccrualTime(ctx sdk.Context, ctype string) (blockTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.BlockTimeKey)
|
||||
// GetPreviousUSDXMintingAccrualTime returns the last time a collateral type accrued USDX minting rewards
|
||||
func (k Keeper) GetPreviousUSDXMintingAccrualTime(ctx sdk.Context, ctype string) (blockTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix)
|
||||
bz := store.Get([]byte(ctype))
|
||||
if bz == nil {
|
||||
return time.Time{}, false
|
||||
@ -98,15 +100,15 @@ func (k Keeper) GetPreviousAccrualTime(ctx sdk.Context, ctype string) (blockTime
|
||||
return blockTime, true
|
||||
}
|
||||
|
||||
// SetPreviousAccrualTime sets the last time a collateral type accrued rewards
|
||||
func (k Keeper) SetPreviousAccrualTime(ctx sdk.Context, ctype string, blockTime time.Time) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.BlockTimeKey)
|
||||
// SetPreviousUSDXMintingAccrualTime sets the last time a collateral type accrued USDX minting rewards
|
||||
func (k Keeper) SetPreviousUSDXMintingAccrualTime(ctx sdk.Context, ctype string, blockTime time.Time) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix)
|
||||
store.Set([]byte(ctype), k.cdc.MustMarshalBinaryBare(blockTime))
|
||||
}
|
||||
|
||||
// IterateAccrualTimes iterates over all previous accrual times and preforms a callback function
|
||||
func (k Keeper) IterateAccrualTimes(ctx sdk.Context, cb func(string, time.Time) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.BlockTimeKey)
|
||||
// IterateUSDXMintingAccrualTimes iterates over all previous USDX minting accrual times and preforms a callback function
|
||||
func (k Keeper) IterateUSDXMintingAccrualTimes(ctx sdk.Context, cb func(string, time.Time) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix)
|
||||
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
@ -120,9 +122,9 @@ func (k Keeper) IterateAccrualTimes(ctx sdk.Context, cb func(string, time.Time)
|
||||
}
|
||||
}
|
||||
|
||||
// GetRewardFactor returns the current reward factor for an individual collateral type
|
||||
func (k Keeper) GetRewardFactor(ctx sdk.Context, ctype string) (factor sdk.Dec, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardFactorKey)
|
||||
// GetUSDXMintingRewardFactor returns the current reward factor for an individual collateral type
|
||||
func (k Keeper) GetUSDXMintingRewardFactor(ctx sdk.Context, ctype string) (factor sdk.Dec, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingRewardFactorKeyPrefix)
|
||||
bz := store.Get([]byte(ctype))
|
||||
if bz == nil {
|
||||
return sdk.ZeroDec(), false
|
||||
@ -131,8 +133,146 @@ func (k Keeper) GetRewardFactor(ctx sdk.Context, ctype string) (factor sdk.Dec,
|
||||
return factor, true
|
||||
}
|
||||
|
||||
// SetRewardFactor sets the current reward factor for an individual collateral type
|
||||
func (k Keeper) SetRewardFactor(ctx sdk.Context, ctype string, factor sdk.Dec) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.RewardFactorKey)
|
||||
// SetUSDXMintingRewardFactor sets the current reward factor for an individual collateral type
|
||||
func (k Keeper) SetUSDXMintingRewardFactor(ctx sdk.Context, ctype string, factor sdk.Dec) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.USDXMintingRewardFactorKeyPrefix)
|
||||
store.Set([]byte(ctype), k.cdc.MustMarshalBinaryBare(factor))
|
||||
}
|
||||
|
||||
// GetHardLiquidityProviderClaim returns the claim in the store corresponding the the input address collateral type and id and a boolean for if the claim was found
|
||||
func (k Keeper) GetHardLiquidityProviderClaim(ctx sdk.Context, addr sdk.AccAddress) (types.HardLiquidityProviderClaim, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardLiquidityClaimKeyPrefix)
|
||||
bz := store.Get(addr)
|
||||
if bz == nil {
|
||||
return types.HardLiquidityProviderClaim{}, false
|
||||
}
|
||||
var c types.HardLiquidityProviderClaim
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &c)
|
||||
return c, true
|
||||
}
|
||||
|
||||
// SetHardLiquidityProviderClaim sets the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) SetHardLiquidityProviderClaim(ctx sdk.Context, c types.HardLiquidityProviderClaim) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardLiquidityClaimKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryBare(c)
|
||||
store.Set(c.Owner, bz)
|
||||
}
|
||||
|
||||
// DeleteHardLiquidityProviderClaim deletes the claim in the store corresponding to the input address, collateral type, and id
|
||||
func (k Keeper) DeleteHardLiquidityProviderClaim(ctx sdk.Context, owner sdk.AccAddress) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardLiquidityClaimKeyPrefix)
|
||||
store.Delete(owner)
|
||||
}
|
||||
|
||||
// IterateHardLiquidityProviderClaims iterates over all claim objects in the store and preforms a callback function
|
||||
func (k Keeper) IterateHardLiquidityProviderClaims(ctx sdk.Context, cb func(c types.HardLiquidityProviderClaim) (stop bool)) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardLiquidityClaimKeyPrefix)
|
||||
iterator := sdk.KVStorePrefixIterator(store, []byte{})
|
||||
defer iterator.Close()
|
||||
for ; iterator.Valid(); iterator.Next() {
|
||||
var c types.HardLiquidityProviderClaim
|
||||
k.cdc.MustUnmarshalBinaryBare(iterator.Value(), &c)
|
||||
if cb(c) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllHardLiquidityProviderClaims returns all Claim objects in the store
|
||||
func (k Keeper) GetAllHardLiquidityProviderClaims(ctx sdk.Context) types.HardLiquidityProviderClaims {
|
||||
cs := types.HardLiquidityProviderClaims{}
|
||||
k.IterateHardLiquidityProviderClaims(ctx, func(c types.HardLiquidityProviderClaim) (stop bool) {
|
||||
cs = append(cs, c)
|
||||
return false
|
||||
})
|
||||
return cs
|
||||
}
|
||||
|
||||
// SetHardSupplyRewardFactor sets the current interest factor for an individual market
|
||||
func (k Keeper) SetHardSupplyRewardFactor(ctx sdk.Context, denom string, borrowIndex sdk.Dec) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardSupplyRewardFactorKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryBare(borrowIndex)
|
||||
store.Set([]byte(denom), bz)
|
||||
}
|
||||
|
||||
// GetHardSupplyRewardFactor returns the current interest factor for an individual market
|
||||
func (k Keeper) GetHardSupplyRewardFactor(ctx sdk.Context, denom string) (sdk.Dec, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardSupplyRewardFactorKeyPrefix)
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
return sdk.ZeroDec(), false
|
||||
}
|
||||
var interestFactor sdk.Dec
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &interestFactor)
|
||||
return interestFactor, true
|
||||
}
|
||||
|
||||
// SetHardBorrowRewardFactor sets the current interest factor for an individual market
|
||||
func (k Keeper) SetHardBorrowRewardFactor(ctx sdk.Context, denom string, borrowIndex sdk.Dec) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardBorrowRewardFactorKeyPrefix)
|
||||
bz := k.cdc.MustMarshalBinaryBare(borrowIndex)
|
||||
store.Set([]byte(denom), bz)
|
||||
}
|
||||
|
||||
// GetHardBorrowRewardFactor returns the current interest factor for an individual market
|
||||
func (k Keeper) GetHardBorrowRewardFactor(ctx sdk.Context, denom string) (sdk.Dec, bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardBorrowRewardFactorKeyPrefix)
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
return sdk.ZeroDec(), false
|
||||
}
|
||||
var interestFactor sdk.Dec
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &interestFactor)
|
||||
return interestFactor, true
|
||||
}
|
||||
|
||||
// GetHardDelegatorRewardFactor returns the current reward factor for an individual collateral type
|
||||
func (k Keeper) GetHardDelegatorRewardFactor(ctx sdk.Context, ctype string) (factor sdk.Dec, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardFactorKeyPrefix)
|
||||
bz := store.Get([]byte(ctype))
|
||||
if bz == nil {
|
||||
return sdk.ZeroDec(), false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &factor)
|
||||
return factor, true
|
||||
}
|
||||
|
||||
// SetHardDelegatorRewardFactor sets the current reward factor for an individual collateral type
|
||||
func (k Keeper) SetHardDelegatorRewardFactor(ctx sdk.Context, ctype string, factor sdk.Dec) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.HardDelegatorRewardFactorKeyPrefix)
|
||||
store.Set([]byte(ctype), k.cdc.MustMarshalBinaryBare(factor))
|
||||
}
|
||||
|
||||
// GetPreviousHardSupplyRewardAccrualTime returns the last time a denom accrued Hard protocol supply-side rewards
|
||||
func (k Keeper) GetPreviousHardSupplyRewardAccrualTime(ctx sdk.Context, denom string) (blockTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardSupplyRewardAccrualTimeKeyPrefix)
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &blockTime)
|
||||
return blockTime, true
|
||||
}
|
||||
|
||||
// SetPreviousHardSupplyRewardAccrualTime sets the last time a denom accrued Hard protocol supply-side rewards
|
||||
func (k Keeper) SetPreviousHardSupplyRewardAccrualTime(ctx sdk.Context, denom string, blockTime time.Time) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardSupplyRewardAccrualTimeKeyPrefix)
|
||||
store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(blockTime))
|
||||
}
|
||||
|
||||
// GetPreviousHardBorrowRewardAccrualTime returns the last time a denom accrued Hard protocol borrow-side rewards
|
||||
func (k Keeper) GetPreviousHardBorrowRewardAccrualTime(ctx sdk.Context, denom string) (blockTime time.Time, found bool) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardBorrowRewardAccrualTimeKeyPrefix)
|
||||
bz := store.Get([]byte(denom))
|
||||
if bz == nil {
|
||||
return time.Time{}, false
|
||||
}
|
||||
k.cdc.MustUnmarshalBinaryBare(bz, &blockTime)
|
||||
return blockTime, true
|
||||
}
|
||||
|
||||
// SetPreviousHardBorrowRewardAccrualTime sets the last time a denom accrued Hard protocol borrow-side rewards
|
||||
func (k Keeper) SetPreviousHardBorrowRewardAccrualTime(ctx sdk.Context, denom string, blockTime time.Time) {
|
||||
store := prefix.NewStore(ctx.KVStore(k.key), types.PreviousHardBorrowRewardAccrualTimeKeyPrefix)
|
||||
store.Set([]byte(denom), k.cdc.MustMarshalBinaryBare(blockTime))
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
hardkeeper "github.com/kava-labs/kava/x/hard/keeper"
|
||||
"github.com/kava-labs/kava/x/incentive/keeper"
|
||||
"github.com/kava-labs/kava/x/incentive/types"
|
||||
)
|
||||
@ -23,10 +24,11 @@ import (
|
||||
type KeeperTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
keeper keeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
addrs []sdk.AccAddress
|
||||
keeper keeper.Keeper
|
||||
hardKeeper hardkeeper.Keeper
|
||||
app app.TestApp
|
||||
ctx sdk.Context
|
||||
addrs []sdk.AccAddress
|
||||
}
|
||||
|
||||
// The default state used by each test
|
||||
@ -52,45 +54,41 @@ func (suite *KeeperTestSuite) getModuleAccount(name string) supplyexported.Modul
|
||||
return sk.GetModuleAccount(suite.ctx, name)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestGetSetDeleteClaim() {
|
||||
func (suite *KeeperTestSuite) TestGetSetDeleteUSDXMintingClaim() {
|
||||
c := types.NewUSDXMintingClaim(suite.addrs[0], c("ukava", 1000000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())})
|
||||
_, found := suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
_, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().False(found)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.SetClaim(suite.ctx, c)
|
||||
suite.keeper.SetUSDXMintingClaim(suite.ctx, c)
|
||||
})
|
||||
testC, found := suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
testC, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(c, testC)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.DeleteClaim(suite.ctx, suite.addrs[0])
|
||||
suite.keeper.DeleteUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
})
|
||||
_, found = suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
_, found = suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().False(found)
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestIterateClaims() {
|
||||
func (suite *KeeperTestSuite) TestIterateUSDXMintingClaims() {
|
||||
for i := 0; i < len(suite.addrs); i++ {
|
||||
c := types.NewUSDXMintingClaim(suite.addrs[i], c("ukava", 100000), types.RewardIndexes{types.NewRewardIndex("bnb-a", sdk.ZeroDec())})
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.SetClaim(suite.ctx, c)
|
||||
suite.keeper.SetUSDXMintingClaim(suite.ctx, c)
|
||||
})
|
||||
}
|
||||
claims := types.USDXMintingClaims{}
|
||||
suite.keeper.IterateClaims(suite.ctx, func(c types.USDXMintingClaim) bool {
|
||||
suite.keeper.IterateUSDXMintingClaims(suite.ctx, func(c types.USDXMintingClaim) bool {
|
||||
claims = append(claims, c)
|
||||
return false
|
||||
})
|
||||
suite.Require().Equal(len(suite.addrs), len(claims))
|
||||
|
||||
claims = suite.keeper.GetAllClaims(suite.ctx)
|
||||
claims = suite.keeper.GetAllUSDXMintingClaims(suite.ctx)
|
||||
suite.Require().Equal(len(suite.addrs), len(claims))
|
||||
}
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
||||
func createPeriodicVestingAccount(origVesting sdk.Coins, periods vesting.Periods, startTime, endTime int64) (*vesting.PeriodicVestingAccount, error) {
|
||||
_, addr := app.GeneratePrivKeyAddressPairs(1)
|
||||
bacc := auth.NewBaseAccountWithAddress(addr[0])
|
||||
@ -112,3 +110,7 @@ func i(in int64) sdk.Int { return sdk.NewInt(in) }
|
||||
func d(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) }
|
||||
func c(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) }
|
||||
func cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) }
|
||||
|
||||
func TestKeeperTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(KeeperTestSuite))
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
||||
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||
}
|
||||
|
||||
// GetRewardPeriod returns the reward period with the specified collateral type if it's found in the params
|
||||
func (k Keeper) GetRewardPeriod(ctx sdk.Context, collateralType string) (types.RewardPeriod, bool) {
|
||||
// GetUSDXMintingRewardPeriod returns the reward period with the specified collateral type if it's found in the params
|
||||
func (k Keeper) GetUSDXMintingRewardPeriod(ctx sdk.Context, collateralType string) (types.RewardPeriod, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, rp := range params.RewardPeriods {
|
||||
for _, rp := range params.USDXMintingRewardPeriods {
|
||||
if rp.CollateralType == collateralType {
|
||||
return rp, true
|
||||
}
|
||||
@ -31,6 +31,28 @@ func (k Keeper) GetRewardPeriod(ctx sdk.Context, collateralType string) (types.R
|
||||
return types.RewardPeriod{}, false
|
||||
}
|
||||
|
||||
// GetHardSupplyRewardPeriod returns the reward period with the specified collateral type if it's found in the params
|
||||
func (k Keeper) GetHardSupplyRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, rp := range params.HardSupplyRewardPeriods {
|
||||
if rp.CollateralType == denom {
|
||||
return rp, true
|
||||
}
|
||||
}
|
||||
return types.RewardPeriod{}, false
|
||||
}
|
||||
|
||||
// GetHardBorrowRewardPeriod returns the reward period with the specified collateral type if it's found in the params
|
||||
func (k Keeper) GetHardBorrowRewardPeriod(ctx sdk.Context, denom string) (types.RewardPeriod, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
for _, rp := range params.HardBorrowRewardPeriods {
|
||||
if rp.CollateralType == denom {
|
||||
return rp, true
|
||||
}
|
||||
}
|
||||
return types.RewardPeriod{}, false
|
||||
}
|
||||
|
||||
// GetMultiplier returns the multiplier with the specified name if it's found in the params
|
||||
func (k Keeper) GetMultiplier(ctx sdk.Context, name types.MultiplierName) (types.Multiplier, bool) {
|
||||
params := k.GetParams(ctx)
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
|
||||
// ClaimReward sends the reward amount to the input address and zero's out the claim in the store
|
||||
func (k Keeper) ClaimReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
|
||||
claim, found := k.GetClaim(ctx, addr)
|
||||
claim, found := k.GetUSDXMintingClaim(ctx, addr)
|
||||
if !found {
|
||||
return sdkerrors.Wrapf(types.ErrClaimNotFound, "address: %s", addr)
|
||||
}
|
||||
|
@ -86,18 +86,21 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithCDPGenState()
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
tc.args.multipliers,
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
|
||||
// setup account state
|
||||
sk := suite.app.GetSupplyKeeper()
|
||||
@ -115,15 +118,15 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
|
||||
err = cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
claim, found := suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
|
||||
|
||||
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
|
||||
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
rewardPeriod, found := suite.keeper.GetRewardPeriod(suite.ctx, tc.args.ctype)
|
||||
rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype)
|
||||
suite.Require().True(found)
|
||||
err = suite.keeper.AccumulateRewards(suite.ctx, rewardPeriod)
|
||||
err = suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = suite.keeper.ClaimReward(suite.ctx, suite.addrs[0], tc.args.multiplier)
|
||||
@ -140,7 +143,7 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
|
||||
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
|
||||
}
|
||||
|
||||
claim, found := suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
fmt.Println(claim)
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(c("ukava", 0), claim.Reward)
|
||||
|
@ -46,10 +46,10 @@ func queryGetClaims(ctx sdk.Context, req abci.RequestQuery, k Keeper) ([]byte, e
|
||||
}
|
||||
var claims types.USDXMintingClaims
|
||||
if len(requestParams.Owner) > 0 {
|
||||
claim, _ := k.GetClaim(ctx, requestParams.Owner)
|
||||
claim, _ := k.GetUSDXMintingClaim(ctx, requestParams.Owner)
|
||||
claims = append(claims, claim)
|
||||
} else {
|
||||
claims = k.GetAllClaims(ctx)
|
||||
claims = k.GetAllUSDXMintingClaims(ctx)
|
||||
}
|
||||
|
||||
var paginatedClaims types.USDXMintingClaims
|
||||
|
@ -1,24 +1,22 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
|
||||
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
||||
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
||||
"github.com/kava-labs/kava/x/incentive/types"
|
||||
)
|
||||
|
||||
// AccumulateRewards updates the rewards accumulated for the input reward period
|
||||
func (k Keeper) AccumulateRewards(ctx sdk.Context, rewardPeriod types.RewardPeriod) error {
|
||||
if !rewardPeriod.Active {
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
previousAccrualTime, found := k.GetPreviousAccrualTime(ctx, rewardPeriod.CollateralType)
|
||||
// AccumulateUSDXMintingRewards updates the rewards accumulated for the input reward period
|
||||
func (k Keeper) AccumulateUSDXMintingRewards(ctx sdk.Context, rewardPeriod types.RewardPeriod) error {
|
||||
previousAccrualTime, found := k.GetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
timeElapsed := CalculateTimeElapsed(rewardPeriod, ctx.BlockTime(), previousAccrualTime)
|
||||
@ -26,50 +24,135 @@ func (k Keeper) AccumulateRewards(ctx sdk.Context, rewardPeriod types.RewardPeri
|
||||
return nil
|
||||
}
|
||||
if rewardPeriod.RewardsPerSecond.Amount.IsZero() {
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
totalPrincipal := k.cdpKeeper.GetTotalPrincipal(ctx, rewardPeriod.CollateralType, types.PrincipalDenom).ToDec()
|
||||
if totalPrincipal.IsZero() {
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
newRewards := timeElapsed.Mul(rewardPeriod.RewardsPerSecond.Amount)
|
||||
cdpFactor, found := k.cdpKeeper.GetInterestFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
rewardFactor := newRewards.ToDec().Mul(cdpFactor).Quo(totalPrincipal)
|
||||
|
||||
previousRewardFactor, found := k.GetRewardFactor(ctx, rewardPeriod.CollateralType)
|
||||
previousRewardFactor, found := k.GetUSDXMintingRewardFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
previousRewardFactor = sdk.ZeroDec()
|
||||
}
|
||||
newRewardFactor := previousRewardFactor.Add(rewardFactor)
|
||||
k.SetRewardFactor(ctx, rewardPeriod.CollateralType, newRewardFactor)
|
||||
k.SetPreviousAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
k.SetUSDXMintingRewardFactor(ctx, rewardPeriod.CollateralType, newRewardFactor)
|
||||
k.SetPreviousUSDXMintingAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeClaim creates or updates a claim such that no new rewards are accrued, but any existing rewards are not lost.
|
||||
// AccumulateHardBorrowRewards updates the rewards accumulated for the input reward period
|
||||
func (k Keeper) AccumulateHardBorrowRewards(ctx sdk.Context, rewardPeriod types.RewardPeriod) error {
|
||||
previousAccrualTime, found := k.GetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
timeElapsed := CalculateTimeElapsed(rewardPeriod, ctx.BlockTime(), previousAccrualTime)
|
||||
if timeElapsed.IsZero() {
|
||||
return nil
|
||||
}
|
||||
if rewardPeriod.RewardsPerSecond.Amount.IsZero() {
|
||||
k.SetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
totalBorrowedCoins, foundTotalBorrowedCoins := k.hardKeeper.GetBorrowedCoins(ctx)
|
||||
if foundTotalBorrowedCoins {
|
||||
totalBorrowed := totalBorrowedCoins.AmountOf(rewardPeriod.CollateralType).ToDec()
|
||||
if totalBorrowed.IsZero() {
|
||||
k.SetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
newRewards := timeElapsed.Mul(rewardPeriod.RewardsPerSecond.Amount)
|
||||
hardFactor, found := k.hardKeeper.GetBorrowInterestFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
rewardFactor := newRewards.ToDec().Mul(hardFactor).Quo(totalBorrowed)
|
||||
|
||||
previousRewardFactor, found := k.GetHardBorrowRewardFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
previousRewardFactor = sdk.ZeroDec()
|
||||
}
|
||||
newRewardFactor := previousRewardFactor.Add(rewardFactor)
|
||||
k.SetHardBorrowRewardFactor(ctx, rewardPeriod.CollateralType, newRewardFactor)
|
||||
}
|
||||
k.SetPreviousHardBorrowRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AccumulateHardSupplyRewards updates the rewards accumulated for the input reward period
|
||||
func (k Keeper) AccumulateHardSupplyRewards(ctx sdk.Context, rewardPeriod types.RewardPeriod) error {
|
||||
previousAccrualTime, found := k.GetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
timeElapsed := CalculateTimeElapsed(rewardPeriod, ctx.BlockTime(), previousAccrualTime)
|
||||
if timeElapsed.IsZero() {
|
||||
return nil
|
||||
}
|
||||
if rewardPeriod.RewardsPerSecond.Amount.IsZero() {
|
||||
k.SetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
|
||||
totalSuppliedCoins, foundTotalSuppliedCoins := k.hardKeeper.GetSuppliedCoins(ctx)
|
||||
if foundTotalSuppliedCoins {
|
||||
totalSupplied := totalSuppliedCoins.AmountOf(rewardPeriod.CollateralType).ToDec()
|
||||
if totalSupplied.IsZero() {
|
||||
k.SetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
newRewards := timeElapsed.Mul(rewardPeriod.RewardsPerSecond.Amount)
|
||||
hardFactor, found := k.hardKeeper.GetSupplyInterestFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
k.SetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
return nil
|
||||
}
|
||||
rewardFactor := newRewards.ToDec().Mul(hardFactor).Quo(totalSupplied)
|
||||
|
||||
previousRewardFactor, found := k.GetHardSupplyRewardFactor(ctx, rewardPeriod.CollateralType)
|
||||
if !found {
|
||||
previousRewardFactor = sdk.ZeroDec()
|
||||
}
|
||||
newRewardFactor := previousRewardFactor.Add(rewardFactor)
|
||||
k.SetHardSupplyRewardFactor(ctx, rewardPeriod.CollateralType, newRewardFactor)
|
||||
}
|
||||
k.SetPreviousHardSupplyRewardAccrualTime(ctx, rewardPeriod.CollateralType, ctx.BlockTime())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeUSDXMintingClaim creates or updates a claim such that no new rewards are accrued, but any existing rewards are not lost.
|
||||
// this function should be called after a cdp is created. If a user previously had a cdp, then closed it, they shouldn't
|
||||
// accrue rewards during the period the cdp was closed. By setting the reward factor to the current global reward factor,
|
||||
// any unclaimed rewards are preserved, but no new rewards are added.
|
||||
func (k Keeper) InitializeClaim(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
_, found := k.GetRewardPeriod(ctx, cdp.Type)
|
||||
func (k Keeper) InitializeUSDXMintingClaim(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
_, found := k.GetUSDXMintingRewardPeriod(ctx, cdp.Type)
|
||||
if !found {
|
||||
// this collateral type is not incentivized, do nothing
|
||||
return
|
||||
}
|
||||
rewardFactor, found := k.GetRewardFactor(ctx, cdp.Type)
|
||||
rewardFactor, found := k.GetUSDXMintingRewardFactor(ctx, cdp.Type)
|
||||
if !found {
|
||||
rewardFactor = sdk.ZeroDec()
|
||||
}
|
||||
claim, found := k.GetClaim(ctx, cdp.Owner)
|
||||
claim, found := k.GetUSDXMintingClaim(ctx, cdp.Owner)
|
||||
if !found { // this is the owner's first usdx minting reward claim
|
||||
claim = types.NewUSDXMintingClaim(cdp.Owner, sdk.NewCoin(types.USDXMintingRewardDenom, sdk.ZeroInt()), types.RewardIndexes{types.NewRewardIndex(cdp.Type, rewardFactor)})
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return
|
||||
}
|
||||
// the owner has an existing usdx minting reward claim
|
||||
@ -79,26 +162,26 @@ func (k Keeper) InitializeClaim(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
} else { // the owner has a previous usdx minting reward for this collateral type
|
||||
claim.RewardIndexes[index] = types.NewRewardIndex(cdp.Type, rewardFactor)
|
||||
}
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// SynchronizeReward updates the claim object by adding any accumulated rewards and updating the reward index value.
|
||||
// SynchronizeUSDXMintingReward updates the claim object by adding any accumulated rewards and updating the reward index value.
|
||||
// this should be called before a cdp is modified, immediately after the 'SynchronizeInterest' method is called in the cdp module
|
||||
func (k Keeper) SynchronizeReward(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
_, found := k.GetRewardPeriod(ctx, cdp.Type)
|
||||
func (k Keeper) SynchronizeUSDXMintingReward(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
_, found := k.GetUSDXMintingRewardPeriod(ctx, cdp.Type)
|
||||
if !found {
|
||||
// this collateral type is not incentivized, do nothing
|
||||
return
|
||||
}
|
||||
|
||||
globalRewardFactor, found := k.GetRewardFactor(ctx, cdp.Type)
|
||||
globalRewardFactor, found := k.GetUSDXMintingRewardFactor(ctx, cdp.Type)
|
||||
if !found {
|
||||
globalRewardFactor = sdk.ZeroDec()
|
||||
}
|
||||
claim, found := k.GetClaim(ctx, cdp.Owner)
|
||||
claim, found := k.GetUSDXMintingClaim(ctx, cdp.Owner)
|
||||
if !found {
|
||||
claim = types.NewUSDXMintingClaim(cdp.Owner, sdk.NewCoin(types.USDXMintingRewardDenom, sdk.ZeroInt()), types.RewardIndexes{types.NewRewardIndex(cdp.Type, globalRewardFactor)})
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return
|
||||
}
|
||||
|
||||
@ -106,7 +189,7 @@ func (k Keeper) SynchronizeReward(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
index, hasRewardIndex := claim.HasRewardIndex(cdp.Type)
|
||||
if !hasRewardIndex { // this is the owner's first usdx minting reward for this collateral type
|
||||
claim.RewardIndexes = append(claim.RewardIndexes, types.NewRewardIndex(cdp.Type, globalRewardFactor))
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return
|
||||
}
|
||||
userRewardFactor := claim.RewardIndexes[index].RewardFactor
|
||||
@ -115,21 +198,212 @@ func (k Keeper) SynchronizeReward(ctx sdk.Context, cdp cdptypes.CDP) {
|
||||
return
|
||||
}
|
||||
claim.RewardIndexes[index].RewardFactor = globalRewardFactor
|
||||
newRewardsAmount := cdp.GetTotalPrincipal().Amount.ToDec().Quo(cdp.InterestFactor).Mul(rewardsAccumulatedFactor).RoundInt()
|
||||
newRewardsAmount := rewardsAccumulatedFactor.Mul(cdp.GetTotalPrincipal().Amount.ToDec()).RoundInt()
|
||||
if newRewardsAmount.IsZero() {
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return
|
||||
}
|
||||
newRewardsCoin := sdk.NewCoin(types.USDXMintingRewardDenom, newRewardsAmount)
|
||||
claim.Reward = claim.Reward.Add(newRewardsCoin)
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return
|
||||
}
|
||||
|
||||
// InitializeHardSupplyReward initializes the supply-side of a hard liquidity provider claim
|
||||
// by creating the claim and setting the supply reward factor index
|
||||
func (k Keeper) InitializeHardSupplyReward(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
var supplyRewardIndexes types.RewardIndexes
|
||||
for _, coin := range deposit.Amount {
|
||||
_, rpFound := k.GetHardSupplyRewardPeriod(ctx, coin.Denom)
|
||||
if !rpFound {
|
||||
continue
|
||||
}
|
||||
|
||||
supplyFactor, foundSupplyFactor := k.GetHardSupplyRewardFactor(ctx, coin.Denom)
|
||||
if !foundSupplyFactor {
|
||||
supplyFactor = sdk.ZeroDec()
|
||||
}
|
||||
|
||||
supplyRewardIndexes = append(supplyRewardIndexes, types.NewRewardIndex(coin.Denom, supplyFactor))
|
||||
}
|
||||
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, deposit.Depositor)
|
||||
if found {
|
||||
// Reset borrow reward indexes
|
||||
claim.BorrowRewardIndexes = types.RewardIndexes{}
|
||||
} else {
|
||||
// Instantiate claim object
|
||||
claim = types.NewHardLiquidityProviderClaim(deposit.Depositor,
|
||||
sdk.NewCoin(types.HardLiquidityRewardDenom, sdk.ZeroInt()),
|
||||
nil, nil, nil)
|
||||
}
|
||||
|
||||
claim.SupplyRewardIndexes = supplyRewardIndexes
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// SynchronizeHardSupplyReward updates the claim object by adding any accumulated rewards
|
||||
// and updating the reward index value
|
||||
func (k Keeper) SynchronizeHardSupplyReward(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, deposit.Depositor)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
for _, coin := range deposit.Amount {
|
||||
supplyFactor, found := k.GetHardSupplyRewardFactor(ctx, coin.Denom)
|
||||
if !found {
|
||||
fmt.Printf("\n[LOG]: %s does not have a supply factor", coin.Denom) // TODO: remove before production
|
||||
continue
|
||||
}
|
||||
|
||||
supplyIndex, hasSupplyRewardIndex := claim.HasSupplyRewardIndex(coin.Denom)
|
||||
if !hasSupplyRewardIndex {
|
||||
continue
|
||||
}
|
||||
|
||||
userRewardFactor := claim.SupplyRewardIndexes[supplyIndex].RewardFactor
|
||||
rewardsAccumulatedFactor := supplyFactor.Sub(userRewardFactor)
|
||||
if rewardsAccumulatedFactor.IsZero() {
|
||||
continue
|
||||
}
|
||||
claim.SupplyRewardIndexes[supplyIndex].RewardFactor = supplyFactor
|
||||
|
||||
newRewardsAmount := rewardsAccumulatedFactor.Mul(deposit.Amount.AmountOf(coin.Denom).ToDec()).RoundInt()
|
||||
if newRewardsAmount.IsZero() || newRewardsAmount.IsNegative() {
|
||||
continue
|
||||
}
|
||||
|
||||
newRewardsCoin := sdk.NewCoin(types.HardLiquidityRewardDenom, newRewardsAmount)
|
||||
claim.Reward = claim.Reward.Add(newRewardsCoin)
|
||||
}
|
||||
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// InitializeHardBorrowReward initializes the borrow-side of a hard liquidity provider claim
|
||||
// by creating the claim and setting the borrow reward factor index
|
||||
func (k Keeper) InitializeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, borrow.Borrower)
|
||||
if !found {
|
||||
claim = types.NewHardLiquidityProviderClaim(borrow.Borrower,
|
||||
sdk.NewCoin(types.HardLiquidityRewardDenom, sdk.ZeroInt()),
|
||||
nil, nil, nil)
|
||||
}
|
||||
|
||||
var borrowRewardIndexes types.RewardIndexes
|
||||
for _, coin := range borrow.Amount {
|
||||
_, rpFound := k.GetHardBorrowRewardPeriod(ctx, coin.Denom)
|
||||
if !rpFound {
|
||||
continue
|
||||
}
|
||||
|
||||
borrowFactor, foundBorrowFactor := k.GetHardBorrowRewardFactor(ctx, coin.Denom)
|
||||
if !foundBorrowFactor {
|
||||
borrowFactor = sdk.ZeroDec()
|
||||
}
|
||||
|
||||
borrowRewardIndexes = append(borrowRewardIndexes, types.NewRewardIndex(coin.Denom, borrowFactor))
|
||||
}
|
||||
|
||||
claim.BorrowRewardIndexes = borrowRewardIndexes
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// SynchronizeHardBorrowReward updates the claim object by adding any accumulated rewards
|
||||
// and updating the reward index value
|
||||
func (k Keeper) SynchronizeHardBorrowReward(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, borrow.Borrower)
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
|
||||
for _, coin := range borrow.Amount {
|
||||
borrowFactor, found := k.GetHardBorrowRewardFactor(ctx, coin.Denom)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
borrowIndex, BorrowRewardIndex := claim.HasBorrowRewardIndex(coin.Denom)
|
||||
if !BorrowRewardIndex {
|
||||
continue
|
||||
}
|
||||
|
||||
userRewardFactor := claim.BorrowRewardIndexes[borrowIndex].RewardFactor
|
||||
rewardsAccumulatedFactor := borrowFactor.Sub(userRewardFactor)
|
||||
if rewardsAccumulatedFactor.IsZero() {
|
||||
continue
|
||||
}
|
||||
claim.BorrowRewardIndexes[borrowIndex].RewardFactor = borrowFactor
|
||||
|
||||
newRewardsAmount := rewardsAccumulatedFactor.Mul(borrow.Amount.AmountOf(coin.Denom).ToDec()).RoundInt()
|
||||
if newRewardsAmount.IsZero() || newRewardsAmount.IsNegative() {
|
||||
continue
|
||||
}
|
||||
|
||||
newRewardsCoin := sdk.NewCoin(types.HardLiquidityRewardDenom, newRewardsAmount)
|
||||
claim.Reward = claim.Reward.Add(newRewardsCoin)
|
||||
}
|
||||
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// UpdateHardSupplyIndexDenoms adds any new deposit denoms to the claim's supply reward index
|
||||
func (k Keeper) UpdateHardSupplyIndexDenoms(ctx sdk.Context, deposit hardtypes.Deposit) {
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, deposit.Depositor)
|
||||
if !found {
|
||||
claim = types.NewHardLiquidityProviderClaim(deposit.Depositor,
|
||||
sdk.NewCoin(types.HardLiquidityRewardDenom, sdk.ZeroInt()),
|
||||
nil, nil, nil)
|
||||
}
|
||||
|
||||
supplyRewardIndexes := claim.SupplyRewardIndexes
|
||||
for _, coin := range deposit.Amount {
|
||||
_, hasIndex := claim.HasSupplyRewardIndex(coin.Denom)
|
||||
if !hasIndex {
|
||||
supplyFactor, foundSupplyFactor := k.GetHardSupplyRewardFactor(ctx, coin.Denom)
|
||||
if foundSupplyFactor {
|
||||
supplyRewardIndexes = append(supplyRewardIndexes, types.NewRewardIndex(coin.Denom, supplyFactor))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(supplyRewardIndexes) == 0 {
|
||||
return
|
||||
}
|
||||
claim.SupplyRewardIndexes = supplyRewardIndexes
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// UpdateHardBorrowIndexDenoms adds any new borrow denoms to the claim's supply reward index
|
||||
func (k Keeper) UpdateHardBorrowIndexDenoms(ctx sdk.Context, borrow hardtypes.Borrow) {
|
||||
claim, found := k.GetHardLiquidityProviderClaim(ctx, borrow.Borrower)
|
||||
if !found {
|
||||
claim = types.NewHardLiquidityProviderClaim(borrow.Borrower,
|
||||
sdk.NewCoin(types.HardLiquidityRewardDenom, sdk.ZeroInt()),
|
||||
nil, nil, nil)
|
||||
}
|
||||
|
||||
borrowRewardIndexes := claim.BorrowRewardIndexes
|
||||
for _, coin := range borrow.Amount {
|
||||
_, hasIndex := claim.HasBorrowRewardIndex(coin.Denom)
|
||||
if !hasIndex {
|
||||
borrowFactor, foundBorrowFactor := k.GetHardBorrowRewardFactor(ctx, coin.Denom)
|
||||
if foundBorrowFactor {
|
||||
borrowRewardIndexes = append(borrowRewardIndexes, types.NewRewardIndex(coin.Denom, borrowFactor))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(borrowRewardIndexes) == 0 {
|
||||
return
|
||||
}
|
||||
claim.BorrowRewardIndexes = borrowRewardIndexes
|
||||
k.SetHardLiquidityProviderClaim(ctx, claim)
|
||||
}
|
||||
|
||||
// ZeroClaim zeroes out the claim object's rewards and returns the updated claim object
|
||||
func (k Keeper) ZeroClaim(ctx sdk.Context, claim types.USDXMintingClaim) types.USDXMintingClaim {
|
||||
claim.Reward = sdk.NewCoin(claim.Reward.Denom, sdk.ZeroInt())
|
||||
k.SetClaim(ctx, claim)
|
||||
k.SetUSDXMintingClaim(ctx, claim)
|
||||
return claim
|
||||
}
|
||||
|
||||
@ -149,8 +423,8 @@ func (k Keeper) SynchronizeClaim(ctx sdk.Context, claim types.USDXMintingClaim)
|
||||
|
||||
// this function assumes a claim already exists, so don't call it if that's not the case
|
||||
func (k Keeper) synchronizeRewardAndReturnClaim(ctx sdk.Context, cdp cdptypes.CDP) types.USDXMintingClaim {
|
||||
k.SynchronizeReward(ctx, cdp)
|
||||
claim, _ := k.GetClaim(ctx, cdp.Owner)
|
||||
k.SynchronizeUSDXMintingReward(ctx, cdp)
|
||||
claim, _ := k.GetUSDXMintingClaim(ctx, cdp.Owner)
|
||||
return claim
|
||||
}
|
||||
|
||||
|
@ -1,23 +1,21 @@
|
||||
package keeper_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
cdpkeeper "github.com/kava-labs/kava/x/cdp/keeper"
|
||||
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
||||
"github.com/kava-labs/kava/x/hard"
|
||||
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
||||
"github.com/kava-labs/kava/x/incentive/types"
|
||||
)
|
||||
|
||||
func (suite *KeeperTestSuite) TestAccumulateRewards() {
|
||||
func (suite *KeeperTestSuite) TestAccumulateUSDXMintingRewards() {
|
||||
type args struct {
|
||||
ctype string
|
||||
rewardsPerSecond sdk.Coin
|
||||
@ -67,7 +65,7 @@ func (suite *KeeperTestSuite) TestAccumulateRewards() {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithCDPGenState()
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// setup cdp state
|
||||
@ -76,28 +74,31 @@ func (suite *KeeperTestSuite) TestAccumulateRewards() {
|
||||
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
|
||||
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
|
||||
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
rewardPeriod, found := suite.keeper.GetRewardPeriod(suite.ctx, tc.args.ctype)
|
||||
rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(suite.ctx, tc.args.ctype)
|
||||
suite.Require().True(found)
|
||||
err := suite.keeper.AccumulateRewards(suite.ctx, rewardPeriod)
|
||||
err := suite.keeper.AccumulateUSDXMintingRewards(suite.ctx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
rewardFactor, found := suite.keeper.GetRewardFactor(suite.ctx, tc.args.ctype)
|
||||
rewardFactor, found := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSyncRewards() {
|
||||
func (suite *KeeperTestSuite) TestSynchronizeUSDXMintingReward() {
|
||||
type args struct {
|
||||
ctype string
|
||||
rewardsPerSecond sdk.Coin
|
||||
@ -143,18 +144,21 @@ func (suite *KeeperTestSuite) TestSyncRewards() {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithCDPGenState()
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.ctype, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
suite.keeper.SetPreviousUSDXMintingAccrualTime(suite.ctx, tc.args.ctype, tc.args.initialTime)
|
||||
suite.keeper.SetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype, sdk.ZeroDec())
|
||||
|
||||
// setup account state
|
||||
sk := suite.app.GetSupplyKeeper()
|
||||
@ -166,7 +170,7 @@ func (suite *KeeperTestSuite) TestSyncRewards() {
|
||||
err := cdpKeeper.AddCdp(suite.ctx, suite.addrs[0], tc.args.initialCollateral, tc.args.initialPrincipal, tc.args.ctype)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
claim, found := suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(sdk.ZeroDec(), claim.RewardIndexes[0].RewardFactor)
|
||||
|
||||
@ -177,9 +181,9 @@ func (suite *KeeperTestSuite) TestSyncRewards() {
|
||||
updatedBlockTime := previousBlockTime.Add(time.Duration(int(time.Second) * t))
|
||||
previousBlockTime = updatedBlockTime
|
||||
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
rewardPeriod, found := suite.keeper.GetRewardPeriod(blockCtx, tc.args.ctype)
|
||||
rewardPeriod, found := suite.keeper.GetUSDXMintingRewardPeriod(blockCtx, tc.args.ctype)
|
||||
suite.Require().True(found)
|
||||
err := suite.keeper.AccumulateRewards(blockCtx, rewardPeriod)
|
||||
err := suite.keeper.AccumulateUSDXMintingRewards(blockCtx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
|
||||
@ -187,137 +191,711 @@ func (suite *KeeperTestSuite) TestSyncRewards() {
|
||||
cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(suite.ctx, suite.addrs[0], tc.args.ctype)
|
||||
suite.Require().True(found)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.SynchronizeReward(suite.ctx, cdp)
|
||||
suite.keeper.SynchronizeUSDXMintingReward(suite.ctx, cdp)
|
||||
})
|
||||
|
||||
rewardFactor, found := suite.keeper.GetRewardFactor(suite.ctx, tc.args.ctype)
|
||||
rewardFactor, found := suite.keeper.GetUSDXMintingRewardFactor(suite.ctx, tc.args.ctype)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
|
||||
claim, found = suite.keeper.GetClaim(suite.ctx, suite.addrs[0])
|
||||
fmt.Println(claim)
|
||||
claim, found = suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, claim.RewardIndexes[0].RewardFactor)
|
||||
suite.Require().Equal(tc.args.expectedRewards, claim.Reward)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestRewardCalculation(t *testing.T) {
|
||||
func (suite *KeeperTestSuite) TestAccumulateHardBorrowRewards() {
|
||||
type args struct {
|
||||
borrow sdk.Coin
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
timeElapsed int
|
||||
expectedRewardFactor sdk.Dec
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
testCases := []test{
|
||||
{
|
||||
"7 seconds",
|
||||
args{
|
||||
borrow: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 7,
|
||||
expectedRewardFactor: d("0.000000856478000001"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"1 day",
|
||||
args{
|
||||
borrow: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 86400,
|
||||
expectedRewardFactor: d("0.010571385600010177"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"0 seconds",
|
||||
args{
|
||||
borrow: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 0,
|
||||
expectedRewardFactor: d("0.0"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Test Params
|
||||
ctype := "bnb-a"
|
||||
initialTime := time.Date(1998, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
rewardsPerSecond := c("ukava", 122_354)
|
||||
initialCollateral := c("bnb", 10_000_000_000)
|
||||
initialPrincipal := c("usdx", 100_000_000)
|
||||
oneYear := time.Hour * 24 * 365
|
||||
rewardPeriod := types.NewRewardPeriod(
|
||||
true,
|
||||
ctype,
|
||||
initialTime,
|
||||
initialTime.Add(4*oneYear),
|
||||
rewardsPerSecond,
|
||||
)
|
||||
// Mint coins to hard module account
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// Setup app and module params
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: initialTime})
|
||||
tApp.InitializeFromGenesisStates(
|
||||
app.NewAuthGenState(addrs[:1], []sdk.Coins{cs(initialCollateral)}),
|
||||
NewPricefeedGenStateMulti(),
|
||||
NewCDPGenStateHighInterest(),
|
||||
NewIncentiveGenState(initialTime, initialTime.Add(oneYear), rewardPeriod),
|
||||
)
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardBorrowRewardFactor(suite.ctx, tc.args.borrow.Denom, sdk.ZeroDec())
|
||||
|
||||
// Create a CDP
|
||||
cdpKeeper := tApp.GetCDPKeeper()
|
||||
err := cdpKeeper.AddCdp(
|
||||
ctx,
|
||||
addrs[0],
|
||||
initialCollateral,
|
||||
initialPrincipal,
|
||||
ctype,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
// Set up hard state (interest factor for the relevant denom)
|
||||
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
|
||||
|
||||
// Calculate expected cdp reward using iteration
|
||||
// User deposits and borrows to increase total borrowed amount
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
|
||||
suite.Require().NoError(err)
|
||||
err = hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Use 10 blocks, each a very long 630720s, to total 6307200s or 1/5th of a year
|
||||
// The cdp stability fee is set to the max value 500%, so this time ensures the debt increases a significant amount (doubles)
|
||||
// High stability fees increase the chance of catching calculation bugs.
|
||||
blockTimes := newRepeatingSliceInt(630720, 10)
|
||||
expectedCDPReward := sdk.ZeroDec() //c(rewardPeriod.RewardsPerSecond.Denom, 0)
|
||||
for _, bt := range blockTimes {
|
||||
ctx = ctx.WithBlockTime(ctx.BlockTime().Add(time.Duration(int(time.Second) * bt)))
|
||||
// Set up chain context at future time
|
||||
runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
|
||||
runCtx := suite.ctx.WithBlockTime(runAtTime)
|
||||
|
||||
// run cdp and incentive begin blockers to update factors
|
||||
tApp.BeginBlocker(ctx, abci.RequestBeginBlock{})
|
||||
// Run Hard begin blocker in order to update the denom's index factor
|
||||
hard.BeginBlocker(runCtx, suite.hardKeeper)
|
||||
|
||||
// calculate expected cdp reward
|
||||
cdpBlockReward, err := calculateCDPBlockReward(ctx, cdpKeeper, addrs[0], ctype, sdk.NewInt(int64(bt)), rewardPeriod)
|
||||
require.NoError(t, err)
|
||||
expectedCDPReward = expectedCDPReward.Add(cdpBlockReward)
|
||||
rewardPeriod, found := suite.keeper.GetHardBorrowRewardPeriod(runCtx, tc.args.borrow.Denom)
|
||||
suite.Require().True(found)
|
||||
err = suite.keeper.AccumulateHardBorrowRewards(runCtx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
rewardFactor, found := suite.keeper.GetHardBorrowRewardFactor(runCtx, tc.args.borrow.Denom)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSynchronizeHardBorrowReward() {
|
||||
type args struct {
|
||||
borrow sdk.Coin
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
blockTimes []int
|
||||
expectedRewardFactor sdk.Dec
|
||||
expectedRewards sdk.Coin
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
|
||||
// calculate cdp reward using factor
|
||||
cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(ctx, addrs[0], ctype)
|
||||
require.True(t, found)
|
||||
incentiveKeeper := tApp.GetIncentiveKeeper()
|
||||
require.NotPanics(t, func() {
|
||||
incentiveKeeper.SynchronizeReward(ctx, cdp)
|
||||
})
|
||||
claim, found := incentiveKeeper.GetClaim(ctx, addrs[0])
|
||||
require.True(t, found)
|
||||
|
||||
// Compare two methods of calculation
|
||||
relativeError := expectedCDPReward.Sub(claim.Reward.Amount.ToDec()).Quo(expectedCDPReward).Abs()
|
||||
maxError := d("0.0001")
|
||||
require.Truef(t, relativeError.LT(maxError),
|
||||
"percent diff %s > %s , expected: %s, actual %s,", relativeError, maxError, expectedCDPReward, claim.Reward.Amount,
|
||||
)
|
||||
}
|
||||
|
||||
// calculateCDPBlockReward computes the reward that should be distributed to a cdp for the current block.
|
||||
func calculateCDPBlockReward(ctx sdk.Context, cdpKeeper cdpkeeper.Keeper, owner sdk.AccAddress, ctype string, timeElapsed sdk.Int, rewardPeriod types.RewardPeriod) (sdk.Dec, error) {
|
||||
// Calculate total rewards to distribute this block
|
||||
newRewards := timeElapsed.Mul(rewardPeriod.RewardsPerSecond.Amount)
|
||||
|
||||
// Calculate cdp's share of total debt
|
||||
totalPrincipal := cdpKeeper.GetTotalPrincipal(ctx, ctype, types.PrincipalDenom).ToDec()
|
||||
// cdpDebt
|
||||
cdp, found := cdpKeeper.GetCdpByOwnerAndCollateralType(ctx, owner, ctype)
|
||||
if !found {
|
||||
return sdk.Dec{}, fmt.Errorf("couldn't find cdp for owner '%s' and collateral type '%s'", owner, ctype)
|
||||
testCases := []test{
|
||||
{
|
||||
"10 blocks",
|
||||
args{
|
||||
borrow: c("bnb", 10000000000), // TODO: 2 decimal diff from TestAccumulateHardBorrowRewards's borrow
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
||||
expectedRewardFactor: d("0.001223540000173228"),
|
||||
expectedRewards: c("hard", 12235400),
|
||||
},
|
||||
},
|
||||
{
|
||||
"10 blocks - long block time",
|
||||
args{
|
||||
borrow: c("bnb", 10000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
|
||||
expectedRewardFactor: d("10.571385603126235340"),
|
||||
expectedRewards: c("hard", 105713856031),
|
||||
},
|
||||
},
|
||||
}
|
||||
accumulatedInterest := cdpKeeper.CalculateNewInterest(ctx, cdp)
|
||||
cdpDebt := cdp.Principal.Add(cdp.AccumulatedFees).Add(accumulatedInterest).Amount
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Calculate cdp's reward
|
||||
return newRewards.Mul(cdpDebt).ToDec().Quo(totalPrincipal), nil
|
||||
// Mint coins to hard module account
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.borrow.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardBorrowRewardFactor(suite.ctx, tc.args.borrow.Denom, sdk.ZeroDec())
|
||||
|
||||
// Set up hard state (interest factor for the relevant denom)
|
||||
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.borrow.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.borrow.Denom, tc.args.initialTime)
|
||||
|
||||
// User deposits and borrows to increase total borrowed amount
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(sdk.NewCoin(tc.args.borrow.Denom, tc.args.borrow.Amount.Mul(sdk.NewInt(2)))))
|
||||
suite.Require().NoError(err)
|
||||
err = hardKeeper.Borrow(suite.ctx, userAddr, sdk.NewCoins(tc.args.borrow))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Check that Hard hooks initialized a HardLiquidityProviderClaim
|
||||
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(sdk.ZeroDec(), claim.BorrowRewardIndexes[0].RewardFactor)
|
||||
|
||||
// Run accumulator at several intervals
|
||||
var timeElapsed int
|
||||
previousBlockTime := suite.ctx.BlockTime()
|
||||
for _, t := range tc.args.blockTimes {
|
||||
timeElapsed += t
|
||||
updatedBlockTime := previousBlockTime.Add(time.Duration(int(time.Second) * t))
|
||||
previousBlockTime = updatedBlockTime
|
||||
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
|
||||
// Run Hard begin blocker for each block ctx to update denom's interest factor
|
||||
hard.BeginBlocker(blockCtx, suite.hardKeeper)
|
||||
|
||||
rewardPeriod, found := suite.keeper.GetHardBorrowRewardPeriod(blockCtx, tc.args.borrow.Denom)
|
||||
suite.Require().True(found)
|
||||
|
||||
err := suite.keeper.AccumulateHardBorrowRewards(blockCtx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
|
||||
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
|
||||
// After we've accumulated, run synchronize
|
||||
borrow, found := hardKeeper.GetBorrow(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.SynchronizeHardBorrowReward(suite.ctx, borrow)
|
||||
})
|
||||
|
||||
// Check that reward factor and claim have been updated as expected
|
||||
rewardFactor, found := suite.keeper.GetHardBorrowRewardFactor(suite.ctx, tc.args.borrow.Denom)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
|
||||
claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, claim.BorrowRewardIndexes[0].RewardFactor)
|
||||
suite.Require().Equal(tc.args.expectedRewards, claim.Reward)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupWithCDPGenState() {
|
||||
func (suite *KeeperTestSuite) TestAccumulateHardSupplyRewards() {
|
||||
type args struct {
|
||||
deposit sdk.Coin
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
timeElapsed int
|
||||
expectedRewardFactor sdk.Dec
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
testCases := []test{
|
||||
{
|
||||
"7 seconds",
|
||||
args{
|
||||
deposit: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 7,
|
||||
expectedRewardFactor: d("0.000000856478000000"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"1 day",
|
||||
args{
|
||||
deposit: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 86400,
|
||||
expectedRewardFactor: d("0.010571385600000000"),
|
||||
},
|
||||
},
|
||||
{
|
||||
"0 seconds",
|
||||
args{
|
||||
deposit: c("bnb", 1000000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
timeElapsed: 0,
|
||||
expectedRewardFactor: d("0.0"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Mint coins to hard module account
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// Set up incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardSupplyRewardFactor(suite.ctx, tc.args.deposit.Denom, sdk.ZeroDec())
|
||||
|
||||
// Set up hard state (interest factor for the relevant denom)
|
||||
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.deposit.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
|
||||
|
||||
// User deposits to increase total supplied amount
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Set up chain context at future time
|
||||
runAtTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * tc.args.timeElapsed))
|
||||
runCtx := suite.ctx.WithBlockTime(runAtTime)
|
||||
|
||||
// Run Hard begin blocker in order to update the denom's index factor
|
||||
hard.BeginBlocker(runCtx, suite.hardKeeper)
|
||||
|
||||
rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriod(runCtx, tc.args.deposit.Denom)
|
||||
suite.Require().True(found)
|
||||
err = suite.keeper.AccumulateHardSupplyRewards(runCtx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
rewardFactor, found := suite.keeper.GetHardSupplyRewardFactor(runCtx, tc.args.deposit.Denom)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestSynchronizeHardSupplyReward() {
|
||||
type args struct {
|
||||
deposit sdk.Coin
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
blockTimes []int
|
||||
expectedRewardFactor sdk.Dec
|
||||
expectedRewards sdk.Coin
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
|
||||
testCases := []test{
|
||||
{
|
||||
"10 blocks",
|
||||
args{
|
||||
deposit: c("bnb", 10000000000), // TODO: 2 decimal diff
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
blockTimes: []int{10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
||||
expectedRewardFactor: d("0.001223540000000000"),
|
||||
expectedRewards: c("hard", 12235400),
|
||||
},
|
||||
},
|
||||
{
|
||||
"10 blocks - long block time",
|
||||
args{
|
||||
deposit: c("bnb", 10000000000),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
blockTimes: []int{86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400, 86400},
|
||||
expectedRewardFactor: d("10.571385600000000000"),
|
||||
expectedRewards: c("hard", 105713856000),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Mint coins to hard module account
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// setup incentive state
|
||||
params := types.NewParams(
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.RewardPeriods{types.NewRewardPeriod(true, tc.args.deposit.Denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)},
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardSupplyRewardFactor(suite.ctx, tc.args.deposit.Denom, sdk.ZeroDec())
|
||||
|
||||
// Set up hard state (interest factor for the relevant denom)
|
||||
suite.hardKeeper.SetSupplyInterestFactor(suite.ctx, tc.args.deposit.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetBorrowInterestFactor(suite.ctx, tc.args.deposit.Denom, sdk.MustNewDecFromStr("1.0"))
|
||||
suite.hardKeeper.SetPreviousAccrualTime(suite.ctx, tc.args.deposit.Denom, tc.args.initialTime)
|
||||
|
||||
// User deposits and borrows to increase total borrowed amount
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, sdk.NewCoins(tc.args.deposit))
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Check that Hard hooks initialized a HardLiquidityProviderClaim
|
||||
claim, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(sdk.ZeroDec(), claim.SupplyRewardIndexes[0].RewardFactor)
|
||||
|
||||
// Run accumulator at several intervals
|
||||
var timeElapsed int
|
||||
previousBlockTime := suite.ctx.BlockTime()
|
||||
for _, t := range tc.args.blockTimes {
|
||||
timeElapsed += t
|
||||
updatedBlockTime := previousBlockTime.Add(time.Duration(int(time.Second) * t))
|
||||
previousBlockTime = updatedBlockTime
|
||||
blockCtx := suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
|
||||
// Run Hard begin blocker for each block ctx to update denom's interest factor
|
||||
hard.BeginBlocker(blockCtx, suite.hardKeeper)
|
||||
|
||||
rewardPeriod, found := suite.keeper.GetHardSupplyRewardPeriod(blockCtx, tc.args.deposit.Denom)
|
||||
suite.Require().True(found)
|
||||
|
||||
err := suite.keeper.AccumulateHardSupplyRewards(blockCtx, rewardPeriod)
|
||||
suite.Require().NoError(err)
|
||||
}
|
||||
updatedBlockTime := suite.ctx.BlockTime().Add(time.Duration(int(time.Second) * timeElapsed))
|
||||
suite.ctx = suite.ctx.WithBlockTime(updatedBlockTime)
|
||||
|
||||
// After we've accumulated, run synchronize
|
||||
deposit, found := hardKeeper.GetDeposit(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().NotPanics(func() {
|
||||
suite.keeper.SynchronizeHardSupplyReward(suite.ctx, deposit)
|
||||
})
|
||||
|
||||
// Check that reward factor and claim have been updated as expected
|
||||
rewardFactor, found := suite.keeper.GetHardSupplyRewardFactor(suite.ctx, tc.args.deposit.Denom)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, rewardFactor)
|
||||
|
||||
claim, found = suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(tc.args.expectedRewardFactor, claim.SupplyRewardIndexes[0].RewardFactor)
|
||||
suite.Require().Equal(tc.args.expectedRewards, claim.Reward)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestUpdateHardSupplyIndexDenoms() {
|
||||
type args struct {
|
||||
firstDeposit sdk.Coins
|
||||
secondDeposit sdk.Coins
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
expectedSupplyIndexDenoms []string
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
|
||||
testCases := []test{
|
||||
{
|
||||
"update adds one supply reward index",
|
||||
args{
|
||||
firstDeposit: cs(c("bnb", 10000000000)),
|
||||
secondDeposit: cs(c("ukava", 10000000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedSupplyIndexDenoms: []string{"bnb", "ukava"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update adds multiple supply reward indexes",
|
||||
args{
|
||||
firstDeposit: cs(c("bnb", 10000000000)),
|
||||
secondDeposit: cs(c("ukava", 10000000000), c("btcb", 10000000000), c("xrp", 10000000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedSupplyIndexDenoms: []string{"bnb", "ukava", "btcb", "xrp"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update doesn't add duplicate supply reward index for same denom",
|
||||
args{
|
||||
firstDeposit: cs(c("bnb", 10000000000)),
|
||||
secondDeposit: cs(c("bnb", 5000000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedSupplyIndexDenoms: []string{"bnb"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Mint coins to hard module account
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := sdk.NewCoins(sdk.NewCoin("usdx", sdk.NewInt(200000000)))
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// Set up generic reward periods
|
||||
var rewardPeriods types.RewardPeriods
|
||||
for _, denom := range tc.args.expectedSupplyIndexDenoms {
|
||||
rewardPeriod := types.NewRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
|
||||
rewardPeriods = append(rewardPeriods, rewardPeriod)
|
||||
}
|
||||
|
||||
// Setup incentive state
|
||||
params := types.NewParams(
|
||||
rewardPeriods, rewardPeriods, rewardPeriods, rewardPeriods,
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
|
||||
// Set each denom's previous accrual time and supply reward factor
|
||||
for _, denom := range tc.args.expectedSupplyIndexDenoms {
|
||||
suite.keeper.SetPreviousHardSupplyRewardAccrualTime(suite.ctx, denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardSupplyRewardFactor(suite.ctx, denom, sdk.ZeroDec())
|
||||
}
|
||||
|
||||
// User deposits (first time)
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.firstDeposit)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Confirm that a claim was created and populated with the correct supply indexes
|
||||
claimAfterFirstDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
for _, coin := range tc.args.firstDeposit {
|
||||
_, hasIndex := claimAfterFirstDeposit.HasSupplyRewardIndex(coin.Denom)
|
||||
suite.Require().True(hasIndex)
|
||||
}
|
||||
suite.Require().True(len(claimAfterFirstDeposit.SupplyRewardIndexes) == len(tc.args.firstDeposit))
|
||||
|
||||
// User deposits (second time)
|
||||
err = hardKeeper.Deposit(suite.ctx, userAddr, tc.args.secondDeposit)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Confirm that the claim contains all expected supply indexes
|
||||
claimAfterSecondDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
for _, denom := range tc.args.expectedSupplyIndexDenoms {
|
||||
_, hasIndex := claimAfterSecondDeposit.HasSupplyRewardIndex(denom)
|
||||
suite.Require().True(hasIndex)
|
||||
}
|
||||
suite.Require().True(len(claimAfterSecondDeposit.SupplyRewardIndexes) == len(tc.args.expectedSupplyIndexDenoms))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) TestUpdateHardBorrowIndexDenoms() {
|
||||
type args struct {
|
||||
initialDeposit sdk.Coins
|
||||
firstBorrow sdk.Coins
|
||||
secondBorrow sdk.Coins
|
||||
rewardsPerSecond sdk.Coin
|
||||
initialTime time.Time
|
||||
expectedBorrowIndexDenoms []string
|
||||
}
|
||||
type test struct {
|
||||
name string
|
||||
args args
|
||||
}
|
||||
|
||||
testCases := []test{
|
||||
{
|
||||
"update adds one borrow reward index",
|
||||
args{
|
||||
initialDeposit: cs(c("bnb", 10000000000)),
|
||||
firstBorrow: cs(c("bnb", 50000000)),
|
||||
secondBorrow: cs(c("ukava", 500000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedBorrowIndexDenoms: []string{"bnb", "ukava"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update adds multiple borrow supply reward indexes",
|
||||
args{
|
||||
initialDeposit: cs(c("btcb", 10000000000)),
|
||||
firstBorrow: cs(c("btcb", 50000000)),
|
||||
secondBorrow: cs(c("ukava", 500000000), c("bnb", 50000000000), c("xrp", 50000000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedBorrowIndexDenoms: []string{"btcb", "ukava", "bnb", "xrp"},
|
||||
},
|
||||
},
|
||||
{
|
||||
"update doesn't add duplicate borrow reward index for same denom",
|
||||
args{
|
||||
initialDeposit: cs(c("bnb", 100000000000)),
|
||||
firstBorrow: cs(c("bnb", 50000000)),
|
||||
secondBorrow: cs(c("bnb", 50000000000)),
|
||||
rewardsPerSecond: c("hard", 122354),
|
||||
initialTime: time.Date(2020, 12, 15, 14, 0, 0, 0, time.UTC),
|
||||
expectedBorrowIndexDenoms: []string{"bnb"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
suite.SetupWithGenState()
|
||||
suite.ctx = suite.ctx.WithBlockTime(tc.args.initialTime)
|
||||
|
||||
// Mint coins to hard module account so it can service borrow requests
|
||||
supplyKeeper := suite.app.GetSupplyKeeper()
|
||||
hardMaccCoins := tc.args.firstBorrow.Add(tc.args.secondBorrow...)
|
||||
supplyKeeper.MintCoins(suite.ctx, hardtypes.ModuleAccountName, hardMaccCoins)
|
||||
|
||||
// Set up generic reward periods
|
||||
var rewardPeriods types.RewardPeriods
|
||||
for _, denom := range tc.args.expectedBorrowIndexDenoms {
|
||||
rewardPeriod := types.NewRewardPeriod(true, denom, tc.args.initialTime, tc.args.initialTime.Add(time.Hour*24*365*4), tc.args.rewardsPerSecond)
|
||||
rewardPeriods = append(rewardPeriods, rewardPeriod)
|
||||
}
|
||||
|
||||
// Setup incentive state
|
||||
params := types.NewParams(
|
||||
rewardPeriods, rewardPeriods, rewardPeriods, rewardPeriods,
|
||||
types.Multipliers{types.NewMultiplier(types.MultiplierName("small"), 1, d("0.25")), types.NewMultiplier(types.MultiplierName("large"), 12, d("1.0"))},
|
||||
tc.args.initialTime.Add(time.Hour*24*365*5),
|
||||
)
|
||||
suite.keeper.SetParams(suite.ctx, params)
|
||||
// Set each initial deposit denom's previous accrual time and supply reward factor
|
||||
for _, coin := range tc.args.initialDeposit {
|
||||
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, coin.Denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardBorrowRewardFactor(suite.ctx, coin.Denom, sdk.ZeroDec())
|
||||
}
|
||||
|
||||
// Set each expected borrow denom's previous accrual time and borrow reward factor
|
||||
for _, denom := range tc.args.expectedBorrowIndexDenoms {
|
||||
suite.keeper.SetPreviousHardBorrowRewardAccrualTime(suite.ctx, denom, tc.args.initialTime)
|
||||
suite.keeper.SetHardBorrowRewardFactor(suite.ctx, denom, sdk.ZeroDec())
|
||||
}
|
||||
|
||||
// User deposits initial funds (so that user can borrow)
|
||||
hardKeeper := suite.app.GetHardKeeper()
|
||||
userAddr := suite.addrs[3]
|
||||
err := hardKeeper.Deposit(suite.ctx, userAddr, tc.args.initialDeposit)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Confirm that claim exists but no borrow reward indexes have been added
|
||||
claimAfterDeposit, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
suite.Require().Equal(0, len(claimAfterDeposit.BorrowRewardIndexes))
|
||||
|
||||
// User borrows (first time)
|
||||
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.firstBorrow)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Confirm that claim's borrow reward indexes have been updated
|
||||
claimAfterFirstBorrow, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
for _, coin := range tc.args.firstBorrow {
|
||||
_, hasIndex := claimAfterFirstBorrow.HasBorrowRewardIndex(coin.Denom)
|
||||
suite.Require().True(hasIndex)
|
||||
}
|
||||
suite.Require().True(len(claimAfterFirstBorrow.BorrowRewardIndexes) == len(tc.args.firstBorrow))
|
||||
|
||||
// User borrows (second time)
|
||||
err = hardKeeper.Borrow(suite.ctx, userAddr, tc.args.secondBorrow)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// Confirm that claim's borrow reward indexes contain expected values
|
||||
claimAfterSecondBorrow, found := suite.keeper.GetHardLiquidityProviderClaim(suite.ctx, suite.addrs[3])
|
||||
suite.Require().True(found)
|
||||
for _, coin := range tc.args.secondBorrow {
|
||||
_, hasIndex := claimAfterSecondBorrow.HasBorrowRewardIndex(coin.Denom)
|
||||
suite.Require().True(hasIndex)
|
||||
}
|
||||
suite.Require().True(len(claimAfterSecondBorrow.BorrowRewardIndexes) == len(tc.args.expectedBorrowIndexDenoms))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *KeeperTestSuite) SetupWithGenState() {
|
||||
tApp := app.NewTestApp()
|
||||
ctx := tApp.NewContext(true, abci.Header{Height: 1, Time: tmtime.Now()})
|
||||
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||
|
||||
authGS := app.NewAuthGenState(
|
||||
[]sdk.AccAddress{addrs[3]},
|
||||
[]sdk.Coins{
|
||||
sdk.NewCoins(
|
||||
sdk.NewCoin("bnb", sdk.NewInt(1000000000000000)),
|
||||
sdk.NewCoin("ukava", sdk.NewInt(1000000000000000)),
|
||||
sdk.NewCoin("btcb", sdk.NewInt(1000000000000000)),
|
||||
sdk.NewCoin("xrp", sdk.NewInt(1000000000000000)),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
tApp.InitializeFromGenesisStates(
|
||||
authGS,
|
||||
NewPricefeedGenStateMulti(),
|
||||
NewCDPGenStateMulti(),
|
||||
NewHardGenStateMulti(),
|
||||
)
|
||||
_, addrs := app.GeneratePrivKeyAddressPairs(5)
|
||||
|
||||
keeper := tApp.GetIncentiveKeeper()
|
||||
hardKeeper := tApp.GetHardKeeper()
|
||||
suite.app = tApp
|
||||
suite.ctx = ctx
|
||||
suite.keeper = keeper
|
||||
suite.hardKeeper = hardKeeper
|
||||
suite.addrs = addrs
|
||||
}
|
||||
|
||||
// newRepeatingSliceInt creates a slice of the specified length containing a single repeating element.
|
||||
func newRepeatingSliceInt(element int, length int) []int {
|
||||
slice := make([]int, length)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = element
|
||||
}
|
||||
return slice
|
||||
}
|
||||
|
@ -17,24 +17,66 @@ import (
|
||||
func DecodeStore(cdc *codec.Codec, kvA, kvB kv.Pair) string {
|
||||
switch {
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.ClaimKeyPrefix):
|
||||
case bytes.Equal(kvA.Key[:1], types.USDXMintingClaimKeyPrefix):
|
||||
var claimA, claimB types.USDXMintingClaim
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &claimA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &claimB)
|
||||
return fmt.Sprintf("%v\n%v", claimA, claimB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.BlockTimeKey):
|
||||
case bytes.Equal(kvA.Key[:1], types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix):
|
||||
var timeA, timeB time.Time
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
|
||||
case bytes.Equal(kvA.Key[:1], types.RewardFactorKey):
|
||||
case bytes.Equal(kvA.Key[:1], types.USDXMintingRewardFactorKeyPrefix):
|
||||
var factorA, factorB sdk.Dec
|
||||
cdc.MustUnmarshalBinaryBare(kvA.Value, &factorA)
|
||||
cdc.MustUnmarshalBinaryBare(kvB.Value, &factorB)
|
||||
return fmt.Sprintf("%s\n%s", factorA, factorB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.HardLiquidityClaimKeyPrefix):
|
||||
// var claimA, claimB types.HardLiquidityProviderClaim
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &claimA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &claimB)
|
||||
// return fmt.Sprintf("%v\n%v", claimA, claimB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.PreviousHardSupplyRewardAccrualTimeKeyPrefix):
|
||||
// var timeA, timeB time.Time
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
// return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.HardSupplyRewardFactorKeyPrefix):
|
||||
// var factorA, factorB sdk.Dec
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &factorA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &factorB)
|
||||
// return fmt.Sprintf("%s\n%s", factorA, factorB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.PreviousHardBorrowRewardAccrualTimeKeyPrefix):
|
||||
// var timeA, timeB time.Time
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
// return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.HardSupplyRewardFactorKeyPrefix):
|
||||
// var factorA, factorB sdk.Dec
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &factorA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &factorB)
|
||||
// return fmt.Sprintf("%s\n%s", factorA, factorB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.PreviousHardDelegatorRewardAccrualTimeKeyPrefix):
|
||||
// var timeA, timeB time.Time
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &timeA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &timeB)
|
||||
// return fmt.Sprintf("%s\n%s", timeA, timeB)
|
||||
|
||||
// case bytes.Equal(kvA.Key[:1], types.HardDelegatorRewardFactorKeyPrefix):
|
||||
// var factorA, factorB sdk.Dec
|
||||
// cdc.MustUnmarshalBinaryBare(kvA.Value, &factorA)
|
||||
// cdc.MustUnmarshalBinaryBare(kvB.Value, &factorB)
|
||||
// return fmt.Sprintf("%s\n%s", factorA, factorB)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid %s key prefix %X", types.ModuleName, kvA.Key[:1]))
|
||||
}
|
||||
|
@ -30,9 +30,16 @@ func TestDecodeDistributionStore(t *testing.T) {
|
||||
factor := sdk.ZeroDec()
|
||||
|
||||
kvPairs := kv.Pairs{
|
||||
kv.Pair{Key: types.ClaimKeyPrefix, Value: cdc.MustMarshalBinaryBare(claim)},
|
||||
kv.Pair{Key: []byte(types.BlockTimeKey), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
kv.Pair{Key: []byte(types.RewardFactorKey), Value: cdc.MustMarshalBinaryBare(factor)},
|
||||
kv.Pair{Key: types.USDXMintingClaimKeyPrefix, Value: cdc.MustMarshalBinaryBare(claim)},
|
||||
kv.Pair{Key: []byte(types.PreviousUSDXMintingRewardAccrualTimeKeyPrefix), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
kv.Pair{Key: []byte(types.USDXMintingRewardFactorKeyPrefix), Value: cdc.MustMarshalBinaryBare(factor)},
|
||||
// kv.Pair{Key: types.HardLiquidityClaimKeyPrefix, Value: cdc.MustMarshalBinaryBare(claim)},
|
||||
// kv.Pair{Key: []byte(types.HardSupplyRewardFactorKeyPrefix), Value: cdc.MustMarshalBinaryBare(factor)},
|
||||
// kv.Pair{Key: []byte(types.PreviousHardSupplyRewardAccrualTimeKeyPrefix), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
// kv.Pair{Key: []byte(types.HardBorrowRewardFactorKeyPrefix), Value: cdc.MustMarshalBinaryBare(factor)},
|
||||
// kv.Pair{Key: []byte(types.PreviousHardBorrowRewardAccrualTimeKeyPrefix), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
// kv.Pair{Key: []byte(types.HardDelegatorRewardFactorKeyPrefix), Value: cdc.MustMarshalBinaryBare(factor)},
|
||||
// kv.Pair{Key: []byte(types.PreviousHardDelegatorRewardAccrualTimeKeyPrefix), Value: cdc.MustMarshalBinaryBare(prevBlockTime)},
|
||||
kv.Pair{Key: []byte{0x99}, Value: []byte{0x99}},
|
||||
}
|
||||
|
||||
@ -40,9 +47,16 @@ func TestDecodeDistributionStore(t *testing.T) {
|
||||
name string
|
||||
expectedLog string
|
||||
}{
|
||||
{"Claim", fmt.Sprintf("%v\n%v", claim, claim)},
|
||||
{"PreviousBlockTime", fmt.Sprintf("%v\n%v", prevBlockTime, prevBlockTime)},
|
||||
{"RewardFactor", fmt.Sprintf("%v\n%v", factor, factor)},
|
||||
{"USDXMintingClaim", fmt.Sprintf("%v\n%v", claim, claim)},
|
||||
{"PreviousUSDXMintingRewardAccrualTime", fmt.Sprintf("%v\n%v", prevBlockTime, prevBlockTime)},
|
||||
{"USDXMintingRewardFactor", fmt.Sprintf("%v\n%v", factor, factor)},
|
||||
// {"HardLiquidityClaim", fmt.Sprintf("%v\n%v", claim, claim)},
|
||||
// {"PreviousHardSupplyRewardAccrualTime", fmt.Sprintf("%v\n%v", prevBlockTime, prevBlockTime)},
|
||||
// {"HardSupplyRewardFactor", fmt.Sprintf("%v\n%v", factor, factor)},
|
||||
// {"PreviousHardBorrowRewardAccrualTime", fmt.Sprintf("%v\n%v", prevBlockTime, prevBlockTime)},
|
||||
// {"HardBorrowRewardFactor", fmt.Sprintf("%v\n%v", factor, factor)},
|
||||
// {"PreviousHardDelegatorRewardAccrualTime", fmt.Sprintf("%v\n%v", prevBlockTime, prevBlockTime)},
|
||||
// {"HardSupplyDelegatorFactor", fmt.Sprintf("%v\n%v", factor, factor)},
|
||||
{"other", ""},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
|
@ -8,40 +8,91 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// USDXMintingClaim stores the usdx mintng rewards that can be claimed by owner
|
||||
type USDXMintingClaim struct {
|
||||
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
|
||||
Reward sdk.Coin `json:"reward" yaml:"reward"`
|
||||
RewardIndexes RewardIndexes `json:"reward_indexes" yaml:"reward_indexes"`
|
||||
const (
|
||||
USDXMintingClaimType = "usdx_minting"
|
||||
HardLiquidityProviderClaimType = "hard_liquidity_provider"
|
||||
)
|
||||
|
||||
// Claim is an interface for handling common claim actions
|
||||
type Claim interface {
|
||||
GetOwner() sdk.AccAddress
|
||||
GetReward() sdk.Coin
|
||||
GetType() string
|
||||
}
|
||||
|
||||
// NewUSDXMintingClaim returns a new USDXMintingClaim
|
||||
func NewUSDXMintingClaim(owner sdk.AccAddress, reward sdk.Coin, rewardIndexes RewardIndexes) USDXMintingClaim {
|
||||
return USDXMintingClaim{
|
||||
Owner: owner,
|
||||
Reward: reward,
|
||||
RewardIndexes: rewardIndexes,
|
||||
}
|
||||
// Claims is a slice of Claim
|
||||
type Claims []Claim
|
||||
|
||||
// BaseClaim is a common type shared by all Claims
|
||||
type BaseClaim struct {
|
||||
Owner sdk.AccAddress `json:"owner" yaml:"owner"`
|
||||
Reward sdk.Coin `json:"reward" yaml:"reward"`
|
||||
}
|
||||
|
||||
// Validate performs a basic check of a Claim fields.
|
||||
func (c USDXMintingClaim) Validate() error {
|
||||
// GetOwner is a getter for Claim Owner
|
||||
func (c BaseClaim) GetOwner() sdk.AccAddress { return c.Owner }
|
||||
|
||||
// GetReward is a getter for Claim Reward
|
||||
func (c BaseClaim) GetReward() sdk.Coin { return c.Reward }
|
||||
|
||||
// GetType returns the claim type, used to identify auctions in event attributes
|
||||
func (c BaseClaim) GetType() string { return "base" }
|
||||
|
||||
// Validate performs a basic check of a BaseClaim fields
|
||||
func (c BaseClaim) Validate() error {
|
||||
if c.Owner.Empty() {
|
||||
return errors.New("claim owner cannot be empty")
|
||||
}
|
||||
if !c.Reward.IsValid() {
|
||||
return fmt.Errorf("invalid reward amount: %s", c.Reward)
|
||||
}
|
||||
return c.RewardIndexes.Validate()
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (c BaseClaim) String() string {
|
||||
return fmt.Sprintf(`Claim:
|
||||
Owner: %s,
|
||||
Reward: %s,
|
||||
`, c.Owner, c.Reward)
|
||||
}
|
||||
|
||||
// -------------- Custom Claim Types --------------
|
||||
|
||||
// USDXMintingClaim is for USDX minting rewards
|
||||
type USDXMintingClaim struct {
|
||||
BaseClaim `json:"base_claim" yaml:"base_claim"`
|
||||
RewardIndexes RewardIndexes `json:"reward_indexes" yaml:"reward_indexes"`
|
||||
}
|
||||
|
||||
// NewUSDXMintingClaim returns a new USDXMintingClaim
|
||||
func NewUSDXMintingClaim(owner sdk.AccAddress, reward sdk.Coin, rewardIndexes RewardIndexes) USDXMintingClaim {
|
||||
return USDXMintingClaim{
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: owner,
|
||||
Reward: reward,
|
||||
},
|
||||
RewardIndexes: rewardIndexes,
|
||||
}
|
||||
}
|
||||
|
||||
// GetType returns the claim type, used to identify auctions in event attributes
|
||||
func (c USDXMintingClaim) GetType() string { return USDXMintingClaimType }
|
||||
|
||||
// Validate performs a basic check of a Claim fields
|
||||
func (c USDXMintingClaim) Validate() error {
|
||||
if err := c.RewardIndexes.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.BaseClaim.Validate()
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (c USDXMintingClaim) String() string {
|
||||
return fmt.Sprintf(`Claim:
|
||||
Owner: %s,
|
||||
Reward: %s,
|
||||
return fmt.Sprintf(`%s
|
||||
Reward Indexes: %s,
|
||||
`, c.Owner, c.Reward, c.RewardIndexes)
|
||||
`, c.BaseClaim, c.RewardIndexes)
|
||||
}
|
||||
|
||||
// HasRewardIndex check if a claim has a reward index for the input collateral type
|
||||
@ -54,7 +105,7 @@ func (c USDXMintingClaim) HasRewardIndex(collateralType string) (int64, bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// USDXMintingClaims array of USDXMintingClaim
|
||||
// USDXMintingClaims slice of USDXMintingClaim
|
||||
type USDXMintingClaims []USDXMintingClaim
|
||||
|
||||
// Validate checks if all the claims are valid and there are no duplicated
|
||||
@ -69,6 +120,106 @@ func (cs USDXMintingClaims) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HardLiquidityProviderClaim stores the hard liquidity provider rewards that can be claimed by owner
|
||||
type HardLiquidityProviderClaim struct {
|
||||
BaseClaim `json:"base_claim" yaml:"base_claim"`
|
||||
SupplyRewardIndexes RewardIndexes `json:"supply_reward_indexes" yaml:"supply_reward_indexes"`
|
||||
BorrowRewardIndexes RewardIndexes `json:"borrow_reward_indexes" yaml:"borrow_reward_indexes"`
|
||||
DelegationRewardIndexes RewardIndexes `json:"delegation_reward_indexes" yaml:"delegation_reward_indexes"`
|
||||
}
|
||||
|
||||
// NewHardLiquidityProviderClaim returns a new HardLiquidityProviderClaim
|
||||
func NewHardLiquidityProviderClaim(owner sdk.AccAddress, reward sdk.Coin, supplyRewardIndexes,
|
||||
borrowRewardIndexes, delegationRewardIndexes RewardIndexes) HardLiquidityProviderClaim {
|
||||
return HardLiquidityProviderClaim{
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: owner,
|
||||
Reward: reward,
|
||||
},
|
||||
SupplyRewardIndexes: supplyRewardIndexes,
|
||||
BorrowRewardIndexes: borrowRewardIndexes,
|
||||
DelegationRewardIndexes: delegationRewardIndexes,
|
||||
}
|
||||
}
|
||||
|
||||
// GetType returns the claim type, used to identify auctions in event attributes
|
||||
func (c HardLiquidityProviderClaim) GetType() string { return HardLiquidityProviderClaimType }
|
||||
|
||||
// Validate performs a basic check of a HardLiquidityProviderClaim fields
|
||||
func (c HardLiquidityProviderClaim) Validate() error {
|
||||
if err := c.SupplyRewardIndexes.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.BorrowRewardIndexes.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.DelegationRewardIndexes.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.BaseClaim.Validate()
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (c HardLiquidityProviderClaim) String() string {
|
||||
return fmt.Sprintf(`%s
|
||||
Supply Reward Indexes: %s,
|
||||
Borrow Reward Indexes: %s,
|
||||
Delegation Reward Indexes: %s,
|
||||
`, c.BaseClaim, c.SupplyRewardIndexes, c.BorrowRewardIndexes, c.DelegationRewardIndexes)
|
||||
}
|
||||
|
||||
// HasSupplyRewardIndex check if a claim has a supply reward index for the input collateral type
|
||||
func (c HardLiquidityProviderClaim) HasSupplyRewardIndex(denom string) (int64, bool) {
|
||||
for index, ri := range c.SupplyRewardIndexes {
|
||||
if ri.CollateralType == denom {
|
||||
return int64(index), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HasBorrowRewardIndex check if a claim has a borrow reward index for the input collateral type
|
||||
func (c HardLiquidityProviderClaim) HasBorrowRewardIndex(denom string) (int64, bool) {
|
||||
for index, ri := range c.BorrowRewardIndexes {
|
||||
if ri.CollateralType == denom {
|
||||
return int64(index), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HasDelegationRewardIndex check if a claim has a delegation reward index for the input collateral type
|
||||
func (c HardLiquidityProviderClaim) HasDelegationRewardIndex(collateralType string) (int64, bool) {
|
||||
for index, ri := range c.SupplyRewardIndexes {
|
||||
if ri.CollateralType == collateralType {
|
||||
return int64(index), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// HardLiquidityProviderClaims slice of HardLiquidityProviderClaim
|
||||
type HardLiquidityProviderClaims []HardLiquidityProviderClaim
|
||||
|
||||
// Validate checks if all the claims are valid and there are no duplicated
|
||||
// entries.
|
||||
func (cs HardLiquidityProviderClaims) Validate() error {
|
||||
for _, c := range cs {
|
||||
if err := c.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// -------------- Subcomponents of Custom Claim Types --------------
|
||||
|
||||
// TODO: refactor RewardPeriod name from 'collateralType' to 'denom'
|
||||
|
||||
// RewardIndex stores reward accumulation information
|
||||
type RewardIndex struct {
|
||||
CollateralType string `json:"collateral_type" yaml:"collateral_type"`
|
||||
|
@ -28,7 +28,11 @@ func TestClaimsValidate(t *testing.T) {
|
||||
{
|
||||
"invalid owner",
|
||||
USDXMintingClaims{
|
||||
{Owner: nil},
|
||||
USDXMintingClaim{
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
@ -36,8 +40,10 @@ func TestClaimsValidate(t *testing.T) {
|
||||
"invalid reward",
|
||||
USDXMintingClaims{
|
||||
{
|
||||
Owner: owner,
|
||||
Reward: sdk.Coin{Denom: "", Amount: sdk.ZeroInt()},
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: owner,
|
||||
Reward: sdk.Coin{Denom: "", Amount: sdk.ZeroInt()},
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
@ -46,8 +52,10 @@ func TestClaimsValidate(t *testing.T) {
|
||||
"invalid collateral type",
|
||||
USDXMintingClaims{
|
||||
{
|
||||
Owner: owner,
|
||||
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: owner,
|
||||
Reward: sdk.NewCoin("bnb", sdk.OneInt()),
|
||||
},
|
||||
RewardIndexes: []RewardIndex{{"", sdk.ZeroDec()}},
|
||||
},
|
||||
},
|
||||
|
@ -14,5 +14,10 @@ func init() {
|
||||
|
||||
// RegisterCodec registers the necessary types for incentive module
|
||||
func RegisterCodec(cdc *codec.Codec) {
|
||||
cdc.RegisterInterface((*Claim)(nil), nil)
|
||||
cdc.RegisterConcrete(USDXMintingClaim{}, "incentive/USDXMintingClaim", nil)
|
||||
cdc.RegisterConcrete(HardLiquidityProviderClaim{}, "incentive/HardLiquidityProviderClaim", nil)
|
||||
|
||||
// Register msgs
|
||||
cdc.RegisterConcrete(MsgClaimUSDXMintingReward{}, "incentive/MsgClaimUSDXMintingReward", nil)
|
||||
}
|
||||
|
@ -6,23 +6,31 @@ import (
|
||||
supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported"
|
||||
|
||||
cdptypes "github.com/kava-labs/kava/x/cdp/types"
|
||||
hardtypes "github.com/kava-labs/kava/x/hard/types"
|
||||
)
|
||||
|
||||
// SupplyKeeper defines the expected supply keeper for module accounts
|
||||
type SupplyKeeper interface {
|
||||
GetModuleAccount(ctx sdk.Context, name string) supplyexported.ModuleAccountI
|
||||
|
||||
SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
|
||||
}
|
||||
|
||||
// CdpKeeper defines the expected cdp keeper for interacting with cdps
|
||||
type CdpKeeper interface {
|
||||
GetInterestFactor(ctx sdk.Context, collateralType string) (sdk.Dec, bool)
|
||||
GetTotalPrincipal(ctx sdk.Context, collateralType string, principalDenom string) (total sdk.Int)
|
||||
GetCdpByOwnerAndCollateralType(ctx sdk.Context, owner sdk.AccAddress, collateralType string) (cdptypes.CDP, bool)
|
||||
GetInterestFactor(ctx sdk.Context, collateralType string) (sdk.Dec, bool)
|
||||
GetCollateral(ctx sdk.Context, collateralType string) (cdptypes.CollateralParam, bool)
|
||||
}
|
||||
|
||||
// HardKeeper defines the expected hard keeper for interacting with Hard protocol
|
||||
type HardKeeper interface {
|
||||
GetSupplyInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool)
|
||||
GetBorrowInterestFactor(ctx sdk.Context, denom string) (sdk.Dec, bool)
|
||||
GetBorrowedCoins(ctx sdk.Context) (coins sdk.Coins, found bool)
|
||||
GetSuppliedCoins(ctx sdk.Context) (coins sdk.Coins, found bool)
|
||||
}
|
||||
|
||||
// AccountKeeper defines the expected keeper interface for interacting with account
|
||||
type AccountKeeper interface {
|
||||
GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account
|
||||
@ -34,3 +42,13 @@ type CDPHooks interface {
|
||||
AfterCDPCreated(ctx sdk.Context, cdp cdptypes.CDP)
|
||||
BeforeCDPModified(ctx sdk.Context, cdp cdptypes.CDP)
|
||||
}
|
||||
|
||||
// HARDHooks event hooks for other keepers to run code in response to HARD modifications
|
||||
type HARDHooks interface {
|
||||
AfterDepositCreated(ctx sdk.Context, deposit hardtypes.Deposit)
|
||||
BeforeDepositModified(ctx sdk.Context, deposit hardtypes.Deposit)
|
||||
AfterDepositModified(ctx sdk.Context, deposit hardtypes.Deposit)
|
||||
AfterBorrowCreated(ctx sdk.Context, borrow hardtypes.Borrow)
|
||||
BeforeBorrowModified(ctx sdk.Context, borrow hardtypes.Borrow)
|
||||
AfterBorrowModified(ctx sdk.Context, deposit hardtypes.Deposit)
|
||||
}
|
||||
|
@ -89,9 +89,6 @@ func (gats GenesisAccumulationTimes) Validate() error {
|
||||
|
||||
// Validate performs validation of GenesisAccumulationTime
|
||||
func (gat GenesisAccumulationTime) Validate() error {
|
||||
if gat.RewardFactor == (sdk.Dec{}) {
|
||||
return fmt.Errorf("reward factor not initialized for %s", gat.CollateralType)
|
||||
}
|
||||
if gat.RewardFactor.LT(sdk.ZeroDec()) {
|
||||
return fmt.Errorf("reward factor should be ≥ 0.0, is %s for %s", gat.RewardFactor, gat.CollateralType)
|
||||
}
|
||||
|
@ -5,11 +5,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
func TestGenesisStateValidate(t *testing.T) {
|
||||
@ -44,7 +42,24 @@ func TestGenesisStateValidate(t *testing.T) {
|
||||
{
|
||||
name: "valid",
|
||||
args: args{
|
||||
params: NewParams(RewardPeriods{NewRewardPeriod(true, "bnb-a", time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 10, 15, 14, 0, 0, 0, time.UTC), sdk.NewCoin("ukava", sdk.NewInt(25000)))}, Multipliers{NewMultiplier(Small, 1, sdk.MustNewDecFromStr("0.33"))}, time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC)),
|
||||
params: NewParams(
|
||||
RewardPeriods{
|
||||
NewRewardPeriod(
|
||||
true,
|
||||
"bnb-a",
|
||||
time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
time.Date(2024, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
sdk.NewCoin("ukava", sdk.NewInt(25000)),
|
||||
),
|
||||
},
|
||||
DefaultRewardPeriods,
|
||||
DefaultRewardPeriods,
|
||||
DefaultRewardPeriods,
|
||||
Multipliers{
|
||||
NewMultiplier(Small, 1, sdk.MustNewDecFromStr("0.33")),
|
||||
},
|
||||
time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
),
|
||||
genAccTimes: GenesisAccumulationTimes{GenesisAccumulationTime{
|
||||
CollateralType: "bnb-a",
|
||||
PreviousAccumulationTime: time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
@ -52,9 +67,10 @@ func TestGenesisStateValidate(t *testing.T) {
|
||||
}},
|
||||
claims: USDXMintingClaims{
|
||||
{
|
||||
Owner: sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))),
|
||||
Reward: sdk.NewCoin("ukava", sdk.NewInt(100000000)),
|
||||
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: sdk.AccAddress(crypto.AddressHash([]byte("KavaTestUser1"))),
|
||||
Reward: sdk.NewCoin("ukava", sdk.NewInt(100000000)),
|
||||
},
|
||||
RewardIndexes: []RewardIndex{
|
||||
{
|
||||
CollateralType: "bnb-a",
|
||||
@ -86,24 +102,6 @@ func TestGenesisStateValidate(t *testing.T) {
|
||||
contains: "reward factor should be ≥ 0.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid genesis accumulation time",
|
||||
args: args{
|
||||
params: DefaultParams(),
|
||||
genAccTimes: GenesisAccumulationTimes{
|
||||
{
|
||||
CollateralType: "btcb-a",
|
||||
PreviousAccumulationTime: time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
RewardFactor: sdk.Dec{},
|
||||
},
|
||||
},
|
||||
claims: DefaultClaims,
|
||||
},
|
||||
errArgs: errArgs{
|
||||
expectPass: false,
|
||||
contains: "reward factor not initialized",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid claim",
|
||||
args: args{
|
||||
@ -111,9 +109,10 @@ func TestGenesisStateValidate(t *testing.T) {
|
||||
genAccTimes: DefaultGenesisAccumulationTimes,
|
||||
claims: USDXMintingClaims{
|
||||
{
|
||||
Owner: sdk.AccAddress{},
|
||||
Reward: sdk.NewCoin("ukava", sdk.NewInt(100000000)),
|
||||
|
||||
BaseClaim: BaseClaim{
|
||||
Owner: sdk.AccAddress{},
|
||||
Reward: sdk.NewCoin("ukava", sdk.NewInt(100000000)),
|
||||
},
|
||||
RewardIndexes: []RewardIndex{
|
||||
{
|
||||
CollateralType: "bnb-a",
|
||||
|
@ -17,11 +17,23 @@ const (
|
||||
QuerierRoute = ModuleName
|
||||
)
|
||||
|
||||
// TODO: Refactor so that each incentive type has:
|
||||
// 1. [Incentive]ClaimKeyPrefix
|
||||
// 2. [Incentve]AccumulatorKeyPrefix { PreviousAccrualTime block.Time, IndexFactors types.IndexFactors }
|
||||
|
||||
// Key Prefixes
|
||||
var (
|
||||
ClaimKeyPrefix = []byte{0x01} // prefix for keys that store claims
|
||||
BlockTimeKey = []byte{0x02} // prefix for key that stores the blocktime
|
||||
RewardFactorKey = []byte{0x03} // prefix for key that stores reward factors
|
||||
USDXMintingClaimKeyPrefix = []byte{0x01} // prefix for keys that store USDX minting claims
|
||||
USDXMintingRewardFactorKeyPrefix = []byte{0x02} // prefix for key that stores USDX minting reward factors
|
||||
PreviousUSDXMintingRewardAccrualTimeKeyPrefix = []byte{0x03} // prefix for key that stores the blocktime
|
||||
HardLiquidityClaimKeyPrefix = []byte{0x04} // prefix for keys that store Hard liquidity claims
|
||||
HardSupplyRewardFactorKeyPrefix = []byte{0x05} // prefix for key that stores Hard supply reward factors
|
||||
PreviousHardSupplyRewardAccrualTimeKeyPrefix = []byte{0x06} // prefix for key that stores the previous time Hard supply rewards accrued
|
||||
HardBorrowRewardFactorKeyPrefix = []byte{0x07} // prefix for key that stores Hard borrow reward factors
|
||||
PreviousHardBorrowRewardAccrualTimeKeyPrefix = []byte{0x08} // prefix for key that stores the previous time Hard borrow rewards accrued
|
||||
HardDelegatorRewardFactorKeyPrefix = []byte{0x09} // prefix for key that stores Hard delegator reward factors
|
||||
PreviousHardDelegatorRewardAccrualTimeKeyPrefix = []byte{0x10} // prefix for key that stores the previous time Hard delegator rewards accrued
|
||||
|
||||
USDXMintingRewardDenom = "ukava"
|
||||
USDXMintingRewardDenom = "ukava"
|
||||
HardLiquidityRewardDenom = "hard"
|
||||
)
|
||||
|
@ -24,8 +24,10 @@ const (
|
||||
|
||||
// Parameter keys and default values
|
||||
var (
|
||||
KeyActive = []byte("Active")
|
||||
KeyRewards = []byte("RewardPeriods")
|
||||
KeyUSDXMintingRewardPeriods = []byte("USDXMintingRewardPeriods")
|
||||
KeyHardSupplyRewardPeriods = []byte("HardSupplyRewardPeriods")
|
||||
KeyHardBorrowRewardPeriods = []byte("HardBorrowRewardPeriods")
|
||||
KeyHardDelegatorRewardPeriods = []byte("HardDelegatorRewardPeriods")
|
||||
KeyClaimEnd = []byte("ClaimEnd")
|
||||
KeyMultipliers = []byte("ClaimMultipliers")
|
||||
DefaultActive = false
|
||||
@ -41,32 +43,44 @@ var (
|
||||
|
||||
// Params governance parameters for the incentive module
|
||||
type Params struct {
|
||||
RewardPeriods RewardPeriods `json:"reward_periods" yaml:"reward_periods"`
|
||||
ClaimMultipliers Multipliers `json:"claim_multipliers" yaml:"claim_multipliers"`
|
||||
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
|
||||
USDXMintingRewardPeriods RewardPeriods `json:"usdx_minting_reward_periods" yaml:"usdx_minting_reward_periods"`
|
||||
HardSupplyRewardPeriods RewardPeriods `json:"hard_supply_reward_periods" yaml:"hard_supply_reward_periods"`
|
||||
HardBorrowRewardPeriods RewardPeriods `json:"hard_borrow_reward_periods" yaml:"hard_borrow_reward_periods"`
|
||||
HardDelegatorRewardPeriods RewardPeriods `json:"hard_delegator_reward_periods" yaml:"hard_delegator_reward_periods"`
|
||||
ClaimMultipliers Multipliers `json:"claim_multipliers" yaml:"claim_multipliers"`
|
||||
ClaimEnd time.Time `json:"claim_end" yaml:"claim_end"`
|
||||
}
|
||||
|
||||
// NewParams returns a new params object
|
||||
func NewParams(rewards RewardPeriods, multipliers Multipliers, claimEnd time.Time) Params {
|
||||
func NewParams(usdxMinting, hardSupply, hardBorrow, hardDelegator RewardPeriods,
|
||||
multipliers Multipliers, claimEnd time.Time) Params {
|
||||
return Params{
|
||||
RewardPeriods: rewards,
|
||||
ClaimMultipliers: multipliers,
|
||||
ClaimEnd: claimEnd,
|
||||
USDXMintingRewardPeriods: usdxMinting,
|
||||
HardSupplyRewardPeriods: hardSupply,
|
||||
HardBorrowRewardPeriods: hardBorrow,
|
||||
HardDelegatorRewardPeriods: hardDelegator,
|
||||
ClaimMultipliers: multipliers,
|
||||
ClaimEnd: claimEnd,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultParams returns default params for incentive module
|
||||
func DefaultParams() Params {
|
||||
return NewParams(DefaultRewardPeriods, DefaultMultipliers, DefaultClaimEnd)
|
||||
return NewParams(DefaultRewardPeriods, DefaultRewardPeriods,
|
||||
DefaultRewardPeriods, DefaultRewardPeriods, DefaultMultipliers, DefaultClaimEnd)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer
|
||||
func (p Params) String() string {
|
||||
return fmt.Sprintf(`Params:
|
||||
Rewards: %s
|
||||
USDX Minting Reward Periods: %s
|
||||
Hard Supply Reward Periods: %s
|
||||
Hard Borrow Reward Periods: %s
|
||||
Hard Delegator Reward Periods: %s
|
||||
Claim Multipliers :%s
|
||||
Claim End Time: %s
|
||||
`, p.RewardPeriods, p.ClaimMultipliers, p.ClaimEnd)
|
||||
`, p.USDXMintingRewardPeriods, p.HardSupplyRewardPeriods, p.HardBorrowRewardPeriods,
|
||||
p.HardDelegatorRewardPeriods, p.ClaimMultipliers, p.ClaimEnd)
|
||||
}
|
||||
|
||||
// ParamKeyTable Key declaration for parameters
|
||||
@ -77,7 +91,10 @@ func ParamKeyTable() params.KeyTable {
|
||||
// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs
|
||||
func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||
return params.ParamSetPairs{
|
||||
params.NewParamSetPair(KeyRewards, &p.RewardPeriods, validateRewardsParam),
|
||||
params.NewParamSetPair(KeyUSDXMintingRewardPeriods, &p.USDXMintingRewardPeriods, validateRewardPeriodsParam),
|
||||
params.NewParamSetPair(KeyHardSupplyRewardPeriods, &p.HardSupplyRewardPeriods, validateRewardPeriodsParam),
|
||||
params.NewParamSetPair(KeyHardBorrowRewardPeriods, &p.HardBorrowRewardPeriods, validateRewardPeriodsParam),
|
||||
params.NewParamSetPair(KeyHardDelegatorRewardPeriods, &p.HardDelegatorRewardPeriods, validateRewardPeriodsParam),
|
||||
params.NewParamSetPair(KeyClaimEnd, &p.ClaimEnd, validateClaimEndParam),
|
||||
params.NewParamSetPair(KeyMultipliers, &p.ClaimMultipliers, validateMultipliersParam),
|
||||
}
|
||||
@ -85,22 +102,27 @@ func (p *Params) ParamSetPairs() params.ParamSetPairs {
|
||||
|
||||
// Validate checks that the parameters have valid values.
|
||||
func (p Params) Validate() error {
|
||||
|
||||
if err := validateMultipliersParam(p.ClaimMultipliers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return validateRewardsParam(p.RewardPeriods)
|
||||
}
|
||||
|
||||
func validateActiveParam(i interface{}) error {
|
||||
_, ok := i.(bool)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid parameter type: %T", i)
|
||||
if err := validateRewardPeriodsParam(p.USDXMintingRewardPeriods); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
if err := validateRewardPeriodsParam(p.HardSupplyRewardPeriods); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validateRewardPeriodsParam(p.HardBorrowRewardPeriods); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return validateRewardPeriodsParam(p.HardDelegatorRewardPeriods)
|
||||
}
|
||||
|
||||
func validateRewardsParam(i interface{}) error {
|
||||
func validateRewardPeriodsParam(i interface{}) error {
|
||||
rewards, ok := i.(RewardPeriods)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid parameter type: %T", i)
|
||||
@ -224,9 +246,6 @@ func (m Multiplier) Validate() error {
|
||||
if m.MonthsLockup < 0 {
|
||||
return fmt.Errorf("expected non-negative lockup, got %d", m.MonthsLockup)
|
||||
}
|
||||
if m.Factor == (sdk.Dec{}) {
|
||||
return fmt.Errorf("claim multiplier factor not initialized for %s", m.Name)
|
||||
}
|
||||
if m.Factor.IsNegative() {
|
||||
return fmt.Errorf("expected non-negative factor, got %s", m.Factor.String())
|
||||
}
|
||||
|
@ -20,9 +20,12 @@ func (suite *ParamTestSuite) SetupTest() {}
|
||||
|
||||
func (suite *ParamTestSuite) TestParamValidation() {
|
||||
type args struct {
|
||||
rewardPeriods types.RewardPeriods
|
||||
multipliers types.Multipliers
|
||||
end time.Time
|
||||
usdxMintingRewardPeriods types.RewardPeriods
|
||||
hardSupplyRewardPeriods types.RewardPeriods
|
||||
hardBorrowRewardPeriods types.RewardPeriods
|
||||
hardDelegatorRewardPeriods types.RewardPeriods
|
||||
multipliers types.Multipliers
|
||||
end time.Time
|
||||
}
|
||||
|
||||
type errArgs struct {
|
||||
@ -39,9 +42,12 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
{
|
||||
"default",
|
||||
args{
|
||||
rewardPeriods: types.DefaultRewardPeriods,
|
||||
multipliers: types.DefaultMultipliers,
|
||||
end: types.DefaultClaimEnd,
|
||||
usdxMintingRewardPeriods: types.DefaultRewardPeriods,
|
||||
hardSupplyRewardPeriods: types.DefaultRewardPeriods,
|
||||
hardBorrowRewardPeriods: types.DefaultRewardPeriods,
|
||||
hardDelegatorRewardPeriods: types.DefaultRewardPeriods,
|
||||
multipliers: types.DefaultMultipliers,
|
||||
end: types.DefaultClaimEnd,
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
@ -51,7 +57,7 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
{
|
||||
"valid",
|
||||
args{
|
||||
rewardPeriods: types.RewardPeriods{types.NewRewardPeriod(
|
||||
usdxMintingRewardPeriods: types.RewardPeriods{types.NewRewardPeriod(
|
||||
true, "bnb-a", time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
sdk.NewCoin(types.USDXMintingRewardDenom, sdk.NewInt(122354)))},
|
||||
multipliers: types.Multipliers{
|
||||
@ -62,40 +68,22 @@ func (suite *ParamTestSuite) TestParamValidation() {
|
||||
types.Large, 1, sdk.MustNewDecFromStr("1.0"),
|
||||
),
|
||||
},
|
||||
end: time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
hardSupplyRewardPeriods: types.DefaultRewardPeriods,
|
||||
hardBorrowRewardPeriods: types.DefaultRewardPeriods,
|
||||
hardDelegatorRewardPeriods: types.DefaultRewardPeriods,
|
||||
end: time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
},
|
||||
errArgs{
|
||||
expectPass: true,
|
||||
contains: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid: empty reward factor",
|
||||
args{
|
||||
rewardPeriods: types.RewardPeriods{types.NewRewardPeriod(
|
||||
true, "bnb-a", time.Date(2020, 10, 15, 14, 0, 0, 0, time.UTC), time.Date(2024, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
sdk.NewCoin(types.USDXMintingRewardDenom, sdk.NewInt(122354)))},
|
||||
multipliers: types.Multipliers{
|
||||
types.NewMultiplier(
|
||||
types.Small, 1, sdk.MustNewDecFromStr("0.25"),
|
||||
),
|
||||
types.NewMultiplier(
|
||||
types.Large, 1, sdk.Dec{},
|
||||
),
|
||||
},
|
||||
end: time.Date(2025, 10, 15, 14, 0, 0, 0, time.UTC),
|
||||
},
|
||||
errArgs{
|
||||
expectPass: false,
|
||||
contains: "claim multiplier factor not initialized",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
suite.Run(tc.name, func() {
|
||||
params := types.NewParams(
|
||||
tc.args.rewardPeriods, tc.args.multipliers, tc.args.end,
|
||||
params := types.NewParams(tc.args.usdxMintingRewardPeriods, tc.args.hardSupplyRewardPeriods,
|
||||
tc.args.hardBorrowRewardPeriods, tc.args.hardDelegatorRewardPeriods, tc.args.multipliers, tc.args.end,
|
||||
)
|
||||
err := params.Validate()
|
||||
if tc.errArgs.expectPass {
|
||||
|
Loading…
Reference in New Issue
Block a user