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

add governance support for superfluid #1191

Merged
merged 25 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f37db19
add governance support for superfluid
mconcat Apr 4, 2022
e5c2ad1
in progress
mconcat Apr 12, 2022
57cf662
fix constant
mconcat Apr 12, 2022
2bbe4ce
apply comments
mconcat Apr 14, 2022
e803e2b
Merge remote-tracking branch 'origin' into mconcat/superfluid-governance
mconcat Apr 18, 2022
5d3fbb1
fix test
mconcat Apr 18, 2022
7e6c698
Apply suggestions from code review
mconcat Apr 25, 2022
d166efe
continue on nonexisting interim account
mconcat Apr 25, 2022
cffc65e
Merge github.com:osmosis-labs/osmosis into mconcat/superfluid-governance
mconcat Apr 25, 2022
506cc44
Merge branch 'mconcat/superfluid-governance' of github.com:osmosis-la…
mconcat Apr 25, 2022
46ee0bd
fix typo
mconcat Apr 27, 2022
3e3433a
panic -> ctx.Logger.Erroer
mconcat Apr 27, 2022
1b515ac
typo
mconcat Apr 27, 2022
6a7ca36
Merge branch 'main' into mconcat/superfluid-governance
mconcat Apr 27, 2022
6de2ba0
Apply suggestions from code review
mconcat Apr 29, 2022
2ca3b8d
add native coin test
mconcat May 2, 2022
8d9a1fa
Merge branch 'main' into mconcat/superfluid-governance
mconcat May 2, 2022
c936770
Apply suggestions from code review
mconcat May 2, 2022
a383fd4
Merge branch 'mconcat/superfluid-governance' of github.com:osmosis-la…
mconcat May 2, 2022
a8685a9
Merge branch 'main' into mconcat/superfluid-governance
mconcat May 5, 2022
5526429
Merge branch 'main' into mconcat/superfluid-governance
alexanderbez May 5, 2022
ce499d2
Address comments from code review
mattverse May 17, 2022
fc5d5e8
Merge branch 'main' into mconcat/superfluid-governance
mattverse May 17, 2022
35a4b99
Merge branch 'main' into mconcat/superfluid-governance
alexanderbez May 17, 2022
c3e4d6d
Merge branch 'main' into mconcat/superfluid-governance
mattverse May 17, 2022
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
74 changes: 74 additions & 0 deletions x/superfluid/keeper/stake.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,3 +350,77 @@ func (k Keeper) forceUndelegateAndBurnOsmoTokens(ctx sdk.Context,
// Eugen’s point: Only rewards message needs to be updated. Rest of messages are fine
// Queries need to be updated
// We can do this at the very end though, since it just relates to queries.

// IterateBondedValidatorsByPower implements govtypes.StakingKeeper
mconcat marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(int64, stakingtypes.ValidatorI) bool) {
k.sk.IterateBondedValidatorsByPower(ctx, fn)
}

// TotalBondedTokens implements govtypes.StakingKeeper
func (k Keeper) TotalBondedTokens(ctx sdk.Context) sdk.Int {
return k.sk.TotalBondedTokens(ctx)
}

