Update reward vesting length calculation (#624)

* use remaining length when sending coins to vesting account

* query claims that have corresponding claim periods

* cleanup comments

* remove debugging statements

* fix bug with inserting period in middle of vesting schedule

* apply review suggestion
This commit is contained in:
Kevin Davis 2020-08-26 20:34:55 -04:00 committed by GitHub
parent fca16da84a
commit c0006ca8eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 297 additions and 140 deletions

View File

@ -1,6 +1,8 @@
package keeper
import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -34,6 +36,7 @@ func (k Keeper) PayoutClaim(ctx sdk.Context, addr sdk.AccAddress, collateralType
types.EventTypeClaim,
sdk.NewAttribute(types.AttributeKeyClaimedBy, addr.String()),
sdk.NewAttribute(types.AttributeKeyClaimAmount, claim.Reward.String()),
sdk.NewAttribute(types.AttributeKeyClaimPeriod, fmt.Sprintf("%d", claim.ClaimPeriodID)),
),
)
return nil
@ -141,16 +144,22 @@ func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress,
// Add the new vesting coins to OriginalVesting
vacc.OriginalVesting = vacc.OriginalVesting.Add(amt...)
// update vesting periods
// EndTime = 100
// BlockTime = 110
// length == 6
if vacc.EndTime < ctx.BlockTime().Unix() {
// edge case one - the vesting account's end time is in the past (ie, all previous vesting periods have completed)
// append a new period to the vesting account, update the end time, update the account in the store and return
newPeriodLength := (ctx.BlockTime().Unix() - vacc.EndTime) + length
newPeriodLength := (ctx.BlockTime().Unix() - vacc.EndTime) + length // 110 - 100 + 6 = 16
newPeriod := types.NewPeriod(amt, newPeriodLength)
vacc.VestingPeriods = append(vacc.VestingPeriods, newPeriod)
vacc.EndTime = ctx.BlockTime().Unix() + length
k.accountKeeper.SetAccount(ctx, vacc)
return
}
// StartTime = 110
// BlockTime = 100
// length = 6
if vacc.StartTime > ctx.BlockTime().Unix() {
// edge case two - the vesting account's start time is in the future (all periods have not started)
// update the start time to now and adjust the period lengths in place - a new period will be inserted in the next code block
@ -158,7 +167,7 @@ func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress,
for i, period := range vacc.VestingPeriods {
updatedPeriod := period
if i == 0 {
updatedPeriod = types.NewPeriod(period.Amount, (vacc.StartTime-ctx.BlockTime().Unix())+period.Length)
updatedPeriod = types.NewPeriod(period.Amount, (vacc.StartTime-ctx.BlockTime().Unix())+period.Length) // 110 - 100 + 6 = 16
}
updatedPeriods = append(updatedPeriods, updatedPeriod)
}
@ -167,11 +176,12 @@ func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress,
}
// logic for inserting a new vesting period into the existing vesting schedule
totalPeriodLength := types.GetTotalVestingPeriodLength(vacc.VestingPeriods)
remainingLength := vacc.EndTime - ctx.BlockTime().Unix()
elapsedTime := ctx.BlockTime().Unix() - vacc.StartTime
proposedEndTime := ctx.BlockTime().Unix() + length
if totalPeriodLength < length {
// in the case that the proposed length is longer than the sum of all previous period lengths, create a new period with length equal to the difference between the proposed length and the previous total length
newPeriodLength := length - totalPeriodLength
if remainingLength < length {
// in the case that the proposed length is longer than the remaining length of all vesting periods, create a new period with length equal to the difference between the proposed length and the previous total length
newPeriodLength := length - remainingLength
newPeriod := types.NewPeriod(amt, newPeriodLength)
vacc.VestingPeriods = append(vacc.VestingPeriods, newPeriod)
// update the end time so that the sum of all period lengths equals endTime - startTime
@ -184,6 +194,19 @@ func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress,
// Expected result:
// {[l: 1, a: 1], [l:2, a: 1], [l:2, a:x], [l:6, a:3], [l:5, a:3]}
// StartTime = 100
// Periods = [5,5,5,5]
// EndTime = 120
// BlockTime = 101
// length = 2
// for period in Periods:
// iteration 1:
// lengthCounter = 5
// if 5 < 101 - 100 + 2 - no
// if 5 = 3 - no
// else
// newperiod = 2 - 0
newPeriods := vesting.Periods{}
lengthCounter := int64(0)
appendRemaining := false
@ -193,14 +216,14 @@ func (k Keeper) addCoinsToVestingSchedule(ctx sdk.Context, addr sdk.AccAddress,
continue
}
lengthCounter += period.Length
if lengthCounter < length {
if lengthCounter < elapsedTime+length { // 1
newPeriods = append(newPeriods, period)
} else if lengthCounter == length {
} else if lengthCounter == elapsedTime+length {
newPeriod := types.NewPeriod(period.Amount.Add(amt...), period.Length)
newPeriods = append(newPeriods, newPeriod)
appendRemaining = true
} else {
newPeriod := types.NewPeriod(amt, length-types.GetTotalVestingPeriodLength(newPeriods))
newPeriod := types.NewPeriod(amt, elapsedTime+length-types.GetTotalVestingPeriodLength(newPeriods))
previousPeriod := types.NewPeriod(period.Amount, period.Length-newPeriod.Length)
newPeriods = append(newPeriods, newPeriod, previousPeriod)
appendRemaining = true

View File

@ -2,6 +2,7 @@ package keeper_test
import (
"errors"
"strings"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
@ -35,7 +36,7 @@ func (suite *KeeperTestSuite) setupChain() {
)
supplyKeeper := tApp.GetSupplyKeeper()
macc := supplyKeeper.GetModuleAccount(ctx, kavadist.ModuleName)
err := supplyKeeper.MintCoins(ctx, macc.GetName(), cs(c("ukava", 500)))
err := supplyKeeper.MintCoins(ctx, macc.GetName(), cs(c("ukava", 600)))
suite.Require().NoError(err)
// sets addrs[0] to be a periodic vesting account
@ -99,157 +100,290 @@ func (suite *KeeperTestSuite) setupExpiredClaims() {
suite.addrs = addrs
}
func createPeriodicVestingAccount(origVesting sdk.Coins, periods vesting.Periods, startTime, endTime int64) (*vesting.PeriodicVestingAccount, error) {
_, addr := app.GeneratePrivKeyAddressPairs(1)
bacc := auth.NewBaseAccountWithAddress(addr[0])
bacc.Coins = origVesting
bva, err := vesting.NewBaseVestingAccount(&bacc, origVesting, endTime)
if err != nil {
return &vesting.PeriodicVestingAccount{}, err
}
pva := vesting.NewPeriodicVestingAccountRaw(bva, startTime, periods)
err = pva.Validate()
if err != nil {
return &vesting.PeriodicVestingAccount{}, err
}
return pva, nil
}
func (suite *KeeperTestSuite) TestSendCoinsToPeriodicVestingAccount() {
suite.setupChain()
type accountArgs struct {
periods vesting.Periods
origVestingCoins sdk.Coins
startTime int64
endTime int64
}
type args struct {
coins sdk.Coins
length int64
}
type errArgs struct {
expectErr bool
errType error
}
type vestingAccountTest struct {
name string
blockTime time.Time
args args
errArgs errArgs
accArgs accountArgs
period vesting.Period
ctxTime time.Time
mintModAccountCoins bool
expectedPeriods vesting.Periods
expectedOriginalVesting sdk.Coins
expectedCoins sdk.Coins
expectedStartTime int64
expectedEndTime int64
}
type errArgs struct {
expectErr bool
contains string
}
type testCase struct {
name string
args args
errArgs errArgs
}
type testCases []testCase
type vestingAccountTests []vestingAccountTest
testCases := vestingAccountTests{
vestingAccountTest{
name: "insert period into an existing vesting schedule",
blockTime: time.Unix(100, 0),
args: args{coins: cs(c("ukava", 100)), length: 5},
errArgs: errArgs{expectErr: false, errType: nil},
tests := testCases{
{
name: "insert period at beginning schedule",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 2, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(101, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: 3, Amount: cs(c("ukava", 6))},
vesting.Period{Length: 2, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
expectedStartTime: 100,
expectedEndTime: 120,
},
expectedOriginalVesting: cs(c("ukava", 500)),
expectedCoins: cs(c("ukava", 500)),
expectedStartTime: int64(100),
expectedEndTime: int64(116),
errArgs: errArgs{
expectErr: false,
contains: "",
},
vestingAccountTest{
name: "append period to the end of an existing vesting schedule",
blockTime: time.Unix(100, 0),
args: args{coins: cs(c("ukava", 100)), length: 17},
errArgs: errArgs{expectErr: false, errType: nil},
},
{
name: "insert period at beginning with new start time",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 7, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(80, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: 7, Amount: cs(c("ukava", 6))},
vesting.Period{Length: 18, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
expectedStartTime: 80,
expectedEndTime: 120,
},
expectedOriginalVesting: cs(c("ukava", 600)),
expectedCoins: cs(c("ukava", 600)),
expectedStartTime: int64(100),
expectedEndTime: int64(117),
errArgs: errArgs{
expectErr: false,
contains: "",
},
vestingAccountTest{
name: "append period to the end of a completed vesting schedule",
blockTime: time.Unix(120, 0),
args: args{coins: cs(c("ukava", 100)), length: 5},
errArgs: errArgs{expectErr: false, errType: nil},
},
{
name: "insert period in middle of schedule",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 7, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(101, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 3, Amount: cs(c("ukava", 6))},
vesting.Period{Length: 2, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
expectedStartTime: 100,
expectedEndTime: 120,
},
expectedOriginalVesting: cs(c("ukava", 700)),
expectedCoins: cs(c("ukava", 700)),
expectedStartTime: int64(100),
expectedEndTime: int64(125),
errArgs: errArgs{
expectErr: false,
contains: "",
},
vestingAccountTest{
name: "prepend period to to an upcoming vesting schedule",
blockTime: time.Unix(90, 0),
args: args{coins: cs(c("ukava", 100)), length: 5},
errArgs: errArgs{expectErr: false, errType: nil},
},
{
name: "append to end of schedule",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 7, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(125, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 12, Amount: cs(c("ukava", 6))}},
expectedStartTime: 100,
expectedEndTime: 132,
},
expectedOriginalVesting: cs(c("ukava", 800)),
expectedCoins: cs(c("ukava", 800)),
expectedStartTime: int64(90),
expectedEndTime: int64(125),
errArgs: errArgs{
expectErr: false,
contains: "",
},
vestingAccountTest{
name: "add period that coincides with an existing end time",
blockTime: time.Unix(90, 0),
args: args{coins: cs(c("ukava", 100)), length: 11},
errArgs: errArgs{expectErr: false, errType: nil},
},
{
name: "add coins to existing period",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 5, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(110, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 200))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(2), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(6), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(5), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(1), Amount: cs(c("ukava", 100))},
vesting.Period{Length: int64(8), Amount: cs(c("ukava", 100))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 11))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
expectedStartTime: 100,
expectedEndTime: 120,
},
expectedOriginalVesting: cs(c("ukava", 900)),
expectedCoins: cs(c("ukava", 900)),
expectedStartTime: int64(90),
expectedEndTime: int64(125),
errArgs: errArgs{
expectErr: false,
contains: "",
},
},
{
name: "insufficient mod account balance",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 7, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(125, 0),
mintModAccountCoins: false,
expectedPeriods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 12, Amount: cs(c("ukava", 6))}},
expectedStartTime: 100,
expectedEndTime: 132,
},
errArgs: errArgs{
expectErr: true,
contains: "insufficient funds",
},
},
{
name: "add large period mid schedule",
args: args{
accArgs: accountArgs{
periods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))}},
origVestingCoins: cs(c("ukava", 20)),
startTime: 100,
endTime: 120,
},
period: vesting.Period{Length: 50, Amount: cs(c("ukava", 6))},
ctxTime: time.Unix(110, 0),
mintModAccountCoins: true,
expectedPeriods: vesting.Periods{
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 5, Amount: cs(c("ukava", 5))},
vesting.Period{Length: 40, Amount: cs(c("ukava", 6))}},
expectedStartTime: 100,
expectedEndTime: 160,
},
errArgs: errArgs{
expectErr: false,
contains: "",
},
vestingAccountTest{
name: "insufficient module account balance",
blockTime: time.Unix(90, 0),
args: args{coins: cs(c("ukava", 1000)), length: 11},
errArgs: errArgs{expectErr: true, errType: types.ErrInsufficientModAccountBalance},
expectedPeriods: vesting.Periods{},
expectedOriginalVesting: sdk.Coins{},
expectedCoins: sdk.Coins{},
expectedStartTime: int64(0),
expectedEndTime: int64(0),
},
}
for _, tc := range testCases {
for _, tc := range tests {
suite.Run(tc.name, func() {
suite.ctx = suite.ctx.WithBlockTime(tc.blockTime)
err := suite.keeper.SendTimeLockedCoinsToAccount(suite.ctx, kavadist.ModuleName, suite.addrs[0], tc.args.coins, tc.args.length)
// create the periodic vesting account
pva, err := createPeriodicVestingAccount(tc.args.accArgs.origVestingCoins, tc.args.accArgs.periods, tc.args.accArgs.startTime, tc.args.accArgs.endTime)
suite.Require().NoError(err)
// setup store state with account and kavadist module account
suite.ctx = suite.ctx.WithBlockTime(tc.args.ctxTime)
ak := suite.app.GetAccountKeeper()
ak.SetAccount(suite.ctx, pva)
// mint module account coins if required
if tc.args.mintModAccountCoins {
sk := suite.app.GetSupplyKeeper()
err = sk.MintCoins(suite.ctx, kavadist.ModuleName, tc.args.period.Amount)
suite.Require().NoError(err)
}
err = suite.keeper.SendTimeLockedCoinsToPeriodicVestingAccount(suite.ctx, kavadist.ModuleName, pva.Address, tc.args.period.Amount, tc.args.period.Length)
if tc.errArgs.expectErr {
suite.Require().True(errors.Is(err, tc.errArgs.errType))
suite.Require().Error(err)
suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains))
} else {
suite.Require().NoError(err)
acc := suite.getAccount(suite.addrs[0])
acc := suite.getAccount(pva.Address)
vacc, ok := acc.(*vesting.PeriodicVestingAccount)
suite.True(ok)
suite.Equal(tc.expectedPeriods, vacc.VestingPeriods)
suite.Equal(tc.expectedOriginalVesting, vacc.OriginalVesting)
suite.Equal(tc.expectedCoins, vacc.Coins)
suite.Equal(tc.expectedStartTime, vacc.StartTime)
suite.Equal(tc.expectedEndTime, vacc.EndTime)
suite.Require().True(ok)
suite.Require().Equal(tc.args.expectedPeriods, vacc.VestingPeriods)
suite.Require().Equal(tc.args.expectedStartTime, vacc.StartTime)
suite.Require().Equal(tc.args.expectedEndTime, vacc.EndTime)
}
})
}
@ -300,7 +434,7 @@ func (suite *KeeperTestSuite) TestPayoutClaim() {
suite.keeper.SetClaim(suite.ctx, c3)
// existing claim with corresponding claim period successfully claimed by existing periodic vesting account
err := suite.keeper.PayoutClaim(suite.ctx, suite.addrs[0], "bnb", 1)
err := suite.keeper.PayoutClaim(suite.ctx.WithBlockTime(time.Unix(3700, 0)), suite.addrs[0], "bnb", 1)
suite.Require().NoError(err)
acc := suite.getAccount(suite.addrs[0])
// account is a periodic vesting account