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

Further generalize swaps and lp #3111

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
30 changes: 15 additions & 15 deletions proto/osmosis/concentrated-liquidity/concentratedPool.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,47 @@ import "gogoproto/gogo.proto";
option go_package = "github.com/osmosis-labs/osmosis/v12/x/concentrated-liquidity";

message Pool {
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
option (cosmos_proto.implements_interface) = "PoolI";

string address = 1 [(gogoproto.moretags) = "yaml:\"address\""];
uint64 id = 2;
string address = 1 [ (gogoproto.moretags) = "yaml:\"address\"" ];
uint64 id = 2;

// Amount of total liquidity
string liquidity = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
];

string token0 = 4;
string token1 = 5;

string current_sqrt_price = 6 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"spot_price\"",
(gogoproto.nullable) = false
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"spot_price\"",
(gogoproto.nullable) = false
];
string current_tick = 7 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"current_tick\"",
(gogoproto.nullable) = false
(gogoproto.moretags) = "yaml:\"current_tick\"",
(gogoproto.nullable) = false
];
}

message TickInfo {
string liquidity = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
];
}

message Position {
string liquidity = 1 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
(gogoproto.moretags) = "yaml:\"liquidity\"",
(gogoproto.nullable) = false
];
}
60 changes: 30 additions & 30 deletions x/concentrated-liquidity/concentratedPool.pb.go

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

12 changes: 6 additions & 6 deletions x/concentrated-liquidity/lp.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
types "github.com/osmosis-labs/osmosis/v12/x/concentrated-liquidity/types"
)

