Skip to content

Commit

Permalink
Merge pull request #118 from gnoswap-labs/mconcat/uint256
Browse files Browse the repository at this point in the history
u256
  • Loading branch information
r3v4s authored Mar 8, 2024
2 parents 858bbf4 + 4e7e4ca commit d35429d
Show file tree
Hide file tree
Showing 40 changed files with 3,897 additions and 1,183 deletions.
65 changes: 40 additions & 25 deletions common/liquidity_amounts.gno
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package common

import (
"gno.land/p/demo/u256"
"gno.land/r/demo/consts"
)

// toAscendingOrder checkes if the first value is greater than
// the second then swaps two values.
func toAscendingOrder(a, b bigint) (bigint, bigint) {
if a > b {
func toAscendingOrder(a, b *u256.Uint) (*u256.Uint, *u256.Uint) {
if a.Gt(b) {
return b, a
}

Expand All @@ -16,40 +17,47 @@ func toAscendingOrder(a, b bigint) (bigint, bigint) {

// calcIntermediateValue computes the intermediate value
// used in liquidity calculations.
func calcIntermediateValue(sqrtRatioAX96, sqrtRatioBX96 bigint) bigint {
return (sqrtRatioAX96 * sqrtRatioBX96) / consts.Q96
func calcIntermediateValue(sqrtRatioAX96, sqrtRatioBX96 *u256.Uint) *u256.Uint {
result := new(u256.Uint).Mul(sqrtRatioAX96, sqrtRatioBX96)
result.Div(result, u256.FromBigint(consts.Q96))

return result
}

// computeLiquidityForAmount0 calculates liquidity for a given amount of token 0.
func computeLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0 bigint) bigint {
func computeLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0 *u256.Uint) *u256.Uint {
sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96)
intermediate := calcIntermediateValue(sqrtRatioAX96, sqrtRatioBX96)
diff := sqrtRatioBX96 - sqrtRatioAX96
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

// we don't need to care about division by zero here.
return amount0 * intermediate / diff
result := new(u256.Uint).Mul(amount0, intermediate)
result.Div(result, diff)
return result
}

// computeLiquidityForAmount1 calculates liquidity for a given amount of token 1.
func computeLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1 bigint) bigint {
func computeLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1 *u256.Uint) *u256.Uint {
sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96)
diff := sqrtRatioBX96 - sqrtRatioAX96
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

return (amount1 * consts.Q96) / diff
result := new(u256.Uint).Mul(amount1, u256.FromBigint(consts.Q96))
result.Div(result, diff)
return result
}

// GetLiquidityForAmounts calculates the liquidity for given amounts od token 0 and token 1.
func GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1 bigint) bigint {
func GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0, amount1 *u256.Uint) *u256.Uint {
sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96)
var liquidity bigint
var liquidity *u256.Uint

if sqrtRatioX96 <= sqrtRatioAX96 {
if sqrtRatioX96.Lte(sqrtRatioAX96) {
liquidity = computeLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0)
} else if sqrtRatioX96 < sqrtRatioBX96 {
liquidity0 := computeLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0)
liquidity1 := computeLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1)

if liquidity0 < liquidity1 {
if liquidity0.Lt(liquidity1) {
liquidity = liquidity0
} else {
liquidity = liquidity1
Expand All @@ -63,32 +71,39 @@ func GetLiquidityForAmounts(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, amount0,
}

// computeAmount0ForLiquidity calculates the amount of token 0 for a given liquidity.
func computeAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity bigint) bigint {
func computeAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) *u256.Uint {
sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96)
diff := sqrtRatioBX96 - sqrtRatioAX96
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

numerator := new(u256.Uint).Lsh(liquidity, 96)
numerator.Mul(numerator, diff)

return (liquidity << 96) * diff / (sqrtRatioBX96 * sqrtRatioAX96)
denominator := new(u256.Uint).Mul(sqrtRatioBX96, sqrtRatioAX96)

return numerator.Div(numerator, denominator)
}

// computeAmount1ForLiquidity calculates the amount of token 1 for a given liquidity.
func computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity bigint) bigint {
func computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) *u256.Uint {
sqrtRatioAX96, sqrtRatioBX96 = toAscendingOrder(sqrtRatioAX96, sqrtRatioBX96)
diff := sqrtRatioBX96 - sqrtRatioAX96
diff := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96)

return liquidity * diff / consts.Q96
result := new(u256.Uint).Mul(liquidity, diff)
result.Div(result, u256.FromBigint(consts.Q96))
return result
}

