diff --git a/app/params/weights.go b/app/params/weights.go index faef9445..b316967e 100644 --- a/app/params/weights.go +++ b/app/params/weights.go @@ -28,4 +28,5 @@ const ( DefaultWeightUpdateWhitelistValidatorsProposal int = 5 DefaultWeightDeleteWhitelistValidatorsProposal int = 5 DefaultWeightCompleteRedelegationUnbonding int = 30 + DefaultWeightTallyWithLiquidStaking int = 30 ) diff --git a/x/liquidstaking/keeper/hooks.go b/x/liquidstaking/keeper/hooks.go index d40be0e5..4e841332 100644 --- a/x/liquidstaking/keeper/hooks.go +++ b/x/liquidstaking/keeper/hooks.go @@ -23,5 +23,5 @@ func (h Hooks) AfterProposalVotingPeriodEnded(_ sdk.Context, _ uint64) { // GetOtherVotes calculate the voting power of the person who participated in liquid staking. func (h Hooks) GetOtherVotes(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) { - h.k.TallyLiquidGov(ctx, votes, otherVotes) + h.k.TallyLiquidStakingGov(ctx, votes, otherVotes) } diff --git a/x/liquidstaking/keeper/liquidstaking_test.go b/x/liquidstaking/keeper/liquidstaking_test.go index 20ec2fb8..f90f9cc3 100644 --- a/x/liquidstaking/keeper/liquidstaking_test.go +++ b/x/liquidstaking/keeper/liquidstaking_test.go @@ -1,14 +1,9 @@ package keeper_test import ( - "fmt" - sdk "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" squadtypes "github.com/cosmosquad-labs/squad/types" "github.com/cosmosquad-labs/squad/x/liquidstaking/types" - "github.com/k0kubun/pp" ) // tests LiquidStaking, LiquidUnstaking @@ -179,246 +174,3 @@ func (s *KeeperTestSuite) TestLiquidStaking() { s.Require().Equal(nas.ProxyAccBalance, sdk.ZeroInt()) s.Require().Equal(nas.NetAmount, sdk.ZeroDec()) } - -// test Liquid Staking gov power -func (s *KeeperTestSuite) TestLiquidStakingGov() { - params := types.DefaultParams() - liquidBondDenom := s.keeper.LiquidBondDenom(s.ctx) - - // v1, v2, v3, v4 - vals, valOpers, _ := s.CreateValidators([]int64{10000000, 10000000, 10000000, 10000000, 10000000}) - params.WhitelistedValidators = []types.WhitelistedValidator{ - {ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)}, - {ValidatorAddress: valOpers[1].String(), TargetWeight: sdk.NewInt(10)}, - {ValidatorAddress: valOpers[2].String(), TargetWeight: sdk.NewInt(10)}, - {ValidatorAddress: valOpers[3].String(), TargetWeight: sdk.NewInt(10)}, - } - s.keeper.SetParams(s.ctx, params) - s.keeper.UpdateLiquidValidatorSet(s.ctx) - - liquidValidators := s.keeper.GetAllLiquidValidators(s.ctx) - - val4, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[3]) - - delA := s.addrs[0] - delB := s.addrs[1] - delC := s.addrs[2] - delD := s.addrs[3] - delE := s.addrs[4] - delF := s.addrs[5] - delG := s.addrs[6] - - _, err := s.app.StakingKeeper.Delegate(s.ctx, delG, sdk.NewInt(60000000), stakingtypes.Unbonded, val4, true) - s.Require().NoError(err) - - // 7 addr B, C, D, E, F, G, H - tp := govtypes.NewTextProposal("Test", "description") - proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp) - s.Require().NoError(err) - - proposal.Status = govtypes.StatusVotingPeriod - s.app.GovKeeper.SetProposal(s.ctx, proposal) - - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[1], govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[3], govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) - - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delC, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delD, govtypes.NewNonSplitVoteOption(govtypes.OptionNoWithVeto))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delE, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delF, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain))) - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delG, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - - s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { - pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String()) - return false - }) - - assertTallyResult := func(yes, no, vito, abstain int64) { - cachedCtx, _ := s.ctx.CacheContext() - _, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal) - s.Require().Equal(sdk.NewInt(yes), result.Yes) - s.Require().Equal(sdk.NewInt(no), result.No) - s.Require().Equal(sdk.NewInt(vito), result.NoWithVeto) - s.Require().Equal(sdk.NewInt(abstain), result.Abstain) - } - - assertTallyResult(80000000, 10000000, 0, 0) - - delAbToken := sdk.NewInt(40000000) - delBbToken := sdk.NewInt(80000000) - delCbToken := sdk.NewInt(60000000) - delDbToken := sdk.NewInt(20000000) - delEbToken := sdk.NewInt(80000000) - delFbToken := sdk.NewInt(120000000) - newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delA, sdk.NewCoin(sdk.DefaultBondDenom, delAbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delA, liquidBondDenom).Amount, delAbToken) - - newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, delBbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, liquidBondDenom).Amount, delBbToken) - - newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delC, sdk.NewCoin(sdk.DefaultBondDenom, delCbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delC, liquidBondDenom).Amount, delCbToken) - - newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delD, sdk.NewCoin(sdk.DefaultBondDenom, delDbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delD, liquidBondDenom).Amount, delDbToken) - - newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delE, sdk.NewCoin(sdk.DefaultBondDenom, delEbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delE, liquidBondDenom).Amount, delEbToken) - - newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delF, sdk.NewCoin(sdk.DefaultBondDenom, delFbToken)) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delF, liquidBondDenom).Amount, delFbToken) - - totalPower := sdk.ZeroInt() - totalShare := sdk.ZeroDec() - s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { - pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String()) - totalPower = totalPower.Add(validator.GetTokens()) - totalShare = totalShare.Add(validator.GetDelegatorShares()) - return false - }) - - assertTallyResult(240000000, 100000000, 20000000, 120000000) - - // Test TallyLiquidGov - otherVotes := make(govtypes.OtherVotes) - testOtherVotes := func(voter sdk.AccAddress, bTokenValue sdk.Int) { - s.Require().Len(otherVotes[voter.String()], liquidValidators.Len()) - totalVotingPower := sdk.ZeroDec() - for _, v := range liquidValidators { - votingPower := otherVotes[voter.String()][v.OperatorAddress] - totalVotingPower = totalVotingPower.Add(votingPower) - // equal when all liquid validator has same currentWeight - s.Require().EqualValues(votingPower, bTokenValue.ToDec().QuoInt64(int64(liquidValidators.Len()))) - } - s.Require().EqualValues(totalVotingPower.TruncateInt(), s.keeper.CalcLiquidStakingVotingPower(s.ctx, voter)) - } - tallyLiquidGov := func() { - cachedCtx, _ := s.ctx.CacheContext() - otherVotes = make(govtypes.OtherVotes) - votes := s.app.GovKeeper.GetVotes(cachedCtx, proposal.ProposalId) - s.keeper.TallyLiquidGov(cachedCtx, &votes, &otherVotes) - squadtypes.PP(otherVotes) - - s.Require().Len(otherVotes, 5) - testOtherVotes(delB, delBbToken) - testOtherVotes(delC, delCbToken) - testOtherVotes(delD, delDbToken) - testOtherVotes(delE, delEbToken) - testOtherVotes(delF, delFbToken) - } - - tallyLiquidGov() - - // Test balance of PoolTokens including bToken - pair1 := s.createPair(delB, params.LiquidBondDenom, sdk.DefaultBondDenom, false) - pool1 := s.createPool(delB, pair1.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false) - tallyLiquidGov() - pair2 := s.createPair(delC, sdk.DefaultBondDenom, params.LiquidBondDenom, false) - pool2 := s.createPool(delC, pair2.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false) - balance := s.app.BankKeeper.GetBalance(s.ctx, delC, pool2.PoolCoinDenom) - fmt.Println(balance) - tallyLiquidGov() - - // Test Farming Queued Staking of bToken - s.CreateFixedAmountPlan(s.addrs[0], map[string]string{params.LiquidBondDenom: "0.4", pool1.PoolCoinDenom: "0.3", pool2.PoolCoinDenom: "0.3"}, map[string]int64{"testdenom": 1}) - s.Stake(delD, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(10000000)))) - queuedStaking, found := s.app.FarmingKeeper.GetQueuedStaking(s.ctx, params.LiquidBondDenom, delD) - s.True(found) - s.Equal(queuedStaking.Amount, sdk.NewInt(10000000)) - tallyLiquidGov() - - // Test Farming Staking Position Staking of bToken - s.AdvanceEpoch() - staking, found := s.app.FarmingKeeper.GetStaking(s.ctx, params.LiquidBondDenom, delD) - s.True(found) - s.Equal(staking.Amount, sdk.NewInt(10000000)) - tallyLiquidGov() - - // Test Farming Queued Staking of PoolTokens including bToken - s.Stake(delC, sdk.NewCoins(sdk.NewCoin(pool2.PoolCoinDenom, sdk.NewInt(10000000)))) - queuedStaking, found = s.app.FarmingKeeper.GetQueuedStaking(s.ctx, pool2.PoolCoinDenom, delC) - s.True(found) - s.Equal(queuedStaking.Amount, sdk.NewInt(10000000)) - tallyLiquidGov() - - // Test Farming Staking Position of PoolTokens including bToken - s.AdvanceEpoch() - staking, found = s.app.FarmingKeeper.GetStaking(s.ctx, pool2.PoolCoinDenom, delC) - s.True(found) - s.Equal(staking.Amount, sdk.NewInt(10000000)) - tallyLiquidGov() -} - -// test Liquid Staking gov power -func (s *KeeperTestSuite) TestLiquidStakingGov2() { - params := types.DefaultParams() - - vals, valOpers, _ := s.CreateValidators([]int64{10000000}) - params.WhitelistedValidators = []types.WhitelistedValidator{ - {ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)}, - } - s.keeper.SetParams(s.ctx, params) - s.keeper.UpdateLiquidValidatorSet(s.ctx) - - val1, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[0]) - - delA := s.addrs[0] - delB := s.addrs[1] - - _, err := s.app.StakingKeeper.Delegate(s.ctx, delA, sdk.NewInt(50000000), stakingtypes.Unbonded, val1, true) - s.Require().NoError(err) - - tp := govtypes.NewTextProposal("Test", "description") - proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp) - s.Require().NoError(err) - - proposal.Status = govtypes.StatusVotingPeriod - s.app.GovKeeper.SetProposal(s.ctx, proposal) - - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delA, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) - - cachedCtx, _ := s.ctx.CacheContext() - _, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal) - s.Require().Equal(sdk.NewInt(50000000), result.Yes) - s.Require().Equal(sdk.NewInt(0), result.No) - s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) - s.Require().Equal(sdk.NewInt(0), result.Abstain) - - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) - cachedCtx, _ = s.ctx.CacheContext() - _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) - s.Require().Equal(sdk.NewInt(50000000), result.Yes) - s.Require().Equal(sdk.NewInt(10000000), result.No) - s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) - s.Require().Equal(sdk.NewInt(0), result.Abstain) - - newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50000000))) - s.Require().NoError(err) - s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, params.LiquidBondDenom).Amount, sdk.NewInt(50000000)) - - cachedCtx, _ = s.ctx.CacheContext() - _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) - s.Require().Equal(sdk.NewInt(50000000), result.Yes) - s.Require().Equal(sdk.NewInt(60000000), result.No) - s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) - s.Require().Equal(sdk.NewInt(0), result.Abstain) - - s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain))) - - cachedCtx, _ = s.ctx.CacheContext() - _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) - s.Require().Equal(sdk.NewInt(50000000), result.Yes) - s.Require().Equal(sdk.NewInt(10000000), result.No) - s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) - s.Require().Equal(sdk.NewInt(50000000), result.Abstain) -} - -// TODO: tally bonded validator case diff --git a/x/liquidstaking/keeper/tally.go b/x/liquidstaking/keeper/tally.go index baef7cf8..f1cf6109 100644 --- a/x/liquidstaking/keeper/tally.go +++ b/x/liquidstaking/keeper/tally.go @@ -118,7 +118,6 @@ func (k Keeper) CalcStakingVotingPower(ctx sdk.Context, addr sdk.AccAddress) sdk } // CalcLiquidStakingVotingPower returns voting power of the addr by liquid bond denom -// TODO: refactor votingPowerStruct (delShares, btoken, poolCoin, farming) func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddress) sdk.Int { liquidBondDenom := k.LiquidBondDenom(ctx) @@ -169,7 +168,7 @@ func (k Keeper) CalcLiquidStakingVotingPower(ctx sdk.Context, addr sdk.AccAddres } } -func (k Keeper) TallyLiquidGov(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) { +func (k Keeper) TallyLiquidStakingGov(ctx sdk.Context, votes *govtypes.Votes, otherVotes *govtypes.OtherVotes) { liquidBondDenom := k.LiquidBondDenom(ctx) // skip when no liquid bond token supply diff --git a/x/liquidstaking/keeper/tally_test.go b/x/liquidstaking/keeper/tally_test.go index 68ed4772..cf29e733 100644 --- a/x/liquidstaking/keeper/tally_test.go +++ b/x/liquidstaking/keeper/tally_test.go @@ -1,20 +1,21 @@ package keeper_test import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" simapp "github.com/cosmosquad-labs/squad/app" + squadtypes "github.com/cosmosquad-labs/squad/types" "github.com/cosmosquad-labs/squad/x/liquidstaking/types" + "github.com/k0kubun/pp" ) // test Liquid Staking gov power func (s *KeeperTestSuite) TestGetVoterBalanceByDenom() { - s.SetupTest() - voter1, _ := sdk.AccAddressFromBech32("cosmos138w269yyeyj0unge54km8572lgf54l8e3yu8lg") voter2, _ := sdk.AccAddressFromBech32("cosmos1u0wfxlachgzqpwnkcwj2vzy025ehzv0qlhujnr") - //voter3, _ := sdk.AccAddressFromBech32("cosmos14sqkxzdjqwmyclur633wg85sjvvahscgfatvv7") - //voter4, _ := sdk.AccAddressFromBech32("cosmos1pr7ux292w5ag3v29jzg3gfspw7hufp8l94xejs") simapp.InitAccountWithCoins(s.app, s.ctx, voter1, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000000)))) simapp.InitAccountWithCoins(s.app, s.ctx, voter2, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(1000000)))) @@ -41,3 +42,363 @@ func (s *KeeperTestSuite) TestGetVoterBalanceByDenom() { s.Require().EqualValues(voterBalanceByDenom[sdk.DefaultBondDenom][voter2.String()], sdk.NewInt(1000000)) s.Require().EqualValues(voterBalanceByDenom[types.DefaultLiquidBondDenom][voter2.String()], sdk.NewInt(500000)) } + +// test Liquid Staking gov power +func (s *KeeperTestSuite) TestTallyLiquidStakingGov() { + params := types.DefaultParams() + liquidBondDenom := s.keeper.LiquidBondDenom(s.ctx) + + // v1, v2, v3, v4 + vals, valOpers, _ := s.CreateValidators([]int64{10000000, 10000000, 10000000, 10000000, 10000000}) + params.WhitelistedValidators = []types.WhitelistedValidator{ + {ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)}, + {ValidatorAddress: valOpers[1].String(), TargetWeight: sdk.NewInt(10)}, + {ValidatorAddress: valOpers[2].String(), TargetWeight: sdk.NewInt(10)}, + {ValidatorAddress: valOpers[3].String(), TargetWeight: sdk.NewInt(10)}, + } + s.keeper.SetParams(s.ctx, params) + s.keeper.UpdateLiquidValidatorSet(s.ctx) + + liquidValidators := s.keeper.GetAllLiquidValidators(s.ctx) + + val4, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[3]) + + delA := s.addrs[0] + delB := s.addrs[1] + delC := s.addrs[2] + delD := s.addrs[3] + delE := s.addrs[4] + delF := s.addrs[5] + delG := s.addrs[6] + + _, err := s.app.StakingKeeper.Delegate(s.ctx, delG, sdk.NewInt(60000000), stakingtypes.Unbonded, val4, true) + s.Require().NoError(err) + + // 7 addr B, C, D, E, F, G, H + tp := govtypes.NewTextProposal("Test", "description") + proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp) + s.Require().NoError(err) + + proposal.Status = govtypes.StatusVotingPeriod + s.app.GovKeeper.SetProposal(s.ctx, proposal) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[1], govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[3], govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delC, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delD, govtypes.NewNonSplitVoteOption(govtypes.OptionNoWithVeto))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delE, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delF, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain))) + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delG, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + + s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String()) + return false + }) + + assertTallyResult := func(yes, no, vito, abstain int64) { + cachedCtx, _ := s.ctx.CacheContext() + _, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(yes), result.Yes) + s.Require().Equal(sdk.NewInt(no), result.No) + s.Require().Equal(sdk.NewInt(vito), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(abstain), result.Abstain) + } + + assertTallyResult(80000000, 10000000, 0, 0) + + delAbToken := sdk.NewInt(40000000) + delBbToken := sdk.NewInt(80000000) + delCbToken := sdk.NewInt(60000000) + delDbToken := sdk.NewInt(20000000) + delEbToken := sdk.NewInt(80000000) + delFbToken := sdk.NewInt(120000000) + newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delA, sdk.NewCoin(sdk.DefaultBondDenom, delAbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delA, liquidBondDenom).Amount, delAbToken) + + newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, delBbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, liquidBondDenom).Amount, delBbToken) + + newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delC, sdk.NewCoin(sdk.DefaultBondDenom, delCbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delC, liquidBondDenom).Amount, delCbToken) + + newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delD, sdk.NewCoin(sdk.DefaultBondDenom, delDbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delD, liquidBondDenom).Amount, delDbToken) + + newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delE, sdk.NewCoin(sdk.DefaultBondDenom, delEbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delE, liquidBondDenom).Amount, delEbToken) + + newShares, bToken, err = s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delF, sdk.NewCoin(sdk.DefaultBondDenom, delFbToken)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delF, liquidBondDenom).Amount, delFbToken) + + totalPower := sdk.ZeroInt() + totalShare := sdk.ZeroDec() + s.app.StakingKeeper.IterateBondedValidatorsByPower(s.ctx, func(index int64, validator stakingtypes.ValidatorI) (stop bool) { + pp.Println(validator.GetOperator().String(), validator.GetDelegatorShares().String()) + totalPower = totalPower.Add(validator.GetTokens()) + totalShare = totalShare.Add(validator.GetDelegatorShares()) + return false + }) + + assertTallyResult(240000000, 100000000, 20000000, 120000000) + + // Test TallyLiquidStakingGov + otherVotes := make(govtypes.OtherVotes) + testOtherVotes := func(voter sdk.AccAddress, bTokenValue sdk.Int) { + s.Require().Len(otherVotes[voter.String()], liquidValidators.Len()) + totalVotingPower := sdk.ZeroDec() + for _, v := range liquidValidators { + votingPower := otherVotes[voter.String()][v.OperatorAddress] + totalVotingPower = totalVotingPower.Add(votingPower) + // equal when all liquid validator has same currentWeight + s.Require().EqualValues(votingPower, bTokenValue.ToDec().QuoInt64(int64(liquidValidators.Len()))) + } + s.Require().EqualValues(totalVotingPower.TruncateInt(), s.keeper.CalcLiquidStakingVotingPower(s.ctx, voter)) + } + tallyLiquidGov := func() { + cachedCtx, _ := s.ctx.CacheContext() + otherVotes = make(govtypes.OtherVotes) + votes := s.app.GovKeeper.GetVotes(cachedCtx, proposal.ProposalId) + s.keeper.TallyLiquidStakingGov(cachedCtx, &votes, &otherVotes) + squadtypes.PP(otherVotes) + + s.Require().Len(otherVotes, 5) + testOtherVotes(delB, delBbToken) + testOtherVotes(delC, delCbToken) + testOtherVotes(delD, delDbToken) + testOtherVotes(delE, delEbToken) + testOtherVotes(delF, delFbToken) + } + + tallyLiquidGov() + + // Test balance of PoolTokens including bToken + pair1 := s.createPair(delB, params.LiquidBondDenom, sdk.DefaultBondDenom, false) + pool1 := s.createPool(delB, pair1.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false) + tallyLiquidGov() + pair2 := s.createPair(delC, sdk.DefaultBondDenom, params.LiquidBondDenom, false) + pool2 := s.createPool(delC, pair2.Id, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(40000000)), sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(44000000))), false) + balance := s.app.BankKeeper.GetBalance(s.ctx, delC, pool2.PoolCoinDenom) + fmt.Println(balance) + tallyLiquidGov() + + // Test Farming Queued Staking of bToken + s.CreateFixedAmountPlan(s.addrs[0], map[string]string{params.LiquidBondDenom: "0.4", pool1.PoolCoinDenom: "0.3", pool2.PoolCoinDenom: "0.3"}, map[string]int64{"testdenom": 1}) + s.Stake(delD, sdk.NewCoins(sdk.NewCoin(params.LiquidBondDenom, sdk.NewInt(10000000)))) + queuedStaking, found := s.app.FarmingKeeper.GetQueuedStaking(s.ctx, params.LiquidBondDenom, delD) + s.True(found) + s.Equal(queuedStaking.Amount, sdk.NewInt(10000000)) + tallyLiquidGov() + + // Test Farming Staking Position Staking of bToken + s.AdvanceEpoch() + staking, found := s.app.FarmingKeeper.GetStaking(s.ctx, params.LiquidBondDenom, delD) + s.True(found) + s.Equal(staking.Amount, sdk.NewInt(10000000)) + tallyLiquidGov() + + // Test Farming Queued Staking of PoolTokens including bToken + s.Stake(delC, sdk.NewCoins(sdk.NewCoin(pool2.PoolCoinDenom, sdk.NewInt(10000000)))) + queuedStaking, found = s.app.FarmingKeeper.GetQueuedStaking(s.ctx, pool2.PoolCoinDenom, delC) + s.True(found) + s.Equal(queuedStaking.Amount, sdk.NewInt(10000000)) + tallyLiquidGov() + + // Test Farming Staking Position of PoolTokens including bToken + s.AdvanceEpoch() + staking, found = s.app.FarmingKeeper.GetStaking(s.ctx, pool2.PoolCoinDenom, delC) + s.True(found) + s.Equal(staking.Amount, sdk.NewInt(10000000)) + tallyLiquidGov() +} + +// test Liquid Staking gov power +func (s *KeeperTestSuite) TestTallyLiquidStakingGov2() { + params := types.DefaultParams() + + vals, valOpers, _ := s.CreateValidators([]int64{10000000}) + params.WhitelistedValidators = []types.WhitelistedValidator{ + {ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)}, + } + s.keeper.SetParams(s.ctx, params) + s.keeper.UpdateLiquidValidatorSet(s.ctx) + + val1, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[0]) + + delA := s.addrs[0] + delB := s.addrs[1] + + _, err := s.app.StakingKeeper.Delegate(s.ctx, delA, sdk.NewInt(50000000), stakingtypes.Unbonded, val1, true) + s.Require().NoError(err) + + tp := govtypes.NewTextProposal("Test", "description") + proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp) + s.Require().NoError(err) + + proposal.Status = govtypes.StatusVotingPeriod + s.app.GovKeeper.SetProposal(s.ctx, proposal) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delA, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + + cachedCtx, _ := s.ctx.CacheContext() + _, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) + s.Require().Equal(sdk.NewInt(0), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) + s.Require().Equal(sdk.NewInt(10000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delB, sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50000000))) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delB, params.LiquidBondDenom).Amount, sdk.NewInt(50000000)) + + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) + s.Require().Equal(sdk.NewInt(60000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delB, govtypes.NewNonSplitVoteOption(govtypes.OptionAbstain))) + + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) + s.Require().Equal(sdk.NewInt(10000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(50000000), result.Abstain) +} + +// TestVotingPower tests voting power of staking, liquid staking +func (s *KeeperTestSuite) TestVotingPower() { + params := types.DefaultParams() + + vals, valOpers, pks := s.CreateValidators([]int64{10000000, 10000000}) + params.WhitelistedValidators = []types.WhitelistedValidator{ + {ValidatorAddress: valOpers[0].String(), TargetWeight: sdk.NewInt(10)}, + {ValidatorAddress: valOpers[1].String(), TargetWeight: sdk.NewInt(5)}, + } + s.keeper.SetParams(s.ctx, params) + s.keeper.UpdateLiquidValidatorSet(s.ctx) + + val1, _ := s.app.StakingKeeper.GetValidator(s.ctx, valOpers[0]) + + delA := s.addrs[0] + + normalStakingAmount := sdk.NewInt(50000000) + liquidStakingAmount := sdk.NewInt(50000000) + _, err := s.app.StakingKeeper.Delegate(s.ctx, delA, normalStakingAmount, stakingtypes.Unbonded, val1, true) + s.Require().NoError(err) + + // normal staking voting power + svp := s.keeper.CalcStakingVotingPower(s.ctx, delA) + s.Require().EqualValues(svp, normalStakingAmount) + + // no liquid staking voting power + lsvp := s.keeper.CalcLiquidStakingVotingPower(s.ctx, delA) + s.Require().EqualValues(lsvp, sdk.ZeroInt()) + + tp := govtypes.NewTextProposal("Test", "description") + proposal, err := s.app.GovKeeper.SubmitProposal(s.ctx, tp) + s.Require().NoError(err) + proposal.Status = govtypes.StatusVotingPeriod + s.app.GovKeeper.SetProposal(s.ctx, proposal) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, delA, govtypes.NewNonSplitVoteOption(govtypes.OptionYes))) + + cachedCtx, _ := s.ctx.CacheContext() + _, _, result := s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) // normalStakingAmount + s.Require().Equal(sdk.NewInt(0), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + s.Require().NoError(s.app.GovKeeper.AddVote(s.ctx, proposal.ProposalId, vals[0], govtypes.NewNonSplitVoteOption(govtypes.OptionNo))) + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(50000000), result.Yes) // normalStakingAmount + s.Require().Equal(sdk.NewInt(10000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + newShares, bToken, err := s.keeper.LiquidStaking(s.ctx, types.LiquidStakingProxyAcc, delA, sdk.NewCoin(sdk.DefaultBondDenom, liquidStakingAmount)) + s.Require().NoError(err) + s.Require().EqualValues(newShares.TruncateInt(), bToken, s.app.BankKeeper.GetBalance(s.ctx, delA, params.LiquidBondDenom).Amount, liquidStakingAmount) + + // normal staking voting power + votingPower := s.keeper.GetVotingPower(s.ctx, delA) + s.Require().EqualValues(votingPower.StakingVotingPower, normalStakingAmount) + // liquid staking voting power + s.Require().EqualValues(votingPower.LiquidStakingVotingPower, liquidStakingAmount) + + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(votingPower.StakingVotingPower.Add(votingPower.LiquidStakingVotingPower), result.Yes) + s.Require().Equal(sdk.NewInt(100000000), result.Yes) // normalStakingAmount + liquidStakingAmount + s.Require().Equal(sdk.NewInt(10000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + // double sign second liquid validator + s.doubleSign(valOpers[1], sdk.ConsAddress(pks[1].Address())) + + // reduced liquid staking voting power because of unbonded liquid validator by double sign + votingPower = s.keeper.GetVotingPower(s.ctx, delA) + s.Require().EqualValues(votingPower.StakingVotingPower, normalStakingAmount) + s.Require().EqualValues(votingPower.LiquidStakingVotingPower, sdk.NewInt(33333334)) + + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(votingPower.StakingVotingPower.Add(votingPower.LiquidStakingVotingPower), result.Yes) + s.Require().Equal(sdk.NewInt(83333334), result.Yes) // staking voting power + reduced liquid staking voting power + s.Require().Equal(sdk.NewInt(10000000), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + + // rebalancing for non-active liquid validator by double sign, voting power don't need to wait unbonding period when rebalancing, redelegation + s.keeper.UpdateLiquidValidatorSet(s.ctx) + + // recovered liquid staking voting power because of rebalancing the liquid validator except slashing amount + votingPower = s.keeper.GetVotingPower(s.ctx, delA) + s.Require().EqualValues(votingPower.StakingVotingPower, normalStakingAmount) + s.Require().EqualValues(votingPower.LiquidStakingVotingPower, sdk.NewInt(49187500)) + + // double sign first liquid validator + s.doubleSign(valOpers[0], sdk.ConsAddress(pks[0].Address())) + + // normal, liquid staking voting power is zero because of unbonded all validators by double signing + votingPower = s.keeper.GetVotingPower(s.ctx, delA) + s.Require().EqualValues(votingPower.StakingVotingPower, sdk.ZeroInt()) + s.Require().EqualValues(votingPower.LiquidStakingVotingPower, sdk.ZeroInt()) + squadtypes.PP(s.keeper.NetAmountState(s.ctx)) + squadtypes.PP(s.keeper.GetAllLiquidValidatorStates(s.ctx)) + + // rebalancing not occurred because no active liquid validators, unbonding started all liquid tokens, no liquid staking voting power + s.keeper.UpdateLiquidValidatorSet(s.ctx) + votingPower = s.keeper.GetVotingPower(s.ctx, delA) + s.Require().EqualValues(votingPower.StakingVotingPower, sdk.ZeroInt()) + s.Require().EqualValues(votingPower.LiquidStakingVotingPower, sdk.ZeroInt()) + + cachedCtx, _ = s.ctx.CacheContext() + _, _, result = s.app.GovKeeper.Tally(cachedCtx, proposal) + s.Require().Equal(sdk.NewInt(0), result.Yes) + s.Require().Equal(sdk.NewInt(0), result.No) + s.Require().Equal(sdk.NewInt(0), result.NoWithVeto) + s.Require().Equal(sdk.NewInt(0), result.Abstain) + +} diff --git a/x/liquidstaking/module.go b/x/liquidstaking/module.go index 3c8784b6..c03a0416 100644 --- a/x/liquidstaking/module.go +++ b/x/liquidstaking/module.go @@ -186,7 +186,7 @@ func (AppModule) GenerateGenesisState(simState *module.SimulationState) { // ProposalContents returns all the liquidstaking content functions used to // simulate governance proposals. func (am AppModule) ProposalContents(simState module.SimulationState) []simtypes.WeightedProposalContent { - return simulation.ProposalContents(am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.keeper) + return simulation.ProposalContents(am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.govKeeper, am.keeper) } // RandomizedParams creates randomized liquidstaking param changes for the simulator. diff --git a/x/liquidstaking/simulation/proposals.go b/x/liquidstaking/simulation/proposals.go index 2e60d5d2..6d2d7789 100644 --- a/x/liquidstaking/simulation/proposals.go +++ b/x/liquidstaking/simulation/proposals.go @@ -7,10 +7,12 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/params/types/proposal" "github.com/cosmos/cosmos-sdk/x/simulation" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/cosmosquad-labs/squad/app/params" + squadtypes "github.com/cosmosquad-labs/squad/types" "github.com/cosmosquad-labs/squad/x/liquidstaking/keeper" "github.com/cosmosquad-labs/squad/x/liquidstaking/types" ) @@ -20,12 +22,13 @@ const ( OpWeightSimulateAddWhitelistValidatorsProposal = "op_weight_add_whitelist_validators_proposal" OpWeightSimulateUpdateWhitelistValidatorsProposal = "op_weight_update_whitelist_validators_proposal" OpWeightSimulateDeleteWhitelistValidatorsProposal = "op_weight_delete_whitelist_validators_proposal" - OpWeightWeightCompleteRedelegationUnbonding = "op_weight_complete_redelegation_unbonding" + OpWeightCompleteRedelegationUnbonding = "op_weight_complete_redelegation_unbonding" + OpWeightTallyWithLiquidStaking = "op_weight_tally_with_liquid_staking" MaxWhitelistValidators = 10 ) // ProposalContents defines the module weighted proposals' contents -func ProposalContents(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, k keeper.Keeper) []simtypes.WeightedProposalContent { +func ProposalContents(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, gk types.GovKeeper, k keeper.Keeper) []simtypes.WeightedProposalContent { return []simtypes.WeightedProposalContent{ simulation.NewWeightedProposalContent( OpWeightSimulateAddWhitelistValidatorsProposal, @@ -43,10 +46,15 @@ func ProposalContents(ak types.AccountKeeper, bk types.BankKeeper, sk types.Stak SimulateDeleteWhitelistValidatorsProposal(ak, bk, sk, k), ), simulation.NewWeightedProposalContent( - OpWeightWeightCompleteRedelegationUnbonding, + OpWeightCompleteRedelegationUnbonding, params.DefaultWeightCompleteRedelegationUnbonding, SimulateCompleteRedelegationUnbonding(ak, bk, sk, k), ), + simulation.NewWeightedProposalContent( + OpWeightTallyWithLiquidStaking, + params.DefaultWeightTallyWithLiquidStaking, + SimulateTallyWithLiquidStaking(ak, bk, sk, gk, k), + ), } } @@ -231,3 +239,57 @@ func SimulateCompleteRedelegationUnbonding(ak types.AccountKeeper, bk types.Bank ) } } + +// SimulateTallyWithLiquidStaking generates tally for TallyLiquidStakingGov. +func SimulateTallyWithLiquidStaking(ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper, gk types.GovKeeper, k keeper.Keeper) simtypes.ContentSimulatorFn { + return func(r *rand.Rand, ctx sdk.Context, accs []simtypes.Account) simtypes.Content { + proposals := gk.GetProposals(ctx) + var targetProposal *govtypes.Proposal + for _, p := range proposals { + if p.Status == govtypes.StatusVotingPeriod { + targetProposal = &p + break + } + } + var voter sdk.AccAddress + if targetProposal != nil { + for i := 1; i < len(accs); i++ { + simAccount, _ := simtypes.RandomAcc(r, accs) + + account := ak.GetAccount(ctx, simAccount.Address) + spendable := bk.SpendableCoins(ctx, account.GetAddress()) + + // spendable must be greater than unstaking coins + if spendable.AmountOf(types.DefaultLiquidBondDenom).GT(sdk.ZeroInt()) { + voter = account.GetAddress() + fmt.Println("[voter]", voter, spendable) + gk.AddVote(ctx, targetProposal.ProposalId, voter, govtypes.WeightedVoteOptions{ // weight sum > 1 + govtypes.WeightedVoteOption{Option: govtypes.OptionYes, Weight: sdk.NewDec(1)}, + }) + + votingPower := k.GetVotingPower(ctx, voter) + + _, _, res := gk.Tally(ctx, *targetProposal) + squadtypes.PP(res) + squadtypes.PP(votingPower) + break + } + } + } + + params := k.GetParams(ctx) + + whitelistStr, err := json.Marshal(¶ms.WhitelistedValidators) + if err != nil { + panic(err) + } + change := proposal.NewParamChange(types.ModuleName, string(types.KeyWhitelistedValidators), string(whitelistStr)) + + // this proposal could be passed due to x/gov simulation voting process + return proposal.NewParameterChangeProposal( + "SimulateTallyWithLiquidStaking", + "SimulateTallyWithLiquidStaking", + []proposal.ParamChange{change}, + ) + } +} diff --git a/x/liquidstaking/types/expected_keepers.go b/x/liquidstaking/types/expected_keepers.go index e3204ec3..5a9d5100 100644 --- a/x/liquidstaking/types/expected_keepers.go +++ b/x/liquidstaking/types/expected_keepers.go @@ -83,6 +83,9 @@ type GovKeeper interface { Tally(ctx sdk.Context, proposal govtypes.Proposal) (passes bool, burnDeposits bool, tallyResults govtypes.TallyResult) GetVotes(ctx sdk.Context, proposalID uint64) (votes govtypes.Votes) GetVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) (vote govtypes.Vote, found bool) + GetProposal(ctx sdk.Context, proposalID uint64) (govtypes.Proposal, bool) + GetProposals(ctx sdk.Context) (proposals govtypes.Proposals) + AddVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress, options govtypes.WeightedVoteOptions) error } // DistrKeeper expected distribution keeper (noalias)