From 78f1ab0f613cc9eb8e547ba8bfb2fed8e714feed Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 27 Jan 2025 13:17:55 +0200 Subject: [PATCH] BE-676 | Add SwapOutGivenInCases for CL pool (#8954) * BE-676 | Add SwapOutGivenInCases for CL pool (cherry picked from commit d98196e1d30d09f75f57b5d29b041a5c5f6e942e) --- .github/workflows/build.yml | 2 +- .github/workflows/contracts.yml | 4 +- app/apptesting/concentrated_liquidity.go | 609 +++++++++++++++++++++++ 3 files changed, 612 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c11e40f104..816859b3aa2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,7 +70,7 @@ jobs: GOWRK=off go build cmd/osmosisd/main.go - name: Upload osmosisd artifact if: env.GIT_DIFF - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: osmosisd-${{ matrix.targetos }}-${{ matrix.arch }} path: cmd/osmosisd/osmosisd diff --git a/.github/workflows/contracts.yml b/.github/workflows/contracts.yml index 1d7cdcd93fa..68540d3de59 100644 --- a/.github/workflows/contracts.yml +++ b/.github/workflows/contracts.yml @@ -77,14 +77,14 @@ jobs: cosmwasm/workspace-optimizer:0.12.10 - name: "Upload optimized contract artifact" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.contract.name }} path: ${{ matrix.contract.workdir }}${{ matrix.contract.build }} retention-days: 1 - name: "Upload Cargo.lock artifact" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Cargo.lock path: ${{ matrix.contract.workdir }}Cargo.lock diff --git a/app/apptesting/concentrated_liquidity.go b/app/apptesting/concentrated_liquidity.go index 2ee20e47c78..aea9f6fcdd2 100644 --- a/app/apptesting/concentrated_liquidity.go +++ b/app/apptesting/concentrated_liquidity.go @@ -794,6 +794,615 @@ var ( ExpectedSqrtPrice: osmomath.BigDecFromDec(osmomath.MustMonotonicSqrt(osmomath.NewDec(4994))), }, } + + // swap in given out test cases + SwapInGivenOutCases = map[string]ConcentratedSwapTest{ + // One price range + // + // 5000 + // 4545 -----|----- 5500 + "single position within one tick: eth (in) -> usdc (out) | zfo": { + TokenOut: sdk.NewCoin(USDC, osmomath.NewInt(42000000)), + TokenInDenom: ETH, + PriceLimit: osmomath.NewBigDec(4993), + SpreadFactor: osmomath.ZeroDec(), + // from math import * + // from decimal import * + + // import sys + + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // liq = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // token_out = Decimal("42000000") + + // sqrt_next = get_next_sqrt_price_from_amount1_out_round_down(liq, sqrt_cur, token_out) + // token_in = token_in = liq * (sqrt_cur - sqrt_next) / (sqrt_cur * sqrt_next) + // print(sqrt_next) + // print(token_in) + ExpectedTokenOut: sdk.NewCoin(USDC, osmomath.NewInt(42000000)), + ExpectedTokenIn: sdk.NewCoin(ETH, osmomath.NewInt(8404)), + ExpectedTick: 30996087, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("70.683007989825007163485199999996399373"), + }, + "single position within one tick: usdc (in) -> eth (out) ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(13370)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(5010), + SpreadFactor: osmomath.ZeroDec(), + // from math import * + // from decimal import * + // import sys + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + // liq = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // token_out = Decimal("13370") + // sqrt_next = get_next_sqrt_price_from_amount0_out_round_up(liq, sqrt_cur, token_out) + // token_in = ceil(liq * abs(sqrt_cur - sqrt_next)) + // print(sqrt_next) + // print(token_in) + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(13370)), + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(66891663)), + ExpectedTick: 31006234, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("70.754747188468900467378792612053774781"), + }, + // Two equal price ranges + // + // 5000 + // 4545 -----|----- 5500 + // 4545 -----|----- 5500 + "two positions within one tick: eth (in) -> usdc (out) | zfo": { + TokenOut: sdk.NewCoin("usdc", osmomath.NewInt(66829187)), + TokenInDenom: "eth", + PriceLimit: osmomath.NewBigDec(4990), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: DefaultLowerPrice, + SecondPositionUpperPrice: DefaultUpperPrice, + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // liq = Decimal("1517882343.751510417627556287") * 2 + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // token_out = Decimal("66829187") + + // sqrt_next = get_next_sqrt_price_from_amount1_out_round_down(liq, sqrt_cur, token_out) + // token_in = token_in = liq * (sqrt_cur - sqrt_next) / (sqrt_cur * sqrt_next) + // print(sqrt_next) + // print(token_in) + ExpectedTokenOut: sdk.NewCoin("usdc", osmomath.NewInt(66829187)), + ExpectedTokenIn: sdk.NewCoin("eth", osmomath.NewInt(13370)), + ExpectedTick: 30996887, + // This value is the direct output of sqrt_next in the script above. + // The precision is exact because we properly handle rounding behavior in intermediate steps. + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("70.688664163727643651554720661097135393"), + // two positions with same liquidity entered + PoolLiqAmount0: osmomath.NewInt(1000000).MulRaw(2), + PoolLiqAmount1: osmomath.NewInt(5000000000).MulRaw(2), + }, + "two positions within one tick: usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin("eth", osmomath.NewInt(8398)), + TokenInDenom: "usdc", + PriceLimit: osmomath.NewBigDec(5020), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: DefaultLowerPrice, + SecondPositionUpperPrice: DefaultUpperPrice, + // from math import * + // from decimal import * + // import sys + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + // liq = Decimal("1517882343.751510417627556287") * 2 + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // token_out = Decimal("8398") + // sqrt_next = get_next_sqrt_price_from_amount0_out_round_up(liq, sqrt_cur, token_out) + // token_in = ceil(liq * abs(sqrt_cur - sqrt_next)) + // + // print(sqrt_next) + // print(token_in) + ExpectedTokenOut: sdk.NewCoin("eth", osmomath.NewInt(8398)), + ExpectedTokenIn: sdk.NewCoin("usdc", osmomath.NewInt(41998216)), + ExpectedTick: 31001956, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("70.724512595179305566327821490232558005"), + // two positions with same liquidity entered + PoolLiqAmount0: osmomath.NewInt(1000000).MulRaw(2), + PoolLiqAmount1: osmomath.NewInt(5000000000).MulRaw(2), + }, + // Consecutive price ranges + // + // 5000 + // 4545 -----|----- 5500 + // 4000 ----------- 4545 + "two positions with consecutive price ranges: eth (in) -> usdc (out) | zfo": { + TokenOut: sdk.NewCoin("usdc", osmomath.NewInt(9103422788)), + TokenInDenom: "eth", + PriceLimit: osmomath.NewBigDec(3900), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(4000), + SecondPositionUpperPrice: osmomath.NewDec(4545), + // from math import * + // from decimal import * + + // import sys + + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("9103422788") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("67.416615162732695594") + + // token_out_1 = round_sdk_prec_down(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // liq_2 = Decimal("1198735489.597250295669959397") + // sqrt_next_2 = get_next_sqrt_price_from_amount1_out_round_down(liq_2, sqrt_next_1, token_out) + + // token_out_2 = round_sdk_prec_down(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + + // token_out = token_out - token_out_2 + + // # Summary: + // token_in = token_in_1 + token_in_2 + // print(sqrt_next_2) + // print(token_in) + // print(token_out_2) + ExpectedTokenOut: sdk.NewCoin("usdc", osmomath.NewInt(9103422788)), + ExpectedTokenIn: sdk.NewCoin("eth", osmomath.NewInt(2000000)), + ExpectedTick: 30095166, + + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("63.993489023888951975210711246458277671"), + ExpectedLowerTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedUpperTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 315000, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 322500, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(4000), + NewUpperPrice: osmomath.NewDec(4545), + }, + // Consecutive price ranges + // + // 5000 + // 4545 -----|----- 5500 + // 5500 ----------- 6250 + // + "two positions with consecutive price ranges: usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1820630)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(6106), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(5500), // 315000 + SecondPositionUpperPrice: osmomath.NewDec(6250), // 322500 + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("1820630") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("74.161984870956629488") + + // token_out_1 = round_sdk_prec_down(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // liq_2 = Decimal("1197767444.955508123483846019") + // sqrt_next_2 = get_next_sqrt_price_from_amount0_out_round_up(liq_2, sqrt_next_1, token_out) + + // token_out_2 = round_sdk_prec_down(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // spread_rewards_growth = spread_factor_1 / liq_1 + spread_factor_2 / liq_2 + // print(sqrt_next_2) + // print(token_in) + // print(token_out_2) + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1820630)), + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(9999999570)), + ExpectedTick: 32105414, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("78.137148837036751554352224945360339905"), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 315000, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 322500, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(5500), + NewUpperPrice: osmomath.NewDec(6250), + }, + // Partially overlapping price ranges + // + // 5000 + // 4545 -----|----- 5500 + // 4000 ----------- 4999 + // + "two positions with partially overlapping price ranges: eth (in) -> usdc (out) | zfo": { + TokenOut: sdk.NewCoin(USDC, osmomath.NewInt(9321276930)), + TokenInDenom: ETH, + PriceLimit: osmomath.NewBigDec(4128), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(4000), + SecondPositionUpperPrice: osmomath.NewDec(4999), + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("9321276930") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("70.703606697254136613") + + // token_out_1 = round_sdk_prec_down(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // second_pos_liq = Decimal("670416215.718827443660400593") + // liq_2 = liq_1 + second_pos_liq + // sqrt_next_2 = Decimal("67.416615162732695594") + + // token_out_2 = round_sdk_prec_down(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + + // token_out = token_out - token_out_2 + + // # Swap step 3 + // liq_3 = second_pos_liq + // sqrt_next_3 = get_next_sqrt_price_from_amount1_out_round_down(liq_3, sqrt_next_2, token_out) + + // token_out_3 = round_sdk_prec_down(calc_amount_one_delta(liq_3, sqrt_next_2, sqrt_next_3, False)) + // token_in_3 = ceil(calc_amount_zero_delta(liq_3, sqrt_next_2, sqrt_next_3, True)) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // spread_rewards_growth = spread_factor_1 / liq_1 + spread_factor_2 / liq_2 + // print(sqrt_next_3) + // print(token_in) + // print(token_out_2) + ExpectedTokenIn: sdk.NewCoin("eth", osmomath.NewInt(2000000)), + ExpectedTokenOut: sdk.NewCoin("usdc", osmomath.NewInt(9321276930)), + ExpectedTick: 30129083, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("64.257943796086567725876595411582357676"), + // Started from DefaultSpreadRewardAccumCoins * 3, crossed tick once, thus becoming + // DefaultSpreadRewardAccumCoins * 3 - DefaultSpreadRewardAccumCoins = DefaultSpreadRewardAccumCoins * 2 + ExpectedLowerTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedUpperTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 300000, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 309990, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(4000), + NewUpperPrice: osmomath.NewDec(4999), + }, + // 5000 + // 4545 -----|----- 5500 + // 4000 ---------- 4999 + "two positions with partially overlapping price ranges, not utilizing full liquidity of second position: eth (in) -> usdc (out) | zfo": { + TokenOut: sdk.NewCoin(USDC, osmomath.NewInt(8479320318)), + TokenInDenom: ETH, + PriceLimit: osmomath.NewBigDec(4128), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(4000), + SecondPositionUpperPrice: osmomath.NewDec(4999), + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("8479320318") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("70.703606697254136613") + + // token_out_1 = round_sdk_prec_down(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // second_pos_liq = Decimal("670416215.718827443660400593") + // liq_2 = liq_1 + second_pos_liq + // sqrt_next_2 = Decimal("67.416615162732695594") + + // token_out_2 = round_sdk_prec_down(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + + // token_out = token_out - token_out_2 + + // # Swap step 3 + // liq_3 = second_pos_liq + // sqrt_next_3 = get_next_sqrt_price_from_amount1_out_round_down(liq_3, sqrt_next_2, token_out) + + // token_out_3 = round_sdk_prec_down(calc_amount_one_delta(liq_3, sqrt_next_2, sqrt_next_3, False)) + // token_in_3 = ceil(calc_amount_zero_delta(liq_3, sqrt_next_2, sqrt_next_3, True))) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // print(sqrt_next_3) + // print(token_in) + // print(token_out_2) + ExpectedTokenIn: sdk.NewCoin(ETH, osmomath.NewInt(1800000)), + ExpectedTokenOut: sdk.NewCoin(USDC, osmomath.NewInt(8479320318)), + ExpectedTick: 30292059, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("65.513815286452064191403749708246274698"), + // Started from DefaultSpreadRewardAccumCoins * 3, crossed tick once, thus becoming + // DefaultSpreadRewardAccumCoins * 3 - DefaultSpreadRewardAccumCoins = DefaultSpreadRewardAccumCoins * 2 + ExpectedLowerTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedUpperTickSpreadRewardGrowth: DefaultSpreadRewardAccumCoins.MulDec(osmomath.NewDec(2)), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 300000, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 309990, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(4000), + NewUpperPrice: osmomath.NewDec(4999), + }, + // Partially overlapping price ranges + + // 5000 + // 4545 -----|----- 5500 + // 5001 ----------- 6250 + // + "two positions with partially overlapping price ranges: usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1864161)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(6056), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(5001), + SecondPositionUpperPrice: osmomath.NewDec(6250), + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("1864161") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("70.717748832948578243") + + // token_out_1 = round_sdk_prec_down(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // second_pos_liq = Decimal("670416088.605668727039240782") + // liq_2 = liq_1 + Decimal("670416088.605668727039240782") + // sqrt_next_2 = Decimal("74.161984870956629488") + + // token_out_2 = round_sdk_prec_down(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + + // token_out = token_out - token_out_2 + + // # Swap step 3 + // liq_3 = second_pos_liq + // sqrt_next_3 = get_next_sqrt_price_from_amount0_out_round_up(liq_3, sqrt_next_2, token_out) + + // token_out_3 = round_sdk_prec_down(calc_amount_zero_delta(liq_3, sqrt_next_2, sqrt_next_3, False)) + // token_in_3 = ceil(calc_amount_one_delta(liq_3, sqrt_next_2, sqrt_next_3, True)) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // print(sqrt_next_3) + // print(token_in) + // print(token_out_2) + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(9999994688)), + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1864161)), + ExpectedTick: 32055918, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("77.819781711876553578435870496972242531"), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 310010, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 322500, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(5001), + NewUpperPrice: osmomath.NewDec(6250), + }, + "two positions with partially overlapping price ranges, not utilizing full liquidity of second position: usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1609138)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(6056), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(5001), + SecondPositionUpperPrice: osmomath.NewDec(6250), + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // # Swap step 1 + // token_out = Decimal("1609138") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") + // sqrt_next_1 = Decimal("70.717748832948578243") + // spread_factor = Decimal("0.05") + + // token_out_1 = round_sdk_prec_down(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + // spread_factor_1 = token_in_1 * spread_factor / (1 - spread_factor) + + // token_out = token_out - token_out_1 + + // # Swap step 2 + // second_pos_liq = Decimal("670416088.605668727039240782") + // liq_2 = liq_1 + Decimal("670416088.605668727039240782") + // sqrt_next_2 = Decimal("74.161984870956629488") + + // token_out_2 = round_sdk_prec_down(calc_amount_zero_delta(liq_2, sqrt_next_1, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_one_delta(liq_2, sqrt_next_1, sqrt_next_2, True)) + // spread_factor_2 = token_in_2 * spread_factor / (1 - spread_factor) + + // token_out = token_out - token_out_2 + + // # Swap step 3 + // liq_3 = second_pos_liq + // sqrt_next_3 = get_next_sqrt_price_from_amount0_out_round_up(liq_3, sqrt_next_2, token_out) + + // token_out_3 = round_sdk_prec_down(calc_amount_zero_delta(liq_3, sqrt_next_2, sqrt_next_3, False)) + // token_in_3 = ceil(calc_amount_one_delta(liq_3, sqrt_next_2, sqrt_next_3, True)) + // spread_factor_2 = token_in_3 * spread_factor / (1 - spread_factor) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // spread_rewards_growth = spread_factor_1 / liq_1 + spread_factor_2 / liq_2 + // print(sqrt_next_3) + // print(token_in) + // print(spread_rewards_growth) + // print(token_out_2) + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(8499999458)), + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1609138)), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 310010, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 322500, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedTick: 31712695, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("75.582372355128594342857800328292876450"), + NewLowerPrice: osmomath.NewDec(5001), + NewUpperPrice: osmomath.NewDec(6250), + }, + // Sequential price ranges with a gap + // + // 5000 + // 4545 -----|----- 5500 + // 5501 ----------- 6250 + // + "two sequential positions with a gap usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1820545)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(6106), + SpreadFactor: osmomath.ZeroDec(), + SecondPositionLowerPrice: osmomath.NewDec(5501), // 315010 + SecondPositionUpperPrice: osmomath.NewDec(6250), // 322500 + // from math import * + // from decimal import * + + // import sys + + // # import custom CL script + // sys.path.insert(0, 'x/concentrated-liquidity/python') + // from clmath import * + + // getcontext().prec = 60 + // precision = Decimal('1.000000000000000000000000000000000000') # 36 decimal precision + + // #Range 1: From 5000 to 5500 + // token_out = Decimal("1820545") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // sqrt_next_1 = Decimal("74.161984870956629488") # sqrt5500 + + // token_out_1 = round_sdk_prec_down(calc_amount_zero_delta(liq_1, sqrt_cur, sqrt_next_1, False)) + // token_in_1 = ceil(calc_amount_one_delta(liq_1, sqrt_cur, sqrt_next_1, True)) + + // token_out = token_out - token_out_1 + + // # Range 2: from 5501 till end + // liq_2 = Decimal("1199528406.187413669481596330") + // sqrt_cur_2 = Decimal("74.168726563154635304") # sqrt5501 + // sqrt_next_2 = get_next_sqrt_price_from_amount0_out_round_up(liq_2, sqrt_cur_2, token_out) + + // token_out_2 = round_sdk_prec_down(calc_amount_zero_delta(liq_2, sqrt_cur_2, sqrt_next_2, False)) + // token_in_2 = ceil(calc_amount_one_delta(liq_2, sqrt_cur_2, sqrt_next_2, True)) + + // # Summary: + // token_in = token_in_1 + token_in_2 + // print(sqrt_next_2) + // print(token_in_2) + // print(token_out_2) + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1820545)), + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(9999994756)), + ExpectedTick: 32105554, + ExpectedSqrtPrice: osmomath.MustNewBigDecFromStr("78.138050797173647031951910080474560428"), + ExpectedSecondLowerTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 315010, ExpectedSpreadRewardGrowth: EmptyCoins}, + ExpectedSecondUpperTickSpreadRewardGrowth: SecondConcentratedPosition{TickIndex: 322500, ExpectedSpreadRewardGrowth: EmptyCoins}, + NewLowerPrice: osmomath.NewDec(5501), + NewUpperPrice: osmomath.NewDec(6250), + }, + // Slippage protection doesn't cause a failure but interrupts early. + "single position within one tick, trade completes but slippage protection interrupts trade early: usdc (in) -> eth (out) | ofz": { + TokenOut: sdk.NewCoin(ETH, osmomath.NewInt(1820545)), + TokenInDenom: USDC, + PriceLimit: osmomath.NewBigDec(5002), + SpreadFactor: osmomath.ZeroDec(), + // from math import * + // from decimal import * + // # Range 1: From 5000 to 5002 + // token_out = Decimal("1820545") + // liq_1 = Decimal("1517882343.751510417627556287") + // sqrt_cur = Decimal("70.710678118654752441") # sqrt5000 + // sqrt_next_1 = Decimal("70.724818840347693040") # sqrt5002 + + // token_out_1 = liq_1 * (sqrt_next_1 - sqrt_cur ) / (sqrt_next_1 * sqrt_cur) + // token_in_1 = ceil(liq_1 * (sqrt_next_1 - sqrt_cur )) + + // # Summary: + // print(sqrt_next_1) + // print(token_in_1) + ExpectedTokenOut: sdk.NewCoin(ETH, osmomath.NewInt(4291)), + ExpectedTokenIn: sdk.NewCoin(USDC, osmomath.NewInt(21463952)), + ExpectedTick: 31002000, + // Since we know we're going up to the price limit, we can calculate the sqrt price exactly. + ExpectedSqrtPrice: osmomath.BigDecFromDec(osmomath.MustMonotonicSqrt(osmomath.NewDec(5002))), + }, + } ) // PrepareConcentratedPool sets up an eth usdc concentrated liquidity pool with a tick spacing of 100,