// IterateDelegations implements govtypes.StakingKeeper
mconcat marked this conversation as resolved.
Show resolved Hide resolved
mconcat marked this conversation as resolved.
Show resolved Hide resolved
// Iterates through staking keeper's delegations, and then all of the superfluid delegations.
// Iterates through staking keeper's delegations, and then all of the superfluid delegations.
func (k Keeper) IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn func(int64, stakingtypes.DelegationI) bool) {
// call the callback with the non-superfluid delegations
var index int64
k.sk.IterateDelegations(ctx, delegator, func(i int64, delegation stakingtypes.DelegationI) (stop bool) {
index = i
return fn(i, delegation)
})

synthlocks := k.lk.GetAllSyntheticLockupsByAddr(ctx, delegator)
for i, lock := range synthlocks {
// get locked coin from the lock ID
interim, ok := k.GetIntermediaryAccountFromLockId(ctx, lock.UnderlyingLockId)
if !ok {
mconcat marked this conversation as resolved.
Show resolved Hide resolved
ctx.Logger().Error("intermediary account retrieval failed with underlying lock", "Lock", lock)
mattverse marked this conversation as resolved.
Show resolved Hide resolved
continue
}

lock, err := k.lk.GetLockByID(ctx, lock.UnderlyingLockId)
if err != nil {
ctx.Logger().Error("lockup retrieval failed with underlying lock", "Lock", lock, "Error", err)
continue
}

coin, err := lock.SingleCoin()
if err != nil {
ctx.Logger().Error("lock fails to meet expected invariant, it contains multiple coins", "Lock", lock, "Error", err)
continue
}

// get osmo-equivalent token amount
amount := k.GetSuperfluidOSMOTokens(ctx, interim.Denom, coin.Amount)

// get validator shares equivalent to the token amount
valAddr, err := sdk.ValAddressFromBech32(interim.ValAddr)
if err != nil {
ctx.Logger().Error("failed to decode validator address", "Intermediary", interim.ValAddr, "LockID", lock.ID, "Error", err)
continue
}

validator, found := k.sk.GetValidator(ctx, valAddr)
if !found {
ctx.Logger().Error("validator does not exist for lock", "Validator", valAddr, "LockID", lock.ID)
continue
}

shares, err := validator.SharesFromTokens(amount)
if err != nil {
// tokens are not valid. continue.
continue
}

// construct delegation and call callback
delegation := stakingtypes.Delegation{
DelegatorAddress: delegator.String(),
ValidatorAddress: interim.ValAddr,
Shares: shares,
}
fn(index+int64(i), delegation)
mattverse marked this conversation as resolved.
Show resolved Hide resolved
}
}
147 changes: 146 additions & 1 deletion x/superfluid/keeper/stake_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
package keeper_test

import (
"fmt"
"time"

abci "github.com/tendermint/tendermint/abci/types"

lockuptypes "github.com/osmosis-labs/osmosis/v7/x/lockup/types"
minttypes "github.com/osmosis-labs/osmosis/v7/x/mint/types"
"github.com/osmosis-labs/osmosis/v7/x/superfluid/keeper"
"github.com/osmosis-labs/osmosis/v7/x/superfluid/types"
abci "github.com/tendermint/tendermint/abci/types"

sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

type normalDelegation struct {
delIndex int64
valIndex int64
coinAmount int64
}

type superfluidDelegation struct {
delIndex int64
valIndex int64
Expand All @@ -30,6 +38,15 @@ type osmoEquivalentMultiplier struct {
price sdk.Dec
}

func (suite *KeeperTestSuite) SetupNormalDelegation(delAddrs []sdk.AccAddress, valAddrs []sdk.ValAddress, del normalDelegation) error {
val, found := suite.App.StakingKeeper.GetValidator(suite.Ctx, valAddrs[del.valIndex])
if !found {
return fmt.Errorf("validator not found")
}
_, err := suite.App.StakingKeeper.Delegate(suite.Ctx, delAddrs[del.delIndex], sdk.NewIntFromUint64(uint64(del.coinAmount)), stakingtypes.Bonded, val, false)
return err
}

func (suite *KeeperTestSuite) SetupSuperfluidDelegations(delAddrs []sdk.AccAddress, valAddrs []sdk.ValAddress, superDelegations []superfluidDelegation, denoms []string) ([]types.SuperfluidIntermediaryAccount, []lockuptypes.PeriodLock) {
flagIntermediaryAcc := make(map[string]bool)
intermediaryAccs := []types.SuperfluidIntermediaryAccount{}
Expand Down Expand Up @@ -895,3 +912,131 @@ func (suite *KeeperTestSuite) TestRefreshIntermediaryDelegationAmounts() {
})
}
}

