Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

u256 #118

Merged
merged 23 commits into from
Mar 8, 2024
Merged

u256 #118

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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