Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add simulation on liquidstaking, improve logics #192

Merged
merged 14 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test: add voting power unit test and tally simulation
  • Loading branch information
dongsam committed Feb 17, 2022
commit aced90b5d75a995145ffb8e5bc7081a07c490dea
1 change: 1 addition & 0 deletions app/params/weights.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ const (
DefaultWeightUpdateWhitelistValidatorsProposal int = 5
DefaultWeightDeleteWhitelistValidatorsProposal int = 5
DefaultWeightCompleteRedelegationUnbonding int = 30
DefaultWeightTallyWithLiquidStaking int = 30
)
2 changes: 1 addition & 1 deletion x/liquidstaking/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
248 changes: 0 additions & 248 deletions x/liquidstaking/keeper/liquidstaking_test.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
3 changes: 1 addition & 2 deletions x/liquidstaking/keeper/tally.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
Loading