Skip to content

Commit

Permalink
Add state for Mint: tick and Position (osmosis-labs#3136)
Browse files Browse the repository at this point in the history
* WIP: tick

* Finish implementing

* Update x/concentrated-liquidity/lp.go

Co-authored-by: Adam Tucker <[email protected]>

* Update x/concentrated-liquidity/lp.go

Co-authored-by: Adam Tucker <[email protected]>

* Update x/concentrated-liquidity/lp.go

Co-authored-by: Adam Tucker <[email protected]>

* Change to negative calculations

* Add comment

* Add getter in osmoutils

* Update osmoutils/store_helper.go

Co-authored-by: Adam Tucker <[email protected]>

* Update x/concentrated-liquidity/position.go

Co-authored-by: Adam Tucker <[email protected]>

Co-authored-by: Adam Tucker <[email protected]>
  • Loading branch information
2 people authored and Ruslan Akhtariev committed Nov 2, 2022
1 parent abba57f commit ed46c0e
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 64 deletions.
13 changes: 13 additions & 0 deletions osmoutils/store_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ func MustGet(store store.KVStore, key []byte, result proto.Message) {
}
}

// GetIfFound gets key from store
// returns a boolean indicating whether value exists for the given key and error
func GetIfFound(store store.KVStore, key []byte, result proto.Message) (found bool, err error) {
b := store.Get(key)
if b == nil {
return false, nil
}
if err := proto.Unmarshal(b, result); err != nil {
return true, err
}
return true, nil
}

