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: add indirect incentivized pools to incentivized pools query #5589

Merged
merged 11 commits into from
Jun 22, 2023
16 changes: 8 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Types of changes (Stanzas):
"Bug Fixes" for any bug fixes.
"Client Breaking" for breaking CLI commands and REST routes used by end-users.
"API Breaking" for breaking exported APIs used by developers building on SDK.
"State Machine Breaking" for any changes that result in a different AppState
"State Machine Breaking" for any changes that result in a different AppState
given same genesisState and txList.
Ref: https://keepachangelog.com/en/1.0.0/
-->
Expand Down Expand Up @@ -61,8 +61,8 @@ ProtoRev Changes (x/protorev):
- Modifies the payment schedule for the dev account from weekly to after every trade.
- Triggers backruns, joinPool, and exitPool using hooks.

TokenFactory before send hooks (x/tokenfactory):
- This enhancement allows for executing custom logic before sending tokens, providing more flexibility
TokenFactory before send hooks (x/tokenfactory):
- This enhancement allows for executing custom logic before sending tokens, providing more flexibility
and control over token transfers.


Expand Down Expand Up @@ -100,7 +100,7 @@ and control over token transfers.
## State Breaking
* [#5380](https://github.com/osmosis-labs/osmosis/pull/5380) feat: add ica authorized messages in upgrade handler
* [#5363](https://github.com/osmosis-labs/osmosis/pull/5363) fix: twap record upgrade handler
* [#5265](https://github.com/osmosis-labs/osmosis/pull/5265) fix: expect single synthetic lock per native lock ID
* [#5265](https://github.com/osmosis-labs/osmosis/pull/5265) fix: expect single synthetic lock per native lock ID
* [#4983](https://github.com/osmosis-labs/osmosis/pull/4983) implement gas consume on denom creation
* [#4830](https://github.com/osmosis-labs/osmosis/pull/4830) Scale gas costs by denoms in gauge (AddToGaugeReward)
* [#5511](https://github.com/osmosis-labs/osmosis/pull/5511) Scale gas costs by denoms in gauge (CreateGauge)
Expand All @@ -111,12 +111,11 @@ and control over token transfers.
* [#5468](https://github.com/osmosis-labs/osmosis/pull/5468) fix: Reduce tokenfactory denom creation gas fee to 1_000_000

## Dependencies
* [#4783](https://github.com/osmosis-labs/osmosis/pull/4783) Update wasmd to 0.31
* [#5404](https://github.com/osmosis-labs/osmosis/pull/5404) Cosmwasm Cherry security patch
* [#4783](https://github.com/osmosis-labs/osmosis/pull/4783) Update wasmd to 0.31
* [#5404](https://github.com/osmosis-labs/osmosis/pull/5404) Cosmwasm Cherry security patch
* [#5320](https://github.com/osmosis-labs/osmosis/pull/5320) minor: huckleberry ibc patch

### Misc Improvements

* [#5356](https://github.com/osmosis-labs/osmosis/pull/5356) Fix wrong restHandler for ReplaceMigrationRecordsProposal
* [#5020](https://github.com/osmosis-labs/osmosis/pull/5020) Add gas config to the client.toml
* [#5105](https://github.com/osmosis-labs/osmosis/pull/5105) Lint stableswap in the same manner as all of Osmosis
Expand All @@ -136,11 +135,12 @@ and control over token transfers.
* [#5239](https://github.com/osmosis-labs/osmosis/pull/5239) Implement `GetTotalPoolShares` public keeper function for GAMM.
* [#5261](https://github.com/osmosis-labs/osmosis/pull/5261) Allows `UpdateFeeTokenProposal` to take in multiple fee tokens instead of just one.
* [#5265](https://github.com/osmosis-labs/osmosis/pull/5265) Ensure a lock cannot point to multiple synthetic locks. Deprecates `SyntheticLockupsByLockupID` in favor of `SyntheticLockupByLockupID`.
* [#4950] (https://github.com/osmosis-labs/osmosis/pull/4950) Add in/out tokens to Concentrated Liquidity's AfterConcentratedPoolSwap hook
* [#4950](https://github.com/osmosis-labs/osmosis/pull/4950) Add in/out tokens to Concentrated Liquidity's AfterConcentratedPoolSwap hook
* [#4629](https://github.com/osmosis-labs/osmosis/pull/4629) Add amino proto annotations
* [#4830](https://github.com/osmosis-labs/osmosis/pull/4830) Add gas cost when we AddToGaugeRewards, linearly increase with coins to add
* [#5000](https://github.com/osmosis-labs/osmosis/pull/5000) osmomath.Power panics for base < 1 to temporarily restrict broken logic for such base.
* [#4336](https://github.com/osmosis-labs/osmosis/pull/4336) Move epochs module into its own go.mod
* [#5589](https://github.com/osmosis-labs/osmosis/pull/5589) Include linked balancer pool in incentivized pools query



Expand Down
1 change: 1 addition & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.DistrKeeper,
appKeepers.PoolManagerKeeper,
appKeepers.EpochsKeeper,
appKeepers.SuperfluidKeeper,
)
appKeepers.PoolIncentivesKeeper = &poolIncentivesKeeper
appKeepers.PoolManagerKeeper.SetPoolIncentivesKeeper(appKeepers.PoolIncentivesKeeper)
Expand Down
25 changes: 25 additions & 0 deletions proto/osmosis/pool-incentives/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,28 @@ message GenesisState {
(gogoproto.moretags) = "yaml:\"pool_to_gauges\""
];
}

// MigrationRecords contains all the links between balancer and concentrated
// pools.
//
// This is copied over from the gamm proto file in order to circumnavigate
// the circular dependency between the two modules.
message MigrationRecords {
repeated BalancerToConcentratedPoolLink balancer_to_concentrated_pool_links =
1 [ (gogoproto.nullable) = false ];
}

// BalancerToConcentratedPoolLink defines a single link between a single
// balancer pool and a single concentrated liquidity pool. This link is used to
// allow a balancer pool to migrate to a single canonical full range
// concentrated liquidity pool position
// A balancer pool can be linked to a maximum of one cl pool, and a cl pool can
// be linked to a maximum of one balancer pool.
//
// This is copied over from the gamm proto file in order to circumnavigate
// the circular dependency between the two modules.
message BalancerToConcentratedPoolLink {
option (gogoproto.equal) = true;
uint64 balancer_pool_id = 1;
uint64 cl_pool_id = 2;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was sadly the cleanest way knew to get passed the circular dependency issue. The only module that pool-incentives had access to that could access gamm's store was superfluid. So I basically called GetAllMigrationInfo in pool-incentives via superfluid and parsed the result into an identical struct. Please lmk if there is a better way to achieve this without a major refactor.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might sound a bit unorthodox but how about we create a separate file that store shared interfaces between modules

for instance;

  • gamm

    • shared_keepers.go (includes PoolIncentiveKeeper)
  • pool-incentives

    • shared_keepers.go (includes GammKeeper)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this! I think it makes it simpler to reason about when we come back around and resolve this with the solution Roman recommended. Added here e70ac41

40 changes: 40 additions & 0 deletions x/pool-incentives/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"context"
"time"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -157,6 +158,45 @@ func (q Querier) IncentivizedPools(ctx context.Context, _ *types.QueryIncentiviz
}
}

// Retrieve the migration records between balancer pools and concentrated liquidity pools.
// This comes from the superfluid keeper, since superfluid is the only pool incentives connected
// module that has access to the gamm modules store.
migrationRecords, err := q.superfluidKeeper.GetAllMigrationInfo(sdkCtx)
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}

// Iterate over all migration records.
for _, record := range migrationRecords.BalancerToConcentratedPoolLinks {
linkedClPoolIsIncentivized := false
var duration time.Duration

// If the linked concentrated liquidity pool is in the list of incentivized pools,
// note this and add its balancer counterpart to the list of incentivized pools.
for _, incentivizedPool := range incentivizedPools {
if incentivizedPool.PoolId == record.ClPoolId {
linkedClPoolIsIncentivized = true
duration = incentivizedPool.LockableDuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct to use this for duration?

AFAIR, we update the largest lockable duration gauge for the balancer pool while using incentivized epoch duration for CL pool which might not necessarily match

Copy link
Member Author

@czarcas7ic czarcas7ic Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its just a holder value, but we can set this as the longest duration just in case, because I agree that is "default"

Done here 18ee857

continue
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider creating a map[uint64]struct{} of incentivized pool IDs in the first loop above. This would allow checking the map here in O(1) instead of repeatedly iterating all incentivized pools for every link

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, added here bba673d


if !linkedClPoolIsIncentivized {
continue
}

gaugeId, err := q.Keeper.GetPoolGaugeId(sdkCtx, record.BalancerPoolId, duration)
if err == nil {
incentivizedPool := types.IncentivizedPool{
PoolId: record.BalancerPoolId,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we never expect PoolId to be a CL pool Id?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In every case the balancer pool will be the pool that is getting indirectly incentivized. This means that there is never a case in which a CL pool does not appear in the incentivized pools list that should be there. That is why we only retrieve the balancer pools at the end, since they are the ones that are getting replaced and funded via the CL gauge

LockableDuration: duration,
GaugeId: gaugeId,
}

incentivizedPools = append(incentivizedPools, incentivizedPool)
}
}

return &types.QueryIncentivizedPoolsResponse{
IncentivizedPools: incentivizedPools,
}, nil
Expand Down
91 changes: 55 additions & 36 deletions x/pool-incentives/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"

cltypes "github.com/osmosis-labs/osmosis/v16/x/concentrated-liquidity/types"
gammtypes "github.com/osmosis-labs/osmosis/v16/x/gamm/types"
lockuptypes "github.com/osmosis-labs/osmosis/v16/x/lockup/types"
"github.com/osmosis-labs/osmosis/v16/x/pool-incentives/types"
)
Expand Down Expand Up @@ -156,14 +158,15 @@ func (s *KeeperTestSuite) TestLockableDurations() {

func (s *KeeperTestSuite) TestIncentivizedPools() {
for _, tc := range []struct {
desc string
poolCreated bool
weights []sdk.Int
clPoolWithGauge bool
clGaugeWeight sdk.Int
perpetual bool
nonPerpetual bool
expectedRecordLength int
desc string
poolCreated bool
weights []sdk.Int
clPoolWithGauge bool
clGaugeWeight sdk.Int
perpetual bool
nonPerpetual bool
setupPoolMigrationLink bool
expectedRecordLength int
}{
{
desc: "No pool exist",
Expand Down Expand Up @@ -192,47 +195,49 @@ func (s *KeeperTestSuite) TestIncentivizedPools() {
expectedRecordLength: 0,
},
{
desc: "Concentrated case",
desc: "Concentrated case, no pool migration link",
poolCreated: true,
clPoolWithGauge: true,
clGaugeWeight: sdk.NewInt(400),
weights: []sdk.Int{sdk.NewInt(100), sdk.NewInt(200), sdk.NewInt(300)},
expectedRecordLength: 4,
expectedRecordLength: 3, // Note we expect 3 instead of 4 here, since the pool migration link is not setup.
},
{
desc: "Concentrated case, setup pool migration link",
poolCreated: true,
clPoolWithGauge: true,
clGaugeWeight: sdk.NewInt(400),
weights: []sdk.Int{sdk.NewInt(100), sdk.NewInt(200), sdk.NewInt(300)},
setupPoolMigrationLink: true,
expectedRecordLength: 4,
},
} {
tc := tc
s.Run(tc.desc, func() {
s.SetupTest()
keeper := s.App.PoolIncentivesKeeper
queryClient := s.queryClient
var poolId uint64
var balancerPoolId uint64

// Replace the longest lockable durations with the epoch duration to match the record that gets auto created when making a cl pool.
epochDuration := s.App.IncentivesKeeper.GetEpochInfo(s.Ctx).Duration
lockableDurations := keeper.GetLockableDurations(s.Ctx)
lockableDurations[len(lockableDurations)-1] = epochDuration
keeper.SetLockableDurations(s.Ctx, lockableDurations)
lockableDurations = keeper.GetLockableDurations(s.Ctx)
s.Require().Equal(3, len(lockableDurations))
s.App.IncentivesKeeper.SetLockableDurations(s.Ctx, lockableDurations)
lockableDurations = s.App.IncentivesKeeper.GetLockableDurations(s.Ctx)
s.Require().Equal(3, len(lockableDurations))

var lockableDurations []time.Duration
if tc.poolCreated {
// prepare a balancer pool
poolId = s.PrepareBalancerPool()
// LockableDurations should be 1, 3, 7 hours from the default genesis state.
lockableDurations = keeper.GetLockableDurations(s.Ctx)
s.Require().Equal(3, len(lockableDurations))
balancerPoolId = s.PrepareBalancerPoolWithCoins(sdk.NewCoin("eth", sdk.NewInt(100000000000)), sdk.NewCoin("usdc", sdk.NewInt(100000000000)))

var distRecords []types.DistrRecord

// If appropriate, create a concentrated pool with a gauge.
// Recall that concentrated pool gauges are created on epoch duration, which
// is set in default genesis to be 168 hours (1 week). Since this is not included
// in the set of lockable durations, creating this gauge serves as a way to ensure
// CL gauges are captured by the IncentivizedPools query.
if tc.clPoolWithGauge {
clPool := s.PrepareConcentratedPool()
epochDuration := s.App.IncentivesKeeper.GetEpochInfo(s.Ctx).Duration

clGaugeId, err := keeper.GetPoolGaugeId(s.Ctx, clPool.GetId(), epochDuration)
s.Require().NoError(err)
distRecords = append(distRecords, types.DistrRecord{GaugeId: clGaugeId, Weight: tc.clGaugeWeight})
}

// Note we set up gauges prior to creating the cl pool, otherwise the cl pool will have no gauge to overwrite, and we cannot verify that it works as expected.
for i := 0; i < len(lockableDurations); i++ {
gaugeId, err := keeper.GetPoolGaugeId(s.Ctx, poolId, lockableDurations[i])
gaugeId, err := keeper.GetPoolGaugeId(s.Ctx, balancerPoolId, lockableDurations[i])
s.Require().NoError(err)
distRecords = append(distRecords, types.DistrRecord{GaugeId: gaugeId, Weight: tc.weights[i]})

Expand Down Expand Up @@ -262,13 +267,27 @@ func (s *KeeperTestSuite) TestIncentivizedPools() {
return distRecords[i].GaugeId < distRecords[j].GaugeId
})

// update records and ensure that non-perpetuals pot cannot get rewards.
// Update records and ensure that non-perpetuals pot cannot get rewards.
_ = keeper.UpdateDistrRecords(s.Ctx, distRecords...)
}
res, err := queryClient.IncentivizedPools(context.Background(), &types.QueryIncentivizedPoolsRequest{})
s.Require().NoError(err)
s.Require().Equal(tc.expectedRecordLength, len(res.IncentivizedPools))

// Create a concentrated pool with a gauge if required.
var clPool cltypes.ConcentratedPoolExtension
if tc.clPoolWithGauge {
clPool = s.PrepareConcentratedPool()
}

// Create a link between the balancer pool and the cl pool if required.
if tc.setupPoolMigrationLink {
record := gammtypes.BalancerToConcentratedPoolLink{BalancerPoolId: balancerPoolId, ClPoolId: clPool.GetId()}
err := s.App.GAMMKeeper.ReplaceMigrationRecords(s.Ctx, []gammtypes.BalancerToConcentratedPoolLink{record})
s.Require().NoError(err)
}
}
// System under test.
res, err := queryClient.IncentivizedPools(context.Background(), &types.QueryIncentivizedPoolsRequest{})
s.Require().NoError(err)
s.Require().Equal(tc.expectedRecordLength, len(res.IncentivizedPools))
})
}
}
Expand Down
4 changes: 3 additions & 1 deletion x/pool-incentives/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ type Keeper struct {
bankKeeper types.BankKeeper
distrKeeper types.DistrKeeper
poolmanagerKeeper types.PoolManagerKeeper
superfluidKeeper types.SuperfluidKeeper
}

func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, incentivesKeeper types.IncentivesKeeper, distrKeeper types.DistrKeeper, poolmanagerKeeper types.PoolManagerKeeper, epochKeeper types.EpochKeeper) Keeper {
func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKeeper types.AccountKeeper, bankKeeper types.BankKeeper, incentivesKeeper types.IncentivesKeeper, distrKeeper types.DistrKeeper, poolmanagerKeeper types.PoolManagerKeeper, epochKeeper types.EpochKeeper, superfluidKeeper types.SuperfluidKeeper) Keeper {
// ensure pool-incentives module account is set
if addr := accountKeeper.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
Expand All @@ -53,6 +54,7 @@ func NewKeeper(storeKey sdk.StoreKey, paramSpace paramtypes.Subspace, accountKee
distrKeeper: distrKeeper,
poolmanagerKeeper: poolmanagerKeeper,
epochKeeper: epochKeeper,
superfluidKeeper: superfluidKeeper,
}
}

Expand Down
4 changes: 4 additions & 0 deletions x/pool-incentives/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,7 @@ type DistrKeeper interface {
type EpochKeeper interface {
GetEpochInfo(ctx sdk.Context, identifier string) epochstypes.EpochInfo
}

type SuperfluidKeeper interface {
GetAllMigrationInfo(ctx sdk.Context) (MigrationRecords, error)
}
Loading