diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f679d15d..812d073e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,43 @@ +## 0.2.2 + +- [\#185](https://github.com/terra-project/core/pull/185): Improve oracle specs +- [\#184](https://github.com/terra-project/core/pull/184): Fix `terracli` docs +- [\#183](https://github.com/terra-project/core/pull/183): Change all GradedVestingAccounts to LazyGradedVestingAccounts. +- [\#179](https://github.com/terra-project/core/pull/179): Conform querier responses to be returned in JSON format +- [\#178](https://github.com/terra-project/core/pull/178): Change BIP44 PATH to 330 + +### Changes +#### [\#185](https://github.com/terra-project/core/pull/185) Oracle `MsgFeederDelegatePermission` specs +Added docs for using `MsgFeederDelegatePermission` to oracle specs + +#### [\#185](https://github.com/terra-project/core/pull/185) Oracle price vote denom error fix +Oracle specs now specify micro units `uluna` and `uusd` for correct denominations for price prevotes and votes + +#### [\#184](https://github.com/terra-project/core/pull/184) Minor terracli fix + +#### [\#183](https://github.com/terra-project/core/pull/183) Oracle param update +``` +OracleRewardBand: 1% => 2% +``` + +#### [\#183](https://github.com/terra-project/core/pull/183) Market param update +``` +DailyLunaDeltaCap: 0.5% => 0.1% +``` + +#### [\#183](https://github.com/terra-project/core/pull/183) LazyGradedVestingAccount + +* Spread out the cliffs for presale investors, with varying degrees of severity (details [\#180](https://github.com/terra-project/core/issues/180)) + +#### [\#179](https://github.com/terra-project/core/pull/179) Align Querier responses to JSON + +* Querier was returning misaligned formats for return values, now aligned to JSON format + +#### [\#178](https://github.com/terra-project/core/pull/178) Correctly use 330 as the coin type field in BIP 44 PATH + +* We were previously using the Cosmos coin type field for the BIP44 path. Changed to Terra's own 330. + + ## 0.2.1 - [\#166](https://github.com/terra-project/core/pull/166): Newly added parameters were not being added to the columbus-2 genesis.json file. Fixed. diff --git a/app/app.go b/app/app.go index bfab024aa..593306c89 100755 --- a/app/app.go +++ b/app/app.go @@ -7,6 +7,7 @@ import ( "sort" "github.com/terra-project/core/types" + "github.com/terra-project/core/update" "github.com/terra-project/core/version" "github.com/terra-project/core/x/budget" "github.com/terra-project/core/x/market" @@ -335,6 +336,9 @@ func (app *TerraApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci. treasuryTags := treasury.EndBlocker(ctx, app.treasuryKeeper) tags = append(tags, treasuryTags...) + updateTags := update.EndBlocker(ctx, app.accountKeeper, app.oracleKeeper, app.marketKeeper) + tags = append(tags, updateTags...) + if app.assertInvariantsBlockly { app.assertRuntimeInvariants() } diff --git a/app/genesis.go b/app/genesis.go index 389f001cc..0d2e48c6f 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -93,12 +93,13 @@ type GenesisAccount struct { AccountNumber uint64 `json:"account_number"` // vesting account fields - OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization - DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation - DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation - StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time) - EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time) - VestingSchedules []types.VestingSchedule `json:"vesting_schedules"` // vesting end time (UNIX Epoch time) + OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization + DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation + DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation + StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time) + EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time) + VestingSchedules []types.VestingSchedule `json:"vesting_schedules"` // vesting schedule (clif: UNIX Epoch time, ratio: dec) + LazyVestingSchedules []types.LazyVestingSchedule `json:"lazy_vesting_schedules"` // lazy vesting schedule (start_time&end_time: UNIX Epoch time, ratio: dec) } // NewGenesisAccount returns new genesis account @@ -122,15 +123,21 @@ func NewGenesisAccountI(acc auth.Account) GenesisAccount { vacc, ok := acc.(auth.VestingAccount) if ok { + gacc.OriginalVesting = vacc.GetOriginalVesting() + gacc.DelegatedFree = vacc.GetDelegatedFree() + gacc.DelegatedVesting = vacc.GetDelegatedVesting() + gacc.StartTime = vacc.GetStartTime() + gacc.EndTime = vacc.GetEndTime() + gvacc, ok := vacc.(types.GradedVestingAccount) if ok { - gacc.OriginalVesting = gvacc.GetOriginalVesting() - gacc.DelegatedFree = gvacc.GetDelegatedFree() - gacc.DelegatedVesting = gvacc.GetDelegatedVesting() - gacc.StartTime = gvacc.GetStartTime() - gacc.EndTime = gvacc.GetEndTime() gacc.VestingSchedules = gvacc.GetVestingSchedules() } + + lgvacc, ok := vacc.(types.LazyGradedVestingAccount) + if ok { + gacc.LazyVestingSchedules = lgvacc.GetLazyVestingSchedules() + } } return gacc @@ -163,11 +170,16 @@ func (ga *GenesisAccount) ToAccount() auth.Account { return &auth.DelayedVestingAccount{ BaseVestingAccount: baseVestingAcc, } - } else { + } else if ga.VestingSchedules != nil { return &types.BaseGradedVestingAccount{ BaseVestingAccount: baseVestingAcc, VestingSchedules: ga.VestingSchedules, } + } else if ga.LazyVestingSchedules != nil { + return &types.BaseLazyGradedVestingAccount{ + BaseVestingAccount: baseVestingAcc, + LazyVestingSchedules: ga.LazyVestingSchedules, + } } } @@ -307,6 +319,12 @@ func validateGenesisStateAccounts(accs []GenesisAccount) error { return fmt.Errorf("schedule is invalid for vesting account; address: %s, denom: %s", addrStr, vestingSchedule.GetDenom()) } } + } else if acc.LazyVestingSchedules != nil && len(acc.LazyVestingSchedules) > 0 { + for _, lazyVestingSchedule := range acc.LazyVestingSchedules { + if !lazyVestingSchedule.IsValid() { + return fmt.Errorf("lazy schedule is invalid for vesting account; address: %s, denom: %s", addrStr, lazyVestingSchedule.GetDenom()) + } + } } else { if acc.EndTime == 0 { return fmt.Errorf("missing end time for vesting account; address: %s", addrStr) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 0911db734..98805d95d 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -405,7 +405,7 @@ paths: x-example: terra16gdxm24ht2mxtpz9cma6tr6a6d47x63hlq4pxt responses: 200: - description: Account information on the blockchain (one of Account and GradedVestingAccount) + description: Account information on the blockchain (one of Account and GradedVestingAccount and LazyGradedVestingAccount) schema: type: object properties: @@ -413,6 +413,8 @@ paths: $ref: "#/definitions/Account" GradedVestingAccount: $ref: "#/definitions/GradedVestingAccount" + LazyGradedVestingAccount: + $ref: "#/definitions/LazyGradedVestingAccount" 204: @@ -2583,7 +2585,6 @@ definitions: ratio: type: string example: "0.100000000000000000" - VestingSchedule: type: object properties: @@ -2594,7 +2595,6 @@ definitions: type: array items: $ref: "#/definitions/Schedule" - BaseGradedVestingAccount: type: object properties: @@ -2603,9 +2603,7 @@ definitions: vesting_schedules: type: array items: - $ref: "#/definitions/VestingSchedule" - - + $ref: "#/definitions/VestingSchedule" GradedVestingAccount: type: object properties: @@ -2614,6 +2612,46 @@ definitions: example: "core/GradedVestingAccount" value: $ref: "#/definitions/BaseGradedVestingAccount" + + LazySchedule: + type: object + properties: + start_time: + type: string + example: "1556085600" + end_time: + type: string + example: "1556085600" + ratio: + type: string + example: "0.100000000000000000" + LazyVestingSchedule: + type: object + properties: + denom: + type: string + example: "usdr" + lazy_schedules: + type: array + items: + $ref: "#/definitions/LazySchedule" + BaseLazyGradedVestingAccount: + type: object + properties: + BaseVestingAccount: + $ref: "#/definitions/BaseVestingAccount" + lazy_vesting_schedules: + type: array + items: + $ref: "#/definitions/LazyVestingSchedule" + LazyGradedVestingAccount: + type: object + properties: + type: + type: string + example: "core/LazyGradedVestingAccount" + value: + $ref: "#/definitions/BaseLazyGradedVestingAccount" TendermintValidator: type: object diff --git a/types/codec.go b/types/codec.go index 409fa06fd..5b7139cac 100644 --- a/types/codec.go +++ b/types/codec.go @@ -7,4 +7,5 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(&Schedule{}, "core/Schedule", nil) cdc.RegisterConcrete(&VestingSchedule{}, "core/VestingSchedule", nil) cdc.RegisterConcrete(&BaseGradedVestingAccount{}, "core/GradedVestingAccount", nil) + cdc.RegisterConcrete(&BaseLazyGradedVestingAccount{}, "core/LazyGradedVestingAccount", nil) } diff --git a/types/lazy_graded_account.go b/types/lazy_graded_account.go new file mode 100644 index 000000000..520d4a3cf --- /dev/null +++ b/types/lazy_graded_account.go @@ -0,0 +1,321 @@ +package types + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" +) + +//----------------------------------------------------------------------------- +// Schedule + +// Schedule no-lint +type LazySchedule struct { + StartTime int64 `json:"start_time"` + EndTime int64 `json:"end_time"` + Ratio sdk.Dec `json:"ratio"` +} + +func NewLazySchedule(startTime, endTime int64, ratio sdk.Dec) LazySchedule { + return LazySchedule{ + StartTime: startTime, + EndTime: endTime, + Ratio: ratio, + } +} + +// GetStartTime returns start time +func (s LazySchedule) GetStartTime() int64 { + return s.StartTime +} + +// GetEndTime returns end time +func (s LazySchedule) GetEndTime() int64 { + return s.EndTime +} + +// GetRatio returns ratio +func (s LazySchedule) GetRatio() sdk.Dec { + return s.Ratio +} + +// String implements the fmt.Stringer interface +func (s LazySchedule) String() string { + return fmt.Sprintf(`LazySchedule: + StartTime: %v, + EndTime: %v, + Ratio: %v`, + s.StartTime, s.EndTime, s.Ratio) +} + +// IsValid checks that the lazy schedule is valid. +func (s LazySchedule) IsValid() bool { + + startTime := s.GetStartTime() + endTime := s.GetEndTime() + ratio := s.GetRatio() + + return startTime >= 0 && endTime >= startTime && ratio.GT(sdk.ZeroDec()) +} + +//----------------------------------------------------------------------------- +// Vesting Lazy Schedule + +// LazyVestingSchedule maps the ratio of tokens that becomes vested by blocktime (in seconds) from genesis. +// The sum of values in the LazySchedule should sum to 1.0. +// CONTRACT: assumes that entries are +type LazyVestingSchedule struct { + Denom string `json:"denom"` + LazySchedules []LazySchedule `json:"schedules"` // maps blocktime to percentage vested. Should sum to 1. +} + +// NewLazyVestingSchedule creates a new vesting lazy schedule instance. +func NewLazyVestingSchedule(denom string, lazySchedules []LazySchedule) LazyVestingSchedule { + return LazyVestingSchedule{ + Denom: denom, + LazySchedules: lazySchedules, + } +} + +// GetVestedRatio returns the ratio of tokens that have vested by blockTime. +func (vs LazyVestingSchedule) GetVestedRatio(blockTime int64) sdk.Dec { + sumRatio := sdk.ZeroDec() + for _, lazySchedule := range vs.LazySchedules { + startTime := lazySchedule.GetStartTime() + endTime := lazySchedule.GetEndTime() + ratio := lazySchedule.GetRatio() + + if blockTime < startTime { + continue + } + + if blockTime < endTime { + ratio = ratio.MulInt64(blockTime - startTime).QuoInt64(endTime - startTime) + } + + sumRatio = sumRatio.Add(ratio) + + } + return sumRatio +} + +// GetDenom returns the denom of vesting layz schedule +func (vs LazyVestingSchedule) GetDenom() string { + return vs.Denom +} + +// IsValid checks that the vesting lazy schedule is valid. +func (vs LazyVestingSchedule) IsValid() bool { + sumRatio := sdk.ZeroDec() + for _, lazySchedule := range vs.LazySchedules { + + if !lazySchedule.IsValid() { + return false + } + + sumRatio = sumRatio.Add(lazySchedule.GetRatio()) + } + + return sumRatio.Equal(sdk.OneDec()) +} + +// String implements the fmt.Stringer interface +func (vs LazyVestingSchedule) String() string { + return fmt.Sprintf(`LazyVestingSchedule: + Denom: %v, + LazySchedules: %v`, + vs.Denom, vs.LazySchedules) +} + +//----------------------------------------------------------------------------- +// Lazy Graded Vesting Account + +// LazyGradedVestingAccount defines an account type that vests coins via a graded vesting lazy schedule. +type LazyGradedVestingAccount interface { + auth.VestingAccount + + GetLazyVestingSchedules() []LazyVestingSchedule + GetLazyVestingSchedule(denom string) (LazyVestingSchedule, bool) +} + +// BaseLazyGradedVestingAccount implements the LazyGradedVestingAccount interface. It vests all +// coins according to a predefined schedule. +var _ LazyGradedVestingAccount = (*BaseLazyGradedVestingAccount)(nil) + +// BaseLazyGradedVestingAccount implements the VestingAccount interface. It vests tokens according to +// a predefined set of vesting schedule. +type BaseLazyGradedVestingAccount struct { + *auth.BaseVestingAccount + + LazyVestingSchedules []LazyVestingSchedule `json:"vesting_lazy_schedules"` +} + +// NewBaseLazyGradedVestingAccount returns a BaseLazyGradedVestingAccount +func NewBaseLazyGradedVestingAccount(baseAcc *auth.BaseAccount, lazyVestingSchedules []LazyVestingSchedule) *BaseLazyGradedVestingAccount { + baseVestingAcc := &auth.BaseVestingAccount{ + BaseAccount: baseAcc, + OriginalVesting: baseAcc.Coins, + EndTime: 0, + } + + return &BaseLazyGradedVestingAccount{baseVestingAcc, lazyVestingSchedules} +} + +// GetLazyVestingSchedules returns the LazyVestingSchedules of the graded lazy vesting account +func (lgva BaseLazyGradedVestingAccount) GetLazyVestingSchedules() []LazyVestingSchedule { + return lgva.LazyVestingSchedules +} + +// GetLazyVestingSchedule returns the VestingSchedule of the given denom +func (lgva BaseLazyGradedVestingAccount) GetLazyVestingSchedule(denom string) (LazyVestingSchedule, bool) { + for _, vs := range lgva.LazyVestingSchedules { + if vs.Denom == denom { + return vs, true + } + } + + return LazyVestingSchedule{}, false +} + +// GetVestedCoins returns the total amount of vested coins for a graded vesting +// account. All coins are vested continuously once the schedule's StartTime has elapsed until EndTime. +func (lgva BaseLazyGradedVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins { + var vestedCoins sdk.Coins + for _, ovc := range lgva.OriginalVesting { + if vestingSchedule, exists := lgva.GetLazyVestingSchedule(ovc.Denom); exists { + vestedRatio := vestingSchedule.GetVestedRatio(blockTime.Unix()) + vestedAmt := ovc.Amount.ToDec().Mul(vestedRatio).RoundInt() + if vestedAmt.Equal(sdk.ZeroInt()) { + continue + } + vestedCoins = append(vestedCoins, sdk.NewCoin(ovc.Denom, vestedAmt)) + } else { + vestedCoins = append(vestedCoins, sdk.NewCoin(ovc.Denom, ovc.Amount)) + } + } + + return vestedCoins +} + +// GetVestingCoins returns the total number of vesting coins for a graded +// vesting account. +func (lgva BaseLazyGradedVestingAccount) GetVestingCoins(blockTime time.Time) sdk.Coins { + return lgva.OriginalVesting.Sub(lgva.GetVestedCoins(blockTime)) +} + +// SpendableCoins returns the total number of spendable coins for a graded +// vesting account. +func (lgva BaseLazyGradedVestingAccount) SpendableCoins(blockTime time.Time) sdk.Coins { + return lgva.spendableCoins(lgva.GetVestingCoins(blockTime)) +} + +// TrackDelegation tracks a desired delegation amount by setting the appropriate +// values for the amount of delegated vesting, delegated free, and reducing the +// overall amount of base coins. +func (lgva *BaseLazyGradedVestingAccount) TrackDelegation(blockTime time.Time, amount sdk.Coins) { + lgva.trackDelegation(lgva.GetVestingCoins(blockTime), amount) +} + +// GetStartTime returns zero since a lazy graded vesting account has no start time. +func (lgva BaseLazyGradedVestingAccount) GetStartTime() int64 { + return 0 +} + +// GetEndTime returns zero since a lazy graded vesting account has no end time. +func (lgva BaseLazyGradedVestingAccount) GetEndTime() int64 { + return 0 +} + +func (lgva BaseLazyGradedVestingAccount) String() string { + var pubkey string + + if lgva.PubKey != nil { + pubkey = sdk.MustBech32ifyAccPub(lgva.PubKey) + } + + return fmt.Sprintf(`BaseLazyGradedVestingAccount: + Address: %s + Pubkey: %s + Coins: %s + AccountNumber: %d + Sequence: %d + OriginalVesting: %s + DelegatedFree: %s + DelegatedVesting: %s + LazyVestingSchedules: %v `, + lgva.Address, pubkey, lgva.Coins, lgva.AccountNumber, lgva.Sequence, + lgva.OriginalVesting, lgva.DelegatedFree, lgva.DelegatedVesting, + lgva.LazyVestingSchedules, + ) +} + +// spendableCoins returns all the spendable coins for a vesting account given a +// set of vesting coins. +// +// CONTRACT: The account's coins, delegated vesting coins, vestingCoins must be +// sorted. +func (lgva BaseLazyGradedVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins { + var spendableCoins sdk.Coins + bc := lgva.GetCoins() + + for _, coin := range bc { + // zip/lineup all coins by their denomination to provide O(n) time + baseAmt := coin.Amount + vestingAmt := vestingCoins.AmountOf(coin.Denom) + delVestingAmt := lgva.DelegatedVesting.AmountOf(coin.Denom) + + // compute min((BC + DV) - V, BC) per the specification + min := sdk.MinInt(baseAmt.Add(delVestingAmt).Sub(vestingAmt), baseAmt) + spendableCoin := sdk.NewCoin(coin.Denom, min) + + if !spendableCoin.IsZero() { + spendableCoins = spendableCoins.Add(sdk.Coins{spendableCoin}) + } + } + + return spendableCoins +} + +// trackDelegation tracks a delegation amount for any given vesting account type +// given the amount of coins currently vesting. It returns the resulting base +// coins. +// +// CONTRACT: The account's coins, delegation coins, vesting coins, and delegated +// vesting coins must be sorted. +func (lgva *BaseLazyGradedVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) { + bc := lgva.GetCoins() + + for _, coin := range amount { + // zip/lineup all coins by their denomination to provide O(n) time + + baseAmt := bc.AmountOf(coin.Denom) + vestingAmt := vestingCoins.AmountOf(coin.Denom) + delVestingAmt := lgva.DelegatedVesting.AmountOf(coin.Denom) + + // Panic if the delegation amount is zero or if the base coins does not + // exceed the desired delegation amount. + if coin.Amount.IsZero() || baseAmt.LT(coin.Amount) { + panic("delegation attempt with zero coins or insufficient funds") + } + + // compute x and y per the specification, where: + // X := min(max(V - DV, 0), D) + // Y := D - X + x := sdk.MinInt(sdk.MaxInt(vestingAmt.Sub(delVestingAmt), sdk.ZeroInt()), coin.Amount) + y := coin.Amount.Sub(x) + + if !x.IsZero() { + xCoin := sdk.NewCoin(coin.Denom, x) + lgva.DelegatedVesting = lgva.DelegatedVesting.Add(sdk.Coins{xCoin}) + } + + if !y.IsZero() { + yCoin := sdk.NewCoin(coin.Denom, y) + lgva.DelegatedFree = lgva.DelegatedFree.Add(sdk.Coins{yCoin}) + } + + lgva.Coins = lgva.Coins.Sub(sdk.Coins{coin}) + } +} diff --git a/types/lazy_graded_account_test.go b/types/lazy_graded_account_test.go new file mode 100644 index 000000000..f2a6ec7d1 --- /dev/null +++ b/types/lazy_graded_account_test.go @@ -0,0 +1,359 @@ +package types + +import ( + "testing" + "time" + + "github.com/terra-project/core/types/assets" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/stretchr/testify/require" +) + +var ( + angelLazySchedule LazyVestingSchedule + seedLazySchedule LazyVestingSchedule + privateLazySchedule LazyVestingSchedule + privateBonusLazySchedule LazyVestingSchedule +) + +// initialize the times! +func init() { + var err error + timeLayoutString := "2006-01-02 15:04:05 -0700 MST" + timeGenesis, err = time.Parse(timeLayoutString, timeGenesisString) + if err != nil { + panic(err) + } + + monthlyTimes = []int64{} + for i := 0; i < 4; i++ { + for j := 0; j < 12; j++ { + monthlyTimes = append(monthlyTimes, timeGenesis.AddDate(i, j, 0).Unix()) + } + } + + angelLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ + NewLazySchedule(monthlyTimes[1], monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[2], monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[3], monthlyTimes[4], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[12], monthlyTimes[13], sdk.NewDecWithPrec(70, 2)), + }) + + seedLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ + NewLazySchedule(monthlyTimes[1], monthlyTimes[2], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[2], monthlyTimes[3], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[3], monthlyTimes[4], sdk.NewDecWithPrec(10, 2)), + NewLazySchedule(monthlyTimes[10], monthlyTimes[11], sdk.NewDecWithPrec(70, 2)), + }) + + privateLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ + NewLazySchedule(monthlyTimes[3], monthlyTimes[9], sdk.NewDec(1)), + }) + + privateBonusLazySchedule = NewLazyVestingSchedule(assets.MicroLunaDenom, []LazySchedule{ + NewLazySchedule(monthlyTimes[6], monthlyTimes[18], sdk.NewDec(1)), + }) + +} + +func TestGetVestedCoinsLazyGradVestingAcc(t *testing.T) { + genesis := timeGenesis + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require no coins are vested until schedule maturation + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + vestedCoins := lgva.GetVestedCoins(genesis) + require.Nil(t, vestedCoins) + + // require coins be vested at the expected cliff + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 1, 0)) + require.True(t, vestedCoins.Empty()) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 2, 0)) + require.Equal(t, scaleCoins(0.1, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 2, 15)) + require.Equal(t, scaleCoins(0.15, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 3, 0)) + require.Equal(t, scaleCoins(0.2, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 4, 0)) + require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(0, 5, 0)) + require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(1, 0, 0)) + require.Equal(t, scaleCoins(0.3, assets.MicroLunaDenom, origCoins), vestedCoins) + + vestedCoins = lgva.GetVestedCoins(timeGenesis.AddDate(1, 1, 0)) + require.Equal(t, scaleCoins(1.0, assets.MicroLunaDenom, origCoins), vestedCoins) +} + +func TestGetVestingCoinsLazyGradVestingAcc(t *testing.T) { + genesis := timeGenesis + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require no coins are vested until schedule maturation + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + vestingCoins := lgva.GetVestingCoins(genesis) + require.Equal(t, vestingCoins, origCoins) + + // require coins be vested at the expected cliff + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 1, 0)) + require.Equal(t, origCoins, vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 2, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(0.1, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 2, 15)) + require.Equal(t, origCoins.Sub(scaleCoins(0.15, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 3, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(0.2, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 4, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(0, 5, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(1, 0, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(0.3, assets.MicroLunaDenom, origCoins)), vestingCoins) + + vestingCoins = lgva.GetVestingCoins(timeGenesis.AddDate(1, 1, 0)) + require.Equal(t, origCoins.Sub(scaleCoins(1.0, assets.MicroLunaDenom, origCoins)), vestingCoins) +} + +func TestSpendableCoinsLazyGradVestingAcc(t *testing.T) { + genesis := timeGenesis + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} + sdrCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require no coins are vested until schedule maturation + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + + spendableCoins := lgva.SpendableCoins(genesis) + require.Equal(t, sdrCoins, spendableCoins) + + // require that all coins are spendable after the maturation of the vesting + // schedule + spendableCoins = lgva.SpendableCoins(timeGenesis.AddDate(1, 1, 0)) + require.Equal(t, origCoins, spendableCoins) + + // require that all luna coins are still vesting after some time + spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) + require.Equal(t, spendableCoins, sdrCoins) + + // receive some coins + relgvamt := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 50)} + lgva.SetCoins(lgva.GetCoins().Add(relgvamt)) + + // require that only received coins and sdrCoins are spendable since the account is still + // vesting + spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) + require.Equal(t, relgvamt.Add(sdrCoins), spendableCoins) + + // spend all spendable coins + lgva.SetCoins(lgva.GetCoins().Sub(spendableCoins)) + + // require that no more coins are spendable + spendableCoins = lgva.SpendableCoins(genesis.Add(12 * time.Hour)) + require.True(t, spendableCoins.Empty()) +} + +func TestTrackDelegationLazyGradVestingAcc(t *testing.T) { + genesis := timeGenesis + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + + bacc.SetCoins(origCoins) + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis, origCoins) + require.Equal(t, origCoins, lgva.DelegatedVesting) + require.Nil(t, lgva.DelegatedFree) + require.Nil(t, lgva.GetCoins()) + + // require the ability to delegate all vested coins + bacc.SetCoins(origCoins) + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis.AddDate(1, 1, 0), origCoins) + require.Nil(t, lgva.DelegatedVesting) + require.Equal(t, origCoins, lgva.DelegatedFree) + require.Nil(t, lgva.GetCoins()) + + // require the ability to delegate all coins half way through the vesting + // schedule + bacc.SetCoins(origCoins) + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis.AddDate(0, 3, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, lgva.DelegatedVesting) + require.Nil(t, lgva.DelegatedFree) + + lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, lgva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 3000)}, lgva.DelegatedFree) + require.Nil(t, lgva.GetCoins()) + + // require no modifications when delegation amount is zero or not enough funds + bacc.SetCoins(origCoins) + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + + require.Panics(t, func() { + lgva.TrackDelegation(genesis.AddDate(1, 0, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 1000000)}) + }) + require.Nil(t, lgva.DelegatedVesting) + require.Nil(t, lgva.DelegatedFree) + require.Equal(t, origCoins, lgva.GetCoins()) +} + +func TestTrackUndelegationLazyGradVestingAcc(t *testing.T) { + genesis := timeGenesis + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require the ability to undelegate all vesting coins + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis, origCoins) + lgva.TrackUndelegation(origCoins) + require.Nil(t, lgva.DelegatedFree) + require.Nil(t, lgva.DelegatedVesting) + require.Equal(t, origCoins, lgva.GetCoins()) + + // require the ability to undelegate all vested coins + bacc.SetCoins(origCoins) + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis.AddDate(1, 1, 0), origCoins) + lgva.TrackUndelegation(origCoins) + require.Nil(t, lgva.DelegatedFree) + require.Nil(t, lgva.DelegatedVesting) + require.Equal(t, origCoins, lgva.GetCoins()) + + // require no modifications when the undelegation amount is zero + bacc.SetCoins(origCoins) + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + require.Panics(t, func() { + lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 0)}) + }) + require.Nil(t, lgva.DelegatedFree) + require.Nil(t, lgva.DelegatedVesting) + require.Equal(t, origCoins, lgva.GetCoins()) + + // vest 50% and delegate to two validators + lgva = NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) + lgva.TrackDelegation(genesis.AddDate(0, 4, 0), sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) + + // undelegate from one validator that got slashed 50% + lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 500)}, lgva.DelegatedFree) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7000)}, lgva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, lgva.GetCoins()) + + // undelegate from the other validator that did not get slashed + lgva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 5000)}) + require.Nil(t, lgva.DelegatedFree) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 2500)}, lgva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 7500), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)}, lgva.GetCoins()) +} + +func TestStringLazyGradVestingAcc(t *testing.T) { + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require the ability to undelegate all vesting coins + lgva := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + require.NotNil(t, lgva.String()) + + lazyVestingSchedule, found := lgva.GetLazyVestingSchedule(assets.MicroLunaDenom) + require.True(t, found) + require.NotNil(t, lazyVestingSchedule.String()) +} + +func TestIsValidLazyGradVestingAcc(t *testing.T) { + + _, _, addr := keyPubAddr() + origCoins := sdk.Coins{sdk.NewInt64Coin(assets.MicroLunaDenom, 10000), sdk.NewInt64Coin(assets.MicroSDRDenom, 10000)} + bacc := auth.NewBaseAccountWithAddress(addr) + bacc.SetCoins(origCoins) + + // require the ability to undelegate all vesting coins + angelAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + angelLazySchedule, + }) + + lazyVestingSchedule, found := angelAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) + require.True(t, found) + require.True(t, lazyVestingSchedule.IsValid()) + + seedAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + seedLazySchedule, + }) + + lazyVestingSchedule, found = seedAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) + require.True(t, found) + require.True(t, lazyVestingSchedule.IsValid()) + + privateAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + privateLazySchedule, + }) + + lazyVestingSchedule, found = privateAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) + require.True(t, found) + require.True(t, lazyVestingSchedule.IsValid()) + + employeeAccount := NewBaseLazyGradedVestingAccount(&bacc, []LazyVestingSchedule{ + privateBonusLazySchedule, + }) + + lazyVestingSchedule, found = employeeAccount.GetLazyVestingSchedule(assets.MicroLunaDenom) + require.True(t, found) + require.True(t, lazyVestingSchedule.IsValid()) +} diff --git a/update/end_blocker.go b/update/end_blocker.go new file mode 100644 index 000000000..d3248018b --- /dev/null +++ b/update/end_blocker.go @@ -0,0 +1,26 @@ +package update + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/terra-project/core/update/plan" + "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/oracle" +) + +// EndBlocker +func EndBlocker( + ctx sdk.Context, + accountKeeper auth.AccountKeeper, + oracleKeeper oracle.Keeper, + marketKeeper market.Keeper) (resTags sdk.Tags) { + + updated := plan.Update230000(ctx, accountKeeper, oracleKeeper, marketKeeper) + + if updated { + resTags.AppendTag(plan.TagUpdate230000, "updated") + } + + return +} diff --git a/update/plan/test_common.go b/update/plan/test_common.go new file mode 100644 index 000000000..619815d8a --- /dev/null +++ b/update/plan/test_common.go @@ -0,0 +1,163 @@ +package plan + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/terra-project/core/types" + "github.com/terra-project/core/types/assets" + "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/mint" + "github.com/terra-project/core/x/oracle" + + "time" + + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/params" + "github.com/cosmos/cosmos-sdk/x/staking" +) + +var ( + oracleDecPrecision = 8 +) + +func setup(t *testing.T) testInput { + input := createTestInput(t) + + defaultOracleParams := oracle.DefaultParams() + defaultOracleParams.VotePeriod = int64(1) // Set to one block for convinience + input.oracleKeeper.SetParams(input.ctx, defaultOracleParams) + + defaultMarketParams := market.DefaultParams() + defaultMarketParams.DailyLunaDeltaCap = sdk.NewDecWithPrec(5, 3) + input.marketKeeper.SetParams(input.ctx, defaultMarketParams) + + return input +} + +type testInput struct { + ctx sdk.Context + cdc *codec.Codec + accKeeper auth.AccountKeeper + oracleKeeper oracle.Keeper + marketKeeper market.Keeper +} + +func newTestCodec() *codec.Codec { + cdc := codec.New() + + types.RegisterCodec(cdc) + oracle.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + + return cdc +} + +func createTestInput(t *testing.T) testInput { + keyAcc := sdk.NewKVStoreKey(auth.StoreKey) + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tKeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + keyOracle := sdk.NewKVStoreKey(oracle.StoreKey) + keyMint := sdk.NewKVStoreKey(mint.StoreKey) + keyMarket := sdk.NewKVStoreKey(market.StoreKey) + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) + tKeyStaking := sdk.NewKVStoreKey(staking.TStoreKey) + keyDistr := sdk.NewKVStoreKey(distr.StoreKey) + tKeyDistr := sdk.NewTransientStoreKey(distr.TStoreKey) + keyFeeCollection := sdk.NewKVStoreKey(auth.FeeStoreKey) + + cdc := newTestCodec() + db := dbm.NewMemDB() + ms := store.NewCommitMultiStore(db) + ctx := sdk.NewContext(ms, abci.Header{Time: time.Now().UTC()}, false, log.NewNopLogger()) + + ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyParams, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyOracle, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyMint, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyStaking, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyDistr, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(tKeyDistr, sdk.StoreTypeTransient, db) + ms.MountStoreWithDB(keyFeeCollection, sdk.StoreTypeIAVL, db) + ms.MountStoreWithDB(keyMarket, sdk.StoreTypeIAVL, db) + + require.NoError(t, ms.LoadLatestVersion()) + + paramsKeeper := params.NewKeeper(cdc, keyParams, tKeyParams) + accKeeper := auth.NewAccountKeeper( + cdc, + keyAcc, + paramsKeeper.Subspace(auth.DefaultParamspace), + auth.ProtoBaseAccount, + ) + + bankKeeper := bank.NewBaseKeeper( + accKeeper, + paramsKeeper.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace, + ) + + stakingKeeper := staking.NewKeeper( + cdc, + keyStaking, tKeyStaking, + bankKeeper, paramsKeeper.Subspace(staking.DefaultParamspace), + staking.DefaultCodespace, + ) + + feeCollectionKeeper := auth.NewFeeCollectionKeeper( + cdc, + keyFeeCollection, + ) + + distrKeeper := distr.NewKeeper( + cdc, keyDistr, paramsKeeper.Subspace(distr.DefaultParamspace), + bankKeeper, &stakingKeeper, feeCollectionKeeper, distr.DefaultCodespace, + ) + + mintKeeper := mint.NewKeeper( + cdc, + keyMint, + stakingKeeper, + bankKeeper, + accKeeper, + ) + + stakingKeeper.SetPool(ctx, staking.InitialPool()) + stakingParams := staking.DefaultParams() + stakingParams.BondDenom = assets.MicroLunaDenom + stakingKeeper.SetParams(ctx, stakingParams) + + oracleKeeper := oracle.NewKeeper( + cdc, + keyOracle, + mintKeeper, + distrKeeper, + feeCollectionKeeper, + stakingKeeper.GetValidatorSet(), + paramsKeeper.Subspace(oracle.DefaultParamspace), + ) + + marketKeeper := market.NewKeeper( + cdc, + keyMarket, + oracleKeeper, + mintKeeper, + paramsKeeper.Subspace(market.DefaultParamspace), + ) + + return testInput{ctx, cdc, accKeeper, oracleKeeper, marketKeeper} +} diff --git a/update/plan/update_230000.go b/update/plan/update_230000.go new file mode 100644 index 000000000..f61a8aa63 --- /dev/null +++ b/update/plan/update_230000.go @@ -0,0 +1,275 @@ +package plan + +import ( + "fmt" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/terra-project/core/types" + "github.com/terra-project/core/types/assets" + "github.com/terra-project/core/x/market" + "github.com/terra-project/core/x/oracle" +) + +var ( + preseedAddresses = [...]string{ + "terra1p54hc4yy2ajg67j645dn73w3378j6k05v52cnk", + "terra1weyggr6rnggy7vdnp79pxr7vskndegcnswh8gl", + "terra1jd2yth3zl2vez34q4m0wxngaazwy0e8csxuucd", + "terra1upg95nlwkfkrq4hhjrn3k9s6ud0aqx36gwnlsn", + "terra1f2z4q5kdelhfk7xq3xmxzlhp6ntrtzu659pl0s", + } + seedAddresses = [...]string{ + "terra1m8lvnh4zju4zcjh34rjhyyuyjk4m79536jf2tm", + "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6", + "terra13cvpljjkm2huadv38dfx8jyucfda0cs6jp3sku", + "terra1kgj2zc0qxltr5vsgycmxp7x337q5s9lvxlh694", + "terra1zjkr4t3dglt8mzykhtvrq5wltapstlmpdly87u", + "terra1y4umfuqfg76t8mfcff6zzx7elvy93jtp4xcdvw", + } + + // TagUpdate230000 is tag key for update 230000 + TagUpdate230000 = "update_230000" +) + +const ( + // seconds per 30 days + secondsPerMonth = 30 * 24 * 60 * 60 + // columbus-1 genesis time = 2019-04-24T06:00:00.000000Z + genesisUnixTime = 1556085600 +) + +// Update230000 update vesting schedule and oracle param +func Update230000(ctx sdk.Context, accKeeper auth.AccountKeeper, oracleKeeper oracle.Keeper, marketKeeper market.Keeper) bool { + + // check update height + if ctx.BlockHeight() != 230000 { + return false + } + + // update vesting schedule + accKeeper.IterateAccounts(ctx, func(acc auth.Account) (stop bool) { + stop = false + + vacc, ok := acc.(auth.VestingAccount) + + if !ok { + return + } + + gvacc, ok := vacc.(types.GradedVestingAccount) + if ok { + + var lazyVestingSchedules []types.LazyVestingSchedule + + isPreseedAccount := false + for _, addr := range preseedAddresses { + if addr == gvacc.GetAddress().String() { + lazyVestingSchedules = updatePreseedSchedules(gvacc) + isPreseedAccount = true + break + } + } + + isSeedAccount := false + for _, addr := range seedAddresses { + if addr == gvacc.GetAddress().String() { + lazyVestingSchedules = updateSeedSchedules(gvacc) + isSeedAccount = true + break + } + } + + if !isPreseedAccount && !isSeedAccount { + // update to LazyGradedVestingAccount + vestingSchedules := gvacc.GetVestingSchedules() + + for _, vs := range vestingSchedules { + var lazySchedules []types.LazySchedule + for _, s := range vs.Schedules { + lazySchedules = append(lazySchedules, types.NewLazySchedule(s.GetCliff(), s.GetCliff()+secondsPerMonth, s.GetRatio())) + } + + lazyVestingSchedule := types.NewLazyVestingSchedule(vs.GetDenom(), lazySchedules) + lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) + } + } + + for _, lvs := range lazyVestingSchedules { + if !lvs.IsValid() { + panic(fmt.Sprintf("not valid schdule: %v\n %v", gvacc, lvs)) + } + } + + baseAccount := &auth.BaseAccount{ + Address: gvacc.GetAddress(), + PubKey: gvacc.GetPubKey(), + Coins: gvacc.GetCoins().Sort(), + AccountNumber: gvacc.GetAccountNumber(), + Sequence: gvacc.GetSequence(), + } + + baseVestingAcc := &auth.BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: gvacc.GetOriginalVesting(), + DelegatedFree: gvacc.GetDelegatedFree(), + DelegatedVesting: gvacc.GetDelegatedVesting(), + EndTime: gvacc.GetEndTime(), + } + + lazyAccount := types.BaseLazyGradedVestingAccount{ + BaseVestingAccount: baseVestingAcc, + LazyVestingSchedules: lazyVestingSchedules, + } + + accKeeper.SetAccount(ctx, lazyAccount) + } + + return + }) + + // update oracle reward band param + oracleParams := oracleKeeper.GetParams(ctx) + oracleParams.OracleRewardBand = sdk.NewDecWithPrec(2, 2) // 2% + oracleKeeper.SetParams(ctx, oracleParams) + + // update market swap delta limit param + marketParams := marketKeeper.GetParams(ctx) + marketParams.DailyLunaDeltaCap = sdk.NewDecWithPrec(1, 3) // 0.1% + marketKeeper.SetParams(ctx, marketParams) + + return true +} + +// preseed account does not have any other schedules +func updatePreseedSchedules(gvacc types.GradedVestingAccount) []types.LazyVestingSchedule { + + vestingSchedules := gvacc.GetVestingSchedules() + if len(vestingSchedules) != 1 || vestingSchedules[0].GetDenom() != assets.MicroLunaDenom { + panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) + } + + vestingSchedule := vestingSchedules[0] + if len(vestingSchedule.Schedules) != 4 { + panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) + } + + // strict preseed account check + for _, s := range vestingSchedule.Schedules { + if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if s.GetCliff() == 1587708000 && s.GetRatio().Equal(sdk.NewDecWithPrec(70, 2)) { + continue + } else { + panic(fmt.Sprintf("Invalid Preseed Account: %v", gvacc)) + } + } + + var lazyVestingSchedules []types.LazyVestingSchedule + var lazyVestingSchedule types.LazyVestingSchedule + var lazySchedules []types.LazySchedule + + genesisTime := time.Unix(genesisUnixTime, 0) + lazySchedules = append(lazySchedules, + types.NewLazySchedule( + genesisTime.AddDate(0, 1, 0).Unix(), genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewLazySchedule( + genesisTime.AddDate(0, 2, 0).Unix(), genesisTime.AddDate(0, 12, 0).Unix(), sdk.NewDecWithPrec(27, 2)), + types.NewLazySchedule( + genesisTime.AddDate(0, 12, 0).Unix(), genesisTime.AddDate(0, 17, 0).Unix(), sdk.NewDecWithPrec(48, 2)), + types.NewLazySchedule( + genesisTime.AddDate(0, 17, 0).Unix(), genesisTime.AddDate(0, 18, 0).Unix(), sdk.NewDecWithPrec(15, 2)), + ) + + lazyVestingSchedule = types.NewLazyVestingSchedule(assets.MicroLunaDenom, lazySchedules) + lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) + + return lazyVestingSchedules +} + +// only terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6 account has difference vesting schedule +// except that all seed account does not have any other schedules +func updateSeedSchedules(gvacc types.GradedVestingAccount) []types.LazyVestingSchedule { + vestingSchedules := gvacc.GetVestingSchedules() + if len(vestingSchedules) != 1 || vestingSchedules[0].GetDenom() != assets.MicroLunaDenom { + panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) + } + + vestingSchedule := vestingSchedules[0] + ratio := sdk.OneDec() + + // strict seed account check + if gvacc.GetAddress().String() == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { + ratio = sdk.NewDecWithPrec(467, 3) + + if len(vestingSchedule.Schedules) != 5 { + panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) + } + + for _, s := range vestingSchedule.Schedules { + if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { + continue + } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { + continue + } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(47, 3)) { + continue + } else if s.GetCliff() == 1582524000 && s.GetRatio().Equal(sdk.NewDecWithPrec(326, 3)) { + continue + } else if s.GetCliff() == 1603519200 && s.GetRatio().Equal(sdk.NewDecWithPrec(533, 3)) { + continue + } else { + panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) + } + } + } else { + if len(vestingSchedule.Schedules) != 4 { + panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) + } + + for _, s := range vestingSchedule.Schedules { + if s.GetCliff() == 1558677600 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if s.GetCliff() == 1561356000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if s.GetCliff() == 1563948000 && s.GetRatio().Equal(sdk.NewDecWithPrec(10, 2)) { + continue + } else if (s.GetCliff() == 1582524000 || s.GetCliff() == 1579845600) && s.GetRatio().Equal(sdk.NewDecWithPrec(70, 2)) { + continue + } else { + panic(fmt.Sprintf("Invalid Seed Account: %v", gvacc)) + } + } + } + + var lazyVestingSchedules []types.LazyVestingSchedule + var lazyVestingSchedule types.LazyVestingSchedule + var lazySchedules []types.LazySchedule + + genesisTime := time.Unix(genesisUnixTime, 0) + lazySchedules = append(lazySchedules, + types.NewLazySchedule( + genesisTime.AddDate(0, 1, 0).Unix(), genesisTime.AddDate(0, 2, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(10, 2))), + types.NewLazySchedule( + genesisTime.AddDate(0, 2, 0).Unix(), genesisTime.AddDate(0, 10, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(30, 2))), + types.NewLazySchedule( + genesisTime.AddDate(0, 10, 0).Unix(), genesisTime.AddDate(0, 13, 0).Unix(), ratio.Mul(sdk.NewDecWithPrec(60, 2))), + ) + + if !ratio.Equal(sdk.OneDec()) { + lazySchedules = append(lazySchedules, + types.NewLazySchedule( + genesisTime.AddDate(0, 18, 0).Unix(), genesisTime.AddDate(0, 19, 0).Unix(), sdk.OneDec().Sub(ratio)), + ) + } + + lazyVestingSchedule = types.NewLazyVestingSchedule(assets.MicroLunaDenom, lazySchedules) + lazyVestingSchedules = append(lazyVestingSchedules, lazyVestingSchedule) + + return lazyVestingSchedules +} diff --git a/update/plan/update_230000_test.go b/update/plan/update_230000_test.go new file mode 100644 index 000000000..af2c23288 --- /dev/null +++ b/update/plan/update_230000_test.go @@ -0,0 +1,364 @@ +package plan + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + + "github.com/terra-project/core/types" + "github.com/terra-project/core/types/assets" + "github.com/terra-project/core/types/util" +) + +var ( + genesisTime = time.Unix(1556085600, 0) + + preseedSchedule types.VestingSchedule + seedSchedule types.VestingSchedule + seedSchedule2 types.VestingSchedule + privateSchedule types.VestingSchedule + + preseedAccounts []types.BaseGradedVestingAccount + seedAccounts []types.BaseGradedVestingAccount + normalAccounts []types.BaseGradedVestingAccount + + normalAddress = [...]string{ + "terra1wpplgwx5q2ph7z2vqm9m0t2jgr6qyjkwhxvff3", + "terra1dp0taj85ruc299rkdvzp4z5pfg6z6swaed74e6", + } + + preseedCoins = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(79411554440)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(3393296)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(20000000000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(2000007013377)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(13015)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(401836579451)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(833906)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4176551929047)), + ), + } + + preseedOriginalVesting = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(30000000000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(20000000000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(10000000000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(5000000000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4500000000000)), + ), + } + + seedCoins = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(1729185508867)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(1753376)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(9347826000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3913044896495)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(7373288)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(59867)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3826087000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3913044998248)), + ), + } + + seedOriginalVesting = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(13043478000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(9347826000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(3826087000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(4347826000000)), + ), + } + + normalCoins = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(350000000000)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(62500000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(695561902462109)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(999916140381807)), + ), + } + + normalOriginalVesting = []sdk.Coins{ + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(350000000000)), + sdk.NewCoin(assets.MicroSDRDenom, sdk.NewInt(62500000000)), + ), + sdk.NewCoins( + sdk.NewCoin(assets.MicroLunaDenom, sdk.NewInt(1000000000000000)), + ), + } +) + +func init() { + + config := sdk.GetConfig() + config.SetCoinType(330) + config.SetFullFundraiserPath("44'/330'/0'/0/0") + config.SetBech32PrefixForAccount(util.Bech32PrefixAccAddr, util.Bech32PrefixAccPub) + config.SetBech32PrefixForValidator(util.Bech32PrefixValAddr, util.Bech32PrefixValPub) + config.SetBech32PrefixForConsensusNode(util.Bech32PrefixConsAddr, util.Bech32PrefixConsPub) + config.Seal() + + preseedSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ + types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 12, 0).Unix(), sdk.NewDecWithPrec(70, 2)), + }) + + seedSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ + types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(10, 2)), + types.NewSchedule(genesisTime.AddDate(0, 10, 0).Unix(), sdk.NewDecWithPrec(70, 2)), + }) + + seedSchedule2 = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ + types.NewSchedule(genesisTime.AddDate(0, 1, 0).Unix(), sdk.NewDecWithPrec(47, 3)), + types.NewSchedule(genesisTime.AddDate(0, 2, 0).Unix(), sdk.NewDecWithPrec(47, 3)), + types.NewSchedule(genesisTime.AddDate(0, 3, 0).Unix(), sdk.NewDecWithPrec(47, 3)), + types.NewSchedule(genesisTime.AddDate(0, 10, 0).Unix(), sdk.NewDecWithPrec(326, 3)), + types.NewSchedule(genesisTime.AddDate(0, 18, 0).Unix(), sdk.NewDecWithPrec(533, 3)), + }) + + privateSchedule = types.NewVestingSchedule(assets.MicroLunaDenom, []types.Schedule{ + types.NewSchedule(genesisTime.AddDate(0, 4, 0).Unix(), sdk.NewDecWithPrec(16, 2)), + types.NewSchedule(genesisTime.AddDate(0, 5, 0).Unix(), sdk.NewDecWithPrec(16, 2)), + types.NewSchedule(genesisTime.AddDate(0, 6, 0).Unix(), sdk.NewDecWithPrec(16, 2)), + types.NewSchedule(genesisTime.AddDate(0, 7, 0).Unix(), sdk.NewDecWithPrec(16, 2)), + types.NewSchedule(genesisTime.AddDate(0, 8, 0).Unix(), sdk.NewDecWithPrec(16, 2)), + types.NewSchedule(genesisTime.AddDate(0, 9, 0).Unix(), sdk.NewDecWithPrec(20, 2)), + }) + + for i, bechAddr := range preseedAddresses { + addr, _ := sdk.AccAddressFromBech32(bechAddr) + baseAccount := &auth.BaseAccount{ + Address: addr, + Coins: preseedCoins[i], + } + + baseVestingAcc := &auth.BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: preseedOriginalVesting[i], + } + + gradedVestingAccount := types.BaseGradedVestingAccount{ + BaseVestingAccount: baseVestingAcc, + VestingSchedules: []types.VestingSchedule{preseedSchedule}, + } + + preseedAccounts = append(preseedAccounts, gradedVestingAccount) + } + + for i, bechAddr := range seedAddresses { + addr, _ := sdk.AccAddressFromBech32(bechAddr) + + baseAccount := &auth.BaseAccount{ + Address: addr, + Coins: seedCoins[i], + } + + baseVestingAcc := &auth.BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: seedOriginalVesting[i], + } + + gradedVestingAccount := types.BaseGradedVestingAccount{ + BaseVestingAccount: baseVestingAcc, + VestingSchedules: []types.VestingSchedule{seedSchedule}, + } + + if bechAddr == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { + gradedVestingAccount.VestingSchedules = []types.VestingSchedule{seedSchedule2} + } + + seedAccounts = append(seedAccounts, gradedVestingAccount) + } + + for i, bechAddr := range normalAddress { + addr, _ := sdk.AccAddressFromBech32(bechAddr) + + baseAccount := &auth.BaseAccount{ + Address: addr, + Coins: normalCoins[i], + } + + baseVestingAcc := &auth.BaseVestingAccount{ + BaseAccount: baseAccount, + OriginalVesting: normalOriginalVesting[i], + } + + gradedVestingAccount := types.BaseGradedVestingAccount{ + BaseVestingAccount: baseVestingAcc, + VestingSchedules: []types.VestingSchedule{privateSchedule}, + } + + normalAccounts = append(normalAccounts, gradedVestingAccount) + } +} + +func TestPreseedAccountUpdate(t *testing.T) { + + for _, acc := range preseedAccounts { + lazyVestingSchedules := updatePreseedSchedules(&acc) + + require.Equal(t, 1, len(lazyVestingSchedules)) + require.Equal(t, assets.MicroLunaDenom, lazyVestingSchedules[0].GetDenom()) + lazySchedule := lazyVestingSchedules[0].LazySchedules + + require.Equal(t, genesisTime.AddDate(0, 1, 0).Unix(), lazySchedule[0].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[0].GetEndTime()) + require.Equal(t, sdk.NewDecWithPrec(10, 2), lazySchedule[0].GetRatio()) + + require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[1].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 12, 0).Unix(), lazySchedule[1].GetEndTime()) + require.Equal(t, sdk.NewDecWithPrec(27, 2), lazySchedule[1].GetRatio()) + + require.Equal(t, genesisTime.AddDate(0, 12, 0).Unix(), lazySchedule[2].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 17, 0).Unix(), lazySchedule[2].GetEndTime()) + require.Equal(t, sdk.NewDecWithPrec(48, 2), lazySchedule[2].GetRatio()) + + require.Equal(t, genesisTime.AddDate(0, 17, 0).Unix(), lazySchedule[3].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 18, 0).Unix(), lazySchedule[3].GetEndTime()) + require.Equal(t, sdk.NewDecWithPrec(15, 2), lazySchedule[3].GetRatio()) + + require.True(t, lazyVestingSchedules[0].IsValid()) + } + +} + +func TestSeedAccountUpdate(t *testing.T) { + + for _, acc := range seedAccounts { + lazyVestingSchedules := updateSeedSchedules(&acc) + + ratio := sdk.OneDec() + + if acc.GetAddress().String() == "terra1y9n2ywyu5dahtxar6k4z4jz97ynt8km4catuk6" { + ratio = sdk.NewDecWithPrec(467, 3) + } + + require.Equal(t, 1, len(lazyVestingSchedules)) + require.Equal(t, assets.MicroLunaDenom, lazyVestingSchedules[0].GetDenom()) + lazySchedule := lazyVestingSchedules[0].LazySchedules + + require.Equal(t, genesisTime.AddDate(0, 1, 0).Unix(), lazySchedule[0].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[0].GetEndTime()) + require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(10, 2)), lazySchedule[0].GetRatio()) + + require.Equal(t, genesisTime.AddDate(0, 2, 0).Unix(), lazySchedule[1].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 10, 0).Unix(), lazySchedule[1].GetEndTime()) + require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(30, 2)), lazySchedule[1].GetRatio()) + + require.Equal(t, genesisTime.AddDate(0, 10, 0).Unix(), lazySchedule[2].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 13, 0).Unix(), lazySchedule[2].GetEndTime()) + require.Equal(t, ratio.Mul(sdk.NewDecWithPrec(60, 2)), lazySchedule[2].GetRatio()) + + if !ratio.Equal(sdk.OneDec()) { + require.Equal(t, genesisTime.AddDate(0, 18, 0).Unix(), lazySchedule[3].GetStartTime()) + require.Equal(t, genesisTime.AddDate(0, 19, 0).Unix(), lazySchedule[3].GetEndTime()) + require.Equal(t, sdk.OneDec().Sub(ratio), lazySchedule[3].GetRatio()) + } + + require.True(t, lazyVestingSchedules[0].IsValid()) + } + +} + +func TestUpdate230000(t *testing.T) { + input := setup(t) + + for _, acc := range preseedAccounts { + input.accKeeper.SetAccount(input.ctx, acc) + } + + for _, acc := range seedAccounts { + input.accKeeper.SetAccount(input.ctx, acc) + } + + for _, acc := range normalAccounts { + input.accKeeper.SetAccount(input.ctx, acc) + } + + updated := Update230000(input.ctx.WithBlockHeight(229999), input.accKeeper, input.oracleKeeper, input.marketKeeper) + require.Equal(t, sdk.NewDecWithPrec(1, 2), input.oracleKeeper.GetParams(input.ctx).OracleRewardBand) + require.Equal(t, sdk.NewDecWithPrec(5, 3), input.marketKeeper.GetParams(input.ctx).DailyLunaDeltaCap) + require.False(t, updated) + + // not yet changed + input.accKeeper.IterateAccounts(input.ctx, func(acc auth.Account) (stop bool) { + stop = false + + vacc, ok := acc.(auth.VestingAccount) + require.True(t, ok) + + _, ok = vacc.(types.GradedVestingAccount) + require.True(t, ok) + return + }) + + updated = Update230000(input.ctx.WithBlockHeight(230000), input.accKeeper, input.oracleKeeper, input.marketKeeper) + require.Equal(t, sdk.NewDecWithPrec(2, 2), input.oracleKeeper.GetParams(input.ctx).OracleRewardBand) + require.Equal(t, sdk.NewDecWithPrec(1, 3), input.marketKeeper.GetParams(input.ctx).DailyLunaDeltaCap) + require.True(t, updated) + + // not yet changed + input.accKeeper.IterateAccounts(input.ctx, func(acc auth.Account) (stop bool) { + stop = false + + vacc, ok := acc.(auth.VestingAccount) + require.True(t, ok) + + _, ok = vacc.(types.LazyGradedVestingAccount) + require.True(t, ok) + return + }) + +}