[R4R] fix: match reward payouts to current harvest v1 payouts (#786)

* fix: payout rewards on 1st or 15th of month

* backport payout test

* fix: add default case
This commit is contained in:
Kevin Davis 2021-02-02 15:10:32 -07:00 committed by GitHub
parent 37be34b4d6
commit 6118876074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 183 additions and 6 deletions

View File

@ -1,6 +1,8 @@
package keeper package keeper
import ( import (
"time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
@ -12,6 +14,15 @@ import (
validatorvesting "github.com/kava-labs/kava/x/validator-vesting" validatorvesting "github.com/kava-labs/kava/x/validator-vesting"
) )
const (
// BeginningOfMonth harvest rewards that are claimed after the 15th at 14:00UTC of the month always vest on the first of the month
BeginningOfMonth = 1
// MidMonth harvest rewards that are claimed before the 15th at 14:00UTC of the month always vest on the 15 of the month
MidMonth = 15
// PaymentHour harvest rewards always vest at 14:00UTC
PaymentHour = 14
)
// ClaimUSDXMintingReward sends the reward amount to the input address and zero's out the claim in the store // ClaimUSDXMintingReward sends the reward amount to the input address and zero's out the claim in the store
func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error { func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, addr sdk.AccAddress, multiplierName types.MultiplierName) error {
claim, found := k.GetUSDXMintingClaim(ctx, addr) claim, found := k.GetUSDXMintingClaim(ctx, addr)
@ -40,7 +51,10 @@ func (k Keeper) ClaimUSDXMintingReward(ctx sdk.Context, addr sdk.AccAddress, mul
return types.ErrZeroClaim return types.ErrZeroClaim
} }
rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount) rewardCoin := sdk.NewCoin(claim.Reward.Denom, rewardAmount)
length := ctx.BlockTime().AddDate(0, int(multiplier.MonthsLockup), 0).Unix() - ctx.BlockTime().Unix() length, err := k.GetPeriodLength(ctx, multiplier)
if err != nil {
return err
}
err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(rewardCoin), length) err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, sdk.NewCoins(rewardCoin), length)
if err != nil { if err != nil {
@ -93,9 +107,12 @@ func (k Keeper) ClaimHardReward(ctx sdk.Context, addr sdk.AccAddress, multiplier
} }
rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount)) rewardCoins = append(rewardCoins, sdk.NewCoin(coin.Denom, rewardAmount))
} }
length := ctx.BlockTime().AddDate(0, int(multiplier.MonthsLockup), 0).Unix() - ctx.BlockTime().Unix() length, err := k.GetPeriodLength(ctx, multiplier)
if err != nil {
return err
}
err := k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, rewardCoins, length) err = k.SendTimeLockedCoinsToAccount(ctx, types.IncentiveMacc, addr, rewardCoins, length)
if err != nil { if err != nil {
return err return err
} }
@ -170,6 +187,29 @@ func (k Keeper) SendTimeLockedCoinsToBaseAccount(ctx sdk.Context, senderModule s
return nil 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
default:
return 0, types.ErrInvalidMultiplier
}
}
// addCoinsToVestingSchedule adds coins to the input account's vesting schedule where length is the amount of time (from the current block time), in seconds, that the coins will be vesting for // addCoinsToVestingSchedule adds coins to the input account's vesting schedule where length is the amount of time (from the current block time), in seconds, that the coins will be vesting for
// the input address must be a periodic vesting account // the input address must be a periodic vesting account
func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, length int64) { func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins, length int64) {

View File

@ -2,7 +2,6 @@ package keeper_test
import ( import (
"errors" "errors"
"fmt"
"strings" "strings"
"time" "time"
@ -55,7 +54,7 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
multiplier: types.MultiplierName("large"), multiplier: types.MultiplierName("large"),
timeElapsed: 86400, timeElapsed: 86400,
expectedBalance: cs(c("usdx", 10000000000), c("ukava", 10576385600)), expectedBalance: cs(c("usdx", 10000000000), c("ukava", 10576385600)),
expectedPeriods: vesting.Periods{vesting.Period{Length: 31536000, Amount: cs(c("ukava", 10571385600))}}, expectedPeriods: vesting.Periods{vesting.Period{Length: 32918400, Amount: cs(c("ukava", 10571385600))}},
isPeriodicVestingAccount: true, isPeriodicVestingAccount: true,
}, },
errArgs{ errArgs{
@ -144,7 +143,6 @@ func (suite *KeeperTestSuite) TestPayoutUSDXMintingClaim() {
} }
claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0]) claim, found := suite.keeper.GetUSDXMintingClaim(suite.ctx, suite.addrs[0])
fmt.Println(claim)
suite.Require().True(found) suite.Require().True(found)
suite.Require().Equal(c("ukava", 0), claim.Reward) suite.Require().Equal(c("ukava", 0), claim.Reward)
} else { } else {
@ -678,3 +676,142 @@ func (suite *KeeperTestSuite) SetupWithAccountState() {
suite.ctx = ctx suite.ctx = ctx
suite.addrs = addrs suite.addrs = addrs
} }
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)
}
})
}
}