func (k Keeper) Mint(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, liquidityIn sdk.Int, lowerTick, upperTick int64) (amtDenom0, amtDenom1 sdk.Int, err error) {
func (k Keeper) Mint(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, liquidityIn sdk.Dec, lowerTick, upperTick int64) (amtDenom0, amtDenom1 sdk.Int, err error) {
// ensure types.MinTick <= lowerTick < types.MaxTick
// TODO (bez): Add unit tests.
if lowerTick < types.MinTick || lowerTick >= types.MaxTick {
Expand All @@ -34,12 +34,12 @@ func (k Keeper) Mint(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, liqui

pool := k.getPoolbyId(ctx, poolId)

currentSqrtPrice := sdk.NewDecWithPrec(int64(pool.CurrentSqrtPrice.Uint64()), 6)
sqrtRatioUpperTick, _ := k.tickToPrice(sdk.NewInt(upperTick)).ApproxSqrt()
sqrtRatioLowerTick, _ := k.tickToPrice(sdk.NewInt(lowerTick)).ApproxSqrt()
currentSqrtPrice := pool.CurrentSqrtPrice
sqrtRatioUpperTick, _ := k.tickToPrice(sdk.NewInt(upperTick))
sqrtRatioLowerTick, _ := k.tickToPrice(sdk.NewInt(lowerTick))

amtDenom0 = calcAmount0Delta(liquidityIn.ToDec(), currentSqrtPrice, sqrtRatioUpperTick).RoundInt()
amtDenom1 = calcAmount1Delta(liquidityIn.ToDec(), currentSqrtPrice, sqrtRatioLowerTick).RoundInt()
amtDenom0 = calcAmount0Delta(liquidityIn, currentSqrtPrice, sqrtRatioUpperTick).RoundInt()
amtDenom1 = calcAmount1Delta(liquidityIn, currentSqrtPrice, sqrtRatioLowerTick).RoundInt()

return amtDenom0, amtDenom1, nil
}
Expand Down
14 changes: 8 additions & 6 deletions x/concentrated-liquidity/lp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ func (s *KeeperTestSuite) TestMint() {
currentTick := sdk.NewInt(85176)
lowerTick := int64(84222)
upperTick := int64(86129)
liquidity, ok := sdk.NewIntFromString("1517882323")
s.Require().True(ok)
currentSqrtP, ok := sdk.NewIntFromString("70710678")
s.Require().True(ok)
liquidity, err := sdk.NewDecFromStr("1517.882323")
s.Require().NoError(err)
// currentSqrtP, ok := sdk.NewIntFromString("70710678")
currentSqrtP, err := sdk.NewDecFromStr("70.710678")
s.Require().NoError(err)
denom0 := "eth"
denom1 := "usdc"

Expand All @@ -30,6 +31,7 @@ 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(998629), asset0) // .998629 ETH
s.Require().Equal(sdk.NewInt(5000208942), asset1) // 5000.20 USDC

s.Require().Equal(sdk.NewInt(1), asset0)
s.Require().Equal(sdk.NewInt(5000), asset1)
}
121 changes: 50 additions & 71 deletions x/concentrated-liquidity/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,7 @@ import (
"github.com/osmosis-labs/osmosis/v12/x/gamm/types"
)

// maintains the current swap state
type SwapState struct {
amountSpecifiedRemaining sdk.Dec // remaining amount of tokens that need to be bought by the pool
amountCalculated sdk.Dec // amount out
sqrtPrice sdk.Dec // new current price when swap is done
tick sdk.Int // new tick when swap is done
}

// tracks state of one iteration of an order filling
type StepState struct {
sqrtPriceStart sdk.Dec // price iteration begins with
nextTick sdk.Int // next initialized tick that will provide liquidity for the swap
sqrtPriceNext sdk.Dec // price at the next tick
amountIn sdk.Dec
amountOut sdk.Dec
}

func (k Keeper) CreateNewConcentratedLiquidityPool(ctx sdk.Context, poolId uint64, denom0, denom1 string, currSqrtPrice, currTick sdk.Int) (Pool, error) {
func (k Keeper) CreateNewConcentratedLiquidityPool(ctx sdk.Context, poolId uint64, denom0, denom1 string, currSqrtPrice sdk.Dec, currTick sdk.Int) (Pool, error) {
poolAddr := types.NewPoolAddress(poolId)
denom0, denom1, err := k.orderInitialPoolDenoms(denom0, denom1)
if err != nil {
Expand Down Expand Up @@ -102,7 +85,7 @@ func (k Keeper) CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coin, tokenOutDen
tokenAmountInAfterFee := tokenIn.Amount.ToDec().Mul(sdk.OneDec().Sub(swapFee))

// get current sqrt price from pool
curSqrtPrice := sdk.NewDecWithPrec(int64(p.CurrentSqrtPrice.Uint64()), 6)
curSqrtPrice := p.CurrentSqrtPrice

// validation
if tokenIn.Denom != asset0 && tokenIn.Denom != asset1 {
Expand All @@ -126,7 +109,7 @@ func (k Keeper) CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coin, tokenOutDen
if err != nil {
return sdk.Coin{}, sdk.Coin{}, fmt.Errorf("issue calculating square root of minPrice")
}
sqrtPCurTick := curSqrtPrice

sqrtPUpperTick, err := maxPrice.ApproxSqrt()
if err != nil {
return sdk.Coin{}, sdk.Coin{}, fmt.Errorf("issue calculating square root of maxPrice")
Expand All @@ -137,42 +120,38 @@ func (k Keeper) CalcOutAmtGivenIn(ctx sdk.Context, tokenIn sdk.Coin, tokenOutDen
amountUSDC := int64(5000000000)

// find liquidity of assetA and assetB
liq0 := liquidity0(amountETH, sqrtPCurTick, sqrtPUpperTick)
liq1 := liquidity1(amountUSDC, sqrtPCurTick, sqrtPLowerTick)
liq0 := liquidity0(amountETH, curSqrtPrice, sqrtPUpperTick)
liq1 := liquidity1(amountUSDC, curSqrtPrice, sqrtPLowerTick)

// utilize the smaller liquidity between assetA and assetB when performing the swap calculation
liq := sdk.MinDec(liq0, liq1)

swapState := SwapState{
amountSpecifiedRemaining: tokenAmountInAfterFee,
amountCalculated: sdk.ZeroDec(),
sqrtPrice: sqrtPCurTick,
tick: priceToTick(sqrtPCurTick.Power(2)),
}
amountSpecifiedRemaining := tokenAmountInAfterFee
amountCalculated := sdk.ZeroDec()
sqrtPrice := curSqrtPrice
tick := priceToTick(curSqrtPrice.Power(2))

Copy link
Member

Choose a reason for hiding this comment

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

As agreed in in person discussion, we should move this back to swapState struct

// TODO: This should be GT 0 but some instances have very small remainder
// need to look into fixing this
for swapState.amountSpecifiedRemaining.GT(sdk.NewDecWithPrec(1, 6)) {
stepState := StepState{}
stepState.sqrtPriceStart = swapState.sqrtPrice
for amountSpecifiedRemaining.GT(sdk.NewDecWithPrec(1, 6)) {
lte := tokenIn.Denom == asset1
nextTick, _ := k.NextInitializedTick(ctx, poolId, swapState.tick.Int64(), lte)
stepState.nextTick = sdk.NewInt(nextTick)
nextSqrtPrice, _ := k.tickToPrice(stepState.nextTick).ApproxSqrt()
stepState.sqrtPriceNext = nextSqrtPrice

swapState.sqrtPrice, stepState.amountIn, stepState.amountOut = computeSwapStep(
swapState.sqrtPrice,
stepState.sqrtPriceNext,
nextTick, _ := k.NextInitializedTick(ctx, poolId, tick.Int64(), lte)
nextSqrtPrice, _ := k.tickToPrice(sdk.NewInt(nextTick))

sqrtPrice, amountIn, amountOut := computeSwapStep(
sqrtPrice,
nextSqrtPrice,
liq,
swapState.amountSpecifiedRemaining,
amountSpecifiedRemaining,
lte,
)
swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.Sub(stepState.amountIn)
swapState.amountCalculated = swapState.amountCalculated.Add(stepState.amountOut)
swapState.tick = priceToTick(swapState.sqrtPrice.Power(2))
amountSpecifiedRemaining = amountSpecifiedRemaining.Sub(amountIn)
amountCalculated = amountCalculated.Add(amountOut)
tick = priceToTick(sqrtPrice.Power(2))
}
newTokenIn.Amount = tokenIn.Amount.Sub(swapState.amountSpecifiedRemaining.RoundInt())
return sdk.NewCoin(tokenIn.Denom, newTokenIn.Amount), sdk.NewCoin(tokenOutDenom, swapState.amountCalculated.RoundInt()), nil

newTokenIn.Amount = tokenIn.Amount.Sub(amountSpecifiedRemaining.RoundInt())
return sdk.NewCoin(tokenIn.Denom, newTokenIn.Amount), sdk.NewCoin(tokenOutDenom, amountCalculated.RoundInt()), nil
}

func (p Pool) SwapInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coins, tokenInDenom string, swapFee sdk.Dec) (tokenIn sdk.Coin, err error) {
Expand All @@ -186,7 +165,8 @@ func (k Keeper) CalcInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coin, tokenInDen
asset1 := p.Token1

// get current sqrt price from pool
curSqrtPrice := sdk.NewDecWithPrec(int64(p.CurrentSqrtPrice.Uint64()), 6)
// curSqrtPrice := sdk.NewDecWithPrec(int64(p.CurrentSqrtPrice.Uint64()), 6)
curSqrtPrice := p.CurrentSqrtPrice

// validation
if tokenOut.Denom != asset0 && tokenOut.Denom != asset1 {
Expand All @@ -210,7 +190,7 @@ func (k Keeper) CalcInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coin, tokenInDen
if err != nil {
return sdk.Coin{}, sdk.Coin{}, fmt.Errorf("issue calculating square root of minPrice")
}
sqrtPCurTick := curSqrtPrice

sqrtPUpperTick, err := maxPrice.ApproxSqrt()
if err != nil {
return sdk.Coin{}, sdk.Coin{}, fmt.Errorf("issue calculating square root of maxPrice")
Expand All @@ -221,42 +201,41 @@ func (k Keeper) CalcInAmtGivenOut(ctx sdk.Context, tokenOut sdk.Coin, tokenInDen
amountUSDC := int64(5000000000)

// find liquidity of assetA and assetB
liq0 := liquidity0(amountETH, sqrtPCurTick, sqrtPUpperTick)
liq1 := liquidity1(amountUSDC, sqrtPCurTick, sqrtPLowerTick)
liq0 := liquidity0(amountETH, curSqrtPrice, sqrtPUpperTick)
liq1 := liquidity1(amountUSDC, curSqrtPrice, sqrtPLowerTick)

// utilize the smaller liquidity between assetA and assetB when performing the swap calculation
liq := sdk.MinDec(liq0, liq1)

swapState := SwapState{
amountSpecifiedRemaining: tokenOutAmt,
amountCalculated: sdk.ZeroDec(),
sqrtPrice: sqrtPCurTick,
tick: priceToTick(sqrtPCurTick.Power(2)),
}
amountSpecifiedRemaining := tokenOutAmt
amountCalculated := sdk.ZeroDec()
sqrtPrice := curSqrtPrice
tick := priceToTick(curSqrtPrice.Power(2))

// TODO: This should be GT 0 but some instances have very small remainder
// need to look into fixing this
for swapState.amountSpecifiedRemaining.GT(sdk.NewDecWithPrec(1, 6)) {
stepState := StepState{}
stepState.sqrtPriceStart = swapState.sqrtPrice
for amountSpecifiedRemaining.GT(sdk.NewDecWithPrec(1, 6)) {
lte := tokenOut.Denom == asset1
nextTick, _ := k.NextInitializedTick(ctx, poolId, swapState.tick.Int64(), lte)
stepState.nextTick = sdk.NewInt(nextTick)
nextSqrtPrice, _ := k.tickToPrice(stepState.nextTick).ApproxSqrt()
stepState.sqrtPriceNext = nextSqrtPrice
nextTick, _ := k.NextInitializedTick(ctx, poolId, tick.Int64(), lte)
nextSqrtPrice, err := k.tickToPrice(sdk.NewInt(nextTick))
if err != nil {
return sdk.Coin{}, sdk.Coin{}, err
}

// TODO: In and out get flipped based on if we are calculating for in or out, need to fix this
swapState.sqrtPrice, stepState.amountIn, stepState.amountOut = computeSwapStep(
swapState.sqrtPrice,
stepState.sqrtPriceNext,
sqrtPrice, amountIn, amountOut := computeSwapStep(
sqrtPrice,
nextSqrtPrice,
liq,
swapState.amountSpecifiedRemaining,
amountSpecifiedRemaining,
lte,
)
stepState.amountOut = stepState.amountOut.Quo(sdk.OneDec().Sub(swapFee))
swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.Sub(stepState.amountIn)
swapState.amountCalculated = swapState.amountCalculated.Add(stepState.amountOut)
swapState.tick = priceToTick(swapState.sqrtPrice.Power(2))

amountSpecifiedRemaining = amountSpecifiedRemaining.Sub(amountIn)
amountCalculated = amountCalculated.Add(amountOut.Quo(sdk.OneDec().Sub(swapFee)))
tick = priceToTick(sqrtPrice.Power(2))
}
return sdk.NewCoin(tokenInDenom, swapState.amountCalculated.RoundInt()), sdk.NewCoin(tokenOut.Denom, tokenOut.Amount), nil
return sdk.NewCoin(tokenInDenom, amountCalculated.RoundInt()), sdk.NewCoin(tokenOut.Denom, tokenOut.Amount), nil
}

func (k Keeper) orderInitialPoolDenoms(denom0, denom1 string) (string, string, error) {
Expand Down
Loading