Skip to content

Commit

Permalink
fix/algebra integral concurrent map access (#663)
Browse files Browse the repository at this point in the history
* fix: algebra integral concurrent map access

* refactor: simplify lock logic to Once logic

* remove unused const

* use IsZero instead

* refactor: more optimization for algebra integral

* refactor: remove a bit more unused consts

* refactor: tiny bit more optimization

---------

Co-authored-by: sunspirit99 <[email protected]>
  • Loading branch information
NgoKimPhu and sunspirit99 authored Dec 26, 2024
1 parent 44016de commit 5c87c68
Show file tree
Hide file tree
Showing 108 changed files with 778 additions and 927 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/KyberNetwork/logger v0.2.1
github.com/KyberNetwork/msgpack/v5 v5.4.2
github.com/KyberNetwork/pancake-v3-sdk v0.2.0
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.0
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.3
github.com/daoleno/uniswap-sdk-core v0.1.7
github.com/daoleno/uniswapv3-sdk v0.4.0
github.com/davecgh/go-spew v1.1.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ github.com/KyberNetwork/uniswap-sdk-core v0.1.7 h1:9CfjLkFZcOxgu0US0ZB1vvdgA1sp3
github.com/KyberNetwork/uniswap-sdk-core v0.1.7/go.mod h1:DPzL8zNicstPzvX74ZeeHsiIUquZRpwviceDHQ8+UQ4=
github.com/KyberNetwork/uniswapv3-sdk v0.5.5 h1:PLkDL8YWgN00yfNGbQpOGRcKRD3eoFXOFzjvuHP9Gmo=
github.com/KyberNetwork/uniswapv3-sdk v0.5.5/go.mod h1:XFySgQXZ+dl0yRze6Rj8jloILcFP8FbXJjcyk/1RmSU=
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.0 h1:AkEmCEBmyYAUU+WHGrgw9uaXHteqNn66CD36FaXoUig=
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.0/go.mod h1:VQNKmTwlX6gyW1gzaSQjYG+xCBpqmgH0VQslYMtqzAg=
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.3 h1:J0odlUz0NVgtawgnanos/lkYUCcs0o76j3JY9pvURCo=
github.com/KyberNetwork/uniswapv3-sdk-uint256 v0.5.3/go.mod h1:VQNKmTwlX6gyW1gzaSQjYG+xCBpqmgH0VQslYMtqzAg=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
Expand Down
120 changes: 54 additions & 66 deletions pkg/liquidity-source/algebra/integral/adaptive_fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ import (
// / @notice Calculates fee based on formula:
// / baseFee + sigmoid1(volatility) + sigmoid2(volatility)
// / maximum value capped by baseFee + alpha1 + alpha2
func getFee(volatility *uint256.Int, config FeeConfiguration) uint16 {
func getFee(volatility *uint256.Int, config *DynamicFeeConfig) uint16 {
// normalize for 15 sec interval
normalizedVolatility := new(uint256.Int).Div(volatility, uFIFTEEN)

sumOfSigmoids := new(uint256.Int).Add(
sigmoid(normalizedVolatility, config.Gamma1, config.Alpha1, uint256.NewInt(uint64(config.Beta1))),
sigmoid(normalizedVolatility, config.Gamma2, config.Alpha2, uint256.NewInt(uint64(config.Beta2))),
sigmoid(normalizedVolatility, config.Gamma1, config.Alpha1, config.Beta1),
sigmoid(normalizedVolatility, config.Gamma2, config.Alpha2, config.Beta2),
)

result := new(uint256.Int).Add(
new(uint256.Int).SetUint64(uint64(config.BaseFee)),
result := normalizedVolatility.Add(
normalizedVolatility.SetUint64(uint64(config.BaseFee)),
sumOfSigmoids,
)

if result.Cmp(MAX_UINT16) > 0 { // should always be true
if result.BitLen() > 15 { // should not happen
panic("Fee calculation exceeded uint16 max value")
}

Expand All @@ -31,105 +31,93 @@ func getFee(volatility *uint256.Int, config FeeConfiguration) uint16 {
// / @notice calculates α / (1 + e^( (β-x) / γ))
// / that is a sigmoid with a maximum value of α, x-shifted by β, and stretched by γ
// / @dev returns uint256 for fuzzy testing. Guaranteed that the result is not greater than alpha
func sigmoid(x *uint256.Int, g uint16, alpha uint16, beta *uint256.Int) *uint256.Int {
// Determine if x is greater than beta
comparison := x.Cmp(beta)

var res *uint256.Int
if comparison > 0 {
// x > beta case
x.Sub(x, beta)

func sigmoid(x *uint256.Int, gU16 uint16, alpha uint16, beta uint32) *uint256.Int {
x.SubUint64(x, uint64(beta))
g := uint64(gU16)
g4 := g*g*g*g
var tmp, res uint256.Int
if x.Sign() > 0 {
// If x >= 6*g, return alpha
sixTimesG := new(uint256.Int).Mul(uSIX, uint256.NewInt(uint64(g)))
if x.Cmp(sixTimesG) >= 0 {
return new(uint256.Int).SetUint64(uint64(alpha))
if x.CmpUint64(6*g) >= 0 {
return tmp.SetUint64(uint64(alpha))
}

g4 := new(uint256.Int).Exp(uint256.NewInt(uint64(g)), uFOUR)
ex := expXg4(x, g, g4)
ex := expXg4(x, g)

// (alpha * ex) / (g4 + ex)
numerator := new(uint256.Int).Mul(uint256.NewInt(uint64(alpha)), ex)
denominator := new(uint256.Int).Add(g4, ex)
res = new(uint256.Int).Div(numerator, denominator)
numerator := res.Mul(res.SetUint64(uint64(alpha)), ex)
denominator := tmp.AddUint64(ex, g4)
return res.Div(numerator, denominator)
} else {
// x <= beta case
x.Sub(beta, x)
x.Abs(x)

// If x >= 6*g, return 0
sixTimesG := new(uint256.Int).Mul(uSIX, uint256.NewInt(uint64(g)))
if x.Cmp(sixTimesG) >= 0 {
if x.CmpUint64(6*g) >= 0 {
return uZERO
}

g4 := new(uint256.Int).Exp(uint256.NewInt(uint64(g)), uFOUR)
ex := new(uint256.Int).Add(g4, expXg4(x, g, g4))
ex := expXg4(x, g)
ex.AddUint64(ex, g4)

// (alpha * g4) / ex
numerator := new(uint256.Int).Mul(uint256.NewInt(uint64(alpha)), g4)
res = new(uint256.Int).Div(numerator, ex)
numerator := res.Mul(res.SetUint64(uint64(alpha)), tmp.SetUint64(g4))
return res.Div(numerator, ex)
}

return res
}

func expXg4(x *uint256.Int, g uint16, gHighestDegree *uint256.Int) *uint256.Int {
func expXg4(x *uint256.Int, g uint64) *uint256.Int {
var closestValue *uint256.Int
var tmp uint256.Int

gBig := uint256.NewInt(uint64(g))
gU := uint256.NewInt(g)

// Predefined e values multiplied by 10^20
xdg := new(uint256.Int).Div(x, gBig)
xdg := tmp.Div(x, gU)
switch xdg.Uint64() {
case 0:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_0)
closestValue = CLOSEST_VALUE_0
case 1:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_1)
closestValue = CLOSEST_VALUE_1
case 2:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_2)
closestValue = CLOSEST_VALUE_2
case 3:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_3)
closestValue = CLOSEST_VALUE_3
case 4:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_4)
closestValue = CLOSEST_VALUE_4
default:
closestValue = uint256.MustFromBig(CLOSEST_VALUE_DEFAULT)
closestValue = CLOSEST_VALUE_DEFAULT
}

x.Mod(x, gBig)
x.Mod(x, gU)

gDiv2 := new(uint256.Int).Div(gBig, uTWO)
if x.Cmp(gDiv2) >= 0 {
if x.CmpUint64(g/2) >= 0 {
// (x - closestValue) >= 0.5, so closestValue := closestValue * e^0.5
x.Sub(x, gDiv2)
closestValue.Mul(closestValue, uint256.MustFromBig(E_HALF_MULTIPLIER)).Div(closestValue, uint256.MustFromBig(E_MULTIPLIER_BIG))
x.SubUint64(x, g/2)
closestValue.Mul(closestValue, E_HALF_MULTIPLIER).Div(closestValue, E_MULTIPLIER_BIG)
}

// After calculating the closestValue x/g is <= 0.5, so that the series in the neighborhood of zero converges with sufficient speed
xLowestDegree := new(uint256.Int).Set(x)
res := new(uint256.Int).Set(gHighestDegree) // g**4
var xLowestDegree uint256.Int
xLowestDegree.Set(x)
res := uint256.NewInt(g*g*g*g) // g**4

gHighestDegree.Div(gHighestDegree, gBig) // g**3
res.Add(res, new(uint256.Int).Mul(xLowestDegree, gHighestDegree)) // g**4 + x*g**3
res.Add(res, tmp.Mul(&xLowestDegree, tmp.SetUint64(g*g*g))) // g**4 + x*g**3

gHighestDegree.Div(gHighestDegree, gBig) // g**2
xLowestDegree.Mul(xLowestDegree, x) // x**2
res.Add(res, new(uint256.Int).Div(
new(uint256.Int).Mul(xLowestDegree, gHighestDegree),
xLowestDegree.Mul(&xLowestDegree, x) // x**2
res.Add(res, tmp.Div(
tmp.Mul(&xLowestDegree, tmp.SetUint64(g*g)),
uTWO,
))

gHighestDegree.Div(gHighestDegree, gBig) // g
xLowestDegree.Mul(xLowestDegree, x) // x**3
res.Add(res, new(uint256.Int).Div(
new(uint256.Int).Add(
new(uint256.Int).Mul(xLowestDegree, new(uint256.Int).Mul(gBig, uFOUR)),
new(uint256.Int).Mul(xLowestDegree, x),
)) // g**4 + x * g**3 + (x**2 * g**2) / 2, res < 71

xLowestDegree.Mul(&xLowestDegree, x) // x**3
res.Add(res, tmp.Div(
tmp.Add(
tmp.Mul(&xLowestDegree, tmp.SetUint64(g*4)),
xLowestDegree.Mul(&xLowestDegree, x),
),
uTWENTYFOUR,
))
)) // g^4 + x * g^3 + (x^2 * g^2)/2 + x^3(g*4 + x)/24, res < 73

res.Mul(res, closestValue).Div(res, uint256.MustFromBig(E_MULTIPLIER_BIG))
res.Mul(res, closestValue).Div(res, E_MULTIPLIER_BIG)

return res
}
50 changes: 16 additions & 34 deletions pkg/liquidity-source/algebra/integral/constant.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package integral

import (
"math/big"
"time"

"github.com/KyberNetwork/int256"
"github.com/holiman/uint256"
)

Expand All @@ -24,13 +22,10 @@ const (
WINDOW = 86400 // 1 day in seconds
UINT16_MODULO = 65536

poolTicksMethod = "ticks"
poolLiquidityMethod = "liquidity"
poolGlobalStateMethod = "globalState"
poolTickSpacingMethod = "tickSpacing"
PoolPrevTickGlobalMethod = "prevTickGlobal"
PoolNextTickGlobalMethod = "nextTickGlobal"
poolPluginMethod = "plugin"
poolLiquidityMethod = "liquidity"
poolGlobalStateMethod = "globalState"
poolTickSpacingMethod = "tickSpacing"
poolPluginMethod = "plugin"

dynamicFeeManagerPluginFeeConfigMethod = "feeConfig"

Expand Down Expand Up @@ -59,42 +54,29 @@ var (
FEE_DENOMINATOR = uint256.NewInt(1e6)
COMMUNITY_FEE_DENOMINATOR = uint256.NewInt(1e3)

ZERO = int256.NewInt(0)
ONE = int256.NewInt(1)

MIN_INT256 = new(int256.Int).Neg(new(int256.Int).Lsh(ZERO, 255)) // -2^255
MAX_UINT256 = new(uint256.Int).Sub(new(uint256.Int).Lsh(uZERO, 256), uONE)

MAX_UINT16 = new(uint256.Int).SetUint64(1<<16 - 1)
MIN_UINT16 = new(uint256.Int).SetUint64(1)

uZERO = uint256.NewInt(0)
uONE = uint256.NewInt(1)
uTWO = uint256.NewInt(2)
uFOUR = uint256.NewInt(4)
uSIX = uint256.NewInt(6)
uEIGHT = uint256.NewInt(8)
uFIFTEEN = uint256.NewInt(15)
uSIXTEEN = uint256.NewInt(16)
uTWENTYFOUR = uint256.NewInt(24)

MIN_SQRT_RATIO = big.NewInt(4295128739)
MAX_SQRT_RATIO, _ = new(big.Int).SetString("1461446703485210103287273052203988822378723970342", 10)
MIN_SQRT_RATIO = uint256.NewInt(4295128739)
MAX_SQRT_RATIO, _ = uint256.FromDecimal("1461446703485210103287273052203988822378723970342")

Q96 = new(uint256.Int).Lsh(uONE, 96) // 1 << 96
Q128 = new(uint256.Int).Lsh(uONE, 128) // 1 << 128
Q96 = new(uint256.Int).Lsh(uONE, 96) // 1 << 96

BASE_FEE_MULTIPLIER = new(uint256.Int).Lsh(uONE, FEE_FACTOR_SHIFT) // 1 << 96
DOUBLE_FEE_MULTIPLIER = new(uint256.Int).Lsh(uONE, 2*FEE_FACTOR_SHIFT) // 1 << 2*96

// Predefined e values multiplied by 10^20 as constants
CLOSEST_VALUE_0, _ = new(big.Int).SetString("100000000000000000000", 10) // 1
CLOSEST_VALUE_1, _ = new(big.Int).SetString("271828182845904523536", 10) // ~= e
CLOSEST_VALUE_2, _ = new(big.Int).SetString("738905609893065022723", 10) // ~= e^2
CLOSEST_VALUE_3, _ = new(big.Int).SetString("2008553692318766774092", 10) // ~= e^3
CLOSEST_VALUE_4, _ = new(big.Int).SetString("5459815003314423907811", 10) // ~= e^4
CLOSEST_VALUE_DEFAULT, _ = new(big.Int).SetString("14841315910257660342111", 10) // ~= e^5

E_HALF_MULTIPLIER, _ = new(big.Int).SetString("164872127070012814684", 10) // e^0.5
E_MULTIPLIER_BIG, _ = new(big.Int).SetString("100000000000000000000", 10) // 1e20
CLOSEST_VALUE_0, _ = uint256.FromDecimal("100000000000000000000") // 1
CLOSEST_VALUE_1, _ = uint256.FromDecimal("271828182845904523536") // ~= e
CLOSEST_VALUE_2, _ = uint256.FromDecimal("738905609893065022723") // ~= e^2
CLOSEST_VALUE_3, _ = uint256.FromDecimal("2008553692318766774092") // ~= e^3
CLOSEST_VALUE_4, _ = uint256.FromDecimal("5459815003314423907811") // ~= e^4
CLOSEST_VALUE_DEFAULT, _ = uint256.FromDecimal("14841315910257660342111") // ~= e^5

E_HALF_MULTIPLIER, _ = uint256.FromDecimal("164872127070012814684") // e^0.5
E_MULTIPLIER_BIG, _ = uint256.FromDecimal("100000000000000000000") // 1e20
)
8 changes: 4 additions & 4 deletions pkg/liquidity-source/algebra/integral/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

var (
ErrStaleTimepoints = errors.New("getting stale timepoint data")
ErrTickNil = errors.New("tick is nil")
ErrTicksEmpty = errors.New("ticks list is empty")
ErrInvalidToken = errors.New("invalid token info")
ErrZeroAmountOut = errors.New("amountOut is 0")
Expand All @@ -19,7 +18,8 @@ var (
ErrInvalidAmountRequired = errors.New("invalid amount required")
ErrZeroAmountRequired = errors.New("zero amount required")

ErrOutOfRangeOrInvalid = errors.New("value out of range or invalid")
ErrLiquiditySub = errors.New("liquidity sub error")
ErrLiquidityAdd = errors.New("liquidity add error")
ErrLiquiditySub = errors.New("liquidity sub error")
ErrLiquidityAdd = errors.New("liquidity add error")
ErrOverflow = errors.New("overflow")
ErrUnderflow = errors.New("underflow")
)
16 changes: 4 additions & 12 deletions pkg/liquidity-source/algebra/integral/math.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package integral

import (
"errors"

"github.com/KyberNetwork/int256"
v3Utils "github.com/KyberNetwork/uniswapv3-sdk-uint256/utils"
"github.com/holiman/uint256"
)

func unsafeDivRoundingUp(x, y *uint256.Int) (*uint256.Int, error) {
if y.Sign() == 0 {
return nil, errors.New("division by zero")
}

quotient := new(uint256.Int).Div(x, y)

remainder := new(uint256.Int).Mod(x, y)
func unsafeDivRoundingUp(x, y *uint256.Int) *uint256.Int {
quotient, remainder := new(uint256.Int).DivMod(x, y, new(uint256.Int))
if remainder.Sign() > 0 {
quotient.Add(quotient, uONE)
quotient.AddUint64(quotient, 1)
}

return quotient, nil
return quotient
}

func addDelta(x *uint256.Int, y *int256.Int) (*uint256.Int, error) {
Expand Down
Loading

0 comments on commit 5c87c68

Please sign in to comment.