// MustSetDec sets dec value to store at key. Panics on any error.
func MustSetDec(store store.KVStore, key []byte, value sdk.Dec) {
MustSet(store, key, &sdk.DecProto{
Expand Down
9 changes: 7 additions & 2 deletions proto/osmosis/concentrated-liquidity/concentratedPool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ message Pool {
}

message TickInfo {
string liquidity = 1 [
string liquidity_gross = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.moretags) = "yaml:\"liquidity_gross\"",
(gogoproto.nullable) = false
];
string liquidity_net = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"liquidity_net\"",
(gogoproto.nullable) = false
];
}
Expand Down
121 changes: 86 additions & 35 deletions x/concentrated-liquidity/concentratedPool.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 21 additions & 7 deletions x/concentrated-liquidity/lp.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,28 @@ func (k Keeper) Mint(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, liqui
if liquidityIn.IsZero() {
return sdk.Int{}, sdk.Int{}, fmt.Errorf("token in amount is zero")
}
// TODO: we need to check if TickInfo is initialized on a specific tick before updating, otherwise get wont work
// k.setTickInfo(ctx, poolId, lowerTick, TickInfo{})
// k.UpdateTickWithNewLiquidity(ctx, poolId, lowerTick, liquidityIn)
// k.setTickInfo(ctx, poolId, upperTick, TickInfo{})
// k.UpdateTickWithNewLiquidity(ctx, poolId, upperTick, liquidityIn)
// k.setPosition(ctx, poolId, owner, lowerTick, upperTick, Position{})
// k.updatePositionWithLiquidity(ctx, poolId, owner, lowerTick, upperTick, liquidityIn)

// update tickInfo state
// TODO: come back to sdk.Int vs sdk.Dec state & truncation
err = k.initOrUpdateTick(ctx, poolId, lowerTick, liquidityIn.TruncateInt(), false)
if err != nil {
return sdk.Int{}, sdk.Int{}, err
}

// TODO: come back to sdk.Int vs sdk.Dec state & truncation
err = k.initOrUpdateTick(ctx, poolId, upperTick, liquidityIn.TruncateInt(), true)
if err != nil {
return sdk.Int{}, sdk.Int{}, err
}

// update position state
// TODO: come back to sdk.Int vs sdk.Dec state & truncation
err = k.initOrUpdatePosition(ctx, poolId, owner, lowerTick, upperTick, liquidityIn.TruncateInt())
if err != nil {
return sdk.Int{}, sdk.Int{}, err
}

// now calculate amount for token0 and token1
pool := k.getPoolbyId(ctx, poolId)

currentSqrtPrice := pool.CurrentSqrtPrice
Expand Down
19 changes: 18 additions & 1 deletion x/concentrated-liquidity/lp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,24 @@ func (s *KeeperTestSuite) TestMint() {

asset0, asset1, err := s.App.ConcentratedLiquidityKeeper.Mint(s.Ctx, poolId, s.TestAccs[0], liquidity, lowerTick, upperTick)
s.Require().NoError(err)

s.Require().Equal(sdk.NewInt(1), asset0)
s.Require().Equal(sdk.NewInt(5000), asset1)

// check position state
// 1517 is from the liquidity originally provided
position, err := s.App.ConcentratedLiquidityKeeper.GetPosition(s.Ctx, poolId, s.TestAccs[0], lowerTick, upperTick)
s.Require().NoError(err)
s.Require().Equal(sdk.NewInt(1517), position.Liquidity)

// check tick state
// 1517 is from the liquidity originally provided
lowerTickInfo, err := s.App.ConcentratedLiquidityKeeper.GetTickInfo(s.Ctx, poolId, lowerTick)
s.Require().NoError(err)
s.Require().Equal(sdk.NewInt(1517), lowerTickInfo.LiquidityGross)
s.Require().Equal(sdk.NewInt(1517), lowerTickInfo.LiquidityNet)

upperTickInfo, err := s.App.ConcentratedLiquidityKeeper.GetTickInfo(s.Ctx, poolId, upperTick)
s.Require().NoError(err)
s.Require().Equal(sdk.NewInt(1517), upperTickInfo.LiquidityGross)
s.Require().Equal(sdk.NewInt(-1517), upperTickInfo.LiquidityNet)
}
33 changes: 23 additions & 10 deletions x/concentrated-liquidity/position.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,44 @@ import (
types "github.com/osmosis-labs/osmosis/v12/x/concentrated-liquidity/types"
)

// nolint: unused
func (k Keeper) updatePositionWithLiquidity(ctx sdk.Context,
func (k Keeper) initOrUpdatePosition(ctx sdk.Context,
poolId uint64,
owner sdk.AccAddress,
lowerTick, upperTick int64,
liquidityDelta sdk.Int,
) {
position := k.getPosition(ctx, poolId, owner, lowerTick, upperTick)
) (err error) {
position, err := k.GetPosition(ctx, poolId, owner, lowerTick, upperTick)
if err != nil {
return err
}

liquidityBefore := position.Liquidity

// note that liquidityIn can be either positive or negative.
// If negative, this would work as a subtraction from liquidityBefore
liquidityAfter := liquidityBefore.Add(liquidityDelta)

position.Liquidity = liquidityAfter

k.setPosition(ctx, poolId, owner, lowerTick, upperTick, position)
return nil
}

// nolint: unused
func (k Keeper) getPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64) Position {
func (k Keeper) GetPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, lowerTick, upperTick int64) (position Position, err error) {
store := ctx.KVStore(k.storeKey)

var position Position
positionStruct := Position{}
key := types.KeyPosition(poolId, owner, lowerTick, upperTick)
osmoutils.MustGet(store, key, &position)

return position
found, err := osmoutils.GetIfFound(store, key, &positionStruct)
// return 0 values if key has not been initialized
if !found {
return Position{Liquidity: sdk.ZeroInt()}, nil
}
if err != nil {
return positionStruct, err
}

return positionStruct, nil
}

// nolint: unused
Expand Down
55 changes: 46 additions & 9 deletions x/concentrated-liquidity/tick.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,41 @@ func (k Keeper) tickToSqrtPrice(tickIndex sdk.Int) (sdk.Dec, error) {
// return sdk.Int{}
// }

func (k Keeper) UpdateTickWithNewLiquidity(ctx sdk.Context, poolId uint64, tickIndex int64, liquidityDelta sdk.Int) {
tickInfo := k.getTickInfo(ctx, poolId, tickIndex)
func (k Keeper) initOrUpdateTick(ctx sdk.Context, poolId uint64, tickIndex int64, liquidityIn sdk.Int, upper bool) (err error) {
tickInfo, err := k.GetTickInfo(ctx, poolId, tickIndex)
if err != nil {
return err
}

// calculate liquidityGross, which does not care about whether liquidityIn is positive or negative
liquidityBefore := tickInfo.LiquidityGross

liquidityBefore := tickInfo.Liquidity
liquidityAfter := liquidityBefore.Add(liquidityDelta)
tickInfo.Liquidity = liquidityAfter
// note that liquidityIn can be either positive or negative.
// If negative, this would work as a subtraction from liquidityBefore
liquidityAfter := liquidityBefore.Add(liquidityIn)

tickInfo.LiquidityGross = liquidityAfter

// calculate liquidityNet, which we take into account and track depending on whether liquidityIn is positive or negative
if upper {
tickInfo.LiquidityNet = tickInfo.LiquidityNet.Sub(liquidityIn)
} else {
tickInfo.LiquidityNet = tickInfo.LiquidityNet.Add(liquidityIn)
}

k.setTickInfo(ctx, poolId, tickIndex, tickInfo)

return nil
}

// nolint: unused
func (k Keeper) crossTick(ctx sdk.Context, poolId uint64, tickIndex int64) (liquidityDelta sdk.Int, err error) {
tickInfo, err := k.GetTickInfo(ctx, poolId, tickIndex)
if err != nil {
return sdk.Int{}, err
}

return tickInfo.LiquidityNet, nil
}

// NextInitializedTick returns the next initialized tick index based on the
Expand Down Expand Up @@ -87,12 +114,22 @@ func (k Keeper) NextInitializedTick(ctx sdk.Context, poolId uint64, tickIndex in
return 0, false
}

func (k Keeper) getTickInfo(ctx sdk.Context, poolId uint64, tickIndex int64) TickInfo {
// getTickInfo gets tickInfo given poolId and tickIndex. Returns a boolean field that returns true if value is found for given key.
func (k Keeper) GetTickInfo(ctx sdk.Context, poolId uint64, tickIndex int64) (tickInfo TickInfo, err error) {
store := ctx.KVStore(k.storeKey)
tickInfo := TickInfo{}
tickStruct := TickInfo{}
key := types.KeyTick(poolId, tickIndex)
osmoutils.MustGet(store, key, &tickInfo)
return tickInfo

found, err := osmoutils.GetIfFound(store, key, &tickStruct)
// return 0 values if key has not been initialized
if !found {
return TickInfo{LiquidityGross: sdk.ZeroInt(), LiquidityNet: sdk.ZeroInt()}, err
}
if err != nil {
return tickStruct, err
}

return tickStruct, nil
}

func (k Keeper) setTickInfo(ctx sdk.Context, poolId uint64, tickIndex int64, tickInfo TickInfo) {
Expand Down

0 comments on commit ed46c0e

Please sign in to comment.