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

update #11

Merged
merged 2 commits into from
Mar 23, 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
7 changes: 5 additions & 2 deletions cmd/osmosisd/cmd/forceprune.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ const (
func forceprune() *cobra.Command {
cmd := &cobra.Command{
Use: "forceprune",
Short: "Example osmosisd forceprune -f 188000 -m 1000, which would keep blockchain and state data of last 188000 blocks (approximately 2 weeks) and ABCI responses of last 1000 blocks.",
Long: "Forceprune options prunes and compacts blockstore.db and state.db. One needs to shut down chain before running forceprune. By default it keeps last 188000 blocks (approximately 2 weeks of data) blockstore and state db (validator and consensus information) and 1000 blocks of abci responses from state.db. Everything beyond these heights in blockstore and state.db is pruned. ABCI Responses are stored in index db and so redundant especially if one is running pruned nodes. As a result we are removing ABCI data from state.db aggressively by default. One can override height for blockstore.db and state.db by using -f option and for abci response by using -m option. Example osmosisd forceprune -f 188000 -m 1000.",
Short: "Forceprune option prunes and compacts blockstore.db and state.db.",
Long: `Forceprune option prunes and compacts blockstore.db and state.db. One needs to shut down chain before running forceprune. By default it keeps last 188000 blocks (approximately 2 weeks of data) blockstore and state db (validator and consensus information) and 1000 blocks of abci responses from state.db. Everything beyond these heights in blockstore and state.db is pruned. ABCI Responses are stored in index db and so redundant especially if one is running pruned nodes. As a result we are removing ABCI data from state.db aggressively by default. One can override height for blockstore.db and state.db by using -f option and for abci response by using -m option.
Example:
osmosisd forceprune -f 188000 -m 1000,
which would keep blockchain and state data of last 188000 blocks (approximately 2 weeks) and ABCI responses of last 1000 blocks.`,
RunE: func(cmd *cobra.Command, args []string) error {
fullHeightFlag, err := cmd.Flags().GetString(fullHeight)
if err != nil {
Expand Down
34 changes: 34 additions & 0 deletions proto/osmosis/concentrated-liquidity/pool-model/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ service Query {
"/osmosis/concentratedliquidity/v1beta1/positions/{address}";
}

// TotalLiquidityForRange the amount of liquidity existing within given range.
rpc TotalLiquidityForRange(QueryTotalLiquidityForRangeRequest)
returns (QueryTotalLiquidityForRangeResponse) {
option (google.api.http).get =
"/osmosis/concentratedliquidity/v1beta1/total_liquidity_for_range";
}

rpc ClaimableFees(QueryClaimableFeesRequest)
returns (QueryClaimableFeesResponse) {
option (google.api.http).get =
Expand Down Expand Up @@ -110,6 +117,33 @@ message LiquidityDepth {
];
}

message LiquidityDepthWithRange {
string liquidity_amount = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"liquidity_net\"",
(gogoproto.nullable) = false
];
string lower_tick = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"tick\"",
(gogoproto.nullable) = false
];
string upper_tick = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"tick\"",
(gogoproto.nullable) = false
];
}

//=============================== TickLiquidityInBatches
message QueryTotalLiquidityForRangeRequest {
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
}
message QueryTotalLiquidityForRangeResponse {
repeated LiquidityDepthWithRange liquidity = 1
[ (gogoproto.nullable) = false ];
}

// ===================== MsgQueryClaimableFees
message QueryClaimableFeesRequest {
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
Expand Down
18 changes: 18 additions & 0 deletions x/concentrated-liquidity/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,24 @@ func (q Querier) LiquidityDepthsForRange(goCtx context.Context, req *clquery.Que
}, nil
}

// TotalLiquidityForRange returns an array of LiquidityDepthWithRange, which contains the range(lower tick and upper tick) and the liquidity amount in the range.
func (q Querier) TotalLiquidityForRange(goCtx context.Context, req *clquery.QueryTotalLiquidityForRangeRequest) (*clquery.QueryTotalLiquidityForRangeResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
}
ctx := sdk.UnwrapSDKContext(goCtx)

liquidity, err := q.Keeper.GetTickLiquidityForRange(
ctx,
req.PoolId,
)
if err != nil {
return nil, err
}

return &clquery.QueryTotalLiquidityForRangeResponse{Liquidity: liquidity}, nil
}

func (q Querier) ClaimableFees(ctx context.Context, req *clquery.QueryClaimableFeesRequest) (*clquery.QueryClaimableFeesResponse, error) {
if req == nil {
return nil, status.Error(codes.InvalidArgument, "empty request")
Expand Down
84 changes: 84 additions & 0 deletions x/concentrated-liquidity/tick.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/osmosis-labs/osmosis/osmoutils"
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/internal/math"
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/internal/swapstrategy"
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/model"
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types"
"github.com/osmosis-labs/osmosis/v15/x/concentrated-liquidity/types/genesis"
Expand Down Expand Up @@ -183,6 +184,75 @@ func GetMinAndMaxTicksFromExponentAtPriceOne(exponentAtPriceOne sdk.Int) (minTic
return math.GetMinAndMaxTicksFromExponentAtPriceOneInternal(exponentAtPriceOne)
}

// GetTickLiquidityForRangeInBatches returns an array of liquidity depth within the given range of lower tick and upper tick.
func (k Keeper) GetTickLiquidityForRange(ctx sdk.Context, poolId uint64) ([]query.LiquidityDepthWithRange, error) {
// sanity check that pool exists and upper tick is greater than lower tick
if !k.poolExists(ctx, poolId) {
return []query.LiquidityDepthWithRange{}, types.PoolNotFoundError{PoolId: poolId}
}

// use false for zeroForOne since we're going from lower tick -> upper tick
zeroForOne := false
swapStrategy := swapstrategy.New(zeroForOne, sdk.ZeroDec(), k.storeKey, sdk.ZeroDec())

// get min and max tick for the pool
p, err := k.getPoolById(ctx, poolId)
if err != nil {
return []query.LiquidityDepthWithRange{}, err
}
exponentAtPriceOne := p.GetPrecisionFactorAtPriceOne()
minTick, maxTick := math.GetMinAndMaxTicksFromExponentAtPriceOneInternal(exponentAtPriceOne)

// set current tick to min tick, and find the first initialized tick starting from min tick -1.
// we do -1 to make min tick inclusive.
currentTick := minTick - 1

nextTick, ok := swapStrategy.NextInitializedTick(ctx, poolId, currentTick)
if !ok {
return []query.LiquidityDepthWithRange{}, types.InvalidTickError{Tick: currentTick, IsLower: false, MinTick: minTick, MaxTick: maxTick}
}

tick, err := k.getTickByTickIndex(ctx, poolId, nextTick)
if err != nil {
return []query.LiquidityDepthWithRange{}, err
}

liquidityDepthsForRange := []query.LiquidityDepthWithRange{}

// use the smallest tick initialized as the starting point for calculating liquidity.
currentLiquidity := tick.LiquidityNet
currentTick = nextTick.Int64()

totalLiquidityWithinRange := currentLiquidity
for currentTick <= maxTick {
nextTick, ok := swapStrategy.NextInitializedTick(ctx, poolId, currentTick)
// break and return the liquidity as is if
// - there are no more next tick that is initialized,
// - we hit upper limit
if !ok {
break
}

tick, err := k.getTickByTickIndex(ctx, poolId, nextTick)
if err != nil {
return []query.LiquidityDepthWithRange{}, err
}

liquidityDepthForRange := query.LiquidityDepthWithRange{
LowerTick: sdk.NewInt(currentTick),
UpperTick: nextTick,
LiquidityAmount: totalLiquidityWithinRange,
}
liquidityDepthsForRange = append(liquidityDepthsForRange, liquidityDepthForRange)

currentLiquidity = tick.LiquidityNet
totalLiquidityWithinRange = totalLiquidityWithinRange.Add(currentLiquidity)
currentTick = nextTick.Int64()
}

return liquidityDepthsForRange, nil
}

// GetPerTickLiquidityDepthFromRange uses the given lower tick and upper tick, iterates over ticks, creates and returns LiquidityDepth array.
// LiquidityNet from the tick is used to indicate liquidity depths.
func (k Keeper) GetPerTickLiquidityDepthFromRange(ctx sdk.Context, poolId uint64, lowerTick, upperTick int64) ([]query.LiquidityDepth, error) {
Expand Down Expand Up @@ -226,3 +296,17 @@ func (k Keeper) GetPerTickLiquidityDepthFromRange(ctx sdk.Context, poolId uint64

return liquidityDepths, nil
}

func (k Keeper) getTickByTickIndex(ctx sdk.Context, poolId uint64, tickIndex sdk.Int) (model.TickInfo, error) {
store := ctx.KVStore(k.storeKey)
keyTick := types.KeyTick(poolId, tickIndex.Int64())
tickStruct := model.TickInfo{}
found, err := osmoutils.Get(store, keyTick, &tickStruct)
if err != nil {
return model.TickInfo{}, err
}
if !found {
return model.TickInfo{}, types.TickNotFoundError{Tick: tickIndex.Int64()}
}
return tickStruct, nil
}
161 changes: 161 additions & 0 deletions x/concentrated-liquidity/tick_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ func withPoolId(tick genesis.FullTick, poolId uint64) genesis.FullTick {
return tick
}

func withLiquidityNetandTickIndex(tick genesis.FullTick, tickIndex int64, liquidityNet sdk.Dec) genesis.FullTick {
tick.TickIndex = tickIndex
tick.Info.LiquidityNet = liquidityNet

return tick
}

func (s *KeeperTestSuite) TestTickOrdering() {
s.SetupTest()

Expand Down Expand Up @@ -601,6 +608,160 @@ func (s *KeeperTestSuite) TestCrossTick() {
}
}

func (s *KeeperTestSuite) TestGetTickLiquidityForRange() {
defaultTick := withPoolId(defaultTick, defaultPoolId)

tests := []struct {
name string
presetTicks []genesis.FullTick

expectedLiquidityDepthForRange []query.LiquidityDepthWithRange
}{
{
name: "one full range position, testing range in between",
presetTicks: []genesis.FullTick{
withLiquidityNetandTickIndex(defaultTick, DefaultMinTick, sdk.NewDec(10)),
withLiquidityNetandTickIndex(defaultTick, DefaultMaxTick, sdk.NewDec(-10)),
},
expectedLiquidityDepthForRange: []query.LiquidityDepthWithRange{
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(DefaultMinTick),
UpperTick: sdk.NewInt(DefaultMaxTick),
},
},
},
{
name: "one ranged position, testing range with greater range than initialized ticks",
presetTicks: []genesis.FullTick{
withLiquidityNetandTickIndex(defaultTick, DefaultMinTick, sdk.NewDec(10)),
withLiquidityNetandTickIndex(defaultTick, 5, sdk.NewDec(-10)),
},
expectedLiquidityDepthForRange: []query.LiquidityDepthWithRange{
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(DefaultMinTick),
UpperTick: sdk.NewInt(5),
},
},
},
// 10 ----------------- 30
// -20 ------------- 20
{
name: "two ranged positions, testing overlapping positions",
presetTicks: []genesis.FullTick{
withLiquidityNetandTickIndex(defaultTick, -20, sdk.NewDec(10)),
withLiquidityNetandTickIndex(defaultTick, 20, sdk.NewDec(-10)),
withLiquidityNetandTickIndex(defaultTick, 10, sdk.NewDec(50)),
withLiquidityNetandTickIndex(defaultTick, 30, sdk.NewDec(-50)),
},
expectedLiquidityDepthForRange: []query.LiquidityDepthWithRange{
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(-20),
UpperTick: sdk.NewInt(10),
},
{
LiquidityAmount: sdk.NewDec(60),
LowerTick: sdk.NewInt(10),
UpperTick: sdk.NewInt(20),
},
{
LiquidityAmount: sdk.NewDec(50),
LowerTick: sdk.NewInt(20),
UpperTick: sdk.NewInt(30),
},
},
},
// 10 ----------------- 30
// min tick --------------------------------------max tick
{
name: "one full ranged position, one narrow position",
presetTicks: []genesis.FullTick{
withLiquidityNetandTickIndex(defaultTick, DefaultMinTick, sdk.NewDec(10)),
withLiquidityNetandTickIndex(defaultTick, DefaultMaxTick, sdk.NewDec(-10)),
withLiquidityNetandTickIndex(defaultTick, 10, sdk.NewDec(50)),
withLiquidityNetandTickIndex(defaultTick, 30, sdk.NewDec(-50)),
},
expectedLiquidityDepthForRange: []query.LiquidityDepthWithRange{
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(DefaultMinTick),
UpperTick: sdk.NewInt(10),
},
{
LiquidityAmount: sdk.NewDec(60),
LowerTick: sdk.NewInt(10),
UpperTick: sdk.NewInt(30),
},
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(30),
UpperTick: sdk.NewInt(DefaultMaxTick),
},
},
},
// 11--13
// 10 ----------------- 30
// -20 ------------- 20
{
name: "three ranged positions, testing overlapping positions",
presetTicks: []genesis.FullTick{
withLiquidityNetandTickIndex(defaultTick, -20, sdk.NewDec(10)),
withLiquidityNetandTickIndex(defaultTick, 20, sdk.NewDec(-10)),
withLiquidityNetandTickIndex(defaultTick, 10, sdk.NewDec(50)),
withLiquidityNetandTickIndex(defaultTick, 30, sdk.NewDec(-50)),
withLiquidityNetandTickIndex(defaultTick, 11, sdk.NewDec(100)),
withLiquidityNetandTickIndex(defaultTick, 13, sdk.NewDec(-100)),
},
expectedLiquidityDepthForRange: []query.LiquidityDepthWithRange{
{
LiquidityAmount: sdk.NewDec(10),
LowerTick: sdk.NewInt(-20),
UpperTick: sdk.NewInt(10),
},
{
LiquidityAmount: sdk.NewDec(60),
LowerTick: sdk.NewInt(10),
UpperTick: sdk.NewInt(11),
},
{
LiquidityAmount: sdk.NewDec(160),
LowerTick: sdk.NewInt(11),
UpperTick: sdk.NewInt(13),
},
{
LiquidityAmount: sdk.NewDec(60),
LowerTick: sdk.NewInt(13),
UpperTick: sdk.NewInt(20),
},
{
LiquidityAmount: sdk.NewDec(50),
LowerTick: sdk.NewInt(20),
UpperTick: sdk.NewInt(30),
},
},
},
}