func (suite *KeeperTestSuite) TestSuperfluidDelegationGovernanceVoting() {
testCases := []struct {
name string
validatorStats []stakingtypes.BondStatus
superDelegations [][]superfluidDelegation
normalDelegations []normalDelegation
}{
{
"with single validator and single delegation",
[]stakingtypes.BondStatus{stakingtypes.Bonded},
[][]superfluidDelegation{{{0, 0, 0, 1000000}}},
nil,
},
{
"with single validator and additional delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded},
[][]superfluidDelegation{{{0, 0, 0, 1000000}, {0, 0, 0, 1000000}}},
nil,
},
{
"with multiple validator and multiple superfluid delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Bonded},
[][]superfluidDelegation{{{0, 0, 0, 1000000}}, {{1, 1, 0, 1000000}}},
nil,
},
{
"with single validator and multiple denom superfluid delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Bonded},
[][]superfluidDelegation{{{0, 0, 0, 1000000}, {0, 0, 1, 1000000}}},
nil,
},
{
"with multiple validators and multiple denom superfluid delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Bonded},
[][]superfluidDelegation{{{0, 0, 0, 1000000}, {0, 1, 1, 1000000}}},
nil,
},
{
"many delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded, stakingtypes.Bonded},
[][]superfluidDelegation{
{{0, 0, 0, 1000000}, {0, 1, 1, 1000000}},
{{1, 0, 0, 1000000}, {1, 0, 1, 1000000}},
{{2, 1, 1, 1000000}, {2, 1, 0, 1000000}},
{{3, 0, 0, 1000000}, {3, 1, 1, 1000000}},
},
nil,
},
{
"with normal delegations",
[]stakingtypes.BondStatus{stakingtypes.Bonded},
[][]superfluidDelegation{
{{0, 0, 0, 1000000}, {0, 0, 1, 1000000}},
},
[]normalDelegation{
{0, 0, 1000000},
},
},
}

for _, tc := range testCases {
tc := tc

suite.Run(tc.name, func() {
suite.SetupTest()

denoms, _ := suite.SetupGammPoolsAndSuperfluidAssets([]sdk.Dec{sdk.NewDec(20), sdk.NewDec(20)})

// Generate delegator addresses
delAddrs := CreateRandomAccounts(len(tc.superDelegations))

// setup validators
valAddrs := suite.SetupValidators(tc.validatorStats)

// setup superfluid delegations
for _, sfdel := range tc.superDelegations {
intermediaryAccs, _ := suite.SetupSuperfluidDelegations(delAddrs, valAddrs, sfdel, denoms)
suite.checkIntermediaryAccountDelegations(intermediaryAccs)
}

// setup normal delegations
for _, del := range tc.normalDelegations {
err := suite.SetupNormalDelegation(delAddrs, valAddrs, del)
suite.NoError(err)
}

// all expected delegated amounts to a validator from a delegator
delegatedAmount := func(delidx, validx int) sdk.Int {
res := sdk.ZeroInt()
for _, del := range tc.superDelegations[delidx] {
if del.valIndex == int64(validx) {
res = res.AddRaw(del.lpAmount)
}
}
if len(tc.normalDelegations) != 0 {
del := tc.normalDelegations[delidx]
res = res.AddRaw(del.coinAmount / 10) // LP price is 10 osmo in this test
}
return res
}
for delidx := range tc.superDelegations {
// store all actual delegations to a validator
sharePerValidatorMap := make(map[string]sdk.Dec)
for validx := range tc.validatorStats {
sharePerValidatorMap[valAddrs[validx].String()] = sdk.ZeroDec()
}
addToSharePerValidatorMap := func(val sdk.ValAddress, share sdk.Dec) {
if existing, ok := sharePerValidatorMap[val.String()]; ok {
share.AddMut(existing)
}
sharePerValidatorMap[val.String()] = share
}

// iterate delegations and add eligible shares to the sharePerValidatorMap
suite.App.SuperfluidKeeper.IterateDelegations(suite.Ctx, delAddrs[delidx], func(_ int64, del stakingtypes.DelegationI) bool {
addToSharePerValidatorMap(del.GetValidatorAddr(), del.GetShares())
return false
})

// check if the expected delegated amount equals to actual
for validx := range tc.validatorStats {
suite.Equal(delegatedAmount(delidx, validx).Int64()*10, sharePerValidatorMap[valAddrs[validx].String()].RoundInt().Int64())
}
}
})
}
}
4 changes: 4 additions & 0 deletions x/superfluid/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ type StakingKeeper interface {
GetUnbondingDelegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (ubd stakingtypes.UnbondingDelegation, found bool)
UnbondingTime(ctx sdk.Context) time.Duration
GetParams(ctx sdk.Context) stakingtypes.Params

IterateBondedValidatorsByPower(ctx sdk.Context, fn func(int64, stakingtypes.ValidatorI) bool)
TotalBondedTokens(ctx sdk.Context) sdk.Int
IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress, fn func(int64, stakingtypes.DelegationI) bool)
}

// DistrKeeper expected distribution keeper.
Expand Down