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

fix balancer liquidity breaking incentives #6145

Merged
merged 6 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
49 changes: 40 additions & 9 deletions app/upgrades/v18/upgrades_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v18_test

import (
"fmt"
poolmanagertypes "github.com/osmosis-labs/osmosis/v17/x/poolmanager/types"
"sort"
"testing"
"time"
Expand Down Expand Up @@ -44,34 +45,64 @@ func assertEqual(suite *UpgradeTestSuite, pre, post interface{}) {
suite.Require().Equal(pre, post)
}

func (suite *UpgradeTestSuite) TestUpgrade() {
func (s *UpgradeTestSuite) TestUpgrade() {
// set up pools first to match v17 state(including linked cl pools)
suite.setupPoolsToMainnetState()
s.setupPoolsToMainnetState()

// corrupt state to match mainnet state
suite.setupCorruptedState()
s.setupCorruptedState()

// with the corrupted state, distribution used to panic in the `AfterEpochEnd` hook,
// specifically from the one from incentives keeper.
// This method ensures that with the corrupted state, we have the same state where
// distribution would fail.
suite.ensurePreUpgradeDistributionPanics()
s.ensurePreUpgradeDistributionPanics()

migrationInfo, err := s.App.GAMMKeeper.GetAllMigrationInfo(s.Ctx)
s.Require().NoError(err)

link := migrationInfo.BalancerToConcentratedPoolLinks[0]
s.Require().Equal(uint64(3), link.BalancerPoolId)

clPoolId := link.ClPoolId

pool, err := s.App.ConcentratedLiquidityKeeper.GetConcentratedPoolById(s.Ctx, clPoolId)
s.Require().NoError(err)

// LP Fails before the upgrade
lpTokens := sdk.NewCoins(sdk.NewCoin(pool.GetToken0(), sdk.NewInt(1_000_000)), sdk.NewCoin(pool.GetToken1(), sdk.NewInt(1_000_000)))
s.FundAcc(s.TestAccs[0], lpTokens)
// require a panic
s.Require().Panics(func() {
_, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId, s.TestAccs[0], lpTokens)
})

// upgrade software
suite.imitateUpgrade()
suite.App.BeginBlocker(suite.Ctx, abci.RequestBeginBlock{})
suite.Ctx = suite.Ctx.WithBlockTime(suite.Ctx.BlockTime().Add(time.Hour * 24))
s.imitateUpgrade()
s.App.BeginBlocker(s.Ctx, abci.RequestBeginBlock{})
s.Ctx = s.Ctx.WithBlockTime(s.Ctx.BlockTime().Add(time.Hour * 24))

// after the accum values have been resetted correctly after upgrade, we expect the accumulator store to be initialized with the correct value,
// which in our test case would be 10000(the amount that was locked)
valueAfterClear := suite.App.LockupKeeper.GetPeriodLocksAccumulation(suite.Ctx, lockuptypes.QueryCondition{
valueAfterClear := s.App.LockupKeeper.GetPeriodLocksAccumulation(s.Ctx, lockuptypes.QueryCondition{
LockQueryType: lockuptypes.ByDuration,
Denom: "gamm/pool/3",
Duration: time.Hour * 24 * 14,
})
valueAfterClear.Equal(sdk.NewInt(shareStaysLocked))

suite.ensurePostUpgradeDistributionWorks()
s.ensurePostUpgradeDistributionWorks()

// Check that can LP and swap into pool 3 with no usses
// LP
_, err = s.App.ConcentratedLiquidityKeeper.CreateFullRangePosition(s.Ctx, clPoolId, s.TestAccs[0], lpTokens)
s.Require().NoError(err)

// Swap
toSwap := sdk.NewCoin(pool.GetToken0(), sdk.NewInt(100))
_, err = s.App.ConcentratedLiquidityKeeper.SwapExactAmountIn(s.Ctx, s.TestAccs[0], pool.(poolmanagertypes.PoolI), toSwap, pool.GetToken1(), sdk.NewInt(1), sdk.ZeroDec())
s.Require().NoError(err)

}

func (suite *UpgradeTestSuite) imitateUpgrade() {
Expand Down
30 changes: 11 additions & 19 deletions x/concentrated-liquidity/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,32 +171,24 @@ func (k Keeper) prepareBalancerPoolAsFullRange(ctx sdk.Context, clPoolId uint64,
// relaxed in the future.
// Note that we check denom compatibility later, and pool weights technically do not matter as they
// are analogous to changing the spot price, which is handled by our lower bounding.
if len(balancerPoolLiquidity) != 2 {
// Note that due to low share ratio, the balancer token liquidity may be truncated to zero.
// Balancer liquidity may also upgrade in-full to CL.
if len(balancerPoolLiquidity) > 2 {
return 0, sdk.ZeroDec(), types.ErrInvalidBalancerPoolLiquidityError{ClPoolId: clPoolId, BalancerPoolId: canonicalBalancerPoolId, BalancerPoolLiquidity: balancerPoolLiquidity}
}

// We ensure that the asset ordering is correct when passing Balancer assets into the CL pool.
var asset0Amount, asset1Amount sdk.Int
if balancerPoolLiquidity[0].Denom == clPool.GetToken0() {
asset0Amount = balancerPoolLiquidity[0].Amount
asset1Amount = balancerPoolLiquidity[1].Amount
denom0 := clPool.GetToken0()
denom1 := clPool.GetToken1()

// Ensure second denom matches (bal1 -> CL1)
if balancerPoolLiquidity[1].Denom != clPool.GetToken1() {
return 0, sdk.ZeroDec(), types.ErrInvalidBalancerPoolLiquidityError{ClPoolId: clPoolId, BalancerPoolId: canonicalBalancerPoolId, BalancerPoolLiquidity: balancerPoolLiquidity}
}
} else if balancerPoolLiquidity[0].Denom == clPool.GetToken1() {
asset0Amount = balancerPoolLiquidity[1].Amount
asset1Amount = balancerPoolLiquidity[0].Amount

// Ensure second denom matches (bal1 -> CL0)
if balancerPoolLiquidity[1].Denom != clPool.GetToken0() {
return 0, sdk.ZeroDec(), types.ErrInvalidBalancerPoolLiquidityError{ClPoolId: clPoolId, BalancerPoolId: canonicalBalancerPoolId, BalancerPoolLiquidity: balancerPoolLiquidity}
}
} else {
// This check's purpose is to confirm that denoms are the same.
clCoins := totalBalancerPoolLiquidity.FilterDenoms([]string{denom0, denom1})
if len(clCoins) != 2 {
return 0, sdk.ZeroDec(), types.ErrInvalidBalancerPoolLiquidityError{ClPoolId: clPoolId, BalancerPoolId: canonicalBalancerPoolId, BalancerPoolLiquidity: balancerPoolLiquidity}
}

asset0Amount := balancerPoolLiquidity.AmountOf(denom0)
asset1Amount := balancerPoolLiquidity.AmountOf(denom1)

// Calculate the amount of liquidity the Balancer amounts qualify in the CL pool. Note that since we use the CL spot price, this is
// safe against prices drifting apart between the two pools (we take the lower bound on the qualifying liquidity in this case).
// The `sqrtPriceLowerTick` and `sqrtPriceUpperTick` fields are set to the appropriate values for a full range position.
Expand Down