From 16c8216d58ebd01b98cecb5bc58fbccbd9139a76 Mon Sep 17 00:00:00 2001 From: Phu Ngo <12547020+NgoKimPhu@users.noreply.github.com> Date: Sun, 12 Jan 2025 00:36:40 +0700 Subject: [PATCH] ft: implement CalcAmountIn for algebra integral (#695) --- .../algebra/integral/errors.go | 23 +- pkg/liquidity-source/algebra/integral/math.go | 18 -- .../algebra/integral/plugin.go | 42 +-- .../algebra/integral/pool_simulator.go | 291 ++++++++---------- .../algebra/integral/pool_simulator_test.go | 168 +++++----- .../algebra/integral/pool_tracker.go | 13 +- .../algebra/integral/ticklens.go | 2 +- pkg/liquidity-source/algebra/integral/type.go | 31 +- 8 files changed, 278 insertions(+), 310 deletions(-) diff --git a/pkg/liquidity-source/algebra/integral/errors.go b/pkg/liquidity-source/algebra/integral/errors.go index cd35a3b7c..1d5f998d3 100644 --- a/pkg/liquidity-source/algebra/integral/errors.go +++ b/pkg/liquidity-source/algebra/integral/errors.go @@ -5,18 +5,23 @@ import ( ) var ( - ErrStaleTimepoints = errors.New("getting stale timepoint data") - ErrTicksEmpty = errors.New("ticks list is empty") - ErrInvalidToken = errors.New("invalid token info") - ErrZeroAmountOut = errors.New("amountOut is 0") + ErrStaleTimepoints = errors.New("getting stale timepoint data") + ErrTicksEmpty = errors.New("ticks list is empty") + ErrInvalidToken = errors.New("invalid token info") + ErrZeroAmountCalculated = errors.New("zero amount calculated") ErrNotSupportFetchFullTick = errors.New("not support fetching full ticks") - ErrIncorrectPluginFee = errors.New("incorrect plugin fee") - ErrInvalidLimitSqrtPrice = errors.New("invalid limit sqrt price") - ErrNotInitialized = errors.New("not initialized") - ErrInvalidAmountRequired = errors.New("invalid amount required") - ErrZeroAmountRequired = errors.New("zero amount required") + ErrIncorrectPluginFee = errors.New("incorrect plugin fee") + ErrInvalidLimitSqrtPrice = errors.New("invalid limit sqrt price") + ErrTargetIsTooOld = errors.New("target is too old") + ErrNotInitialized = errors.New("not initialized") + ErrPoolLocked = errors.New("pool has been locked and not usable") + ErrInvalidAmountRequired = errors.New("invalid amount required") + ErrZeroAmountRequired = errors.New("zero amount required") + ErrZeroPrice = errors.New("price cannot be zero") + ErrZeroLiquidity = errors.New("liquidity cannot be zero") + ErrInvalidPriceUpperLower = errors.New("price upper must not be less than price lower") ErrLiquiditySub = errors.New("liquidity sub error") ErrLiquidityAdd = errors.New("liquidity add error") diff --git a/pkg/liquidity-source/algebra/integral/math.go b/pkg/liquidity-source/algebra/integral/math.go index 5a491224a..0b79327f0 100644 --- a/pkg/liquidity-source/algebra/integral/math.go +++ b/pkg/liquidity-source/algebra/integral/math.go @@ -6,15 +6,6 @@ import ( "github.com/holiman/uint256" ) -func unsafeDivRoundingUp(x, y *uint256.Int) *uint256.Int { - quotient, remainder := new(uint256.Int).DivMod(x, y, new(uint256.Int)) - if remainder.Sign() > 0 { - quotient.AddUint64(quotient, 1) - } - - return quotient -} - func addDelta(x *uint256.Int, y *int256.Int) (*uint256.Int, error) { if y.Sign() >= 0 { uY, err := ToUInt256(y) @@ -48,12 +39,3 @@ func ToUInt256(x *int256.Int) (*uint256.Int, error) { return res, nil } - -func ToInt256(x *uint256.Int) (*int256.Int, error) { - var res = new(int256.Int) - if err := v3Utils.ToInt256(x, res); err != nil { - return nil, err - } - - return res, nil -} diff --git a/pkg/liquidity-source/algebra/integral/plugin.go b/pkg/liquidity-source/algebra/integral/plugin.go index b3932a744..dd9f9df2c 100644 --- a/pkg/liquidity-source/algebra/integral/plugin.go +++ b/pkg/liquidity-source/algebra/integral/plugin.go @@ -1,8 +1,6 @@ package integral import ( - "errors" - "fmt" "sync" "github.com/KyberNetwork/elastic-go-sdk/v2/utils" @@ -12,10 +10,6 @@ import ( "github.com/holiman/uint256" ) -var ( - ErrTargetIsTooOld = errors.New("target is too old") -) - type TimepointStorage struct { mu sync.RWMutex data map[uint16]Timepoint @@ -481,19 +475,19 @@ func getOutputTokenDelta10(to, from, liquidity *uint256.Int) (*uint256.Int, erro func getToken0Delta(priceLower, priceUpper, liquidity *uint256.Int, roundUp bool) (*uint256.Int, error) { if priceUpper.Cmp(priceLower) < 0 { - return nil, errors.New("price upper must not be less than price lower") + return nil, ErrInvalidPriceUpperLower } priceDelta := new(uint256.Int).Sub(priceUpper, priceLower) - liquidityShifted := new(uint256.Int).Lsh(liquidity, RESOLUTION) if roundUp { - division, err := v3Utils.MulDivRoundingUp(priceDelta, liquidityShifted, priceUpper) + delta, err := v3Utils.MulDivRoundingUp(priceDelta, liquidityShifted, priceUpper) if err != nil { return nil, err } - return unsafeDivRoundingUp(division, priceLower), nil + v3Utils.DivRoundingUp(delta, priceLower, delta) + return delta, nil } mulDivResult, overflow := priceDelta.MulDivOverflow(priceDelta, liquidityShifted, priceUpper) @@ -505,7 +499,7 @@ func getToken0Delta(priceLower, priceUpper, liquidity *uint256.Int, roundUp bool func getToken1Delta(priceLower, priceUpper, liquidity *uint256.Int, roundUp bool) (*uint256.Int, error) { if priceUpper.Cmp(priceLower) < 0 { - return nil, errors.New("price upper must not be less than price lower") + return nil, ErrInvalidPriceUpperLower } priceDelta := new(uint256.Int).Sub(priceUpper, priceLower) @@ -534,13 +528,11 @@ func getNewPrice( zeroToOne, fromInput bool, ) (*uint256.Int, error) { if price.IsZero() { - return nil, fmt.Errorf("price cannot be zero") - } - if liquidity.IsZero() { - return nil, fmt.Errorf("liquidity cannot be zero") - } - if amount.IsZero() { - return new(uint256.Int).Set(price), nil + return nil, ErrZeroPrice + } else if liquidity.IsZero() { + return nil, ErrZeroLiquidity + } else if amount.IsZero() { + return price.Clone(), nil } liquidityShifted := new(uint256.Int).Lsh(liquidity, RESOLUTION) @@ -571,17 +563,13 @@ func getNewPrice( return resultPrice, nil } else { if fromInput { - var ( - shiftedAmount *uint256.Int - overflow bool - ) + shiftedAmount := new(uint256.Int) if amount.BitLen() < 160 { - shiftedAmount = new(uint256.Int).Lsh(amount, RESOLUTION) + shiftedAmount.Lsh(amount, RESOLUTION) shiftedAmount.Div(shiftedAmount, liquidity) } else { - shiftedAmount, overflow = new(uint256.Int).MulDivOverflow(amount, - new(uint256.Int).Lsh(uONE, RESOLUTION), liquidity) - if overflow { + shiftedAmount.Lsh(uONE, RESOLUTION) + if _, overflow := shiftedAmount.MulDivOverflow(amount, shiftedAmount, liquidity); overflow { return nil, ErrOverflow } } @@ -598,7 +586,7 @@ func getNewPrice( ) if amount.BitLen() < 160 { shiftedAmount = new(uint256.Int).Lsh(amount, RESOLUTION) - shiftedAmount = unsafeDivRoundingUp(shiftedAmount, liquidity) + v3Utils.DivRoundingUp(shiftedAmount, liquidity, shiftedAmount) } else { shiftedAmount, err = v3Utils.MulDivRoundingUp(amount, new(uint256.Int).Lsh(uONE, RESOLUTION), liquidity) if err != nil { diff --git a/pkg/liquidity-source/algebra/integral/pool_simulator.go b/pkg/liquidity-source/algebra/integral/pool_simulator.go index 6b6be4067..aecfe31ea 100644 --- a/pkg/liquidity-source/algebra/integral/pool_simulator.go +++ b/pkg/liquidity-source/algebra/integral/pool_simulator.go @@ -1,7 +1,6 @@ package integral import ( - "errors" "fmt" "math" "math/big" @@ -11,9 +10,8 @@ import ( "github.com/KyberNetwork/int256" "github.com/KyberNetwork/logger" + v3Entities "github.com/KyberNetwork/uniswapv3-sdk-uint256/entities" v3Utils "github.com/KyberNetwork/uniswapv3-sdk-uint256/utils" - v3Entities "github.com/daoleno/uniswapv3-sdk/entities" - "github.com/daoleno/uniswapv3-sdk/utils" "github.com/goccy/go-json" "github.com/holiman/uint256" "github.com/samber/lo" @@ -94,7 +92,7 @@ func NewPoolSimulator(entityPool entity.Pool, defaultGas int64) (*PoolSimulator, return &PoolSimulator{ Pool: pool.Pool{Info: info}, globalState: extra.GlobalState, - liquidity: uint256.MustFromBig(extra.Liquidity), + liquidity: extra.Liquidity, ticks: ticks, gas: defaultGas, tickMin: int32(tickMin), @@ -110,92 +108,105 @@ func NewPoolSimulator(entityPool entity.Pool, defaultGas int64) (*PoolSimulator, } func (p *PoolSimulator) CalcAmountOut(param pool.CalcAmountOutParams) (*pool.CalcAmountOutResult, error) { - tokenAmountIn := param.TokenAmountIn - tokenOut := param.TokenOut - - var ( - tokenInIndex = p.GetTokenIndex(tokenAmountIn.Token) - tokenOutIndex = p.GetTokenIndex(tokenOut) - - zeroForOne bool - overrideFee, pluginFee uint32 - err error - ) - - if tokenInIndex < 0 || tokenOutIndex < 0 { - return nil, ErrInvalidToken + tokenAmtIn, tokenOut := param.TokenAmountIn, param.TokenOut + tokenIn := tokenAmtIn.Token + amtRequired, overflow := uint256.FromBig(tokenAmtIn.Amount) + if overflow { + return nil, ErrInvalidAmountRequired } - if tokenInIndex == 0 { - zeroForOne = true + amtSpent, amtCalculated, fees, gas, stateUpdate, err := p.swap(tokenIn, tokenOut, amtRequired) + if err != nil { + return nil, err + } else if amtCalculated.IsZero() { + return nil, ErrZeroAmountCalculated } - if p.useBasePluginV2 { - overrideFee, pluginFee, err = p.beforeSwapV2(zeroForOne) - if err != nil { - return nil, err - } - } else { - overrideFee, pluginFee, err = p.beforeSwapV1() - if err != nil { - return nil, err - } - } + return &pool.CalcAmountOutResult{ + TokenAmountOut: &pool.TokenAmount{ + Token: tokenOut, + Amount: amtCalculated.Neg(amtCalculated).ToBig(), + }, + RemainingTokenAmountIn: &pool.TokenAmount{ + Token: tokenIn, + Amount: amtRequired.Sub(amtRequired, amtSpent).ToBig(), + }, + Fee: &pool.TokenAmount{ + Token: tokenIn, + Amount: new(uint256.Int).Add(fees.communityFeeAmount, fees.pluginFeeAmount).ToBig(), + }, + Gas: gas, + SwapInfo: stateUpdate, + }, nil +} - if !p.globalState.Unlocked { - return nil, errors.New("pool has been locked and not usable") +func (p *PoolSimulator) CalcAmountIn(param pool.CalcAmountInParams) (*pool.CalcAmountInResult, error) { + tokenIn, tokenAmtOut := param.TokenIn, param.TokenAmountOut + tokenOut := tokenAmtOut.Token + amtRequired, overflow := uint256.FromBig(tokenAmtOut.Amount) + if overflow { + return nil, ErrInvalidAmountRequired } - priceLimit, err := p.getSqrtPriceLimit(zeroForOne) + amtSpent, amtCalculated, fees, gas, stateUpdate, err := p.swap(tokenIn, tokenOut, amtRequired.Neg(amtRequired)) if err != nil { return nil, err + } else if amtCalculated.IsZero() { + return nil, ErrZeroAmountCalculated } - amountRequired, err := int256.FromBig(tokenAmountIn.Amount) - if err != nil { - return nil, ErrInvalidAmountRequired + return &pool.CalcAmountInResult{ + TokenAmountIn: &pool.TokenAmount{ + Token: tokenIn, + Amount: amtCalculated.ToBig(), + }, + RemainingTokenAmountOut: &pool.TokenAmount{ + Token: tokenOut, + Amount: amtRequired.Neg(amtRequired.Sub(amtRequired, amtSpent)).ToBig(), + }, + Fee: &pool.TokenAmount{ + Token: tokenIn, + Amount: new(uint256.Int).Add(fees.communityFeeAmount, fees.pluginFeeAmount).ToBig(), + }, + Gas: gas, + SwapInfo: stateUpdate, + }, nil +} + +func (p *PoolSimulator) swap(tokenIn, tokenOut string, amtRequired *uint256.Int) (amtSpent, amtCalculated *uint256.Int, + fees FeesAmount, gas int64, stateUpdate StateUpdate, err error) { + if !p.globalState.Unlocked { + err = ErrPoolLocked + return } - amount0, amount1, currentPrice, currentTick, currentLiquidity, fees, err := p.calculateSwap( - overrideFee, pluginFee, zeroForOne, amountRequired, priceLimit) - if err != nil { - return nil, err + tokenInIndex, tokenOutIndex := p.GetTokenIndex(tokenIn), p.GetTokenIndex(tokenOut) + if tokenInIndex < 0 || tokenOutIndex < 0 { + err = ErrInvalidToken + return } - newState := GlobalState{ - Price: currentPrice, - Tick: currentTick, - LastFee: p.globalState.LastFee, - PluginConfig: p.globalState.PluginConfig, - CommunityFee: p.globalState.CommunityFee, - Unlocked: p.globalState.Unlocked, + zeroForOne := tokenInIndex == 0 + overrideFee, pluginFee, err := lo.Ternary(p.useBasePluginV2, p.beforeSwapV2, p.beforeSwapV1)(zeroForOne) + if err != nil { + return } - var amountOut *int256.Int - if zeroForOne { - amountOut = new(int256.Int).Neg(amount1) - } else { - amountOut = new(int256.Int).Neg(amount0) + priceLimit, err := p.getSqrtPriceLimit(zeroForOne) + if err != nil { + return } - if amountOut.IsZero() { - return nil, ErrZeroAmountOut + amtSpent, amtCalculated, currentPrice, currentTick, currentLiquidity, fees, err := p.calculateSwap( + overrideFee, pluginFee, zeroForOne, amtRequired, priceLimit) + if err != nil { + return } - return &pool.CalcAmountOutResult{ - TokenAmountOut: &pool.TokenAmount{ - Token: tokenOut, - Amount: amountOut.ToBig(), - }, - Fee: &pool.TokenAmount{ - Token: param.TokenAmountIn.Token, - Amount: new(uint256.Int).Add(fees.communityFeeAmount, fees.pluginFeeAmount).ToBig(), - }, - Gas: p.gas, - SwapInfo: StateUpdate{ - GlobalState: newState, - Liquidity: currentLiquidity, - }, + return amtSpent, amtCalculated, fees, p.gas, StateUpdate{ + Liquidity: currentLiquidity, + Price: currentPrice, + Tick: currentTick, }, nil } @@ -214,8 +225,9 @@ func (p *PoolSimulator) UpdateBalance(params pool.UpdateBalanceParams) { p.Info.Address, p.Info.Exchange) return } - p.liquidity = new(uint256.Int).Set(si.Liquidity) - p.globalState = si.GlobalState + p.liquidity = si.Liquidity + p.globalState.Price = si.Price + p.globalState.Tick = si.Tick } func (p *PoolSimulator) GetMetaInfo(tokenIn string, _ string) interface{} { @@ -274,7 +286,7 @@ func (p *PoolSimulator) writeTimepoint(onWrite func() error) (err error) { return err } -func (p *PoolSimulator) beforeSwapV1() (uint32, uint32, error) { +func (p *PoolSimulator) beforeSwapV1(_ bool) (uint32, uint32, error) { if p.globalState.PluginConfig&BEFORE_SWAP_FLAG == 0 { return 0, 0, nil } @@ -361,8 +373,8 @@ func (p *PoolSimulator) getAverageVolatilityLast() (*uint256.Int, error) { return volatilityAverage, nil } -func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne bool, amountRequired *int256.Int, - limitSqrtPrice *uint256.Int) (*int256.Int, *int256.Int, *uint256.Int, int32, *uint256.Int, FeesAmount, error) { +func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne bool, amountRequired *uint256.Int, + limitSqrtPrice *uint256.Int) (*uint256.Int, *uint256.Int, *uint256.Int, int32, *uint256.Int, FeesAmount, error) { if amountRequired.IsZero() { return nil, nil, nil, 0, nil, FeesAmount{}, ErrZeroAmountRequired } @@ -374,44 +386,36 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b } cache.amountRequiredInitial = amountRequired - cache.exactInput = amountRequired.IsPositive() - cache.pluginFee = pluginFee - cache.amountCalculated = new(int256.Int) + cache.exactInput = amountRequired.Sign() > 0 + cache.amountCalculated = new(uint256.Int) currentLiquidity := p.liquidity - currentPrice := p.globalState.Price - currentTick := p.globalState.Tick - cache.fee = uint32(p.globalState.LastFee) - cache.communityFee = uint256.NewInt(uint64(p.globalState.CommunityFee)) - if currentPrice.IsZero() { return nil, nil, nil, 0, nil, FeesAmount{}, ErrNotInitialized } + currentTick := p.globalState.Tick + if pluginFee > 0 { + cache.pluginFee = uint256.NewInt(uint64(pluginFee)) + } if overrideFee != 0 { - cache.fee = overrideFee + pluginFee - } else { - if pluginFee != 0 { - cache.fee += pluginFee - } - - if cache.fee >= 1e6 { + cache.fee = uint64(overrideFee + pluginFee) + } else if fee := uint32(p.globalState.LastFee) + pluginFee; fee != 0 { + if fee >= 1e6 { return nil, nil, nil, 0, nil, FeesAmount{}, ErrIncorrectPluginFee } + cache.fee = uint64(fee) } + feeU := uint256.NewInt(cache.fee) + cache.communityFee = uint256.NewInt(uint64(p.globalState.CommunityFee)) - if zeroToOne { - if limitSqrtPrice.Cmp(currentPrice) >= 0 || limitSqrtPrice.Cmp(MIN_SQRT_RATIO) <= 0 { - return nil, nil, nil, 0, nil, FeesAmount{}, ErrInvalidLimitSqrtPrice - } - } else { - if limitSqrtPrice.Cmp(currentPrice) <= 0 || limitSqrtPrice.Cmp(MAX_SQRT_RATIO) >= 0 { - return nil, nil, nil, 0, nil, FeesAmount{}, ErrInvalidLimitSqrtPrice - } + if zeroToOne && limitSqrtPrice.Cmp(currentPrice) >= 0 || limitSqrtPrice.Cmp(MIN_SQRT_RATIO) <= 0 || + !zeroToOne && limitSqrtPrice.Cmp(currentPrice) <= 0 || limitSqrtPrice.Cmp(MAX_SQRT_RATIO) >= 0 { + return nil, nil, nil, 0, nil, FeesAmount{}, ErrInvalidLimitSqrtPrice } - var step PriceMovementCache + step := PriceMovementCache{nextTickPrice: new(uint256.Int)} initializedTick := currentTick // swap until there is remaining input or output tokens, or we reach the price limit. // limit by maxSwapLoop to make sure we won't loop infinitely because of a bug somewhere @@ -438,12 +442,9 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b } step.stepSqrtPrice = currentPrice - - nextTickPrice, err := utils.GetSqrtRatioAtTick(int(step.nextTick)) - if err != nil { + if err = v3Utils.GetSqrtRatioAtTickV2(int(step.nextTick), step.nextTickPrice); err != nil { return nil, nil, nil, 0, nil, FeesAmount{}, err } - step.nextTickPrice = uint256.MustFromBig(nextTickPrice) var targetPrice = step.nextTickPrice if zeroToOne == (step.nextTickPrice.Cmp(limitSqrtPrice) < 0) { @@ -456,27 +457,16 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b return nil, nil, nil, 0, nil, FeesAmount{}, err } - output, err := ToInt256(step.output) - if err != nil { - return nil, nil, nil, 0, nil, FeesAmount{}, err - } - - amountDelta, err := ToInt256(new(uint256.Int).Add(step.input, step.feeAmount)) - if err != nil { - return nil, nil, nil, 0, nil, FeesAmount{}, err - } - if cache.exactInput { - amountRequired.Sub(amountRequired, amountDelta) - cache.amountCalculated = new(int256.Int).Sub(cache.amountCalculated, output) + amountRequired.Sub(amountRequired, step.input).Sub(amountRequired, step.feeAmount) + cache.amountCalculated.Sub(cache.amountCalculated, step.output) } else { - amountRequired.Add(amountRequired, output) - cache.amountCalculated = new(int256.Int).Add(cache.amountCalculated, amountDelta) + amountRequired.Add(amountRequired, step.output) + cache.amountCalculated.Add(cache.amountCalculated, step.input).Add(cache.amountCalculated, step.feeAmount) } - if cache.pluginFee > 0 && cache.fee > 0 { - delta, err := v3Utils.MulDiv(step.feeAmount, uint256.NewInt(uint64(cache.pluginFee)), - uint256.NewInt(uint64(cache.fee))) + if cache.pluginFee != nil && cache.fee > 0 { + delta, err := v3Utils.MulDiv(step.feeAmount, cache.pluginFee, feeU) if err != nil { return nil, nil, nil, 0, nil, FeesAmount{}, err } @@ -486,10 +476,10 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b } if cache.communityFee.Sign() > 0 { - delta := new(uint256.Int).Div( - new(uint256.Int).Mul(step.feeAmount, cache.communityFee), - COMMUNITY_FEE_DENOMINATOR, - ) + delta, err := v3Utils.MulDiv(step.feeAmount, cache.communityFee, COMMUNITY_FEE_DENOMINATOR) + if err != nil { + return nil, nil, nil, 0, nil, FeesAmount{}, err + } step.feeAmount.Sub(step.feeAmount, delta) fees.communityFeeAmount.Add(fees.communityFeeAmount, delta) @@ -501,17 +491,15 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b return nil, nil, nil, 0, nil, FeesAmount{}, err } - liquidityNet := int256.MustFromBig(tickData.LiquidityNet) - - var liquidityDelta = new(int256.Int) + var liquidityDelta *int256.Int if zeroToOne { currentTick = step.nextTick - 1 initializedTick = step.nextTick - 1 - liquidityDelta = liquidityDelta.Neg(liquidityNet) + liquidityDelta = new(int256.Int).Neg(tickData.LiquidityNet) } else { currentTick = step.nextTick initializedTick = step.nextTick - liquidityDelta = liquidityNet + liquidityDelta = tickData.LiquidityNet } currentLiquidity, err = addDelta(currentLiquidity, liquidityDelta) @@ -520,11 +508,10 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b } } else if currentPrice.Cmp(step.stepSqrtPrice) != 0 { - currentTickInt, err := utils.GetTickAtSqrtRatio(currentPrice.ToBig()) + currentTickInt, err := v3Utils.GetTickAtSqrtRatioV2(currentPrice) if err != nil { return nil, nil, nil, 0, nil, FeesAmount{}, err } - currentTick = int32(currentTickInt) break @@ -535,42 +522,18 @@ func (p *PoolSimulator) calculateSwap(overrideFee, pluginFee uint32, zeroToOne b } } - amountSpent := new(int256.Int).Sub(cache.amountRequiredInitial, amountRequired) - - amount0, amount1 := cache.amountCalculated, amountSpent - if zeroToOne == cache.exactInput { - amount0, amount1 = amountSpent, cache.amountCalculated - } - - return amount0, amount1, currentPrice, currentTick, currentLiquidity, fees, nil + amountSpent := new(uint256.Int).Sub(cache.amountRequiredInitial, amountRequired) + return amountSpent, cache.amountCalculated, currentPrice, currentTick, currentLiquidity, fees, nil } -func movePriceTowardsTarget( - zeroToOne bool, - currentPrice, targetPrice, liquidity *uint256.Int, - amountAvailableInt256 *int256.Int, - fee uint32, -) (*uint256.Int, *uint256.Int, *uint256.Int, *uint256.Int, error) { - amountAvailable, err := ToUInt256(amountAvailableInt256) - if err != nil { - return nil, nil, nil, nil, err - } - - var getInputTokenAmount, getOutputTokenAmount func(target, current, liquidity *uint256.Int) (*uint256.Int, error) - +func movePriceTowardsTarget(zeroToOne bool, currentPrice, targetPrice, liquidity, amountAvailable *uint256.Int, + fee uint64) (resultPrice, input, output, feeAmount *uint256.Int, err error) { + getInputTokenAmount, getOutputTokenAmount := getInputTokenDelta10, getOutputTokenDelta10 if zeroToOne { - getInputTokenAmount = getInputTokenDelta01 - getOutputTokenAmount = getOutputTokenDelta01 - } else { - getInputTokenAmount = getInputTokenDelta10 - getOutputTokenAmount = getOutputTokenDelta10 + getInputTokenAmount, getOutputTokenAmount = getInputTokenDelta01, getOutputTokenDelta01 } - var ( - resultPrice, input, output, feeAmount *uint256.Int - ) - - feeDenoMinusFee := new(uint256.Int).SubUint64(FEE_DENOMINATOR, uint64(fee)) + feeDenoMinusFee := new(uint256.Int).SubUint64(FEE_DENOMINATOR, fee) if amountAvailable.Sign() >= 0 { amountAvailableAfterFee, overflow := new(uint256.Int).MulDivOverflow(amountAvailable, feeDenoMinusFee, FEE_DENOMINATOR) @@ -585,7 +548,7 @@ func movePriceTowardsTarget( if amountAvailableAfterFee.Cmp(input) >= 0 { resultPrice = targetPrice - feeAmount, err = v3Utils.MulDivRoundingUp(input, uint256.NewInt(uint64(fee)), feeDenoMinusFee) + feeAmount, err = v3Utils.MulDivRoundingUp(input, uint256.NewInt(fee), feeDenoMinusFee) if err != nil { return nil, nil, nil, nil, err } @@ -618,7 +581,7 @@ func movePriceTowardsTarget( return nil, nil, nil, nil, err } - amountAvailable.Neg(amountAvailable) + amountAvailable = new(uint256.Int).Neg(amountAvailable) if amountAvailable.Sign() < 0 { return nil, nil, nil, nil, ErrInvalidAmountRequired } @@ -648,7 +611,7 @@ func movePriceTowardsTarget( return nil, nil, nil, nil, err } - feeAmount, err = v3Utils.MulDivRoundingUp(input, uint256.NewInt(uint64(fee)), + feeAmount, err = v3Utils.MulDivRoundingUp(input, uint256.NewInt(fee), feeDenoMinusFee) if err != nil { return nil, nil, nil, nil, err diff --git a/pkg/liquidity-source/algebra/integral/pool_simulator_test.go b/pkg/liquidity-source/algebra/integral/pool_simulator_test.go index f59901ab8..15a9191ec 100644 --- a/pkg/liquidity-source/algebra/integral/pool_simulator_test.go +++ b/pkg/liquidity-source/algebra/integral/pool_simulator_test.go @@ -1,11 +1,14 @@ package integral import ( + "fmt" "math/big" + "math/rand" "sync" "testing" - v3Entities "github.com/daoleno/uniswapv3-sdk/entities" + "github.com/KyberNetwork/int256" + v3Entities "github.com/KyberNetwork/uniswapv3-sdk-uint256/entities" "github.com/goccy/go-json" "github.com/holiman/uint256" "github.com/stretchr/testify/assert" @@ -13,6 +16,7 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" + "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/testutil" ) @@ -32,21 +36,21 @@ var ( mockTickmax int32 = 887220 mockTicks, _ = v3Entities.NewTickListDataProvider([]v3Entities.Tick{ - {Index: -887220, LiquidityGross: big.NewInt(35733795), LiquidityNet: big.NewInt(35733795)}, - {Index: -4500, LiquidityGross: big.NewInt(1469002688), LiquidityNet: big.NewInt(1469002688)}, - {Index: -1740, LiquidityGross: big.NewInt(815264000), LiquidityNet: big.NewInt(815264000)}, - {Index: -1080, LiquidityGross: big.NewInt(4716862354), LiquidityNet: big.NewInt(4716862354)}, - {Index: -960, LiquidityGross: big.NewInt(2130488), LiquidityNet: big.NewInt(2130488)}, - {Index: -540, LiquidityGross: big.NewInt(59681565), LiquidityNet: big.NewInt(59681565)}, - {Index: -120, LiquidityGross: big.NewInt(173321441467), LiquidityNet: big.NewInt(173321441467)}, - {Index: -60, LiquidityGross: big.NewInt(265085097155), LiquidityNet: big.NewInt(-81557785779)}, - {Index: 60, LiquidityGross: big.NewInt(91763655688), LiquidityNet: big.NewInt(-91763655688)}, - {Index: 540, LiquidityGross: big.NewInt(2130488), LiquidityNet: big.NewInt(-2130488)}, - {Index: 960, LiquidityGross: big.NewInt(59681565), LiquidityNet: big.NewInt(-59681565)}, - {Index: 1080, LiquidityGross: big.NewInt(3555869904), LiquidityNet: big.NewInt(-3555869904)}, - {Index: 1800, LiquidityGross: big.NewInt(1976256450), LiquidityNet: big.NewInt(-1976256450)}, - {Index: 1860, LiquidityGross: big.NewInt(1469002688), LiquidityNet: big.NewInt(-1469002688)}, - {Index: 887220, LiquidityGross: big.NewInt(35733795), LiquidityNet: big.NewInt(-35733795)}, + {Index: -887220, LiquidityGross: uint256.NewInt(35733795), LiquidityNet: int256.NewInt(35733795)}, + {Index: -4500, LiquidityGross: uint256.NewInt(1469002688), LiquidityNet: int256.NewInt(1469002688)}, + {Index: -1740, LiquidityGross: uint256.NewInt(815264000), LiquidityNet: int256.NewInt(815264000)}, + {Index: -1080, LiquidityGross: uint256.NewInt(4716862354), LiquidityNet: int256.NewInt(4716862354)}, + {Index: -960, LiquidityGross: uint256.NewInt(2130488), LiquidityNet: int256.NewInt(2130488)}, + {Index: -540, LiquidityGross: uint256.NewInt(59681565), LiquidityNet: int256.NewInt(59681565)}, + {Index: -120, LiquidityGross: uint256.NewInt(173321441467), LiquidityNet: int256.NewInt(173321441467)}, + {Index: -60, LiquidityGross: uint256.NewInt(265085097155), LiquidityNet: int256.NewInt(-81557785779)}, + {Index: 60, LiquidityGross: uint256.NewInt(91763655688), LiquidityNet: int256.NewInt(-91763655688)}, + {Index: 540, LiquidityGross: uint256.NewInt(2130488), LiquidityNet: int256.NewInt(-2130488)}, + {Index: 960, LiquidityGross: uint256.NewInt(59681565), LiquidityNet: int256.NewInt(-59681565)}, + {Index: 1080, LiquidityGross: uint256.NewInt(3555869904), LiquidityNet: int256.NewInt(-3555869904)}, + {Index: 1800, LiquidityGross: uint256.NewInt(1976256450), LiquidityNet: int256.NewInt(-1976256450)}, + {Index: 1860, LiquidityGross: uint256.NewInt(1469002688), LiquidityNet: int256.NewInt(-1469002688)}, + {Index: 887220, LiquidityGross: uint256.NewInt(35733795), LiquidityNet: int256.NewInt(-35733795)}, }, mockTickSpacing) mockTimepoints = NewTimepointStorage(map[uint16]Timepoint{ @@ -191,21 +195,16 @@ func TestCalcAmountOut(t *testing.T) { expectedResult: &pool.CalcAmountOutResult{ TokenAmountOut: &pool.TokenAmount{ Token: "0xf55bec9cafdbe8730f096aa55dad6d22d44099df", - Amount: big.NewInt(984666), // Expected amount after swap + Amount: big.NewInt(984666), }, Fee: &pool.TokenAmount{ Token: "0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4", - Amount: big.NewInt(2250), // Expected fees + Amount: big.NewInt(2250), }, SwapInfo: StateUpdate{ - GlobalState: GlobalState{ - Unlocked: true, - LastFee: 15000, - Tick: -4, - PluginConfig: mockPluginConfig, - CommunityFee: mockCommunityFee, - }, - Liquidity: uint256.NewInt(98862330578), // Expected liquidity + Liquidity: uint256.NewInt(98862330578), + Price: uint256.MustFromDecimal("79214348439875248928556576945"), + Tick: -4, }, Gas: mockGas, }, @@ -274,21 +273,16 @@ func TestCalcAmountOut(t *testing.T) { expectedResult: &pool.CalcAmountOutResult{ TokenAmountOut: &pool.TokenAmount{ Token: "0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4", - Amount: big.NewInt(985314), // Expected amount after swap + Amount: big.NewInt(985314), }, Fee: &pool.TokenAmount{ Token: "0xf55bec9cafdbe8730f096aa55dad6d22d44099df", - Amount: big.NewInt(2250), // Expected fees + Amount: big.NewInt(2250), }, SwapInfo: StateUpdate{ - GlobalState: GlobalState{ - Unlocked: true, - LastFee: 15000, - Tick: -4, - PluginConfig: mockPluginConfig, - CommunityFee: mockCommunityFee, - }, - Liquidity: uint256.NewInt(98862330578), // Expected liquidity + Liquidity: uint256.NewInt(98862330578), + Price: uint256.MustFromDecimal("79215926928314930666100919082"), + Tick: -4, }, Gas: mockGas, }, @@ -357,21 +351,16 @@ func TestCalcAmountOut(t *testing.T) { expectedResult: &pool.CalcAmountOutResult{ TokenAmountOut: &pool.TokenAmount{ Token: "0xf55bec9cafdbe8730f096aa55dad6d22d44099df", - Amount: big.NewInt(1425476892), // Expected amount after swap + Amount: big.NewInt(1425476892), }, Fee: &pool.TokenAmount{ Token: "0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4", - Amount: big.NewInt(3207826239749998), // Expected fees + Amount: big.NewInt(3207826239749998), }, SwapInfo: StateUpdate{ - GlobalState: GlobalState{ - Unlocked: true, - LastFee: 15000, - Tick: -487914, - PluginConfig: mockPluginConfig, - CommunityFee: mockCommunityFee, - }, - Liquidity: uint256.NewInt(98862330578), // Expected liquidity + Liquidity: uint256.NewInt(35733795), + Price: uint256.MustFromDecimal("2016016943697492749"), + Tick: -487914, }, Gas: mockGas, }, @@ -440,21 +429,16 @@ func TestCalcAmountOut(t *testing.T) { expectedResult: &pool.CalcAmountOutResult{ TokenAmountOut: &pool.TokenAmount{ Token: "0x06efdbff2a14a7c8e15944d1f4a48f9f95f663a4", - Amount: big.NewInt(768004061), // Expected amount after swap + Amount: big.NewInt(768004061), }, Fee: &pool.TokenAmount{ Token: "0xf55bec9cafdbe8730f096aa55dad6d22d44099df", - Amount: big.NewInt(1839166), // Expected fees + Amount: big.NewInt(1839166), }, SwapInfo: StateUpdate{ - GlobalState: GlobalState{ - Unlocked: true, - LastFee: 15000, - Tick: 1721, - PluginConfig: mockPluginConfig, - CommunityFee: mockCommunityFee, - }, - Liquidity: uint256.NewInt(98862330578), // Expected liquidity + Liquidity: uint256.NewInt(3480992933), + Price: uint256.MustFromDecimal("86350404125395664252363004498"), + Tick: 1721, }, Gas: mockGas, }, @@ -482,12 +466,7 @@ func TestCalcAmountOut(t *testing.T) { expectedSwapInfo := tt.expectedResult.SwapInfo.(StateUpdate) actualSwapInfo := result.SwapInfo.(StateUpdate) - require.NotEmpty(t, actualSwapInfo.GlobalState) - assert.Equal(t, expectedSwapInfo.GlobalState.CommunityFee, actualSwapInfo.GlobalState.CommunityFee) - assert.Equal(t, expectedSwapInfo.GlobalState.PluginConfig, actualSwapInfo.GlobalState.PluginConfig) - assert.Equal(t, expectedSwapInfo.GlobalState.Unlocked, actualSwapInfo.GlobalState.Unlocked) - assert.Equal(t, expectedSwapInfo.GlobalState.LastFee, actualSwapInfo.GlobalState.LastFee) - assert.Equal(t, expectedSwapInfo.GlobalState.Tick, actualSwapInfo.GlobalState.Tick) + assert.Equal(t, expectedSwapInfo, actualSwapInfo) require.NotEmpty(t, result.SwapInfo) assert.Equal(t, tt.expectedResult.TokenAmountOut, result.TokenAmountOut) @@ -498,14 +477,13 @@ func TestCalcAmountOut(t *testing.T) { var mockPool = []byte(`{"address":"0xbe9c1d237d002c8d9402f30c16ace1436d008f0c","exchange":"silverswap","type":"algebra-integral","timestamp":1733225338,"reserves":["9999999999999944","2620057588865"],"tokens":[{"address":"0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83","name":"Wrapped Fantom","symbol":"WFTM","decimals":18,"weight":50,"swappable":true},{"address":"0xfe7eda5f2c56160d406869a8aa4b2f365d544c7b","name":"Axelar Wrapped ETH","symbol":"axlETH","decimals":18,"weight":50,"swappable":true}],"extra":"{\"liq\":161865919478591,\"gS\":{\"price\":\"1282433937397070526017841373\",\"tick\":82476,\"lF\":100,\"pC\":193,\"cF\":100,\"un\":true},\"ticks\":[{\"Index\":-887220,\"LiquidityGross\":161865919478591,\"LiquidityNet\":161865919478591},{\"Index\":887220,\"LiquidityGross\":161865919478591,\"LiquidityNet\":-161865919478591}],\"tS\":60,\"tP\":{\"0\":{\"init\":true,\"ts\":1712116096,\"cum\":0,\"vo\":\"0\",\"tick\":-82476,\"avgT\":-82476,\"wsI\":0},\"1\":{\"init\":false,\"ts\":0,\"cum\":0,\"vo\":\"0\",\"tick\":0,\"avgT\":0,\"wsI\":0},\"2\":{\"init\":false,\"ts\":0,\"cum\":0,\"vo\":\"0\",\"tick\":0,\"avgT\":0,\"wsI\":0},\"65535\":{\"init\":false,\"ts\":0,\"cum\":0,\"vo\":\"0\",\"tick\":0,\"avgT\":0,\"wsI\":0}},\"vo\":{\"tpIdx\":0,\"lastTs\":1712116096,\"init\":true},\"sF\":{\"0to1fF\":null,\"1to0fF\":null},\"dF\":{\"a1\":2900,\"a2\":12000,\"b1\":360,\"b2\":60000,\"g1\":59,\"g2\":8500,\"vB\":0,\"vG\":0,\"bF\":100}}","staticExtra":"{\"pluginV2\":false}","blockNumber":99019509}`) -func TestCalcAmountOut_FromPool(t *testing.T) { - var p entity.Pool - err := json.Unmarshal(mockPool, &p) - require.NoError(t, err) - - ps, err := NewPoolSimulator(p, 280000) - require.NoError(t, err) +var ( + p entity.Pool + _ = json.Unmarshal(mockPool, &p) + ps, err = NewPoolSimulator(p, 280000) +) +func TestCalcAmountOut_FromPool(t *testing.T) { res, err := testutil.MustConcurrentSafe(t, func() (*pool.CalcAmountOutResult, error) { return ps.CalcAmountOut(pool.CalcAmountOutParams{ TokenAmountIn: pool.TokenAmount{ @@ -519,3 +497,55 @@ func TestCalcAmountOut_FromPool(t *testing.T) { require.NoError(t, err) assert.Equal(t, big.NewInt(25555842204), res.TokenAmountOut.Amount) } + +func TestPoolSimulator_CalcAmountIn(t *testing.T) { + for i := 0; i < 64; i++ { + tokenIn := p.Tokens[i%2].Address + tokenOut := p.Tokens[(i+1)%2].Address + amountOut := big.NewInt(int64(rand.Uint32())) + t.Run(fmt.Sprintf("token%d -> %s token%d", i%2, amountOut, (i+1)%2), func(t *testing.T) { + resIn, err := testutil.MustConcurrentSafe(t, func() (*pool.CalcAmountInResult, error) { + return ps.CalcAmountIn(pool.CalcAmountInParams{ + TokenAmountOut: pool.TokenAmount{ + Token: tokenOut, + Amount: amountOut, + }, + TokenIn: tokenIn, + }) + }) + require.NoError(t, err) + + if resIn.RemainingTokenAmountOut.Amount.Sign() > 0 { + resIn, err = testutil.MustConcurrentSafe(t, func() (*pool.CalcAmountInResult, error) { + return ps.CalcAmountIn(pool.CalcAmountInParams{ + TokenAmountOut: pool.TokenAmount{ + Token: tokenOut, + Amount: amountOut.Sub(amountOut, resIn.RemainingTokenAmountOut.Amount).Div(amountOut, + bignumber.Two), + }, + TokenIn: tokenIn, + }) + }) + require.NoError(t, err) + } + + resOut, err := testutil.MustConcurrentSafe(t, func() (*pool.CalcAmountOutResult, error) { + return ps.CalcAmountOut(pool.CalcAmountOutParams{ + TokenAmountIn: pool.TokenAmount{ + Token: tokenIn, + Amount: resIn.TokenAmountIn.Amount, + }, + TokenOut: tokenOut, + }) + }) + require.NoError(t, err) + + finalAmtOut := resOut.TokenAmountOut.Amount + finalAmtOut.Sub(finalAmtOut, resIn.RemainingTokenAmountOut.Amount) + origAmountOutF, _ := amountOut.Float64() + finalAmountOutF, _ := finalAmtOut.Float64() + assert.InEpsilonf(t, origAmountOutF, finalAmountOutF, 1e-4, + "expected ~%s, got %s", amountOut, finalAmtOut) + }) + } +} diff --git a/pkg/liquidity-source/algebra/integral/pool_tracker.go b/pkg/liquidity-source/algebra/integral/pool_tracker.go index 00f686e7f..7f4f1256d 100644 --- a/pkg/liquidity-source/algebra/integral/pool_tracker.go +++ b/pkg/liquidity-source/algebra/integral/pool_tracker.go @@ -7,7 +7,7 @@ import ( "github.com/KyberNetwork/ethrpc" "github.com/KyberNetwork/logger" - v3Entities "github.com/daoleno/uniswapv3-sdk/entities" + v3Entities "github.com/KyberNetwork/uniswapv3-sdk-uint256/entities" "github.com/ethereum/go-ethereum/common" "github.com/goccy/go-json" "github.com/holiman/uint256" @@ -15,7 +15,6 @@ import ( "github.com/KyberNetwork/kyberswap-dex-lib/pkg/entity" sourcePool "github.com/KyberNetwork/kyberswap-dex-lib/pkg/source/pool" - "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/bignumber" graphqlpkg "github.com/KyberNetwork/kyberswap-dex-lib/pkg/util/graphql" "github.com/KyberNetwork/kyberswap-dex-lib/pkg/valueobject" ) @@ -107,7 +106,7 @@ func (d *PoolTracker) GetNewPoolState( } // LiquidityGross = 0 means that the tick is uninitialized - if tick.LiquidityGross.Cmp(bignumber.ZeroBI) == 0 { + if tick.LiquidityGross.IsZero() { continue } @@ -115,7 +114,7 @@ func (d *PoolTracker) GetNewPoolState( } extraBytes, err := json.Marshal(&Extra{ - Liquidity: rpcData.Liquidity, + Liquidity: uint256.MustFromBig(rpcData.Liquidity), GlobalState: rpcData.State, Ticks: ticks, TickSpacing: int32(rpcData.TickSpacing.Int64()), @@ -403,7 +402,8 @@ func (d *PoolTracker) getDynamicFeeData(ctx context.Context, pluginAddress strin return result, nil } -func (d *PoolTracker) fetchTimepoints(ctx context.Context, pluginAddress string, blockNumber *big.Int, currentIndex uint16) (map[uint16]Timepoint, error) { +func (d *PoolTracker) fetchTimepoints(ctx context.Context, pluginAddress string, blockNumber *big.Int, + currentIndex uint16) (map[uint16]Timepoint, error) { blockTimestamp := uint32(time.Now().Unix()) yesterday := blockTimestamp - WINDOW timepoints, err := d.getPoolTimepoints(ctx, blockNumber, pluginAddress, currentIndex, yesterday) @@ -414,7 +414,8 @@ func (d *PoolTracker) fetchTimepoints(ctx context.Context, pluginAddress string, return timepoints, nil } -func (d *PoolTracker) getPoolTimepoints(ctx context.Context, blockNumber *big.Int, pluginAddress string, currentIndex uint16, yesterday uint32) (map[uint16]Timepoint, error) { +func (d *PoolTracker) getPoolTimepoints(ctx context.Context, blockNumber *big.Int, pluginAddress string, + currentIndex uint16, yesterday uint32) (map[uint16]Timepoint, error) { timepoints := make(map[uint16]Timepoint, UINT16_MODULO) currentIndexPrev := currentIndex - 1 diff --git a/pkg/liquidity-source/algebra/integral/ticklens.go b/pkg/liquidity-source/algebra/integral/ticklens.go index dd37e5cc6..0f9af0abf 100644 --- a/pkg/liquidity-source/algebra/integral/ticklens.go +++ b/pkg/liquidity-source/algebra/integral/ticklens.go @@ -90,7 +90,7 @@ func (d *PoolTracker) getPoolTicksFromSC(ctx context.Context, pool entity.Pool, combined = append(combined, TickResp{ TickIdx: strconv.Itoa(t.Index), LiquidityGross: t.LiquidityGross.String(), - LiquidityNet: t.LiquidityNet.String(), + LiquidityNet: t.LiquidityNet.Dec(), }) } } diff --git a/pkg/liquidity-source/algebra/integral/type.go b/pkg/liquidity-source/algebra/integral/type.go index b2c12c7df..eca481713 100644 --- a/pkg/liquidity-source/algebra/integral/type.go +++ b/pkg/liquidity-source/algebra/integral/type.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/KyberNetwork/int256" - v3Entities "github.com/daoleno/uniswapv3-sdk/entities" + v3Entities "github.com/KyberNetwork/uniswapv3-sdk-uint256/entities" "github.com/holiman/uint256" ) @@ -46,16 +46,14 @@ type Tick struct { } func (t TickResp) transformTickRespToTick() (v3Entities.Tick, error) { - liquidityGross := new(big.Int) - liquidityGross, ok := liquidityGross.SetString(t.LiquidityGross, 10) - if !ok { - return v3Entities.Tick{}, fmt.Errorf("can not convert liquidityGross string to bigInt, tick: %v", t.TickIdx) + liquidityGross, err := uint256.FromDecimal(t.LiquidityGross) + if err != nil { + return v3Entities.Tick{}, fmt.Errorf("can not convert liquidityGross string to uint256, tick: %v", t.TickIdx) } - liquidityNet := new(big.Int) - liquidityNet, ok = liquidityNet.SetString(t.LiquidityNet, 10) - if !ok { - return v3Entities.Tick{}, fmt.Errorf("can not convert liquidityNet string to bigInt, tick: %v", t.TickIdx) + liquidityNet, err := int256.FromDec(t.LiquidityNet) + if err != nil { + return v3Entities.Tick{}, fmt.Errorf("can not convert liquidityNet string to uint256, tick: %v", t.TickIdx) } tickIdx, err := strconv.Atoi(t.TickIdx) @@ -131,7 +129,7 @@ type TimepointRPC struct { } type Extra struct { - Liquidity *big.Int `json:"liq"` + Liquidity *uint256.Int `json:"liq"` GlobalState GlobalState `json:"gS"` Ticks []v3Entities.Tick `json:"ticks"` TickSpacing int32 `json:"tS"` @@ -170,8 +168,9 @@ type StaticExtra struct { // StateUpdate to be returned instead of updating state when calculating amountOut type StateUpdate struct { - Liquidity *uint256.Int - GlobalState GlobalState + Liquidity *uint256.Int + Price *uint256.Int + Tick int32 } type PoolMeta struct { @@ -198,12 +197,12 @@ type FeesAmount struct { } type SwapCalculationCache struct { + amountRequiredInitial *uint256.Int // The initial value of the exact input/output amount + amountCalculated *uint256.Int // The additive amount of total output/input calculated through the swap + pluginFee *uint256.Int // The plugin fee communityFee *uint256.Int // The community fee of the selling token, uint256 to minimize casts - amountRequiredInitial *int256.Int // The initial value of the exact input/output amount - amountCalculated *int256.Int // The additive amount of total output/input calculated through the swap + fee uint64 // The current fee value in hundredths of a bip, i.e. 1e-6 exactInput bool // Whether the exact input or output is specified - fee uint32 // The current fee value in hundredths of a bip, i.e. 1e-6 - pluginFee uint32 // The plugin fee } type PriceMovementCache struct {