for _, test := range tests {
s.Run(test.name, func() {
// Init suite for each test.
s.Setup()

// Create a default CL pool
s.PrepareConcentratedPool()
for _, tick := range test.presetTicks {
s.App.ConcentratedLiquidityKeeper.SetTickInfo(s.Ctx, tick.PoolId, tick.TickIndex, tick.Info)
}

liquidityForRange, err := s.App.ConcentratedLiquidityKeeper.GetTickLiquidityForRange(s.Ctx, defaultPoolId)
s.Require().NoError(err)
s.Require().Equal(liquidityForRange, test.expectedLiquidityDepthForRange)
})
}
}

func (s *KeeperTestSuite) TestGetLiquidityDepthFromIterator() {
firstTickLiquidityDepth := query.LiquidityDepth{
TickIndex: sdk.NewInt(-3),
Expand Down
8 changes: 8 additions & 0 deletions x/concentrated-liquidity/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ func (e TickIndexMinimumError) Error() string {
return fmt.Sprintf("tickIndex must be greater than or equal to %d", e.MinTick)
}

type TickNotFoundError struct {
Tick int64
}

func (e TickNotFoundError) Error() string {
return fmt.Sprintf("tick %d is not found", e.Tick)
}

type ExponentAtPriceOneError struct {
ProvidedExponentAtPriceOne sdk.Int
PrecisionValueAtPriceOneMin sdk.Int
Expand Down
Loading