// GetAmountsForLiquidity calculates the amounts of token 0 and token 1 for a given liquidity.
func GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity bigint) (bigint, bigint) {
var amount0, amount1 bigint
func GetAmountsForLiquidity(sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96, liquidity *u256.Uint) (*u256.Uint, *u256.Uint) {
var amount0, amount1 *u256.Uint

if sqrtRatioAX96 > sqrtRatioBX96 {
if sqrtRatioAX96.Gt(sqrtRatioBX96) {
sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96
}

if sqrtRatioX96 <= sqrtRatioAX96 {
if sqrtRatioX96.Lte(sqrtRatioAX96) {
amount0 = computeAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity)
} else if sqrtRatioX96 < sqrtRatioBX96 {
} else if sqrtRatioX96.Lt(sqrtRatioBX96) {
amount0 = computeAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity)
amount1 = computeAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity)
} else {
Expand Down
168 changes: 88 additions & 80 deletions common/tick_math.gno
Original file line number Diff line number Diff line change
@@ -1,81 +1,82 @@
package common

import (
"gno.land/p/demo/u256"
"gno.land/p/demo/ufmt"
"gno.land/r/demo/consts"
)

var tickRatioMap = map[bigint]bigint{
0x1: 0xfffcb933bd6fad37aa2d162d1a594001,
0x2: 0xfff97272373d413259a46990580e213a,
0x4: 0xfff2e50f5f656932ef12357cf3c7fdcc,
0x8: 0xffe5caca7e10e4e61c3624eaa0941cd0,
0x10: 0xffcb9843d60f6159c9db58835c926644,
0x20: 0xff973b41fa98c081472e6896dfb254c0,
0x40: 0xff2ea16466c96a3843ec78b326b52861,
0x80: 0xfe5dee046a99a2a811c461f1969c3053,
0x100: 0xfcbe86c7900a88aedcffc83b479aa3a4,
0x200: 0xf987a7253ac413176f2b074cf7815e54,
0x400: 0xf3392b0822b70005940c7a398e4b70f3,
0x800: 0xe7159475a2c29b7443b29c7fa6e889d9,
0x1000: 0xd097f3bdfd2022b8845ad8f792aa5825,
0x2000: 0xa9f746462d870fdf8a65dc1f90e061e5,
0x4000: 0x70d869a156d2a1b890bb3df62baf32f7,
0x8000: 0x31be135f97d08fd981231505542fcfa6,
0x10000: 0x9aa508b5b7a84e1c677de54f3e99bc9,
0x20000: 0x5d6af8dedb81196699c329225ee604,
0x40000: 0x2216e584f5fa1ea926041bedfe98,
0x80000: 0x48a170391f7dc42444e8fa2,
var tickRatioMap = map[uint64]*u256.Uint{
0x1: u256.FromBigint(0xfffcb933bd6fad37aa2d162d1a594001),
0x2: u256.FromBigint(0xfff97272373d413259a46990580e213a),
0x4: u256.FromBigint(0xfff2e50f5f656932ef12357cf3c7fdcc),
0x8: u256.FromBigint(0xffe5caca7e10e4e61c3624eaa0941cd0),
0x10: u256.FromBigint(0xffcb9843d60f6159c9db58835c926644),
0x20: u256.FromBigint(0xff973b41fa98c081472e6896dfb254c0),
0x40: u256.FromBigint(0xff2ea16466c96a3843ec78b326b52861),
0x80: u256.FromBigint(0xfe5dee046a99a2a811c461f1969c3053),
0x100: u256.FromBigint(0xfcbe86c7900a88aedcffc83b479aa3a4),
0x200: u256.FromBigint(0xf987a7253ac413176f2b074cf7815e54),
0x400: u256.FromBigint(0xf3392b0822b70005940c7a398e4b70f3),
0x800: u256.FromBigint(0xe7159475a2c29b7443b29c7fa6e889d9),
0x1000: u256.FromBigint(0xd097f3bdfd2022b8845ad8f792aa5825),
0x2000: u256.FromBigint(0xa9f746462d870fdf8a65dc1f90e061e5),
0x4000: u256.FromBigint(0x70d869a156d2a1b890bb3df62baf32f7),
0x8000: u256.FromBigint(0x31be135f97d08fd981231505542fcfa6),
0x10000: u256.FromBigint(0x9aa508b5b7a84e1c677de54f3e99bc9),
0x20000: u256.FromBigint(0x5d6af8dedb81196699c329225ee604),
0x40000: u256.FromBigint(0x2216e584f5fa1ea926041bedfe98),
0x80000: u256.FromBigint(0x48a170391f7dc42444e8fa2),
}

var binaryLogConsts = [8]bigint{
0x0,
0x3,
0xF,
0xFF,
0xFFFF,
0xFFFFFFFF,
0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
var binaryLogConsts = [8]*u256.Uint{
u256.FromBigint(0x0),
u256.FromBigint(0x3),
u256.FromBigint(0xF),
u256.FromBigint(0xFF),
u256.FromBigint(0xFFFF),
u256.FromBigint(0xFFFFFFFF),
u256.FromBigint(0xFFFFFFFFFFFFFFFF),
u256.FromBigint(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF),
}

func TickMathGetSqrtRatioAtTick(tick int32) bigint {
func TickMathGetSqrtRatioAtTick(tick int32) *u256.Uint {
absTick := absTick(tick)
require(
absTick <= bigint(consts.MAX_TICK),
absTick <= uint64(consts.MAX_TICK),
ufmt.Sprintf(
"[POOL] tick_math.gno__TickMathGetSqrtRatioAtTick() || absTick(%d) <= consts.MAX_TICK(%d)",
absTick, consts.MAX_TICK,
),
)

ratio := consts.Q128
ratio := u256.FromBigint(consts.Q128)
for mask, value := range tickRatioMap {
if absTick&mask != 0 {
ratio = (ratio * value) >> 128
ratio.Mul(ratio, value)
ratio.Rsh(ratio, 128)
}
}

if tick > 0 {
ratio = consts.MAX_UINT256 / ratio
ratio.Div(u256.FromBigint(consts.Q96), ratio)
}

shifted := ratio >> 32
remainder := ratio % (1 << 32)
shifted := new(u256.Uint).Rsh(ratio, 32)
remainder := new(u256.Uint).Mod(ratio, u256.NewUint(1<<32))

if shifted+remainder == 0 {
return shifted + 0
if new(u256.Uint).Add(shifted, remainder).IsZero() {
return shifted
}

return shifted + 1
return shifted.Add(shifted, u256.One())
}

func TickMathGetTickAtSqrtRatio(sqrtPriceX96 bigint) int32 {
func TickMathGetTickAtSqrtRatio(sqrtPriceX96 *u256.Uint) int32 {
require(
sqrtPriceX96 >= consts.MIN_SQRT_RATIO && sqrtPriceX96 < consts.MAX_SQRT_RATIO,
ufmt.Sprintf("[POOL] sqrtPriceX96(%d) is out of range [%d, %d)", sqrtPriceX96, consts.MIN_SQRT_RATIO, consts.MAX_SQRT_RATIO),
)
ratio := sqrtPriceX96 << 32
sqrtPriceX96.Gte(u256.FromBigint(consts.MIN_SQRT_RATIO)) && sqrtPriceX96.Lt(u256.FromBigint(consts.MAX_SQRT_RATIO)),
ufmt.Sprintf("[POOL] sqrtPriceX96(%d) is out of range [%d, %d)", sqrtPriceX96.Dec(), consts.MIN_SQRT_RATIO, consts.MAX_SQRT_RATIO))
ratio := new(u256.Uint).Lsh(sqrtPriceX96, 32)

msb, adjustedRatio := findMSB(ratio)
adjustedRatio = adjustRatio(ratio, msb)
Expand All @@ -87,19 +88,19 @@ func TickMathGetTickAtSqrtRatio(sqrtPriceX96 bigint) int32 {
}

// findMSB computes the MSB (most significant bit) of the given ratio.
func findMSB(ratio bigint) (bigint, bigint) {
msb := bigint(0)
func findMSB(ratio *u256.Uint) (uint64, *u256.Uint) {
msb := uint64(0)

for i := 7; i >= 1; i-- {
f := gt(ratio, binaryLogConsts[i]) << i
msb = msb | bigint(f)
ratio = ratio >> f
msb = msb | f
ratio.Rsh(ratio, uint(f))
}

// handle the remaining bits
{
f := gt(ratio, 0x1)
msb = msb | bigint(f)
f := gt(ratio, u256.One())
msb = msb | f
}

return msb, ratio
Expand All @@ -108,32 +109,35 @@ func findMSB(ratio bigint) (bigint, bigint) {
// adjustRatio adjusts the given ratio based on the MSB found.
//
// This adjustment ensures that the ratio falls within the specific range.
func adjustRatio(ratio, msb bigint) bigint {
func adjustRatio(ratio *u256.Uint, msb uint64) *u256.Uint {
if msb >= 128 {
return ratio >> uint64(msb-127)
return new(u256.Uint).Rsh(ratio, uint(msb-127))
}

return ratio << uint64(127-msb)
return new(u256.Uint).Lsh(ratio, uint(127-msb))
}

// calculateLog2 calculates the binary logarith, of the adjusted ratio using a fixed-point arithmetic.
//
// This function iteratively squares the ratio and adjusts the result to compute the log base 2, which will determine the tick value.
func calculateLog2(msb, ratio bigint) bigint {
log_2 := (msb - 128) << 64
func calculateLog2(msb uint64, ratio *u256.Uint) *u256.Int {
log_2 := u256.NewInt(int64(msb - 128))
log_2.Lsh(log_2, 64)

for i := 63; i >= 51; i-- {
ratio = ratio * ratio >> 127
f := ratio >> 128
log_2 = log_2 | (f << i)
ratio = ratio >> uint64(f)
ratio.Mul(ratio, ratio)
ratio.Rsh(ratio, 127)
f := new(u256.Uint).Rsh(ratio, 128)
log_2.Or(log_2, new(u256.Uint).Lsh(f, uint(i)).Int())
ratio.Rsh(ratio, uint(f.Uint64())) // XXXXXXXXX possibly overflow
}

// handle the remaining bits
{
ratio = ratio * ratio >> 127
f := ratio >> 128
log_2 = log_2 | (f << 50)
ratio.Mul(ratio, ratio)
ratio.Rsh(ratio, 127)
f := new(u256.Uint).Rsh(ratio, 128)
log_2.Or(log_2, new(u256.Uint).Lsh(f, uint(50)).Int())
}

return log_2
Expand All @@ -143,23 +147,27 @@ func calculateLog2(msb, ratio bigint) bigint {
//
// It calculates the upper and lower bounds for each tick, and selects the appropriate tock value
// based on the given sqrtPriceX96.
func getTickValue(log2, sqrtPriceX96 bigint) int32 {
func getTickValue(log2 *u256.Int, sqrtPriceX96 *u256.Uint) int32 {
// ref: https://github.com/Uniswap/v3-core/issues/500
// 2^64 / log2 (√1.0001) = 255738958999603826347141
log_sqrt10001 := log2 * 255738958999603826347141
log_sqrt10001 := new(u256.Int).Mul(log2, u256.FromBigint(255738958999603826347141).Int())

// ref: https://ethereum.stackexchange.com/questions/113844/how-does-uniswap-v3s-logarithm-library-tickmath-sol-work/113912#113912
// 0.010000497 x 2^128 = 3402992956809132418596140100660247210
tickLow := int32(int64((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128))
tickLow256 := new(u256.Int).Sub(log_sqrt10001, u256.FromBigint(3402992956809132418596140100660247210).Int())
tickLow256.Rsh(tickLow256, 128)
tickLow := int32(tickLow256.Int64()) // XXXXX: needs to be checked if bound

// ref: https://ethereum.stackexchange.com/questions/113844/how-does-uniswap-v3s-logarithm-library-tickmath-sol-work/113912#113912
// 0.856 x 2^128 = 291339464771989622907027621153398088495
tickHi := int32(int64((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128))
tickHi256 := new(u256.Int).Add(log_sqrt10001, u256.FromBigint(291339464771989622907027621153398088495).Int())
tickHi256.Rsh(tickHi256, 128)
tickHi := int32(tickHi256.Int64()) // XXXXX: needs to be checked if bound

var tick int32
if tickLow == tickHi {
tick = tickLow
} else if TickMathGetSqrtRatioAtTick(tickHi) <= sqrtPriceX96 {
} else if TickMathGetSqrtRatioAtTick(tickHi).Lte(sqrtPriceX96) {
tick = tickHi
} else {
tick = tickLow
Expand All @@ -168,24 +176,24 @@ func getTickValue(log2, sqrtPriceX96 bigint) int32 {
return tick
}

func gt(x, y bigint) uint64 {
if x > y {
func gt(x, y *u256.Uint) uint64 {
if x.Gt(y) {
return 1
} else {
return 0
}

return 0
}

func absTick(n int32) bigint {
if n < 0 {
return -bigint(n)
}

return bigint(n)
}

func require(condition bool, message string) {
if !condition {
panic(message)
}
}

func absTick(n int32) uint64 {
if n < 0 {
return uint64(-n)
}

return uint64(n)
}
Loading

0 comments on commit d35429d

Please sign in to comment.