From 1d3b58a5bc2b87ee2bf8232e9b1a9d0d98dd7101 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 19:13:46 +0900 Subject: [PATCH 01/23] u256 --- u256/bits.gno | 601 +++++++++++++++++++++++ u256/bits_errors.gno | 17 + u256/bits_table.gno | 79 +++ u256/gno.mod | 1 + u256/u256.gno | 1096 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1794 insertions(+) create mode 100644 u256/bits.gno create mode 100644 u256/bits_errors.gno create mode 100644 u256/bits_table.gno create mode 100644 u256/gno.mod create mode 100644 u256/u256.gno diff --git a/u256/bits.gno b/u256/bits.gno new file mode 100644 index 00000000..7195d38f --- /dev/null +++ b/u256/bits.gno @@ -0,0 +1,601 @@ + + +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:generate go run make_tables.go + +// Package bits implements bit counting and manipulation +// functions for the predeclared unsigned integer types. +// +// Functions in this package may be implemented directly by +// the compiler, for better performance. For those functions +// the code in this package will not be used. Which +// functions are implemented by the compiler depends on the +// architecture and the Go release. +package u256 + +const uintSize = 32 << (^uint(0) >> 63) // 32 or 64 + +// UintSize is the size of a uint in bits. +const UintSize = uintSize + +// --- LeadingZeros --- + +// LeadingZeros returns the number of leading zero bits in x; the result is [UintSize] for x == 0. +func LeadingZeros(x uint) int { return UintSize - Len(x) } + +// LeadingZeros8 returns the number of leading zero bits in x; the result is 8 for x == 0. +func LeadingZeros8(x uint8) int { return 8 - Len8(x) } + +// LeadingZeros16 returns the number of leading zero bits in x; the result is 16 for x == 0. +func LeadingZeros16(x uint16) int { return 16 - Len16(x) } + +// LeadingZeros32 returns the number of leading zero bits in x; the result is 32 for x == 0. +func LeadingZeros32(x uint32) int { return 32 - Len32(x) } + +// LeadingZeros64 returns the number of leading zero bits in x; the result is 64 for x == 0. +func LeadingZeros64(x uint64) int { return 64 - Len64(x) } + +// --- TrailingZeros --- + +// See http://supertech.csail.mit.edu/papers/debruijn.pdf +const deBruijn32 = 0x077CB531 + +var deBruijn32tab = [32]byte{ + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9, +} + +const deBruijn64 = 0x03f79d71b4ca8b09 + +var deBruijn64tab = [64]byte{ + 0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, + 62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, + 63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11, + 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, +} + +// TrailingZeros returns the number of trailing zero bits in x; the result is [UintSize] for x == 0. +func TrailingZeros(x uint) int { + if UintSize == 32 { + return TrailingZeros32(uint32(x)) + } + return TrailingZeros64(uint64(x)) +} + +// TrailingZeros8 returns the number of trailing zero bits in x; the result is 8 for x == 0. +func TrailingZeros8(x uint8) int { + return int(ntz8tab[x]) +} + +// TrailingZeros16 returns the number of trailing zero bits in x; the result is 16 for x == 0. +func TrailingZeros16(x uint16) int { + if x == 0 { + return 16 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[uint32(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros32 returns the number of trailing zero bits in x; the result is 32 for x == 0. +func TrailingZeros32(x uint32) int { + if x == 0 { + return 32 + } + // see comment in TrailingZeros64 + return int(deBruijn32tab[(x&-x)*deBruijn32>>(32-5)]) +} + +// TrailingZeros64 returns the number of trailing zero bits in x; the result is 64 for x == 0. +func TrailingZeros64(x uint64) int { + if x == 0 { + return 64 + } + // If popcount is fast, replace code below with return popcount(^x & (x - 1)). + // + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multiplying by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn (64 bit) constant + // is such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) + return int(deBruijn64tab[(x&-x)*deBruijn64>>(64-6)]) +} + +// --- OnesCount --- + +const m0 = 0x5555555555555555 // 01010101 ... +const m1 = 0x3333333333333333 // 00110011 ... +const m2 = 0x0f0f0f0f0f0f0f0f // 00001111 ... +const m3 = 0x00ff00ff00ff00ff // etc. +const m4 = 0x0000ffff0000ffff + +// OnesCount returns the number of one bits ("population count") in x. +func OnesCount(x uint) int { + if UintSize == 32 { + return OnesCount32(uint32(x)) + } + return OnesCount64(uint64(x)) +} + +// OnesCount8 returns the number of one bits ("population count") in x. +func OnesCount8(x uint8) int { + return int(pop8tab[x]) +} + +// OnesCount16 returns the number of one bits ("population count") in x. +func OnesCount16(x uint16) int { + return int(pop8tab[x>>8] + pop8tab[x&0xff]) +} + +// OnesCount32 returns the number of one bits ("population count") in x. +func OnesCount32(x uint32) int { + return int(pop8tab[x>>24] + pop8tab[x>>16&0xff] + pop8tab[x>>8&0xff] + pop8tab[x&0xff]) +} + +// OnesCount64 returns the number of one bits ("population count") in x. +func OnesCount64(x uint64) int { + // Implementation: Parallel summing of adjacent bits. + // See "Hacker's Delight", Chap. 5: Counting Bits. + // The following pattern shows the general approach: + // + // x = x>>1&(m0&m) + x&(m0&m) + // x = x>>2&(m1&m) + x&(m1&m) + // x = x>>4&(m2&m) + x&(m2&m) + // x = x>>8&(m3&m) + x&(m3&m) + // x = x>>16&(m4&m) + x&(m4&m) + // x = x>>32&(m5&m) + x&(m5&m) + // return int(x) + // + // Masking (& operations) can be left away when there's no + // danger that a field's sum will carry over into the next + // field: Since the result cannot be > 64, 8 bits is enough + // and we can ignore the masks for the shifts by 8 and up. + // Per "Hacker's Delight", the first line can be simplified + // more, but it saves at best one instruction, so we leave + // it alone for clarity. + const m = 1<<64 - 1 + x = x>>1&(m0&m) + x&(m0&m) + x = x>>2&(m1&m) + x&(m1&m) + x = (x>>4 + x) & (m2 & m) + x += x >> 8 + x += x >> 16 + x += x >> 32 + return int(x) & (1<<7 - 1) +} + +// --- RotateLeft --- + +// RotateLeft returns the value of x rotated left by (k mod [UintSize]) bits. +// To rotate x right by k bits, call RotateLeft(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft(x uint, k int) uint { + if UintSize == 32 { + return uint(RotateLeft32(uint32(x), k)) + } + return uint(RotateLeft64(uint64(x), k)) +} + +// RotateLeft8 returns the value of x rotated left by (k mod 8) bits. +// To rotate x right by k bits, call RotateLeft8(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft8(x uint8, k int) uint8 { + const n = 8 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft16 returns the value of x rotated left by (k mod 16) bits. +// To rotate x right by k bits, call RotateLeft16(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft16(x uint16, k int) uint16 { + const n = 16 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft32 returns the value of x rotated left by (k mod 32) bits. +// To rotate x right by k bits, call RotateLeft32(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft32(x uint32, k int) uint32 { + const n = 32 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// RotateLeft64 returns the value of x rotated left by (k mod 64) bits. +// To rotate x right by k bits, call RotateLeft64(x, -k). +// +// This function's execution time does not depend on the inputs. +func RotateLeft64(x uint64, k int) uint64 { + const n = 64 + s := uint(k) & (n - 1) + return x<>(n-s) +} + +// --- Reverse --- + +// Reverse returns the value of x with its bits in reversed order. +func Reverse(x uint) uint { + if UintSize == 32 { + return uint(Reverse32(uint32(x))) + } + return uint(Reverse64(uint64(x))) +} + +// Reverse8 returns the value of x with its bits in reversed order. +func Reverse8(x uint8) uint8 { + return rev8tab[x] +} + +// Reverse16 returns the value of x with its bits in reversed order. +func Reverse16(x uint16) uint16 { + return uint16(rev8tab[x>>8]) | uint16(rev8tab[x&0xff])<<8 +} + +// Reverse32 returns the value of x with its bits in reversed order. +func Reverse32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes32(x) +} + +// Reverse64 returns the value of x with its bits in reversed order. +func Reverse64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>1&(m0&m) | x&(m0&m)<<1 + x = x>>2&(m1&m) | x&(m1&m)<<2 + x = x>>4&(m2&m) | x&(m2&m)<<4 + return ReverseBytes64(x) +} + +// --- ReverseBytes --- + +// ReverseBytes returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes(x uint) uint { + if UintSize == 32 { + return uint(ReverseBytes32(uint32(x))) + } + return uint(ReverseBytes64(uint64(x))) +} + +// ReverseBytes16 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes16(x uint16) uint16 { + return x>>8 | x<<8 +} + +// ReverseBytes32 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes32(x uint32) uint32 { + const m = 1<<32 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + return x>>16 | x<<16 +} + +// ReverseBytes64 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +func ReverseBytes64(x uint64) uint64 { + const m = 1<<64 - 1 + x = x>>8&(m3&m) | x&(m3&m)<<8 + x = x>>16&(m4&m) | x&(m4&m)<<16 + return x>>32 | x<<32 +} + +// --- Len --- + +// Len returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len(x uint) int { + if UintSize == 32 { + return Len32(uint32(x)) + } + return Len64(uint64(x)) +} + +// Len8 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len8(x uint8) int { + return int(len8tab[x]) +} + +// Len16 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len16(x uint16) (n int) { + if x >= 1<<8 { + x >>= 8 + n = 8 + } + return n + int(len8tab[x]) +} + +// Len32 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len32(x uint32) (n int) { + if x >= 1<<16 { + x >>= 16 + n = 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// Len64 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +func Len64(x uint64) (n int) { + if x >= 1<<32 { + x >>= 32 + n = 32 + } + if x >= 1<<16 { + x >>= 16 + n += 16 + } + if x >= 1<<8 { + x >>= 8 + n += 8 + } + return n + int(len8tab[x]) +} + +// --- Add with carry --- + +// Add returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add(x, y, carry uint) (sum, carryOut uint) { + if UintSize == 32 { + s32, c32 := Add32(uint32(x), uint32(y), uint32(carry)) + return uint(s32), uint(c32) + } + s64, c64 := Add64(uint64(x), uint64(y), uint64(carry)) + return uint(s64), uint(c64) +} + +// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add32(x, y, carry uint32) (sum, carryOut uint32) { + sum64 := uint64(x) + uint64(y) + uint64(carry) + sum = uint32(sum64) + carryOut = uint32(sum64 >> 32) + return +} + +// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Add64(x, y, carry uint64) (sum, carryOut uint64) { + sum = x + y + carry + // The sum will overflow if both top bits are set (x & y) or if one of them + // is (x | y), and a carry from the lower place happened. If such a carry + // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum). + carryOut = ((x & y) | ((x | y) &^ sum)) >> 63 + return +} + +// --- Subtract with borrow --- + +// Sub returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub(x, y, borrow uint) (diff, borrowOut uint) { + if UintSize == 32 { + d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow)) + return uint(d32), uint(b32) + } + d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow)) + return uint(d64), uint(b64) +} + +// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) { + diff = x - y - borrow + // The difference will underflow if the top bit of x is not set and the top + // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow + // from the lower place happens. If that borrow happens, the result will be + // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff). + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31 + return +} + +// Sub64 returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) { + diff = x - y - borrow + // See Sub32 for the bit logic. + borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63 + return +} + +// --- Full-width multiply --- + +// Mul returns the full-width product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul(x, y uint) (hi, lo uint) { + if UintSize == 32 { + h, l := Mul32(uint32(x), uint32(y)) + return uint(h), uint(l) + } + h, l := Mul64(uint64(x), uint64(y)) + return uint(h), uint(l) +} + +// Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul32(x, y uint32) (hi, lo uint32) { + tmp := uint64(x) * uint64(y) + hi, lo = uint32(tmp>>32), uint32(tmp) + return +} + +// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +func Mul64(x, y uint64) (hi, lo uint64) { + const mask32 = 1<<32 - 1 + x0 := x & mask32 + x1 := x >> 32 + y0 := y & mask32 + y1 := y >> 32 + w0 := x0 * y0 + t := x1*y0 + w0>>32 + w1 := t & mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi = x1*y1 + w2 + w1>>32 + lo = x * y + return +} + +// --- Full-width divide --- + +// Div returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div(hi, lo, y uint) (quo, rem uint) { + if UintSize == 32 { + q, r := Div32(uint32(hi), uint32(lo), uint32(y)) + return uint(q), uint(r) + } + q, r := Div64(uint64(hi), uint64(lo), uint64(y)) + return uint(q), uint(r) +} + +// Div32 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div32 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div32(hi, lo, y uint32) (quo, rem uint32) { + if y != 0 && y <= hi { + panic(overflowError) + } + z := uint64(hi)<<32 | uint64(lo) + quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y)) + return +} + +// Div64 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// Div64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +func Div64(hi, lo, y uint64) (quo, rem uint64) { + if y == 0 { + panic(divideError) + } + if y <= hi { + panic(overflowError) + } + + // If high part is zero, we can directly return the results. + if hi == 0 { + return lo / y, lo % y + } + + s := uint(LeadingZeros64(y)) + y <<= s + + const ( + two32 = 1 << 32 + mask32 = two32 - 1 + ) + yn1 := y >> 32 + yn0 := y & mask32 + un32 := hi<>(64-s) + un10 := lo << s + un1 := un10 >> 32 + un0 := un10 & mask32 + q1 := un32 / yn1 + rhat := un32 - q1*yn1 + + for q1 >= two32 || q1*yn0 > two32*rhat+un1 { + q1-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + un21 := un32*two32 + un1 - q1*y + q0 := un21 / yn1 + rhat = un21 - q0*yn1 + + for q0 >= two32 || q0*yn0 > two32*rhat+un0 { + q0-- + rhat += yn1 + if rhat >= two32 { + break + } + } + + return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s +} + +// Rem returns the remainder of (hi, lo) divided by y. Rem panics for +// y == 0 (division by zero) but, unlike Div, it doesn't panic on a +// quotient overflow. +func Rem(hi, lo, y uint) uint { + if UintSize == 32 { + return uint(Rem32(uint32(hi), uint32(lo), uint32(y))) + } + return uint(Rem64(uint64(hi), uint64(lo), uint64(y))) +} + +// Rem32 returns the remainder of (hi, lo) divided by y. Rem32 panics +// for y == 0 (division by zero) but, unlike [Div32], it doesn't panic +// on a quotient overflow. +func Rem32(hi, lo, y uint32) uint32 { + return uint32((uint64(hi)<<32 | uint64(lo)) % uint64(y)) +} + +// Rem64 returns the remainder of (hi, lo) divided by y. Rem64 panics +// for y == 0 (division by zero) but, unlike [Div64], it doesn't panic +// on a quotient overflow. +func Rem64(hi, lo, y uint64) uint64 { + // We scale down hi so that hi < y, then use Div64 to compute the + // rem with the guarantee that it won't panic on quotient overflow. + // Given that + // hi ≡ hi%y (mod y) + // we have + // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + _, rem := Div64(hi%y, lo, y) + return rem +} \ No newline at end of file diff --git a/u256/bits_errors.gno b/u256/bits_errors.gno new file mode 100644 index 00000000..30b8ae33 --- /dev/null +++ b/u256/bits_errors.gno @@ -0,0 +1,17 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !compiler_bootstrap + +package u256 + +import ( + "errors" +) + +//go:linkname overflowError runtime.overflowError +var overflowError error = errors.New("u256: integer overflow") + +//go:linkname divideError runtime.divideError +var divideError error = errors.New("u256: integer divide by zero") \ No newline at end of file diff --git a/u256/bits_table.gno b/u256/bits_table.gno new file mode 100644 index 00000000..4d7c5397 --- /dev/null +++ b/u256/bits_table.gno @@ -0,0 +1,79 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by go run make_tables.go. DO NOT EDIT. + +package u256 + +const ntz8tab = "" + + "\x08\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x07\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x06\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x05\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + + "\x04\x00\x01\x00\x02\x00\x01\x00\x03\x00\x01\x00\x02\x00\x01\x00" + +const pop8tab = "" + + "\x00\x01\x01\x02\x01\x02\x02\x03\x01\x02\x02\x03\x02\x03\x03\x04" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x01\x02\x02\x03\x02\x03\x03\x04\x02\x03\x03\x04\x03\x04\x04\x05" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x02\x03\x03\x04\x03\x04\x04\x05\x03\x04\x04\x05\x04\x05\x05\x06" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x03\x04\x04\x05\x04\x05\x05\x06\x04\x05\x05\x06\x05\x06\x06\x07" + + "\x04\x05\x05\x06\x05\x06\x06\x07\x05\x06\x06\x07\x06\x07\x07\x08" + +const rev8tab = "" + + "\x00\x80\x40\xc0\x20\xa0\x60\xe0\x10\x90\x50\xd0\x30\xb0\x70\xf0" + + "\x08\x88\x48\xc8\x28\xa8\x68\xe8\x18\x98\x58\xd8\x38\xb8\x78\xf8" + + "\x04\x84\x44\xc4\x24\xa4\x64\xe4\x14\x94\x54\xd4\x34\xb4\x74\xf4" + + "\x0c\x8c\x4c\xcc\x2c\xac\x6c\xec\x1c\x9c\x5c\xdc\x3c\xbc\x7c\xfc" + + "\x02\x82\x42\xc2\x22\xa2\x62\xe2\x12\x92\x52\xd2\x32\xb2\x72\xf2" + + "\x0a\x8a\x4a\xca\x2a\xaa\x6a\xea\x1a\x9a\x5a\xda\x3a\xba\x7a\xfa" + + "\x06\x86\x46\xc6\x26\xa6\x66\xe6\x16\x96\x56\xd6\x36\xb6\x76\xf6" + + "\x0e\x8e\x4e\xce\x2e\xae\x6e\xee\x1e\x9e\x5e\xde\x3e\xbe\x7e\xfe" + + "\x01\x81\x41\xc1\x21\xa1\x61\xe1\x11\x91\x51\xd1\x31\xb1\x71\xf1" + + "\x09\x89\x49\xc9\x29\xa9\x69\xe9\x19\x99\x59\xd9\x39\xb9\x79\xf9" + + "\x05\x85\x45\xc5\x25\xa5\x65\xe5\x15\x95\x55\xd5\x35\xb5\x75\xf5" + + "\x0d\x8d\x4d\xcd\x2d\xad\x6d\xed\x1d\x9d\x5d\xdd\x3d\xbd\x7d\xfd" + + "\x03\x83\x43\xc3\x23\xa3\x63\xe3\x13\x93\x53\xd3\x33\xb3\x73\xf3" + + "\x0b\x8b\x4b\xcb\x2b\xab\x6b\xeb\x1b\x9b\x5b\xdb\x3b\xbb\x7b\xfb" + + "\x07\x87\x47\xc7\x27\xa7\x67\xe7\x17\x97\x57\xd7\x37\xb7\x77\xf7" + + "\x0f\x8f\x4f\xcf\x2f\xaf\x6f\xef\x1f\x9f\x5f\xdf\x3f\xbf\x7f\xff" + +const len8tab = "" + + "\x00\x01\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04\x04\x04\x04\x04" + + "\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06\x06" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" + + "\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08" \ No newline at end of file diff --git a/u256/gno.mod b/u256/gno.mod new file mode 100644 index 00000000..749a02f5 --- /dev/null +++ b/u256/gno.mod @@ -0,0 +1 @@ +module gno.land/p/u256 \ No newline at end of file diff --git a/u256/u256.gno b/u256/u256.gno new file mode 100644 index 00000000..3780c5da --- /dev/null +++ b/u256/u256.gno @@ -0,0 +1,1096 @@ +// Ported from https://github.com/holiman/uint256/ + +package u256 + +import ( + "errors" + "strconv" +) + +const MaxUint64 = 1<<64 - 1 + +// Int is represented as an array of 4 uint64, in little-endian order, +// so that Int[3] is the most significant, and Int[0] is the least significant +type Int [4]uint64 + +// NewInt returns a new initialized Int. +func NewInt(val uint64) *Int { + z := &Int{} + z.SetUint64(val) + return z +} + +// Uint64 returns the lower 64-bits of z +func (z *Int) Uint64() uint64 { + return z[0] +} + +// SetUint64 sets z to the value x +func (z *Int) SetUint64(x uint64) *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, x + return z +} + +// IsUint64 reports whether z can be represented as a uint64. +func (z *Int) IsUint64() bool { + return (z[1] | z[2] | z[3]) == 0 +} + +// IsZero returns true if z == 0 +func (z *Int) IsZero() bool { + return (z[0] | z[1] | z[2] | z[3]) == 0 +} + +// Clear sets z to 0 +func (z *Int) Clear() *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, 0 + return z +} + +// SetAllOne sets all the bits of z to 1 +func (z *Int) SetAllOne() *Int { + z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 + return z +} + +// Not sets z = ^x and returns z. +func (z *Int) Not(x *Int) *Int { + z[3], z[2], z[1], z[0] = ^x[3], ^x[2], ^x[1], ^x[0] + return z +} + +// Gt returns true if z > x +func (z *Int) Gt(x *Int) bool { + return x.Lt(z) +} + +// Lt returns true if z < x +func (z *Int) Lt(x *Int) bool { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + _, carry := Sub64(z[0], x[0], 0) + _, carry = Sub64(z[1], x[1], carry) + _, carry = Sub64(z[2], x[2], carry) + _, carry = Sub64(z[3], x[3], carry) + return carry != 0 +} + +// Eq returns true if z == x +func (z *Int) Eq(x *Int) bool { + return (z[0] == x[0]) && (z[1] == x[1]) && (z[2] == x[2]) && (z[3] == x[3]) +} + +// Cmp compares z and x and returns: +// +// -1 if z < x +// 0 if z == x +// +1 if z > x +func (z *Int) Cmp(x *Int) (r int) { + // z < x <=> z - x < 0 i.e. when subtraction overflows. + d0, carry := Sub64(z[0], x[0], 0) + d1, carry := Sub64(z[1], x[1], carry) + d2, carry := Sub64(z[2], x[2], carry) + d3, carry := Sub64(z[3], x[3], carry) + if carry == 1 { + return -1 + } + if d0|d1|d2|d3 == 0 { + return 0 + } + return 1 +} + +// Set sets z to x and returns z. +func (z *Int) Set(x *Int) *Int { + *z = *x + return z +} + +// SetOne sets z to 1 +func (z *Int) SetOne() *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, 1 + return z +} + +// Add sets z to the sum x+y +func (z *Int) Add(x, y *Int) *Int { + var carry uint64 + z[0], carry = Add64(x[0], y[0], 0) + z[1], carry = Add64(x[1], y[1], carry) + z[2], carry = Add64(x[2], y[2], carry) + z[3], _ = Add64(x[3], y[3], carry) + return z +} + +// AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred +func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { + var carry uint64 + z[0], carry = Add64(x[0], y[0], 0) + z[1], carry = Add64(x[1], y[1], carry) + z[2], carry = Add64(x[2], y[2], carry) + z[3], carry = Add64(x[3], y[3], carry) + return z, carry != 0 +} + +// SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed +func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { + var carry uint64 + z[0], carry = Sub64(x[0], y[0], 0) + z[1], carry = Sub64(x[1], y[1], carry) + z[2], carry = Sub64(x[2], y[2], carry) + z[3], carry = Sub64(x[3], y[3], carry) + return z, carry != 0 +} + +// Sub sets z to the difference x-y +func (z *Int) Sub(x, y *Int) *Int { + var carry uint64 + z[0], carry = Sub64(x[0], y[0], 0) + z[1], carry = Sub64(x[1], y[1], carry) + z[2], carry = Sub64(x[2], y[2], carry) + z[3], _ = Sub64(x[3], y[3], carry) + return z +} + +// Mul sets z to the product x*y +func (z *Int) Mul(x, y *Int) *Int { + var ( + res Int + carry uint64 + res1, res2, res3 uint64 + ) + + carry, res[0] = Mul64(x[0], y[0]) + carry, res1 = umulHop(carry, x[1], y[0]) + carry, res2 = umulHop(carry, x[2], y[0]) + res3 = x[3]*y[0] + carry + + carry, res[1] = umulHop(res1, x[0], y[1]) + carry, res2 = umulStep(res2, x[1], y[1], carry) + res3 = res3 + x[2]*y[1] + carry + + carry, res[2] = umulHop(res2, x[0], y[2]) + res3 = res3 + x[1]*y[2] + carry + + res[3] = res3 + x[0]*y[3] + + return z.Set(&res) +} + +// MulOverflow sets z to the product x*y, and returns z and whether overflow occurred +func (z *Int) MulOverflow(x, y *Int) (*Int, bool) { + p := umul(x, y) + copy(z[:], p[:4]) + return z, (p[4] | p[5] | p[6] | p[7]) != 0 +} + +// umulStep computes (hi * 2^64 + lo) = z + (x * y) + carry. +func umulStep(z, x, y, carry uint64) (hi, lo uint64) { + hi, lo = Mul64(x, y) + lo, carry = Add64(lo, carry, 0) + hi, _ = Add64(hi, 0, carry) + lo, carry = Add64(lo, z, 0) + hi, _ = Add64(hi, 0, carry) + return hi, lo +} + +// umulHop computes (hi * 2^64 + lo) = z + (x * y) +func umulHop(z, x, y uint64) (hi, lo uint64) { + hi, lo = Mul64(x, y) + lo, carry := Add64(lo, z, 0) + hi, _ = Add64(hi, 0, carry) + return hi, lo +} + +// umul computes full 256 x 256 -> 512 multiplication. +func umul(x, y *Int) [8]uint64 { + var ( + res [8]uint64 + carry, carry4, carry5, carry6 uint64 + res1, res2, res3, res4, res5 uint64 + ) + + carry, res[0] = Mul64(x[0], y[0]) + carry, res1 = umulHop(carry, x[1], y[0]) + carry, res2 = umulHop(carry, x[2], y[0]) + carry4, res3 = umulHop(carry, x[3], y[0]) + + carry, res[1] = umulHop(res1, x[0], y[1]) + carry, res2 = umulStep(res2, x[1], y[1], carry) + carry, res3 = umulStep(res3, x[2], y[1], carry) + carry5, res4 = umulStep(carry4, x[3], y[1], carry) + + carry, res[2] = umulHop(res2, x[0], y[2]) + carry, res3 = umulStep(res3, x[1], y[2], carry) + carry, res4 = umulStep(res4, x[2], y[2], carry) + carry6, res5 = umulStep(carry5, x[3], y[2], carry) + + carry, res[3] = umulHop(res3, x[0], y[3]) + carry, res[4] = umulStep(res4, x[1], y[3], carry) + carry, res[5] = umulStep(res5, x[2], y[3], carry) + res[7], res[6] = umulStep(carry6, x[3], y[3], carry) + + return res +} + +// Div sets z to the quotient x/y for returns z. +// If y == 0, z is set to 0 +func (z *Int) Div(x, y *Int) *Int { + if y.IsZero() || y.Gt(x) { + return z.Clear() + } + if x.Eq(y) { + return z.SetOne() + } + // Shortcut some cases + if x.IsUint64() { + return z.SetUint64(x.Uint64() / y.Uint64()) + } + + // At this point, we know + // x/y ; x > y > 0 + + var quot Int + udivrem(quot[:], x[:], y) + return z.Set(") +} + + +// udivrem divides u by d and produces both quotient and remainder. +// The quotient is stored in provided quot - len(u)-len(d)+1 words. +// It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. +// See Knuth, Volume 2, section 4.3.1, Algorithm D. +func udivrem(quot, u []uint64, d *Int) (rem Int) { + var dLen int + for i := len(d) - 1; i >= 0; i-- { + if d[i] != 0 { + dLen = i + 1 + break + } + } + + shift := uint(LeadingZeros64(d[dLen-1])) + + var dnStorage Int + dn := dnStorage[:dLen] + for i := dLen - 1; i > 0; i-- { + dn[i] = (d[i] << shift) | (d[i-1] >> (64 - shift)) + } + dn[0] = d[0] << shift + + var uLen int + for i := len(u) - 1; i >= 0; i-- { + if u[i] != 0 { + uLen = i + 1 + break + } + } + + if uLen < dLen { + copy(rem[:], u) + return rem + } + + var unStorage [9]uint64 + un := unStorage[:uLen+1] + un[uLen] = u[uLen-1] >> (64 - shift) + for i := uLen - 1; i > 0; i-- { + un[i] = (u[i] << shift) | (u[i-1] >> (64 - shift)) + } + un[0] = u[0] << shift + + // TODO: Skip the highest word of numerator if not significant. + + if dLen == 1 { + r := udivremBy1(quot, un, dn[0]) + rem.SetUint64(r >> shift) + return rem + } + + udivremKnuth(quot, un, dn) + + for i := 0; i < dLen-1; i++ { + rem[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) + } + rem[dLen-1] = un[dLen-1] >> shift + + return rem +} + + +// udivremBy1 divides u by single normalized word d and produces both quotient and remainder. +// The quotient is stored in provided quot. +func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { + reciprocal := reciprocal2by1(d) + rem = u[len(u)-1] // Set the top word as remainder. + for j := len(u) - 2; j >= 0; j-- { + quot[j], rem = udivrem2by1(rem, u[j], d, reciprocal) + } + return rem +} + +// udivremKnuth implements the division of u by normalized multiple word d from the Knuth's division algorithm. +// The quotient is stored in provided quot - len(u)-len(d) words. +// Updates u to contain the remainder - len(d) words. +func udivremKnuth(quot, u, d []uint64) { + dh := d[len(d)-1] + dl := d[len(d)-2] + reciprocal := reciprocal2by1(dh) + + for j := len(u) - len(d) - 1; j >= 0; j-- { + u2 := u[j+len(d)] + u1 := u[j+len(d)-1] + u0 := u[j+len(d)-2] + + var qhat, rhat uint64 + if u2 >= dh { // Division overflows. + qhat = ^uint64(0) + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } else { + qhat, rhat = udivrem2by1(u2, u1, dh, reciprocal) + ph, pl := Mul64(qhat, dl) + if ph > rhat || (ph == rhat && pl > u0) { + qhat-- + // TODO: Add "qhat one to big" adjustment (not needed for correctness, but helps avoiding "add back" case). + } + } + + // Multiply and subtract. + borrow := subMulTo(u[j:], d, qhat) + u[j+len(d)] = u2 - borrow + if u2 < borrow { // Too much subtracted, add back. + qhat-- + u[j+len(d)] += addTo(u[j:], d) + } + + quot[j] = qhat // Store quotient digit. + } +} + +// isBitSet returns true if bit n-th is set, where n = 0 is LSB. +// The n must be <= 255. +func (z *Int) isBitSet(n uint) bool { + return (z[n/64] & (1 << (n % 64))) != 0 +} + +// addTo computes x += y. +// Requires len(x) >= len(y). +func addTo(x, y []uint64) uint64 { + var carry uint64 + for i := 0; i < len(y); i++ { + x[i], carry = Add64(x[i], y[i], carry) + } + return carry +} + + +// subMulTo computes x -= y * multiplier. +// Requires len(x) >= len(y). +func subMulTo(x, y []uint64, multiplier uint64) uint64 { + var borrow uint64 + for i := 0; i < len(y); i++ { + s, carry1 := Sub64(x[i], borrow, 0) + ph, pl := Mul64(y[i], multiplier) + t, carry2 := Sub64(s, pl, 0) + x[i] = t + borrow = ph + carry1 + carry2 + } + return borrow +} + +// reciprocal2by1 computes <^d, ^0> / d. +func reciprocal2by1(d uint64) uint64 { + reciprocal, _ := Div64(^d, ^uint64(0), d) + return reciprocal +} + +// udivrem2by1 divides / d and produces both quotient and remainder. +// It uses the provided d's reciprocal. +// Implementation ported from https://github.com/chfast/intx and is based on +// "Improved division by invariant integers", Algorithm 4. +func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { + qh, ql := Mul64(reciprocal, uh) + ql, carry := Add64(ql, ul, 0) + qh, _ = Add64(qh, uh, carry) + qh++ + + r := ul - qh*d + + if r > ql { + qh-- + r += d + } + + if r >= d { + qh++ + r -= d + } + + return qh, r +} + + +// Lsh sets z = x << n and returns z. +func (z *Int) Lsh(x *Int, n uint) *Int { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.lsh64(x) + case 128: + return z.lsh128(x) + case 192: + return z.lsh192(x) + default: + return z.Clear() + } + } + var ( + a, b uint64 + ) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.lsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.lsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.lsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z[0] >> (64 - n) + z[0] = z[0] << n + +sh64: + b = z[1] >> (64 - n) + z[1] = (z[1] << n) | a + +sh128: + a = z[2] >> (64 - n) + z[2] = (z[2] << n) | b + +sh192: + z[3] = (z[3] << n) | a + + return z +} + + +// Rsh sets z = x >> n and returns z. +func (z *Int) Rsh(x *Int, n uint) *Int { + // n % 64 == 0 + if n&0x3f == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.rsh64(x) + case 128: + return z.rsh128(x) + case 192: + return z.rsh192(x) + default: + return z.Clear() + } + } + var ( + a, b uint64 + ) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.Clear() + } + z.rsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.rsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.rsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + a = z[3] << (64 - n) + z[3] = z[3] >> n + +sh64: + b = z[2] << (64 - n) + z[2] = (z[2] >> n) | a + +sh128: + a = z[1] << (64 - n) + z[1] = (z[1] >> n) | b + +sh192: + z[0] = (z[0] >> n) | a + + return z +} + +// SRsh (Signed/Arithmetic right shift) +// considers z to be a signed integer, during right-shift +// and sets z = x >> n and returns z. +func (z *Int) SRsh(x *Int, n uint) *Int { + // If the MSB is 0, SRsh is same as Rsh. + if !x.isBitSet(255) { + return z.Rsh(x, n) + } + if n%64 == 0 { + switch n { + case 0: + return z.Set(x) + case 64: + return z.srsh64(x) + case 128: + return z.srsh128(x) + case 192: + return z.srsh192(x) + default: + return z.SetAllOne() + } + } + var ( + a uint64 = MaxUint64 << (64 - n%64) + ) + // Big swaps first + switch { + case n > 192: + if n > 256 { + return z.SetAllOne() + } + z.srsh192(x) + n -= 192 + goto sh192 + case n > 128: + z.srsh128(x) + n -= 128 + goto sh128 + case n > 64: + z.srsh64(x) + n -= 64 + goto sh64 + default: + z.Set(x) + } + + // remaining shifts + z[3], a = (z[3]>>n)|a, z[3]<<(64-n) + +sh64: + z[2], a = (z[2]>>n)|a, z[2]<<(64-n) + +sh128: + z[1], a = (z[1]>>n)|a, z[1]<<(64-n) + +sh192: + z[0] = (z[0] >> n) | a + + return z +} + +func (z *Int) lsh64(x *Int) *Int { + z[3], z[2], z[1], z[0] = x[2], x[1], x[0], 0 + return z +} +func (z *Int) lsh128(x *Int) *Int { + z[3], z[2], z[1], z[0] = x[1], x[0], 0, 0 + return z +} +func (z *Int) lsh192(x *Int) *Int { + z[3], z[2], z[1], z[0] = x[0], 0, 0, 0 + return z +} +func (z *Int) rsh64(x *Int) *Int { + z[3], z[2], z[1], z[0] = 0, x[3], x[2], x[1] + return z +} +func (z *Int) rsh128(x *Int) *Int { + z[3], z[2], z[1], z[0] = 0, 0, x[3], x[2] + return z +} +func (z *Int) rsh192(x *Int) *Int { + z[3], z[2], z[1], z[0] = 0, 0, 0, x[3] + return z +} +func (z *Int) srsh64(x *Int) *Int { + z[3], z[2], z[1], z[0] = MaxUint64, x[3], x[2], x[1] + return z +} +func (z *Int) srsh128(x *Int) *Int { + z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, x[3], x[2] + return z +} +func (z *Int) srsh192(x *Int) *Int { + z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, MaxUint64, x[3] + return z +} + +// Or sets z = x | y and returns z. +func (z *Int) Or(x, y *Int) *Int { + z[0] = x[0] | y[0] + z[1] = x[1] | y[1] + z[2] = x[2] | y[2] + z[3] = x[3] | y[3] + return z +} + +// And sets z = x & y and returns z. +func (z *Int) And(x, y *Int) *Int { + z[0] = x[0] & y[0] + z[1] = x[1] & y[1] + z[2] = x[2] & y[2] + z[3] = x[3] & y[3] + return z +} + +// Xor sets z = x ^ y and returns z. +func (z *Int) Xor(x, y *Int) *Int { + z[0] = x[0] ^ y[0] + z[1] = x[1] ^ y[1] + z[2] = x[2] ^ y[2] + z[3] = x[3] ^ y[3] + return z +} + +// MarshalJSON implements json.Marshaler. +// MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible +// with big.Int: big.Int marshals into JSON 'native' numeric format. +// +// The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large +// integer space. Thus, U256 uses string-format, which is not compatible with +// big.int (big.Int refuses to unmarshal a string representation). +func (z *Int) MarshalJSON() ([]byte, error) { + return []byte(`"` + z.Dec() + `"`), nil +} + +// UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either +// - Quoted string: either hexadecimal OR decimal +// - Not quoted string: only decimal +func (z *Int) UnmarshalJSON(input []byte) error { + if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { + // if not quoted, it must be decimal + return z.fromDecimal(string(input)) + } + return z.UnmarshalText(input[1 : len(input)-1]) +} + +// MarshalText implements encoding.TextMarshaler +// MarshalText marshals using the decimal representation (compatible with big.Int) +func (z *Int) MarshalText() ([]byte, error) { + return []byte(z.Dec()), nil +} + +// UnmarshalText implements encoding.TextUnmarshaler. This method +// can unmarshal either hexadecimal or decimal. +// - For hexadecimal, the input _must_ be prefixed with 0x or 0X +func (z *Int) UnmarshalText(input []byte) error { + if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { + return z.fromHex(string(input)) + } + return z.fromDecimal(string(input)) +} + +const ( + hextable = "0123456789abcdef" + bintable = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x06\a\b\t\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\n\v\f\r\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + badNibble = 0xff +) + +// fromHex is the internal implementation of parsing a hex-string. +func (z *Int) fromHex(hex string) error { + if err := checkNumberS(hex); err != nil { + return err + } + if len(hex) > 66 { + return ErrBig256Range + } + z.Clear() + end := len(hex) + for i := 0; i < 4; i++ { + start := end - 16 + if start < 2 { + start = 2 + } + for ri := start; ri < end; ri++ { + nib := bintable[hex[ri]] + if nib == badNibble { + return ErrSyntax + } + z[i] = z[i] << 4 + z[i] += uint64(nib) + } + end = start + } + return nil +} + +// FromDecimal is a convenience-constructor to create an Int from a +// decimal (base 10) string. Numbers larger than 256 bits are not accepted. +func FromDecimal(decimal string) (*Int, error) { + var z Int + if err := z.SetFromDecimal(decimal); err != nil { + return nil, err + } + return &z, nil +} + +const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" + +// SetFromDecimal sets z from the given string, interpreted as a decimal number. +// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 10) method. +// Notable differences: +// - This method does not accept underscore input, e.g. "100_000", +// - This method does not accept negative zero as valid, e.g "-0", +// - (this method does not accept any negative input as valid)) +func (z *Int) SetFromDecimal(s string) (err error) { + // Remove max one leading + + if len(s) > 0 && s[0] == '+' { + s = s[1:] + } + // Remove any number of leading zeroes + if len(s) > 0 && s[0] == '0' { + var i int + var c rune + for i, c = range s { + if c != '0' { + break + } + } + s = s[i:] + } + if len(s) < len(twoPow256Sub1) { + return z.fromDecimal(s) + } + if len(s) == len(twoPow256Sub1) { + if s > twoPow256Sub1 { + return ErrBig256Range + } + return z.fromDecimal(s) + } + return ErrBig256Range +} + +var ( + ErrEmptyString = errors.New("empty hex string") + ErrSyntax = errors.New("invalid hex string") + ErrMissingPrefix = errors.New("hex string without 0x prefix") + ErrEmptyNumber = errors.New("hex string \"0x\"") + ErrLeadingZero = errors.New("hex number with leading zero digits") + ErrBig256Range = errors.New("hex number > 256 bits") + ErrBadBufferLength = errors.New("bad ssz buffer length") + ErrBadEncodedLength = errors.New("bad ssz encoded length") +) + + +func checkNumberS(input string) error { + l := len(input) + if l == 0 { + return ErrEmptyString + } + if l < 2 || input[0] != '0' || + (input[1] != 'x' && input[1] != 'X') { + return ErrMissingPrefix + } + if l == 2 { + return ErrEmptyNumber + } + if len(input) > 3 && input[2] == '0' { + return ErrLeadingZero + } + return nil +} + +// multipliers holds the values that are needed for fromDecimal +var multipliers = [5]*Int{ + nil, // represents first round, no multiplication needed + {10000000000000000000, 0, 0, 0}, // 10 ^ 19 + {687399551400673280, 5421010862427522170, 0, 0}, // 10 ^ 38 + {5332261958806667264, 17004971331911604867, 2938735877055718769, 0}, // 10 ^ 57 + {0, 8607968719199866880, 532749306367912313, 1593091911132452277}, // 10 ^ 76 +} + +// fromDecimal is a helper function to only ever be called via SetFromDecimal +// this function takes a string and chunks it up, calling ParseUint on it up to 5 times +// these chunks are then multiplied by the proper power of 10, then added together. +func (z *Int) fromDecimal(bs string) error { + // first clear the input + z.Clear() + // the maximum value of uint64 is 18446744073709551615, which is 20 characters + // one less means that a string of 19 9's is always within the uint64 limit + var ( + num uint64 + err error + remaining = len(bs) + ) + if remaining == 0 { + return errors.New("EOF") + } + // We proceed in steps of 19 characters (nibbles), from least significant to most significant. + // This means that the first (up to) 19 characters do not need to be multiplied. + // In the second iteration, our slice of 19 characters needs to be multipleied + // by a factor of 10^19. Et cetera. + for i, mult := range multipliers { + if remaining <= 0 { + return nil // Done + } else if remaining > 19 { + num, err = parseUint(bs[remaining-19:remaining], 10, 64) + } else { + // Final round + num, err = parseUint(bs, 10, 64) + } + if err != nil { + return err + } + // add that number to our running total + if i == 0 { + z.SetUint64(num) + } else { + base := NewInt(num) + z.Add(z, base.Mul(base, mult)) + } + // Chop off another 19 characters + if remaining > 19 { + bs = bs[0 : remaining-19] + } + remaining -= 19 + } + return nil +} + +// lower(c) is a lower-case letter if and only if +// c is either that lower-case letter or the equivalent upper-case letter. +// Instead of writing c == 'x' || c == 'X' one can write lower(c) == 'x'. +// Note that lower of non-letters can produce other non-letters. +func lower(c byte) byte { + return c | ('x' - 'X') +} + +const intSize = 32 << (^uint(0) >> 63) + +// IntSize is the size in bits of an int or uint value. +const IntSize = intSize + +// ParseUint is like ParseInt but for unsigned numbers. +// +// A sign prefix is not permitted. +func parseUint(s string, base int, bitSize int) (uint64, error) { + const fnParseUint = "ParseUint" + + if s == "" { + return 0, errors.New("syntax error: ParseUint empty string") + } + + base0 := base == 0 + + s0 := s + switch { + case 2 <= base && base <= 36: + // valid base; nothing to do + + case base == 0: + // Look for octal, hex prefix. + base = 10 + if s[0] == '0' { + switch { + case len(s) >= 3 && lower(s[1]) == 'b': + base = 2 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'o': + base = 8 + s = s[2:] + case len(s) >= 3 && lower(s[1]) == 'x': + base = 16 + s = s[2:] + default: + base = 8 + s = s[1:] + } + } + + default: + return 0, errors.New("invalid base") + } + + if bitSize == 0 { + bitSize = IntSize + } else if bitSize < 0 || bitSize > 64 { + return 0, errors.New("invalid bit size") + } + + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // Use compile-time constants for common cases. + var cutoff uint64 + switch base { + case 10: + cutoff = MaxUint64/10 + 1 + case 16: + cutoff = MaxUint64/16 + 1 + default: + cutoff = MaxUint64/uint64(base) + 1 + } + + maxVal := uint64(1)<= byte(base) { + return 0, errors.New("syntax error") + } + + if n >= cutoff { + // n*base overflows + return maxVal, errors.New("range error") + } + n *= uint64(base) + + n1 := n + uint64(d) + if n1 < n || n1 > maxVal { + // n+d overflows + return maxVal, errors.New("range error") + } + n = n1 + } + + if underscores && !underscoreOK(s0) { + return 0, errors.New("syntax error") + } + + return n, nil +} + + +// underscoreOK reports whether the underscores in s are allowed. +// Checking them in this one function lets all the parsers skip over them simply. +// Underscore must appear only between digits or between a base prefix and a digit. +func underscoreOK(s string) bool { + // saw tracks the last character (class) we saw: + // ^ for beginning of number, + // 0 for a digit or base prefix, + // _ for an underscore, + // ! for none of the above. + saw := '^' + i := 0 + + // Optional sign. + if len(s) >= 1 && (s[0] == '-' || s[0] == '+') { + s = s[1:] + } + + // Optional base prefix. + hex := false + if len(s) >= 2 && s[0] == '0' && (lower(s[1]) == 'b' || lower(s[1]) == 'o' || lower(s[1]) == 'x') { + i = 2 + saw = '0' // base prefix counts as a digit for "underscore as digit separator" + hex = lower(s[1]) == 'x' + } + + // Number proper. + for ; i < len(s); i++ { + // Digits are always okay. + if '0' <= s[i] && s[i] <= '9' || hex && 'a' <= lower(s[i]) && lower(s[i]) <= 'f' { + saw = '0' + continue + } + // Underscore must follow digit. + if s[i] == '_' { + if saw != '0' { + return false + } + saw = '_' + continue + } + // Underscore must also be followed by digit. + if saw == '_' { + return false + } + // Saw non-digit, non-underscore. + saw = '!' + } + return saw != '_' +} + +// Dec returns the decimal representation of z. +func (z *Int) Dec() string { + if z.IsZero() { + return "0" + } + if z.IsUint64() { + return strconv.FormatUint(z.Uint64(), 10) + } + // The max uint64 value being 18446744073709551615, the largest + // power-of-ten below that is 10000000000000000000. + // When we do a DivMod using that number, the remainder that we + // get back is the lower part of the output. + // + // The ascii-output of remainder will never exceed 19 bytes (since it will be + // below 10000000000000000000). + // + // Algorithm example using 100 as divisor + // + // 12345 % 100 = 45 (rem) + // 12345 / 100 = 123 (quo) + // -> output '45', continue iterate on 123 + var ( + // out is 98 bytes long: 78 (max size of a string without leading zeroes, + // plus slack so we can copy 19 bytes every iteration). + // We init it with zeroes, because when strconv appends the ascii representations, + // it will omit leading zeroes. + out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + divisor = NewInt(10000000000000000000) // 20 digits + y = new(Int).Set(z) // copy to avoid modifying z + pos = len(out) // position to write to + buf = make([]byte, 0, 19) // buffer to write uint64:s to + ) + for { + // Obtain Q and R for divisor + var quot Int + rem := udivrem(quot[:], y[:], divisor) + y.Set(") // Set Q for next loop + // Convert the R to ascii representation + buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + // Copy in the ascii digits + copy(out[pos-len(buf):], buf) + if y.IsZero() { + break + } + // Move 19 digits left + pos -= 19 + } + // skip leading zeroes by only using the 'used size' of buf + return string(out[pos-len(buf):]) +} \ No newline at end of file From 620a8e33a9f746e8405f07e1cc03aefa91c24833 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 19:19:19 +0900 Subject: [PATCH 02/23] commented out marshal --- u256/u256.gno | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/u256/u256.gno b/u256/u256.gno index 3780c5da..da658629 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -672,7 +672,7 @@ func (z *Int) Xor(x, y *Int) *Int { z[3] = x[3] ^ y[3] return z } - +/* // MarshalJSON implements json.Marshaler. // MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible // with big.Int: big.Int marshals into JSON 'native' numeric format. @@ -754,9 +754,9 @@ func FromDecimal(decimal string) (*Int, error) { } return &z, nil } - +*/ const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" - +/* // SetFromDecimal sets z from the given string, interpreted as a decimal number. // OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 10) method. // Notable differences: @@ -790,6 +790,7 @@ func (z *Int) SetFromDecimal(s string) (err error) { } return ErrBig256Range } +*/ var ( ErrEmptyString = errors.New("empty hex string") @@ -801,7 +802,7 @@ var ( ErrBadBufferLength = errors.New("bad ssz buffer length") ErrBadEncodedLength = errors.New("bad ssz encoded length") ) - +/* func checkNumberS(input string) error { l := len(input) @@ -1093,4 +1094,5 @@ func (z *Int) Dec() string { } // skip leading zeroes by only using the 'used size' of buf return string(out[pos-len(buf):]) -} \ No newline at end of file +} +*/ \ No newline at end of file From 4ca9775189c666a0899c94339aace8922ac1268e Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 11 Dec 2023 20:10:19 +0900 Subject: [PATCH 03/23] CallTest --- u256/runtest.gno | 30 +++++++ u256/strconv.gno | 203 +++++++++++++++++++++++++++++++++++++++++++++++ u256/u256.gno | 19 ++--- 3 files changed, 239 insertions(+), 13 deletions(-) create mode 100644 u256/runtest.gno create mode 100644 u256/strconv.gno diff --git a/u256/runtest.gno b/u256/runtest.gno new file mode 100644 index 00000000..62a3e089 --- /dev/null +++ b/u256/runtest.gno @@ -0,0 +1,30 @@ +package u256 + +func Hello() { + q96 := "79228162514264337593543950336" + bigStr := "833325865908927437441174649481942846" + zeroStr := "0" + /* + 833325865908927437441174649481942846.67 + >> 10518051.1508 + */ + + b96, err := FromDecimal(q96) + if err != nil { + println("FROMDECIMAL ERROR Q96: ", err) + } + + bigNum, err := FromDecimal(zeroStr) + if err != nil { + println("FROMDECIMAL ERROR BIG: ", err) + } + + zero, err := FromDecimal(zeroStr) + if err != nil { + println("FROMDECIMAL ERROR ZERO: ", err) + } + + zero.Div(bigNum, b96) + + println(zero.Dec()) +} diff --git a/u256/strconv.gno b/u256/strconv.gno new file mode 100644 index 00000000..e4485762 --- /dev/null +++ b/u256/strconv.gno @@ -0,0 +1,203 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package u256 + +const fastSmalls = true // enable fast path for small integers + +// FormatUint returns the string representation of i in the given base, +// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' +// for digit values >= 10. +func FormatUint(i uint64, base int) string { + if fastSmalls && i < nSmalls && base == 10 { + return small(int(i)) + } + _, s := formatBits(nil, i, base, false, false) + return s +} + +// FormatInt returns the string representation of i in the given base, +// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z' +// for digit values >= 10. +func FormatInt(i int64, base int) string { + if fastSmalls && 0 <= i && i < nSmalls && base == 10 { + return small(int(i)) + } + _, s := formatBits(nil, uint64(i), base, i < 0, false) + return s +} + +// Itoa is equivalent to FormatInt(int64(i), 10). +func Itoa(i int) string { + return FormatInt(int64(i), 10) +} + +// AppendInt appends the string form of the integer i, +// as generated by FormatInt, to dst and returns the extended buffer. +func AppendInt(dst []byte, i int64, base int) []byte { + if fastSmalls && 0 <= i && i < nSmalls && base == 10 { + return append(dst, small(int(i))...) + } + dst, _ = formatBits(dst, uint64(i), base, i < 0, true) + return dst +} + +// AppendUint appends the string form of the unsigned integer i, +// as generated by FormatUint, to dst and returns the extended buffer. +func AppendUint(dst []byte, i uint64, base int) []byte { + if fastSmalls && i < nSmalls && base == 10 { + return append(dst, small(int(i))...) + } + dst, _ = formatBits(dst, i, base, false, true) + return dst +} + +// small returns the string for an i with 0 <= i < nSmalls. +func small(i int) string { + if i < 10 { + return digits[i : i+1] + } + return smallsString[i*2 : i*2+2] +} + +const nSmalls = 100 + +const smallsString = "00010203040506070809" + + "10111213141516171819" + + "20212223242526272829" + + "30313233343536373839" + + "40414243444546474849" + + "50515253545556575859" + + "60616263646566676869" + + "70717273747576777879" + + "80818283848586878889" + + "90919293949596979899" + +const host32bit = ^uint(0)>>32 == 0 + +const digits = "0123456789abcdefghijklmnopqrstuvwxyz" + +// formatBits computes the string representation of u in the given base. +// If neg is set, u is treated as negative int64 value. If append_ is +// set, the string is appended to dst and the resulting byte slice is +// returned as the first result value; otherwise the string is returned +// as the second result value. +func formatBits(dst []byte, u uint64, base int, neg, append_ bool) (d []byte, s string) { + if base < 2 || base > len(digits) { + panic("strconv: illegal AppendInt/FormatInt base") + } + // 2 <= base && base <= len(digits) + + var a [64 + 1]byte // +1 for sign of 64bit value in base 2 + i := len(a) + + if neg { + u = -u + } + + // convert bits + // We use uint values where we can because those will + // fit into a single register even on a 32bit machine. + if base == 10 { + // common case: use constants for / because + // the compiler can optimize it into a multiply+shift + + if host32bit { + // convert the lower digits using 32bit operations + for u >= 1e9 { + // Avoid using r = a%b in addition to q = a/b + // since 64bit division and modulo operations + // are calculated by runtime functions on 32bit machines. + q := u / 1e9 + us := uint(u - q*1e9) // u % 1e9 fits into a uint + for j := 4; j > 0; j-- { + is := us % 100 * 2 + us /= 100 + i -= 2 + a[i+1] = smallsString[is+1] + a[i+0] = smallsString[is+0] + } + + // us < 10, since it contains the last digit + // from the initial 9-digit us. + i-- + a[i] = smallsString[us*2+1] + + u = q + } + // u < 1e9 + } + + // u guaranteed to fit into a uint + us := uint(u) + for us >= 100 { + is := us % 100 * 2 + us /= 100 + i -= 2 + a[i+1] = smallsString[is+1] + a[i+0] = smallsString[is+0] + } + + // us < 100 + is := us * 2 + i-- + a[i] = smallsString[is+1] + if us >= 10 { + i-- + a[i] = smallsString[is] + } + + } else if isPowerOfTwo(base) { + // Use shifts and masks instead of / and %. + // Base is a power of 2 and 2 <= base <= len(digits) where len(digits) is 36. + // The largest power of 2 below or equal to 36 is 32, which is 1 << 5; + // i.e., the largest possible shift count is 5. By &-ind that value with + // the constant 7 we tell the compiler that the shift count is always + // less than 8 which is smaller than any register width. This allows + // the compiler to generate better code for the shift operation. + shift := uint(TrailingZeros(uint(base))) & 7 + b := uint64(base) + m := uint(base) - 1 // == 1<= b { + i-- + a[i] = digits[uint(u)&m] + u >>= shift + } + // u < base + i-- + a[i] = digits[uint(u)] + } else { + // general case + b := uint64(base) + for u >= b { + i-- + // Avoid using r = a%b in addition to q = a/b + // since 64bit division and modulo operations + // are calculated by runtime functions on 32bit machines. + q := u / b + a[i] = digits[uint(u-q*b)] + u = q + } + // u < base + i-- + a[i] = digits[uint(u)] + } + + // add sign, if any + if neg { + i-- + a[i] = '-' + } + + if append_ { + d = append(dst, a[i:]...) + return + } + s = string(a[i:]) + return +} + +func isPowerOfTwo(x int) bool { + return x&(x-1) == 0 +} diff --git a/u256/u256.gno b/u256/u256.gno index da658629..b5a1bef5 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -254,7 +254,6 @@ func (z *Int) Div(x, y *Int) *Int { return z.Set(") } - // udivrem divides u by d and produces both quotient and remainder. // The quotient is stored in provided quot - len(u)-len(d)+1 words. // It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. @@ -316,7 +315,6 @@ func udivrem(quot, u []uint64, d *Int) (rem Int) { return rem } - // udivremBy1 divides u by single normalized word d and produces both quotient and remainder. // The quotient is stored in provided quot. func udivremBy1(quot, u []uint64, d uint64) (rem uint64) { @@ -382,7 +380,6 @@ func addTo(x, y []uint64) uint64 { return carry } - // subMulTo computes x -= y * multiplier. // Requires len(x) >= len(y). func subMulTo(x, y []uint64, multiplier uint64) uint64 { @@ -428,7 +425,6 @@ func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { return qh, r } - // Lsh sets z = x << n and returns z. func (z *Int) Lsh(x *Int, n uint) *Int { // n % 64 == 0 @@ -488,7 +484,6 @@ sh192: return z } - // Rsh sets z = x >> n and returns z. func (z *Int) Rsh(x *Int, n uint) *Int { // n % 64 == 0 @@ -672,7 +667,7 @@ func (z *Int) Xor(x, y *Int) *Int { z[3] = x[3] ^ y[3] return z } -/* + // MarshalJSON implements json.Marshaler. // MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible // with big.Int: big.Int marshals into JSON 'native' numeric format. @@ -695,12 +690,14 @@ func (z *Int) UnmarshalJSON(input []byte) error { return z.UnmarshalText(input[1 : len(input)-1]) } + // MarshalText implements encoding.TextMarshaler // MarshalText marshals using the decimal representation (compatible with big.Int) func (z *Int) MarshalText() ([]byte, error) { return []byte(z.Dec()), nil } + // UnmarshalText implements encoding.TextUnmarshaler. This method // can unmarshal either hexadecimal or decimal. // - For hexadecimal, the input _must_ be prefixed with 0x or 0X @@ -754,9 +751,9 @@ func FromDecimal(decimal string) (*Int, error) { } return &z, nil } -*/ + const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" -/* + // SetFromDecimal sets z from the given string, interpreted as a decimal number. // OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 10) method. // Notable differences: @@ -790,7 +787,6 @@ func (z *Int) SetFromDecimal(s string) (err error) { } return ErrBig256Range } -*/ var ( ErrEmptyString = errors.New("empty hex string") @@ -802,7 +798,6 @@ var ( ErrBadBufferLength = errors.New("bad ssz buffer length") ErrBadEncodedLength = errors.New("bad ssz encoded length") ) -/* func checkNumberS(input string) error { l := len(input) @@ -994,7 +989,6 @@ func parseUint(s string, base int, bitSize int) (uint64, error) { return n, nil } - // underscoreOK reports whether the underscores in s are allowed. // Checking them in this one function lets all the parsers skip over them simply. // Underscore must appear only between digits or between a base prefix and a digit. @@ -1083,7 +1077,7 @@ func (z *Int) Dec() string { rem := udivrem(quot[:], y[:], divisor) y.Set(") // Set Q for next loop // Convert the R to ascii representation - buf = strconv.AppendUint(buf[:0], rem.Uint64(), 10) + buf = AppendUint(buf[:0], rem.Uint64(), 10) // Copy in the ascii digits copy(out[pos-len(buf):], buf) if y.IsZero() { @@ -1095,4 +1089,3 @@ func (z *Int) Dec() string { // skip leading zeroes by only using the 'used size' of buf return string(out[pos-len(buf):]) } -*/ \ No newline at end of file From ef7aa520cf1670ae5b1635d7514d0845cf326a31 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 11 Dec 2023 20:24:07 +0900 Subject: [PATCH 04/23] With Println --- u256/runtest.gno | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/u256/runtest.gno b/u256/runtest.gno index 62a3e089..f946521e 100644 --- a/u256/runtest.gno +++ b/u256/runtest.gno @@ -2,29 +2,38 @@ package u256 func Hello() { q96 := "79228162514264337593543950336" + println("1111") bigStr := "833325865908927437441174649481942846" + println("2222") zeroStr := "0" + println("3333") /* 833325865908927437441174649481942846.67 >> 10518051.1508 */ b96, err := FromDecimal(q96) + println("4444") if err != nil { println("FROMDECIMAL ERROR Q96: ", err) } bigNum, err := FromDecimal(zeroStr) + println("5555") if err != nil { println("FROMDECIMAL ERROR BIG: ", err) } zero, err := FromDecimal(zeroStr) + println("6666") if err != nil { println("FROMDECIMAL ERROR ZERO: ", err) } zero.Div(bigNum, b96) + println("7777") println(zero.Dec()) + + println("8888") } From 18e8935013a65496232b70394956414e1ca0a456 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 20:32:11 +0900 Subject: [PATCH 05/23] fix --- u256/u256.gno | 238 +++++++++++++++++++++++++------------------------- 1 file changed, 120 insertions(+), 118 deletions(-) diff --git a/u256/u256.gno b/u256/u256.gno index b5a1bef5..08970803 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -11,7 +11,9 @@ const MaxUint64 = 1<<64 - 1 // Int is represented as an array of 4 uint64, in little-endian order, // so that Int[3] is the most significant, and Int[0] is the least significant -type Int [4]uint64 +type Int struct { + arr [4]uint64 +} // NewInt returns a new initialized Int. func NewInt(val uint64) *Int { @@ -22,40 +24,40 @@ func NewInt(val uint64) *Int { // Uint64 returns the lower 64-bits of z func (z *Int) Uint64() uint64 { - return z[0] + return z.arr[0] } // SetUint64 sets z to the value x func (z *Int) SetUint64(x uint64) *Int { - z[3], z[2], z[1], z[0] = 0, 0, 0, x + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x return z } // IsUint64 reports whether z can be represented as a uint64. func (z *Int) IsUint64() bool { - return (z[1] | z[2] | z[3]) == 0 + return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 } // IsZero returns true if z == 0 func (z *Int) IsZero() bool { - return (z[0] | z[1] | z[2] | z[3]) == 0 + return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 } // Clear sets z to 0 func (z *Int) Clear() *Int { - z[3], z[2], z[1], z[0] = 0, 0, 0, 0 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 return z } // SetAllOne sets all the bits of z to 1 func (z *Int) SetAllOne() *Int { - z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 return z } // Not sets z = ^x and returns z. func (z *Int) Not(x *Int) *Int { - z[3], z[2], z[1], z[0] = ^x[3], ^x[2], ^x[1], ^x[0] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] return z } @@ -67,16 +69,16 @@ func (z *Int) Gt(x *Int) bool { // Lt returns true if z < x func (z *Int) Lt(x *Int) bool { // z < x <=> z - x < 0 i.e. when subtraction overflows. - _, carry := Sub64(z[0], x[0], 0) - _, carry = Sub64(z[1], x[1], carry) - _, carry = Sub64(z[2], x[2], carry) - _, carry = Sub64(z[3], x[3], carry) + _, carry := Sub64(z.arr[0], x.arr[0], 0) + _, carry = Sub64(z.arr[1], x.arr[1], carry) + _, carry = Sub64(z.arr[2], x.arr[2], carry) + _, carry = Sub64(z.arr[3], x.arr[3], carry) return carry != 0 } // Eq returns true if z == x func (z *Int) Eq(x *Int) bool { - return (z[0] == x[0]) && (z[1] == x[1]) && (z[2] == x[2]) && (z[3] == x[3]) + return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) } // Cmp compares z and x and returns: @@ -86,10 +88,10 @@ func (z *Int) Eq(x *Int) bool { // +1 if z > x func (z *Int) Cmp(x *Int) (r int) { // z < x <=> z - x < 0 i.e. when subtraction overflows. - d0, carry := Sub64(z[0], x[0], 0) - d1, carry := Sub64(z[1], x[1], carry) - d2, carry := Sub64(z[2], x[2], carry) - d3, carry := Sub64(z[3], x[3], carry) + d0, carry := Sub64(z.arr[0], x.arr[0], 0) + d1, carry := Sub64(z.arr[1], x.arr[1], carry) + d2, carry := Sub64(z.arr[2], x.arr[2], carry) + d3, carry := Sub64(z.arr[3], x.arr[3], carry) if carry == 1 { return -1 } @@ -107,47 +109,47 @@ func (z *Int) Set(x *Int) *Int { // SetOne sets z to 1 func (z *Int) SetOne() *Int { - z[3], z[2], z[1], z[0] = 0, 0, 0, 1 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 1 return z } // Add sets z to the sum x+y func (z *Int) Add(x, y *Int) *Int { var carry uint64 - z[0], carry = Add64(x[0], y[0], 0) - z[1], carry = Add64(x[1], y[1], carry) - z[2], carry = Add64(x[2], y[2], carry) - z[3], _ = Add64(x[3], y[3], carry) + z.arr[0], carry = Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = Add64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = Add64(x.arr[3], y.arr[3], carry) return z } // AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { var carry uint64 - z[0], carry = Add64(x[0], y[0], 0) - z[1], carry = Add64(x[1], y[1], carry) - z[2], carry = Add64(x[2], y[2], carry) - z[3], carry = Add64(x[3], y[3], carry) + z.arr[0], carry = Add64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = Add64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = Add64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = Add64(x.arr[3], y.arr[3], carry) return z, carry != 0 } // SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { var carry uint64 - z[0], carry = Sub64(x[0], y[0], 0) - z[1], carry = Sub64(x[1], y[1], carry) - z[2], carry = Sub64(x[2], y[2], carry) - z[3], carry = Sub64(x[3], y[3], carry) + z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], carry = Sub64(x.arr[3], y.arr[3], carry) return z, carry != 0 } // Sub sets z to the difference x-y func (z *Int) Sub(x, y *Int) *Int { var carry uint64 - z[0], carry = Sub64(x[0], y[0], 0) - z[1], carry = Sub64(x[1], y[1], carry) - z[2], carry = Sub64(x[2], y[2], carry) - z[3], _ = Sub64(x[3], y[3], carry) + z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = Sub64(x.arr[3], y.arr[3], carry) return z } @@ -159,19 +161,19 @@ func (z *Int) Mul(x, y *Int) *Int { res1, res2, res3 uint64 ) - carry, res[0] = Mul64(x[0], y[0]) - carry, res1 = umulHop(carry, x[1], y[0]) - carry, res2 = umulHop(carry, x[2], y[0]) - res3 = x[3]*y[0] + carry + carry, res.arr[0] = Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + res3 = x.arr[3]*y.arr[0] + carry - carry, res[1] = umulHop(res1, x[0], y[1]) - carry, res2 = umulStep(res2, x[1], y[1], carry) - res3 = res3 + x[2]*y[1] + carry + carry, res.arr[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + res3 = res3 + x.arr[2]*y.arr[1] + carry - carry, res[2] = umulHop(res2, x[0], y[2]) - res3 = res3 + x[1]*y[2] + carry + carry, res.arr[2] = umulHop(res2, x.arr[0], y.arr[2]) + res3 = res3 + x.arr[1]*y.arr[2] + carry - res[3] = res3 + x[0]*y[3] + res.arr[3] = res3 + x.arr[0]*y.arr[3] return z.Set(&res) } @@ -179,7 +181,7 @@ func (z *Int) Mul(x, y *Int) *Int { // MulOverflow sets z to the product x*y, and returns z and whether overflow occurred func (z *Int) MulOverflow(x, y *Int) (*Int, bool) { p := umul(x, y) - copy(z[:], p[:4]) + copy(z.arr[:], p[:4]) return z, (p[4] | p[5] | p[6] | p[7]) != 0 } @@ -209,25 +211,25 @@ func umul(x, y *Int) [8]uint64 { res1, res2, res3, res4, res5 uint64 ) - carry, res[0] = Mul64(x[0], y[0]) - carry, res1 = umulHop(carry, x[1], y[0]) - carry, res2 = umulHop(carry, x[2], y[0]) - carry4, res3 = umulHop(carry, x[3], y[0]) + carry, res[0] = Mul64(x.arr[0], y.arr[0]) + carry, res1 = umulHop(carry, x.arr[1], y.arr[0]) + carry, res2 = umulHop(carry, x.arr[2], y.arr[0]) + carry4, res3 = umulHop(carry, x.arr[3], y.arr[0]) - carry, res[1] = umulHop(res1, x[0], y[1]) - carry, res2 = umulStep(res2, x[1], y[1], carry) - carry, res3 = umulStep(res3, x[2], y[1], carry) - carry5, res4 = umulStep(carry4, x[3], y[1], carry) + carry, res[1] = umulHop(res1, x.arr[0], y.arr[1]) + carry, res2 = umulStep(res2, x.arr[1], y.arr[1], carry) + carry, res3 = umulStep(res3, x.arr[2], y.arr[1], carry) + carry5, res4 = umulStep(carry4, x.arr[3], y.arr[1], carry) - carry, res[2] = umulHop(res2, x[0], y[2]) - carry, res3 = umulStep(res3, x[1], y[2], carry) - carry, res4 = umulStep(res4, x[2], y[2], carry) - carry6, res5 = umulStep(carry5, x[3], y[2], carry) + carry, res[2] = umulHop(res2, x.arr[0], y.arr[2]) + carry, res3 = umulStep(res3, x.arr[1], y.arr[2], carry) + carry, res4 = umulStep(res4, x.arr[2], y.arr[2], carry) + carry6, res5 = umulStep(carry5, x.arr[3], y.arr[2], carry) - carry, res[3] = umulHop(res3, x[0], y[3]) - carry, res[4] = umulStep(res4, x[1], y[3], carry) - carry, res[5] = umulStep(res5, x[2], y[3], carry) - res[7], res[6] = umulStep(carry6, x[3], y[3], carry) + carry, res[3] = umulHop(res3, x.arr[0], y.arr[3]) + carry, res[4] = umulStep(res4, x.arr[1], y.arr[3], carry) + carry, res[5] = umulStep(res5, x.arr[2], y.arr[3], carry) + res[7], res[6] = umulStep(carry6, x.arr[3], y.arr[3], carry) return res } @@ -250,7 +252,7 @@ func (z *Int) Div(x, y *Int) *Int { // x/y ; x > y > 0 var quot Int - udivrem(quot[:], x[:], y) + udivrem(quot.arr[:], x.arr[:], y) return z.Set(") } @@ -260,21 +262,21 @@ func (z *Int) Div(x, y *Int) *Int { // See Knuth, Volume 2, section 4.3.1, Algorithm D. func udivrem(quot, u []uint64, d *Int) (rem Int) { var dLen int - for i := len(d) - 1; i >= 0; i-- { - if d[i] != 0 { + for i := len(d.arr) - 1; i >= 0; i-- { + if d.arr[i] != 0 { dLen = i + 1 break } } - shift := uint(LeadingZeros64(d[dLen-1])) + shift := uint(LeadingZeros64(d.arr[dLen-1])) var dnStorage Int - dn := dnStorage[:dLen] + dn := dnStorage.arr[:dLen] for i := dLen - 1; i > 0; i-- { - dn[i] = (d[i] << shift) | (d[i-1] >> (64 - shift)) + dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) } - dn[0] = d[0] << shift + dn[0] = d.arr[0] << shift var uLen int for i := len(u) - 1; i >= 0; i-- { @@ -285,7 +287,7 @@ func udivrem(quot, u []uint64, d *Int) (rem Int) { } if uLen < dLen { - copy(rem[:], u) + copy(rem.arr[:], u) return rem } @@ -308,9 +310,9 @@ func udivrem(quot, u []uint64, d *Int) (rem Int) { udivremKnuth(quot, un, dn) for i := 0; i < dLen-1; i++ { - rem[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) + rem.arr[i] = (un[i] >> shift) | (un[i+1] << (64 - shift)) } - rem[dLen-1] = un[dLen-1] >> shift + rem.arr[dLen-1] = un[dLen-1] >> shift return rem } @@ -367,7 +369,7 @@ func udivremKnuth(quot, u, d []uint64) { // isBitSet returns true if bit n-th is set, where n = 0 is LSB. // The n must be <= 255. func (z *Int) isBitSet(n uint) bool { - return (z[n/64] & (1 << (n % 64))) != 0 + return (z.arr[n/64] & (1 << (n % 64))) != 0 } // addTo computes x += y. @@ -467,19 +469,19 @@ func (z *Int) Lsh(x *Int, n uint) *Int { } // remaining shifts - a = z[0] >> (64 - n) - z[0] = z[0] << n + a = z.arr[0] >> (64 - n) + z.arr[0] = z.arr[0] << n sh64: - b = z[1] >> (64 - n) - z[1] = (z[1] << n) | a + b = z.arr[1] >> (64 - n) + z.arr[1] = (z.arr[1] << n) | a sh128: - a = z[2] >> (64 - n) - z[2] = (z[2] << n) | b + a = z.arr[2] >> (64 - n) + z.arr[2] = (z.arr[2] << n) | b sh192: - z[3] = (z[3] << n) | a + z.arr[3] = (z.arr[3] << n) | a return z } @@ -526,19 +528,19 @@ func (z *Int) Rsh(x *Int, n uint) *Int { } // remaining shifts - a = z[3] << (64 - n) - z[3] = z[3] >> n + a = z.arr[3] << (64 - n) + z.arr[3] = z.arr[3] >> n sh64: - b = z[2] << (64 - n) - z[2] = (z[2] >> n) | a + b = z.arr[2] << (64 - n) + z.arr[2] = (z.arr[2] >> n) | a sh128: - a = z[1] << (64 - n) - z[1] = (z[1] >> n) | b + a = z.arr[1] << (64 - n) + z.arr[1] = (z.arr[1] >> n) | b sh192: - z[0] = (z[0] >> n) | a + z.arr[0] = (z.arr[0] >> n) | a return z } @@ -590,81 +592,81 @@ func (z *Int) SRsh(x *Int, n uint) *Int { } // remaining shifts - z[3], a = (z[3]>>n)|a, z[3]<<(64-n) + z.arr[3], a = (z.arr[3]>>n)|a, z.arr[3]<<(64-n) sh64: - z[2], a = (z[2]>>n)|a, z[2]<<(64-n) + z.arr[2], a = (z.arr[2]>>n)|a, z.arr[2]<<(64-n) sh128: - z[1], a = (z[1]>>n)|a, z[1]<<(64-n) + z.arr[1], a = (z.arr[1]>>n)|a, z.arr[1]<<(64-n) sh192: - z[0] = (z[0] >> n) | a + z.arr[0] = (z.arr[0] >> n) | a return z } func (z *Int) lsh64(x *Int) *Int { - z[3], z[2], z[1], z[0] = x[2], x[1], x[0], 0 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 return z } func (z *Int) lsh128(x *Int) *Int { - z[3], z[2], z[1], z[0] = x[1], x[0], 0, 0 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 return z } func (z *Int) lsh192(x *Int) *Int { - z[3], z[2], z[1], z[0] = x[0], 0, 0, 0 + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 return z } func (z *Int) rsh64(x *Int) *Int { - z[3], z[2], z[1], z[0] = 0, x[3], x[2], x[1] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] return z } func (z *Int) rsh128(x *Int) *Int { - z[3], z[2], z[1], z[0] = 0, 0, x[3], x[2] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] return z } func (z *Int) rsh192(x *Int) *Int { - z[3], z[2], z[1], z[0] = 0, 0, 0, x[3] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] return z } func (z *Int) srsh64(x *Int) *Int { - z[3], z[2], z[1], z[0] = MaxUint64, x[3], x[2], x[1] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] return z } func (z *Int) srsh128(x *Int) *Int { - z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, x[3], x[2] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] return z } func (z *Int) srsh192(x *Int) *Int { - z[3], z[2], z[1], z[0] = MaxUint64, MaxUint64, MaxUint64, x[3] + z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] return z } // Or sets z = x | y and returns z. func (z *Int) Or(x, y *Int) *Int { - z[0] = x[0] | y[0] - z[1] = x[1] | y[1] - z[2] = x[2] | y[2] - z[3] = x[3] | y[3] + z.arr[0] = x.arr[0] | y.arr[0] + z.arr[1] = x.arr[1] | y.arr[1] + z.arr[2] = x.arr[2] | y.arr[2] + z.arr[3] = x.arr[3] | y.arr[3] return z } // And sets z = x & y and returns z. func (z *Int) And(x, y *Int) *Int { - z[0] = x[0] & y[0] - z[1] = x[1] & y[1] - z[2] = x[2] & y[2] - z[3] = x[3] & y[3] + z.arr[0] = x.arr[0] & y.arr[0] + z.arr[1] = x.arr[1] & y.arr[1] + z.arr[2] = x.arr[2] & y.arr[2] + z.arr[3] = x.arr[3] & y.arr[3] return z } // Xor sets z = x ^ y and returns z. func (z *Int) Xor(x, y *Int) *Int { - z[0] = x[0] ^ y[0] - z[1] = x[1] ^ y[1] - z[2] = x[2] ^ y[2] - z[3] = x[3] ^ y[3] + z.arr[0] = x.arr[0] ^ y.arr[0] + z.arr[1] = x.arr[1] ^ y.arr[1] + z.arr[2] = x.arr[2] ^ y.arr[2] + z.arr[3] = x.arr[3] ^ y.arr[3] return z } @@ -734,8 +736,8 @@ func (z *Int) fromHex(hex string) error { if nib == badNibble { return ErrSyntax } - z[i] = z[i] << 4 - z[i] += uint64(nib) + z.arr[i] = z.arr[i] << 4 + z.arr[i] += uint64(nib) } end = start } @@ -820,10 +822,10 @@ func checkNumberS(input string) error { // multipliers holds the values that are needed for fromDecimal var multipliers = [5]*Int{ nil, // represents first round, no multiplication needed - {10000000000000000000, 0, 0, 0}, // 10 ^ 19 - {687399551400673280, 5421010862427522170, 0, 0}, // 10 ^ 38 - {5332261958806667264, 17004971331911604867, 2938735877055718769, 0}, // 10 ^ 57 - {0, 8607968719199866880, 532749306367912313, 1593091911132452277}, // 10 ^ 76 + &Int{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 + &Int{[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 + &Int{[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 + &Int{[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 } // fromDecimal is a helper function to only ever be called via SetFromDecimal @@ -1074,7 +1076,7 @@ func (z *Int) Dec() string { for { // Obtain Q and R for divisor var quot Int - rem := udivrem(quot[:], y[:], divisor) + rem := udivrem(quot.arr[:], y.arr[:], divisor) y.Set(") // Set Q for next loop // Convert the R to ascii representation buf = AppendUint(buf[:0], rem.Uint64(), 10) From 954709ec7ddbf6f622997e663fa8263481cd9696 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Mon, 11 Dec 2023 20:37:27 +0900 Subject: [PATCH 06/23] return string --- u256/runtest.gno | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/u256/runtest.gno b/u256/runtest.gno index f946521e..c9df8cf4 100644 --- a/u256/runtest.gno +++ b/u256/runtest.gno @@ -1,6 +1,6 @@ package u256 -func Hello() { +func Hello() string { q96 := "79228162514264337593543950336" println("1111") bigStr := "833325865908927437441174649481942846" @@ -36,4 +36,6 @@ func Hello() { println(zero.Dec()) println("8888") + + return zero.Dec() } From 69b232ddac94d7533fca9e699bf3f1c8b25ae185 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 20:43:46 +0900 Subject: [PATCH 07/23] remove strconv --- u256/u256.gno | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/u256/u256.gno b/u256/u256.gno index 08970803..7d78daba 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -4,7 +4,6 @@ package u256 import ( "errors" - "strconv" ) const MaxUint64 = 1<<64 - 1 @@ -1047,7 +1046,7 @@ func (z *Int) Dec() string { return "0" } if z.IsUint64() { - return strconv.FormatUint(z.Uint64(), 10) + return FormatUint(z.Uint64(), 10) } // The max uint64 value being 18446744073709551615, the largest // power-of-ten below that is 10000000000000000000. From 8e6092c9f2393152895211f86fce0af64ad7b714 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 20:46:29 +0900 Subject: [PATCH 08/23] asdf --- u256/runtest.gno | 42 ++++-------------------------------------- 1 file changed, 4 insertions(+), 38 deletions(-) diff --git a/u256/runtest.gno b/u256/runtest.gno index c9df8cf4..e83d80d9 100644 --- a/u256/runtest.gno +++ b/u256/runtest.gno @@ -1,41 +1,7 @@ package u256 -func Hello() string { - q96 := "79228162514264337593543950336" - println("1111") - bigStr := "833325865908927437441174649481942846" - println("2222") - zeroStr := "0" - println("3333") - /* - 833325865908927437441174649481942846.67 - >> 10518051.1508 - */ - - b96, err := FromDecimal(q96) - println("4444") - if err != nil { - println("FROMDECIMAL ERROR Q96: ", err) - } - - bigNum, err := FromDecimal(zeroStr) - println("5555") - if err != nil { - println("FROMDECIMAL ERROR BIG: ", err) - } - - zero, err := FromDecimal(zeroStr) - println("6666") - if err != nil { - println("FROMDECIMAL ERROR ZERO: ", err) - } - - zero.Div(bigNum, b96) - println("7777") - - println(zero.Dec()) - - println("8888") - - return zero.Dec() +func Hello() uint64 { + x := NewInt(1) + y := NewInt(2) + return x.Add(x, y).Uint64() } From bd8099d8af9b224efa3571920a635d2dcd36a072 Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 11 Dec 2023 20:48:50 +0900 Subject: [PATCH 09/23] asdf --- u256/runtest.gno | 3 +-- u256/u256.gno | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/u256/runtest.gno b/u256/runtest.gno index e83d80d9..4a40833c 100644 --- a/u256/runtest.gno +++ b/u256/runtest.gno @@ -2,6 +2,5 @@ package u256 func Hello() uint64 { x := NewInt(1) - y := NewInt(2) - return x.Add(x, y).Uint64() + return x.Uint64() } diff --git a/u256/u256.gno b/u256/u256.gno index 7d78daba..4741312d 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -16,8 +16,7 @@ type Int struct { // NewInt returns a new initialized Int. func NewInt(val uint64) *Int { - z := &Int{} - z.SetUint64(val) + z := &Int{arr: [4]uint64{val, 0, 0, 0}} return z } From 537d84d128a1ea67df93d26676e276c90341242e Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 7 Feb 2024 15:17:39 +0900 Subject: [PATCH 10/23] applying u256 to pool --- pool/bit_math.gno | 155 +++++++++++++++++++++++++++---------------- pool/tick_bitmap.gno | 11 +-- u256/u256.gno | 27 +++++++- u256/u256_test.gno | 11 +++ 4 files changed, 141 insertions(+), 63 deletions(-) create mode 100644 u256/u256_test.gno diff --git a/pool/bit_math.gno b/pool/bit_math.gno index e22ddd73..270d1778 100644 --- a/pool/bit_math.gno +++ b/pool/bit_math.gno @@ -2,67 +2,106 @@ package pool import ( "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" + + "gno.land/p/demo/u256" ) -type bitShift struct { - bitPattern bigint // mask or limit - shift uint -} +func bitMathMostSignificantBit(x *u256.Int) uint8 { + require(x.Gt(u256.NewInt(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || x(%s) > 0", x)) + + r := uint8(0) + if x.Gte(u256.FromBigint(0x100000000000000000000000000000000)) { + x.Rsh(x, 128) + r += 128 + } + + if x.Gte(u256.FromBigint(0x10000000000000000)) { + x.Rsh(x, 64) + r += 64 + } + + if x.Gte(u256.NewInt(0x100000000)) { + x.Rsh(x, 32) + r += 32 + } + + if x.Gte(u256.NewInt(0x10000)) { + x.Rsh(x, 16) + r += 16 + } + + if x.Gte(u256.NewInt(0x100)) { + x.Rsh(x, 8) + r += 8 + } + + if x.Gte(u256.NewInt(0x10)) { + x.Rsh(x, 4) + r += 4 + } + + if x.Gte(u256.NewInt(0x4)) { + x.Rsh(x, 2) + r += 2 + } + + if x.Gte(u256.NewInt(0x2)) { + r += 1 + } -func bitMathMostSignificantBit(x bigint) uint8 { - requirePositive( - x, - ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || expected x(%d) > 0", x), - ) - - shifts := []bitShift{ - {0x100000000000000000000000000000000, 128}, - {0x10000000000000000, 64}, - {0x100000000, 32}, - {0x10000, 16}, - {0x100, 8}, - {0x10, 4}, - {0x4, 2}, - {0x2, 1}, - } - - r := uint8(0) - for _, s := range shifts { - if x >= s.bitPattern { - x >>= s.shift - r += uint8(s.shift) - } - } - - return r + return r } -func bitMathLeastSignificantBit(x bigint) uint8 { - requirePositive( - x, - ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || expected x(%d) > 0", x), - ) - - shifts := []bitShift{ - {consts.MAX_UINT128, 128}, - {consts.MAX_UINT64, 64}, - {consts.MAX_UINT32, 32}, - {consts.MAX_UINT16, 16}, - {consts.MAX_UINT8, 8}, - {0xf, 4}, - {0x3, 2}, - {0x1, 1}, - } - - r := uint8(255) - for _, s := range shifts { - if x&s.bitPattern > 0 { - r -= uint8(s.shift) - } else { - x >>= s.shift - } - } - - return r +func bitMathLeastSignificantBit(x *u256.Int) uint8 { + require(x.Gt(u256.NewInt(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || x(%s) > 0", x)) + + r := uint8(255) + + if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT128)).Gt(u256.NewInt(0)) { + r -= 128 + } else { + x.Rsh(x, 128) + } + + if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT64)).Gt(u256.NewInt(0)) { + r -= 64 + } else { + x.Rsh(x, 64) + } + + if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT32)).Gt(u256.NewInt(0)) { + r -= 32 + } else { + x.Rsh(x, 32) + } + + if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT16)).Gt(u256.NewInt(0)) { + r -= 16 + } else { + x.Rsh(x, 16) + } + + if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT8)).Gt(u256.NewInt(0)) { + r -= 8 + } else { + x.Rsh(x, 8) + } + + if u256.NewInt(0).And(x, u256.FromBigint(0xf)).Gt(u256.NewInt(0)) { + r -= 4 + } else { + x.Rsh(x, 4) + } + + if u256.NewInt(0).And(x, u256.FromBigint(0x3)).Gt(u256.NewInt(0)) { + r -= 2 + } else { + x.Rsh(x, 2) + } + + if u256.NewInt(0).And(x, u256.FromBigint(0x1)).Gt(u256.NewInt(0)) { + r -= 1 + } + + return r } diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index c7def151..00bbfbc8 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -2,7 +2,8 @@ package pool import ( "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" + + "gno.land/p/demo/u256" ) func tickBitmapPosition(tick int32) (int16, uint8) { @@ -25,7 +26,9 @@ func (pool *Pool) tickBitmapFlipTick( ) wordPos, bitPos := tickBitmapPosition(tick / tickSpacing) - mask := bigint(1) << uint64(bitPos) // 2 ** bitPos + mask := + bigint(1) << + uint64(bitPos) // 2 ** bitPos requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) >= 0", mask)) @@ -54,7 +57,7 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( initialized := masked != 0 if initialized { - next := (compress - int32(bitPos-bitMathMostSignificantBit(masked))) * tickSpacing + next := (compress - int32(bitPos-bitMathMostSignificantBit(u256.FromBigint(mask)))) * tickSpacing return next, initialized } @@ -74,7 +77,7 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( initialized := masked != 0 if initialized { - next := (compress + 1 + int32(bitMathLeastSignificantBit(masked)-bitPos)) * tickSpacing + next := (compress + 1 + int32(bitMathLeastSignificantBit(u256.FromBigint(masked))-bitPos)) * tickSpacing return next, initialized } else { next := (compress + 1 + int32(int64(consts.MAX_UINT8-bigint(uint64(bitPos))))) * tickSpacing diff --git a/u256/u256.gno b/u256/u256.gno index 4741312d..bc024b40 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -8,18 +8,39 @@ import ( const MaxUint64 = 1<<64 - 1 +// TODO: remove +const MaxUint256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + // Int is represented as an array of 4 uint64, in little-endian order, // so that Int[3] is the most significant, and Int[0] is the least significant type Int struct { arr [4]uint64 } +// TODO: to be removed +func FromBigint(x bigint) *Int { + if x > MaxUint256 { + panic("overflow") + } + + var z Int + z.arr[0] = uint64(x) + z.arr[1] = uint64(x >> 64) + z.arr[2] = uint64(x >> 128) + z.arr[3] = uint64(x >> 192) + + return &z +} // NewInt returns a new initialized Int. func NewInt(val uint64) *Int { z := &Int{arr: [4]uint64{val, 0, 0, 0}} return z } - +/* +func ToString(x *Int) string { + return "tostring not implemented" +} +*/ // Uint64 returns the lower 64-bits of z func (z *Int) Uint64() uint64 { return z.arr[0] @@ -64,6 +85,10 @@ func (z *Int) Gt(x *Int) bool { return x.Lt(z) } +func (z *Int) Gte(x *Int) bool { + return !z.Lt(x) +} + // Lt returns true if z < x func (z *Int) Lt(x *Int) bool { // z < x <=> z - x < 0 i.e. when subtraction overflows. diff --git a/u256/u256_test.gno b/u256/u256_test.gno new file mode 100644 index 00000000..50201e5a --- /dev/null +++ b/u256/u256_test.gno @@ -0,0 +1,11 @@ +package u256 + +import ( + "testing" +) + +func TestArithmetic(t *testing.T) { + x := NewInt(1) + y := NewInt(2) + z := NewInt(0).Add(x, y) +} \ No newline at end of file From a4fd7376b521d3ae324155b683990c2782100c74 Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 7 Feb 2024 18:15:32 +0900 Subject: [PATCH 11/23] pool refactor to use u256 in progress --- pool/_RPC_api.gno | 45 ++++---- pool/_RPC_dry.gno | 24 +++-- pool/bit_math.gno | 36 +++---- pool/liquidity_math.gno | 24 +++-- pool/pool.gno | 145 ++++++++++++++++++++++--- pool/pool_manager.gno | 19 ++-- pool/position.gno | 26 ++--- pool/sqrt_price_math.gno | 201 +++++------------------------------ pool/swap_math.gno | 20 ++-- pool/tick.gno | 65 +++++------ pool/tick_bitmap.gno | 10 +- pool/type.gno | 70 ++++++------ pool/utils.gno | 12 ++- position/sqrt_price_math.gno | 10 +- u256/i256.gno | 78 ++++++++++++++ u256/runtest.gno | 2 +- u256/u256.gno | 193 +++++++++++++++++++-------------- u256/u256_test.gno | 6 +- 18 files changed, 528 insertions(+), 458 deletions(-) create mode 100644 u256/i256.gno diff --git a/pool/_RPC_api.gno b/pool/_RPC_api.gno index 6f03346a..24e369c3 100644 --- a/pool/_RPC_api.gno +++ b/pool/_RPC_api.gno @@ -9,6 +9,7 @@ import ( "time" "gno.land/p/demo/ufmt" + "gno.land/p/demo/u256" ) type RpcPool struct { @@ -17,8 +18,8 @@ type RpcPool struct { Token0Path string `json:"token0Path"` Token1Path string `json:"token1Path"` - BalancesToken0 bigint `json:"balanceToken0"` - BalancesToken1 bigint `json:"balanceToken1"` + BalancesToken0 *u256.Uint `json:"balanceToken0"` + BalancesToken1 *u256.Uint `json:"balanceToken1"` // fee is the fee tier of the pool Fee uint16 `json:"fee"` @@ -27,22 +28,22 @@ type RpcPool struct { TickSpacing int32 `json:"tickSpacing"` // maxLiquidityPerTick is the maximum amount of liquidity that can be added per tick - MaxLiquidityPerTick bigint `json:"maxLiquidityPerTick"` + MaxLiquidityPerTick *u256.Uint `json:"maxLiquidityPerTick"` // slot0 is the current tick and price of the pool - Slot0SqrtPriceX96 bigint `json:"sqrtPriceX96"` + Slot0SqrtPriceX96 *u256.Uint `json:"sqrtPriceX96"` Slot0Tick int32 `json:"tick"` Slot0FeeProtocol uint8 `json:"feeProtocol"` Slot0Unlocked bool `json:"unlocked"` - FeeGrowthGlobal0X128 bigint `json:"feeGrowthGlobal0X128"` - FeeGrowthGlobal1X128 bigint `json:"feeGrowthGlobal1X128"` + FeeGrowthGlobal0X128 *u256.Uint `json:"feeGrowthGlobal0X128"` + FeeGrowthGlobal1X128 *u256.Uint `json:"feeGrowthGlobal1X128"` - ProtocolFeesToken0 bigint `json:"protocolFeeToken0"` - ProtocolFeesToken1 bigint `json:"protocolFeeToken1"` + ProtocolFeesToken0 *u256.Uint `json:"protocolFeeToken0"` + ProtocolFeesToken1 *u256.Uint `json:"protocolFeeToken1"` // liquidity is the total amount of liquidity in the pool - Liquidity bigint `json:"liquidity"` + Liquidity *u256.Uint `json:"liquidity"` // ticks is a mapping from tick index to tick Ticks RpcTicks `json:"ticks"` @@ -56,16 +57,16 @@ type RpcPool struct { type RpcTicks map[int32]RpcTickInfo // tick => RpcTickInfo type RpcTickInfo struct { - LiquidityGross bigint `json:"liquidityGross"` - LiquidityNet bigint `json:"liquidityNet"` + LiquidityGross *u256.Uint `json:"liquidityGross"` + LiquidityNet *u256.Int `json:"liquidityNet"` - FeeGrowthOutside0X128 bigint `json:"feeGrowthOutside0X128"` - FeeGrowthOutside1X128 bigint `json:"feeGrowthOutside1X128"` + FeeGrowthOutside0X128 *u256.Uint `json:"feeGrowthOutside0X128"` + FeeGrowthOutside1X128 *u256.Uint `json:"feeGrowthOutside1X128"` - TickCumulativeOutside bigint `json:"tickCumulativeOutside"` + //TickCumulativeOutside bigint `json:"tickCumulativeOutside"` - SecondsPerLiquidityOutsideX bigint `json:"secondsPerLiquidityOutsideX"` - SecondsOutside bigint `json:"secondsOutside"` + //SecondsPerLiquidityOutsideX bigint `json:"secondsPerLiquidityOutsideX"` + //SecondsOutside bigint `json:"secondsOutside"` Initialized bool `json:"initialized"` } @@ -76,10 +77,10 @@ type RpcPosition struct { TickLower bigint `json:"tickLower"` TickUpper bigint `json:"tickUpper"` - Liquidity bigint `json:"liquidity"` + Liquidity *u256.Uint `json:"liquidity"` - Token0Owed bigint `json:"token0Owed"` - Token1Owed bigint `json:"token1Owed"` + Token0Owed *u256.Uint `json:"token0Owed"` + Token1Owed *u256.Uint `json:"token1Owed"` } type ResponseQueryBase struct { @@ -153,9 +154,9 @@ func rpcMakePool(poolPath string) RpcPool { LiquidityNet: tickInfo.liquidityNet, FeeGrowthOutside0X128: tickInfo.feeGrowthOutside0X128, FeeGrowthOutside1X128: tickInfo.feeGrowthOutside1X128, - TickCumulativeOutside: tickInfo.tickCumulativeOutside, - SecondsPerLiquidityOutsideX: tickInfo.secondsPerLiquidityOutsideX128, - SecondsOutside: tickInfo.secondsOutside, + //TickCumulativeOutside: tickInfo.tickCumulativeOutside, + //SecondsPerLiquidityOutsideX: tickInfo.secondsPerLiquidityOutsideX128, + //SecondsOutside: tickInfo.secondsOutside, Initialized: tickInfo.initialized, } } diff --git a/pool/_RPC_dry.gno b/pool/_RPC_dry.gno index 8aec30a2..074ebefb 100644 --- a/pool/_RPC_dry.gno +++ b/pool/_RPC_dry.gno @@ -5,8 +5,7 @@ import ( "gno.land/p/demo/common" "gno.land/p/demo/ufmt" - - "gno.land/r/demo/consts" + "gno.land/p/demo/u256" ) func DrySwap( @@ -15,23 +14,26 @@ func DrySwap( pFee uint16, recipient std.Address, zeroForOne bool, - amountSpecified bigint, - sqrtPriceLimitX96 bigint, + amountSpecified_ bigint, + sqrtPriceLimitX96_ bigint, ) (bigint, bigint, bool) { - if amountSpecified == 0 { + if amountSpecified_ == 0 { return 0, 0, false } pool := GetPool(token0Path, token1Path, pFee) slot0Start := pool.slot0 + amountSpecified := u256.FromBigint(amountSpecified_) + sqrtPriceLimitX96 := u256.FromBigint(sqrtPriceLimitX96_) + if zeroForOne { - if !(sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > consts.MIN_SQRT_RATIO) { + if !(sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Gt(u256.FromBigint(MIN_SQRT_RATIO))) { return 0, 0, false } } else { - if !(sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < consts.MAX_SQRT_RATIO) { + if !(sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Lt(u256.FromBigint(MAX_SQRT_RATIO))) { return 0, 0, false } } @@ -51,12 +53,12 @@ func DrySwap( } } - exactInput := amountSpecified > 0 + exactInput := amountSpecified.Gt(u256.Zero()) var state SwapState if zeroForOne { state = SwapState{ - amountSpecifiedRemaining: amountSpecified, + amountSpecifiedRemaining: amountSpecified.Int(), amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, @@ -66,7 +68,7 @@ func DrySwap( } } else { state = SwapState{ - amountSpecifiedRemaining: amountSpecified, + amountSpecifiedRemaining: amountSpecified.Int(), amountCalculated: 0, sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, @@ -77,7 +79,7 @@ func DrySwap( } // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit - for state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96 { + for state.amountSpecifiedRemaining.NotZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { var step StepComputations step.sqrtPriceStartX96 = state.sqrtPriceX96 diff --git a/pool/bit_math.gno b/pool/bit_math.gno index 270d1778..6db50d8a 100644 --- a/pool/bit_math.gno +++ b/pool/bit_math.gno @@ -6,8 +6,8 @@ import ( "gno.land/p/demo/u256" ) -func bitMathMostSignificantBit(x *u256.Int) uint8 { - require(x.Gt(u256.NewInt(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || x(%s) > 0", x)) +func bitMathMostSignificantBit(x *u256.Uint) uint8 { + require(x.Gt(u256.NewUint(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || x(%s) > 0", x)) r := uint8(0) if x.Gte(u256.FromBigint(0x100000000000000000000000000000000)) { @@ -20,86 +20,86 @@ func bitMathMostSignificantBit(x *u256.Int) uint8 { r += 64 } - if x.Gte(u256.NewInt(0x100000000)) { + if x.Gte(u256.NewUint(0x100000000)) { x.Rsh(x, 32) r += 32 } - if x.Gte(u256.NewInt(0x10000)) { + if x.Gte(u256.NewUint(0x10000)) { x.Rsh(x, 16) r += 16 } - if x.Gte(u256.NewInt(0x100)) { + if x.Gte(u256.NewUint(0x100)) { x.Rsh(x, 8) r += 8 } - if x.Gte(u256.NewInt(0x10)) { + if x.Gte(u256.NewUint(0x10)) { x.Rsh(x, 4) r += 4 } - if x.Gte(u256.NewInt(0x4)) { + if x.Gte(u256.NewUint(0x4)) { x.Rsh(x, 2) r += 2 } - if x.Gte(u256.NewInt(0x2)) { + if x.Gte(u256.NewUint(0x2)) { r += 1 } return r } -func bitMathLeastSignificantBit(x *u256.Int) uint8 { - require(x.Gt(u256.NewInt(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || x(%s) > 0", x)) +func bitMathLeastSignificantBit(x *u256.Uint) uint8 { + require(x.Gt(u256.NewUint(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || x(%s) > 0", x)) r := uint8(255) - if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT128)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT128)).Gt(u256.NewUint(0)) { r -= 128 } else { x.Rsh(x, 128) } - if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT64)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT64)).Gt(u256.NewUint(0)) { r -= 64 } else { x.Rsh(x, 64) } - if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT32)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT32)).Gt(u256.NewUint(0)) { r -= 32 } else { x.Rsh(x, 32) } - if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT16)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT16)).Gt(u256.NewUint(0)) { r -= 16 } else { x.Rsh(x, 16) } - if u256.NewInt(0).And(x, u256.FromBigint(MAX_UINT8)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT8)).Gt(u256.NewUint(0)) { r -= 8 } else { x.Rsh(x, 8) } - if u256.NewInt(0).And(x, u256.FromBigint(0xf)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(0xf)).Gt(u256.NewUint(0)) { r -= 4 } else { x.Rsh(x, 4) } - if u256.NewInt(0).And(x, u256.FromBigint(0x3)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(0x3)).Gt(u256.NewUint(0)) { r -= 2 } else { x.Rsh(x, 2) } - if u256.NewInt(0).And(x, u256.FromBigint(0x1)).Gt(u256.NewInt(0)) { + if u256.NewUint(0).And(x, u256.FromBigint(0x1)).Gt(u256.NewUint(0)) { r -= 1 } diff --git a/pool/liquidity_math.gno b/pool/liquidity_math.gno index 78f0f56c..4b5cc461 100644 --- a/pool/liquidity_math.gno +++ b/pool/liquidity_math.gno @@ -1,13 +1,21 @@ package pool -import "gno.land/p/demo/ufmt" +import ( + "gno.land/p/demo/ufmt" -func liquidityMathAddDelta(x bigint, y bigint) bigint { - z := x + y + "gno.land/p/demo/u256" +) - if z < 0 { - panic(ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || Expected z(%d) to be non-negative", z)) - } - - return z +func liquidityMathAddDelta(x *u256.Uint, y *u256.Int) *u256.Uint { + if y < 0 { + z := x - (-y) + requireUnsigned(z, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= 0__#1", z)) + require(z < x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) < x(%d)", z, x)) + return z + } else { + z := x + y + requireUnsigned(z, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= 0__#2", z)) + require(z >= x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= x(%d)", z, x)) + return z + } } diff --git a/pool/pool.gno b/pool/pool.gno index 8aea42b1..faa63f21 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -6,9 +6,7 @@ import ( "gno.land/p/demo/common" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" - - gv "gno.land/r/demo/gov" + "gno.land/p/demo/u256" ) // only position contract can call this function @@ -19,13 +17,15 @@ func Mint( recipient std.Address, tickLower int32, tickUpper int32, - liquidityAmount bigint, -) (bigint, bigint) { - requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected std.PrevRealm().PkgPath()(%s), got %s", consts.POSITION_PATH, std.PrevRealm().PkgPath())) + liquidityAmount_ bigint, +) (bigint, bigint /* *u256.Int, *u256.Int */) { + require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Mint() || PrevRealmPath(%s) == \"gno.land/r/demo/position\"", PrevRealmPath())) requirePositive(liquidityAmount, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected liquidityAmount(%d) > 0", liquidityAmount)) - pool := GetPool(token0Path, token1Path, fee) + liquidityAmount := u256.FromBigint(liquidityAmount_) + + pool := GetPool(pToken0Path, pToken1Path, pFee) _, amount0Int, amount1Int := pool.modifyPosition( ModifyPositionParams{ recipient, // owner @@ -40,7 +40,7 @@ func Mint( amount1 := bigint(amount1Int) requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected amount1(%d) >= 0", amount1)) - var balance0Before, balance1Before bigint + var balance0Before, balance1Before u256.Int if amount0 > 0 { balance0Before := bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())) @@ -496,12 +496,10 @@ func CollectProtocol( token1Path string, fee uint16, recipient std.Address, - amount0Requested bigint, - amount1Requested bigint, + amount0Requested *u256.Uint, + amount1Requested *u256.Uint, ) (bigint, bigint) { - requireUnsigned(amount0Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0Requested(%d) >= 0", amount0Requested)) - requireUnsigned(amount1Requested, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1Requested(%d) >= 0", amount1Requested)) - require(isAdmin(std.PrevRealm().Addr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", std.PrevRealm().Addr())) + require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr())) pool := GetPool(token0Path, token1Path, fee) @@ -535,6 +533,127 @@ func CollectProtocol( return amount0, amount1 } +// XXXXX: i made some mistake on int/uint while refactoring this part, please double check it before merge +func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u256.Int, *u256.Int) { + position := pool.updatePosition( + params.owner, + params.tickLower, + params.tickUpper, + params.liquidityDelta, + pool.slot0.tick, + ) + + amount0 := u256.Zero() + amount1 := u256.Zero() + + if params.liquidityDelta != 0 { + if pool.slot0.tick < params.tickLower { + amount0 = sqrtPriceMathGetAmount0Delta( + TickMathGetSqrtRatioAtTick(params.tickLower), + TickMathGetSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta, + ) + } else if pool.slot0.tick < params.tickUpper { + liquidityBefore := pool.liquidity + + amount0 = sqrtPriceMathGetAmount0Delta( + pool.slot0.sqrtPriceX96, + TickMathGetSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta, + ) + + amount1 = sqrtPriceMathGetAmount1Delta( + TickMathGetSqrtRatioAtTick(params.tickLower), + pool.slot0.sqrtPriceX96, + params.liquidityDelta, + ) + + pool.liquidity = liquidityMathAddDelta(liquidityBefore, params.liquidityDelta) + + } else { + amount1 = sqrtPriceMathGetAmount1Delta( + TickMathGetSqrtRatioAtTick(params.tickLower), + TickMathGetSqrtRatioAtTick(params.tickUpper), + params.liquidityDelta, + ) + } + } + + return position, amount0, amount1 +} + +func (pool *Pool) updatePosition( + owner std.Address, + tickLower int32, + tickUpper int32, + liquidityDelta *u256.Int, + tick int32, +) PositionInfo { + var _feeGrowthGlobal0X128 bigint = pool.feeGrowthGlobal0X128 + var _feeGrowthGlobal1X128 bigint = pool.feeGrowthGlobal1X128 + + var flippedLower, flippedUpper bool + if liquidityDelta != 0 { + flippedLower = pool.tickUpdate( + tickLower, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + false, + pool.maxLiquidityPerTick, + ) + + flippedUpper = pool.tickUpdate( + tickUpper, + tick, + liquidityDelta, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + true, + pool.maxLiquidityPerTick, + ) + + if flippedLower { + pool.tickBitmapFlipTick(tickLower, pool.tickSpacing) + } + + if flippedUpper { + pool.tickBitmapFlipTick(tickUpper, pool.tickSpacing) + } + } + + // NO LIQ, ONLY BURN 0 + + feeGrowthInside0X128, feeGrowthInside1X128 := pool.tickGetFeeGrowthInside( + tickLower, + tickUpper, + tick, + _feeGrowthGlobal0X128, + _feeGrowthGlobal1X128, + ) + + key := positionGetKey(owner, tickLower, tickUpper) + + position := pool.positionUpdateWithKey( + key, + liquidityDelta, + feeGrowthInside0X128, + feeGrowthInside1X128, + ) + + if liquidityDelta < 0 { + if flippedLower { + pool.tickClear(tickLower) + } + + if flippedUpper { + pool.tickClear(tickUpper) + } + } + return position +} + func (pool *Pool) saveProtocolFees(amount0, amount1 bigint) (bigint, bigint) { if amount0 > 0 && amount0 == pool.protocolFees.token0 { amount0-- diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index fab50b43..b5a8200f 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -5,11 +5,10 @@ import ( "strconv" "gno.land/p/demo/common" + "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" - - "gno.land/r/demo/gns" + gns "gno.land/r/demo/gns" ) var ( @@ -37,15 +36,17 @@ func CreatePool( token0Path string, token1Path string, fee uint16, - sqrtPriceX96 bigint, + sqrtPriceX96_ bigint, ) *Pool { require(initialized, ufmt.Sprintf("[POOl] pool_manager.gno__gno__CreatePool() || expected initialized(%t) == true", initialized)) require(token0Path != token1Path, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || expected token0Path(%s) != token1Path(%s)", token0Path, token1Path)) - if token1Path < token0Path { - token0Path, token1Path = token1Path, token0Path - tick := -(common.TickMathGetTickAtSqrtRatio(sqrtPriceX96)) - sqrtPriceX96 = common.TickMathGetSqrtRatioAtTick(tick) + sqrtPriceX96 := u256.FromBigint(sqrtPriceX96_) + + if tokenBPath < tokenAPath { + tokenAPath, tokenBPath = tokenBPath, tokenAPath + tick := -(TickMathGetTickAtSqrtRatio(sqrtPriceX96)) + sqrtPriceX96 = TickMathGetSqrtRatioAtTick(tick) } // check tickSpacing for fee @@ -124,7 +125,7 @@ func newPool( token1Path string, fee uint16, tickSpacing int32, - sqrtPriceX96 bigint, + sqrtPriceX96 *u256.Uint, ) *Pool { maxLiquidityPerTick := tickTickSpacingToMaxLiquidityPerTick(tickSpacing) tick := common.TickMathGetTickAtSqrtRatio(sqrtPriceX96) diff --git a/pool/position.gno b/pool/position.gno index a4f4c374..fb1d0ce4 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -4,8 +4,8 @@ import ( "encoding/base64" "std" + "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" ) func positionGetKey( @@ -20,15 +20,12 @@ func positionGetKey( } func (pool *Pool) positionUpdateWithKey( - positionKey string, - liquidityDelta bigint, - feeGrowthInside0X128 bigint, - feeGrowthInside1X128 bigint, + key string, + liquidityDelta *u256.Int, + feeGrowthInside0X128 *u256.Uint, + feeGrowthInside1X128 *u256.Uint, ) PositionInfo { - requireUnsigned(feeGrowthInside0X128, ufmt.Sprintf("[POOL] position.gno__positionUpdateWithKey() || feeGrowthInside0X128(%d) >= 0", feeGrowthInside0X128)) - requireUnsigned(feeGrowthInside1X128, ufmt.Sprintf("[POOL] position.gno__positionUpdateWithKey() || feeGrowthInside1X128(%d) >= 0", feeGrowthInside1X128)) - - position := pool.positions[positionKey] + position := pool.positions[key] p := positionUpdate(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128) pool.positions[positionKey] = p @@ -38,14 +35,11 @@ func (pool *Pool) positionUpdateWithKey( func positionUpdate( self PositionInfo, - liquidityDelta bigint, - feeGrowthInside0X128 bigint, - feeGrowthInside1X128 bigint, + liquidityDelta *u256.Int, + feeGrowthInside0X128 *u256.Uint, + feeGrowthInside1X128 *u256.Uint, ) PositionInfo { - requireUnsigned(feeGrowthInside0X128, ufmt.Sprintf("[POOL] position.gno__positionUpdate() || feeGrowthInside0X128(%d) >= 0", feeGrowthInside0X128)) - requireUnsigned(feeGrowthInside1X128, ufmt.Sprintf("[POOL] position.gno__positionUpdate() || feeGrowthInside1X128(%d) >= 0", feeGrowthInside1X128)) - - var liquidityNext bigint + var liquidityNext *u256.Int if liquidityDelta == 0 { liquidityNext = self.liquidity } else { diff --git a/pool/sqrt_price_math.gno b/pool/sqrt_price_math.gno index 82588b60..148a3084 100644 --- a/pool/sqrt_price_math.gno +++ b/pool/sqrt_price_math.gno @@ -1,38 +1,17 @@ package pool import ( + "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" ) func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp( - sqrtPX96 bigint, - liquidity bigint, - amount bigint, + sqrtPX96 *u256.Uint, + liquidity *u256.Uint, + amount *u256.Uint, add bool, ) bigint { - requireUnsigned( - sqrtPX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || expected sqrtPX96 must be unsigned, got: %d", - sqrtPX96, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || expected liquidity must be unsigned, got: %d", - liquidity, - )) - - requireUnsigned( - amount, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || expected amount must be unsigned, got: %d", - amount, - )) - - if amount == 0 { + if amount.IsZero() { return sqrtPX96 } @@ -65,32 +44,11 @@ func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp( } func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown( - sqrtPX96 bigint, - liquidity bigint, - amount bigint, + sqrtPX96 *u256.Uint, + liquidity *u256.Uint, + amount *u256.Uint, add bool, ) bigint { - requireUnsigned( - sqrtPX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected sqrtPX96 must be unsigned, got: %d", - sqrtPX96, - )) - - requireUnsigned( - amount, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected amount must be unsigned, got: %d", - amount, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected liquidity must be unsigned, got: %d", - liquidity, - )) - var quotient bigint if amount <= consts.MAX_UINT160 { quotient = (amount << 96) / liquidity @@ -129,32 +87,11 @@ func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown( } func sqrtPriceMathGetNextSqrtPriceFromInput( - sqrtPX96 bigint, - liquidity bigint, - amountIn bigint, + sqrtPX96 *u256.Uint, + liquidity *u256.Uint, + amountIn *u256.Uint, zeroForOne bool, ) (sqrtQ bigint) { - requireUnsigned( - sqrtPX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromInput() || expected sqrtPX96 must be unsigned, got: %d", - sqrtPX96, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromInput() || expected liquidity must be unsigned, got: %d", - liquidity, - )) - - requireUnsigned( - amountIn, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromInput() || expected amountIn must be unsigned, got: %d", - amountIn, - )) - if zeroForOne { amount0Result := sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) requireUnsigned( @@ -178,23 +115,11 @@ func sqrtPriceMathGetNextSqrtPriceFromInput( } func sqrtPriceMathGetNextSqrtPriceFromOutput( - sqrtPX96 bigint, - liquidity bigint, - amountOut bigint, + sqrtPX96 *u256.Uint, + liquidity *u256.Uint, + amountOut *u256.Int, zeroForOne bool, ) (sqrtQ bigint) { - requireUnsigned( - sqrtPX96, - ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromOutput() || expected sqrtPX96 >= 0, got: %s", - sqrtPX96, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromOutput() || expected liquidity >= 0, got: %d", - liquidity, - )) - if zeroForOne { amount1Result := sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) requireUnsigned( @@ -215,31 +140,10 @@ func sqrtPriceMathGetNextSqrtPriceFromOutput( } func sqrtPriceMathGetAmount0DeltaHelper( - sqrtRatioAX96 bigint, - sqrtRatioBX96 bigint, - liquidity bigint, + sqrtRatioAX96 *u256.Uint, + sqrtRatioBX96 *u256.Uint, + liquidity *u256.Uint, ) bigint { - requireUnsigned( - sqrtRatioAX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || expected sqrtRatioAX96 must be unsigned, got: %d", - sqrtRatioAX96, - )) - - requireUnsigned( - sqrtRatioBX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || expected sqrtRatioBX96 must be unsigned, got: %d", - sqrtRatioBX96, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || expected liquidity must be unsigned, got: %d", - liquidity, - )) - if sqrtRatioAX96 > sqrtRatioBX96 { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 } @@ -251,30 +155,10 @@ func sqrtPriceMathGetAmount0DeltaHelper( } func sqrtPriceMathGetAmount1DeltaHelper( - sqrtRatioAX96 bigint, - sqrtRatioBX96 bigint, - liquidity bigint, + sqrtRatioAX96 *u256.Uint, + sqrtRatioBX96 *u256.Uint, + liquidity *u256.Uint, ) bigint { - requireUnsigned( - sqrtRatioAX96, - ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount1DeltaHelper() || expected sqrtRatioAX96 must be unsigned, got: %d", - sqrtRatioAX96, - )) - - requireUnsigned( - sqrtRatioBX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount1DeltaHelper() || expected sqrtRatioBX96 must be unsigned, got: %d", - sqrtRatioBX96, - )) - - requireUnsigned( - liquidity, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount1DeltaHelper() || expected liquidity must be unsigned, got: %d", - liquidity, - )) - if sqrtRatioAX96 > sqrtRatioBX96 { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 } @@ -283,25 +167,11 @@ func sqrtPriceMathGetAmount1DeltaHelper( } func sqrtPriceMathGetAmount0Delta( - sqrtRatioAX96 bigint, - sqrtRatioBX96 bigint, - liquidity bigint, -) bigint { - requireUnsigned( - sqrtRatioAX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount0Delta() || expected sqrtRatioAX96 must be unsigned, got: %d", - sqrtRatioAX96, - )) - - requireUnsigned( - sqrtRatioBX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount0Delta() || expected sqrtRatioBX96 must be unsigned, got: %d", - sqrtRatioBX96, - )) - - if liquidity < 0 { + sqrtRatioAX96 *u256.Uint, + sqrtRatioBX96 *u256.Uint, + liquidity *u256.Int, +) u256.Int { + if liquidity.IsNegative() { return -sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, absBigint(-liquidity)) } @@ -309,24 +179,11 @@ func sqrtPriceMathGetAmount0Delta( } func sqrtPriceMathGetAmount1Delta( - sqrtRatioAX96 bigint, - sqrtRatioBX96 bigint, - liquidity bigint, + sqrtRatioAX96 *u256.Uint, + sqrtRatioBX96 *u256.Uint, + liquidity *u256.Int, ) bigint { - requireUnsigned( - sqrtRatioAX96, - ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount1Delta() || expected sqrtRatioAX96 must be unsigned, got: %d", - sqrtRatioAX96, - )) - - requireUnsigned( - sqrtRatioBX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetAmount1Delta() || expected sqrtRatioBX96 must be unsigned, got: %d", - sqrtRatioBX96, - )) - - if liquidity < 0 { + if liquidity.IsNegative() { return -sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, absBigint(-liquidity)) } diff --git a/pool/swap_math.gno b/pool/swap_math.gno index 6df9c733..2ab5c326 100644 --- a/pool/swap_math.gno +++ b/pool/swap_math.gno @@ -2,24 +2,22 @@ package pool import ( "gno.land/p/demo/ufmt" + + "gno.land/p/demo/u256" ) func swapMathComputeSwapStep( - sqrtRatioCurrentX96 bigint, - sqrtRatioTargetX96 bigint, - liquidity bigint, - amountRemaining bigint, + sqrtRatioCurrentX96 *u256.Uint, + sqrtRatioTargetX96 *u256.Uint, + liquidity *u256.Uint, + amountRemaining *u256.Int, feePips uint32, -) (sqrtRatioNextX96, amountIn, amountOut, feeAmount bigint) { - requireUnsigned(sqrtRatioCurrentX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioCurrentX96(%d) >= 0", sqrtRatioCurrentX96)) - requireUnsigned(sqrtRatioTargetX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioTargetX96(%d) >= 0", sqrtRatioTargetX96)) - requireUnsigned(liquidity, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || liquidity(%d) >= 0", liquidity)) - - isToken1Expensive := sqrtRatioCurrentX96 >= sqrtRatioTargetX96 +) (sqrtRatioNextX96, amountIn, amountOut, feeAmount *u256.Uint) { + isToken1Expensive := sqrtRatioCurrentX96.Gte(sqrtRatioTargetX96) // POSTIVIE == EXACT_IN => Estimated AmountOut // NEGATIVE == EXACT_OUT => Estimated AmountIn - exactIn := amountRemaining >= 0 + exactIn := amountRemaining.Gte(u256.Zero()) if exactIn { amountRemainingLessFee := amountRemaining * bigint(uint64(1000000-feePips)) / 1000000 diff --git a/pool/tick.gno b/pool/tick.gno index 958e4405..524cff58 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -1,76 +1,68 @@ package pool import ( + "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" ) -func tickTickSpacingToMaxLiquidityPerTick(tickSpacing int32) bigint { - minTick := (consts.MIN_TICK / tickSpacing) * tickSpacing - maxTick := (consts.MAX_TICK / tickSpacing) * tickSpacing +func tickTickSpacingToMaxLiquidityPerTick(tickSpacing int32) *u256.Uint { + minTick := (MIN_TICK / tickSpacing) * tickSpacing + maxTick := (MAX_TICK / tickSpacing) * tickSpacing numTicks := bigint((maxTick-minTick)/tickSpacing) + 1 - requireUnsigned(consts.MAX_UINT128/numTicks, ufmt.Sprintf("[POOL] tick.gno__tickTickSpacingToMaxLiquidityPerTick() || consts.MAX_UINT128(%d) / numTicks(%d) >= 0(%d)", consts.MAX_UINT128, numTicks, consts.MAX_UINT128/numTicks)) - return consts.MAX_UINT128 / numTicks + return new(u256.Uint).Div(MAX_UINT128, numTicks) } func (pool *Pool) tickGetFeeGrowthInside( tickLower int32, tickUpper int32, tickCurrent int32, - feeGrowthGlobal0X128 bigint, - feeGrowthGlobal1X128 bigint, -) (feeGrowthInside0X128, feeGrowthInside1X128 bigint) { - requireUnsigned(feeGrowthGlobal0X128, ufmt.Sprintf("[POOL] tick.gno__tickGetFeeGrowthInside() || feeGrowthGlobal0X128(%d) >= 0", feeGrowthGlobal0X128)) - requireUnsigned(feeGrowthGlobal1X128, ufmt.Sprintf("[POOL] tick.gno__tickGetFeeGrowthInside() || feeGrowthGlobal1X128(%d) >= 0", feeGrowthGlobal1X128)) - + feeGrowthGlobal0X128 *u256.Uint, + feeGrowthGlobal1X128 *u256.Uint, +) (feeGrowthInside0X128, feeGrowthInside1X128 *u256.Uint) { lower := pool.ticks[tickLower] upper := pool.ticks[tickUpper] - var feeGrowthBelow0X128, feeGrowthBelow1X128 bigint + var feeGrowthBelow0X128, feeGrowthBelow1X128 *u256.Uint if tickCurrent >= tickLower { feeGrowthBelow0X128 = lower.feeGrowthOutside0X128 feeGrowthBelow1X128 = lower.feeGrowthOutside1X128 } else { - feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128 - feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128 + feeGrowthBelow0X128.Sub(feeGrowthGlobal0X128, lower.feeGrowthOutside0X128) + feeGrowthBelow1X128.Sub(feeGrowthGlobal1X128, lower.feeGrowthOutside1X128) } - var feeGrowthAbove0X128, feeGrowthAbove1X128 bigint + var feeGrowthAbove0X128, feeGrowthAbove1X128 *u256.Uint if tickCurrent < tickUpper { feeGrowthAbove0X128 = upper.feeGrowthOutside0X128 feeGrowthAbove1X128 = upper.feeGrowthOutside1X128 } else { - feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128 - feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128 + feeGrowthAbove0X128.Sub(feeGrowthGlobal0X128, upper.feeGrowthOutside0X128) + feeGrowthAbove1X128.Sub(feeGrowthGlobal1X128, upper.feeGrowthOutside1X128) } - feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128 - feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128 + feeGrowthInside0X128.Sub(feeGrowthGlobal0X128, feeGrowthBelow0X128) + feeGrowthInside0X128.Sub(feeGrowthInside0X128, feeGrowthAbove0X128) + + feeGrowthInside1X128.Sub(feeGrowthGlobal1X128, feeGrowthBelow1X128) + feeGrowthInside1X128.Sub(feeGrowthInside1X128, feeGrowthAbove1X128) - requireUnsigned(feeGrowthInside0X128, ufmt.Sprintf("[POOL] tick.gno__tickGetFeeGrowthInside() || feeGrowthInside0X128(%d) >= 0", feeGrowthInside0X128)) - requireUnsigned(feeGrowthInside1X128, ufmt.Sprintf("[POOL] tick.gno__tickGetFeeGrowthInside() || feeGrowthInside1X128(%d) >= 0", feeGrowthInside1X128)) return feeGrowthInside0X128, feeGrowthInside1X128 } func (pool *Pool) tickUpdate( tick int32, tickCurrent int32, - liquidityDelta bigint, - feeGrowthGlobal0X128 bigint, - feeGrowthGlobal1X128 bigint, + liquidityDelta *u256.Int, + feeGrowthGlobal0X128 *u256.Uint, + feeGrowthGlobal1X128 *u256.Uint, upper bool, - maxLiquidity bigint, + maxLiquidity *u256.Uint, ) (flipped bool) { - - requireUnsigned(feeGrowthGlobal0X128, ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || feeGrowthGlobal0X128(%d) >= 0", feeGrowthGlobal0X128)) - requireUnsigned(feeGrowthGlobal1X128, ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || feeGrowthGlobal1X128(%d) >= 0", feeGrowthGlobal1X128)) - requireUnsigned(maxLiquidity, ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || maxLiquidity(%d) >= 0", maxLiquidity)) - info := pool.ticks[tick] liquidityGrossBefore := info.liquidityGross liquidityGrossAfter := liquidityMathAddDelta(liquidityGrossBefore, liquidityDelta) - require(liquidityGrossAfter <= maxLiquidity, ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%d) <= maxLiquidity(%d)", liquidityGrossAfter, maxLiquidity)) + require(liquidityGrossAfter.Lte(maxLiquidity), ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%d) <= maxLiquidity(%d)", liquidityGrossAfter, maxLiquidity)) flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0) @@ -102,12 +94,9 @@ func (pool *Pool) tickClear(tick int32) { func (pool *Pool) tickCross( tick int32, - feeGrowthGlobal0X128 bigint, - feeGrowthGlobal1X128 bigint, -) bigint { - requireUnsigned(feeGrowthGlobal0X128, ufmt.Sprintf("[POOL] tick.gno__tickCross() || feeGrowthGlobal0X128(%d) >= 0", feeGrowthGlobal0X128)) - requireUnsigned(feeGrowthGlobal1X128, ufmt.Sprintf("[POOL] tick.gno__tickCross() || feeGrowthGlobal1X128(%d) >= 0", feeGrowthGlobal1X128)) - + feeGrowthGlobal0X128 *u256.Uint, + feeGrowthGlobal1X128 *u256.Uint, +) *u256.Int { info := pool.ticks[tick] info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128 info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128 diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index 00bbfbc8..d5fb3411 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -17,18 +17,12 @@ func (pool *Pool) tickBitmapFlipTick( tick int32, tickSpacing int32, ) { - require( - tick%tickSpacing == 0, - ufmt.Sprintf( - "[POOL] tick_bitmap.gno__tickBitmapFlipTick() || tick(%d) %% tickSpacing(%d) == 0(%d)", - tick, tickSpacing, tick%tickSpacing, - ), - ) - + require(tick%tickSpacing == 0, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || tick(%d) MOD tickSpacing(%d) != 0 (got:%d)", tick, tickSpacing, tick%tickSpacing)) wordPos, bitPos := tickBitmapPosition(tick / tickSpacing) mask := bigint(1) << uint64(bitPos) // 2 ** bitPos + mask := bigint(int64(1)) << uint64(bitPos) // 2 ** bitPos requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) >= 0", mask)) diff --git a/pool/type.gno b/pool/type.gno index 8387e2e1..842f44cd 100644 --- a/pool/type.gno +++ b/pool/type.gno @@ -2,78 +2,80 @@ package pool import ( "std" + + "gno.land/p/demo/u256" ) type Slot0 struct { - sqrtPriceX96 bigint + sqrtPriceX96 *u256.Uint tick int32 feeProtocol uint8 unlocked bool } type Balances struct { - token0 bigint - token1 bigint + token0 *u256.Uint + token1 *u256.Uint } type ProtocolFees struct { - token0 bigint - token1 bigint + token0 *u256.Uint + token1 *u256.Uint } type ModifyPositionParams struct { owner std.Address tickLower int32 tickUpper int32 - liquidityDelta bigint + liquidityDelta *u256.Int } type SwapCache struct { feeProtocol uint8 - liquidityStart bigint + liquidityStart *u256.Uint } type SwapState struct { - amountSpecifiedRemaining bigint - amountCalculated bigint - sqrtPriceX96 bigint + amountSpecifiedRemaining *u256.Int + amountCalculated *u256.Int + sqrtPriceX96 *u256.Uint tick int32 - feeGrowthGlobalX128 bigint - protocolFee bigint - liquidity bigint + feeGrowthGlobalX128 *u256.Uint + protocolFee *u256.Uint + liquidity *u256.Uint } type StepComputations struct { - sqrtPriceStartX96 bigint + sqrtPriceStartX96 *u256.Uint tickNext int32 initialized bool - sqrtPriceNextX96 bigint - amountIn bigint - amountOut bigint - feeAmount bigint + sqrtPriceNextX96 *u256.Uint + amountIn *u256.Uint + amountOut *u256.Uint + feeAmount *u256.Uint } type PositionInfo struct { - liquidity bigint + liquidity *u256.Uint - feeGrowthInside0LastX128 bigint - feeGrowthInside1LastX128 bigint + feeGrowthInside0LastX128 *u256.Uint + feeGrowthInside1LastX128 *u256.Uint - tokensOwed0 bigint - tokensOwed1 bigint + tokensOwed0 *u256.Uint + tokensOwed1 *u256.Uint } type TickInfo struct { - liquidityGross bigint - liquidityNet bigint + liquidityGross *u256.Uint + liquidityNet *u256.Int - feeGrowthOutside0X128 bigint - feeGrowthOutside1X128 bigint + feeGrowthOutside0X128 *u256.Uint + feeGrowthOutside1X128 *u256.Uint - tickCumulativeOutside bigint + //tickCumulativeOutside bigint - secondsPerLiquidityOutsideX128 bigint - secondsOutside bigint + //secondsPerLiquidityOutsideX128 bigint + // secondsOutside bigint initialized bool } @@ -97,20 +99,20 @@ type Pool struct { tickSpacing int32 // maxLiquidityPerTick is the maximum amount of liquidity that can be added per tick - maxLiquidityPerTick bigint + maxLiquidityPerTick *u256.Uint // slot0 is the current tick and price of the pool slot0 Slot0 - feeGrowthGlobal0X128 bigint - feeGrowthGlobal1X128 bigint + feeGrowthGlobal0X128 *u256.Uint + feeGrowthGlobal1X128 *u256.Uint // protocolFees is the amount of fees collected by the protocol // collected by CollectFeeProtocol() protocolFees ProtocolFees // liquidity is the total amount of liquidity in the pool - liquidity bigint + liquidity *u256.Uint // ticks is a mapping from tick index to tick ticks Ticks diff --git a/pool/utils.gno b/pool/utils.gno index e67104e3..f35adb97 100644 --- a/pool/utils.gno +++ b/pool/utils.gno @@ -6,13 +6,15 @@ import ( "gno.land/p/demo/ufmt" "gno.land/r/demo/users" - "gno.land/r/demo/consts" + "gno.land/p/demo/ufmt" + + "gno.land/p/demo/u256" ) -func checkTicks(tickLower, tickUpper int32) { - require(tickLower < tickUpper, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) < tickUpper(%d)", tickLower, tickUpper)) - require(tickLower >= consts.MIN_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) >= consts.MIN_TICK(%d)", tickLower, consts.MIN_TICK)) - require(tickUpper <= consts.MAX_TICK, ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickUpper(%d) <= consts.MAX_TICK(%d)", tickUpper, consts.MAX_TICK)) +func checkTicks(tickLower, tickUpper u256.Int) { + require(tickLower.Lt(tickUpper), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) < tickUpper(%d)", tickLower, tickUpper)) + require(tickLower.Gte(MIN_TICK), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) >= MIN_TICK(%d)", tickLower, MIN_TICK)) + require(tickUpper.Lte(MAX_TICK), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickUpper(%d) <= MAX_TICK(%d)", tickUpper, MAX_TICK)) } func a2u(addr std.Address) users.AddressOrName { diff --git a/position/sqrt_price_math.gno b/position/sqrt_price_math.gno index 416c9193..4125d797 100644 --- a/position/sqrt_price_math.gno +++ b/position/sqrt_price_math.gno @@ -119,14 +119,10 @@ func sqrtPriceMathGetNextSqrtPriceFromOutput( } func sqrtPriceMathGetAmount0DeltaHelper( - sqrtRatioAX96 bigint, - sqrtRatioBX96 bigint, - liquidity bigint, + sqrtRatioAX96 *u256.Uint, + sqrtRatioBX96 *u256.Uint, + liquidity *u256.Uint, ) bigint { - requireUnsigned(sqrtRatioAX96, ufmt.Sprintf("[POSITION] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || sqrtRatioAX96(%s) >= 0", sqrtRatioAX96)) - requireUnsigned(sqrtRatioBX96, ufmt.Sprintf("[POSITION] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || sqrtRatioBX96(%s) >= 0", sqrtRatioBX96)) - requireUnsigned(liquidity, ufmt.Sprintf("[POSITION] sqrt_price_math.gno__sqrtPriceMathGetAmount0DeltaHelper() || liquidity(%s) >= 0", liquidity)) - if sqrtRatioAX96 > sqrtRatioBX96 { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 } diff --git a/u256/i256.gno b/u256/i256.gno new file mode 100644 index 00000000..5aba58f3 --- /dev/null +++ b/u256/i256.gno @@ -0,0 +1,78 @@ +package u256 + +// signed integer wrapper + +type Int struct { + v Uint +} + +func NewInt(v int64) *Int { + if v >= 0 { + return &Int{v: *NewUint(uint64(v))} + } + return &Int{ + v: Uint{ + arr: [4]uint64{ + uint64(v), // bit preserving cast, little endian + 0xffffffffffffffff, + 0xffffffffffffffff, + 0xffffffffffffffff, + }, + }, + } +} + +func (x *Int) Eq(y *Int) bool { + return x.v.Eq(&y.v) +} + +func (x *Int) Signum() int { + if x.v.arr[3] == 0 && x.v.arr[2] == 0 && x.v.arr[1] == 0 && x.v.arr[0] == 0 { + return 0 + } + if x.v.arr[3] < 0x8000000000000000 { + return 1 + } + return -1 +} + +func (x *Int) Gt(y *Int) bool { + xs := x.Signum() + ys := y.Signum() + + if xs != ys { + return xs > ys + } + if xs == 0 { + return false + } + if xs > 0 { + return x.v.Gt(&y.v) + } + return y.v.Gt(&x.v) +} + +func (x *Int) Gte(y *Int) bool { + xs := x.Signum() + ys := y.Signum() + + if xs != ys { + return xs > ys + } + if xs == 0 { + return true + } + if xs > 0 { + return x.v.Gte(&y.v) + } + return y.v.Gte(&x.v) +} + +func (x *Int) Int64() int64 { + // TODO: overflow check + if x.v.arr[3] < 0x8000000000000000 { + return int64(x.v.arr[0]) + } + // TODO: check if this is correct + return -int64(^x.v.arr[0] + 1) +} \ No newline at end of file diff --git a/u256/runtest.gno b/u256/runtest.gno index 4a40833c..96e32847 100644 --- a/u256/runtest.gno +++ b/u256/runtest.gno @@ -1,6 +1,6 @@ package u256 func Hello() uint64 { - x := NewInt(1) + x := NewUint(1) return x.Uint64() } diff --git a/u256/u256.gno b/u256/u256.gno index bc024b40..ccc7cadb 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -11,18 +11,36 @@ const MaxUint64 = 1<<64 - 1 // TODO: remove const MaxUint256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 -// Int is represented as an array of 4 uint64, in little-endian order, -// so that Int[3] is the most significant, and Int[0] is the least significant -type Int struct { +func Zero() *Uint { + return NewUint(0) +} + +func One() *Uint { + return NewUint(1) +} + +// Uint is represented as an array of 4 uint64, in little-endian order, +// so that Uint[3] is the most significant, and Uint[0] is the least significant +type Uint struct { arr [4]uint64 } + +func (x *Uint) Int() *Int { + // panic if x > MaxInt64 + if x.arr[3] > 0x7fffffffffffffff { + panic("overflow") + } + + return &Int{v: *x} +} + // TODO: to be removed -func FromBigint(x bigint) *Int { +func FromBigint(x bigint) *Uint { if x > MaxUint256 { panic("overflow") } - var z Int + var z Uint z.arr[0] = uint64(x) z.arr[1] = uint64(x >> 64) z.arr[2] = uint64(x >> 128) @@ -31,66 +49,66 @@ func FromBigint(x bigint) *Int { return &z } -// NewInt returns a new initialized Int. -func NewInt(val uint64) *Int { - z := &Int{arr: [4]uint64{val, 0, 0, 0}} +// NewUint returns a new initialized Uint. +func NewUint(val uint64) *Uint { + z := &Uint{arr: [4]uint64{val, 0, 0, 0}} return z } /* -func ToString(x *Int) string { +func ToString(x *Uint) string { return "tostring not implemented" } */ // Uint64 returns the lower 64-bits of z -func (z *Int) Uint64() uint64 { +func (z *Uint) Uint64() uint64 { return z.arr[0] } // SetUint64 sets z to the value x -func (z *Int) SetUint64(x uint64) *Int { +func (z *Uint) SetUint64(x uint64) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x return z } // IsUint64 reports whether z can be represented as a uint64. -func (z *Int) IsUint64() bool { +func (z *Uint) IsUint64() bool { return (z.arr[1] | z.arr[2] | z.arr[3]) == 0 } // IsZero returns true if z == 0 -func (z *Int) IsZero() bool { +func (z *Uint) IsZero() bool { return (z.arr[0] | z.arr[1] | z.arr[2] | z.arr[3]) == 0 } // Clear sets z to 0 -func (z *Int) Clear() *Int { +func (z *Uint) Clear() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 0 return z } // SetAllOne sets all the bits of z to 1 -func (z *Int) SetAllOne() *Int { +func (z *Uint) SetAllOne() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, MaxUint64 return z } // Not sets z = ^x and returns z. -func (z *Int) Not(x *Int) *Int { +func (z *Uint) Not(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = ^x.arr[3], ^x.arr[2], ^x.arr[1], ^x.arr[0] return z } // Gt returns true if z > x -func (z *Int) Gt(x *Int) bool { +func (z *Uint) Gt(x *Uint) bool { return x.Lt(z) } -func (z *Int) Gte(x *Int) bool { +func (z *Uint) Gte(x *Uint) bool { return !z.Lt(x) } // Lt returns true if z < x -func (z *Int) Lt(x *Int) bool { +func (z *Uint) Lt(x *Uint) bool { // z < x <=> z - x < 0 i.e. when subtraction overflows. _, carry := Sub64(z.arr[0], x.arr[0], 0) _, carry = Sub64(z.arr[1], x.arr[1], carry) @@ -100,7 +118,7 @@ func (z *Int) Lt(x *Int) bool { } // Eq returns true if z == x -func (z *Int) Eq(x *Int) bool { +func (z *Uint) Eq(x *Uint) bool { return (z.arr[0] == x.arr[0]) && (z.arr[1] == x.arr[1]) && (z.arr[2] == x.arr[2]) && (z.arr[3] == x.arr[3]) } @@ -109,7 +127,7 @@ func (z *Int) Eq(x *Int) bool { // -1 if z < x // 0 if z == x // +1 if z > x -func (z *Int) Cmp(x *Int) (r int) { +func (z *Uint) Cmp(x *Uint) (r int) { // z < x <=> z - x < 0 i.e. when subtraction overflows. d0, carry := Sub64(z.arr[0], x.arr[0], 0) d1, carry := Sub64(z.arr[1], x.arr[1], carry) @@ -125,29 +143,36 @@ func (z *Int) Cmp(x *Int) (r int) { } // Set sets z to x and returns z. -func (z *Int) Set(x *Int) *Int { +func (z *Uint) Set(x *Uint) *Uint { *z = *x return z } // SetOne sets z to 1 -func (z *Int) SetOne() *Int { +func (z *Uint) SetOne() *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, 1 return z } // Add sets z to the sum x+y -func (z *Int) Add(x, y *Int) *Int { +func (z *Uint) Add(x, y *Uint) *Uint { var carry uint64 z.arr[0], carry = Add64(x.arr[0], y.arr[0], 0) z.arr[1], carry = Add64(x.arr[1], y.arr[1], carry) z.arr[2], carry = Add64(x.arr[2], y.arr[2], carry) z.arr[3], _ = Add64(x.arr[3], y.arr[3], carry) + // Different from the original implementation! + // We panic on overflow + /* + if carry != 0 { + panic("overflow") + } + */ return z } // AddOverflow sets z to the sum x+y, and returns z and whether overflow occurred -func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { +func (z *Uint) AddOverflow(x, y *Uint) (*Uint, bool) { var carry uint64 z.arr[0], carry = Add64(x.arr[0], y.arr[0], 0) z.arr[1], carry = Add64(x.arr[1], y.arr[1], carry) @@ -157,7 +182,7 @@ func (z *Int) AddOverflow(x, y *Int) (*Int, bool) { } // SubOverflow sets z to the difference x-y and returns z and true if the operation underflowed -func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { +func (z *Uint) SubOverflow(x, y *Uint) (*Uint, bool) { var carry uint64 z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) @@ -167,19 +192,27 @@ func (z *Int) SubOverflow(x, y *Int) (*Int, bool) { } // Sub sets z to the difference x-y -func (z *Int) Sub(x, y *Int) *Int { +func (z *Uint) Sub(x, y *Uint) *Uint { var carry uint64 z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) z.arr[2], carry = Sub64(x.arr[2], y.arr[2], carry) z.arr[3], _ = Sub64(x.arr[3], y.arr[3], carry) + // Different from the original implementation! + // We panic on underflow + /* + if carry != 0 { + panic("underflow") + } + */ return z } +// commented out for possible overflow // Mul sets z to the product x*y -func (z *Int) Mul(x, y *Int) *Int { +func (z *Uint) Mul(x, y *Uint) *Uint { var ( - res Int + res Uint carry uint64 res1, res2, res3 uint64 ) @@ -202,7 +235,7 @@ func (z *Int) Mul(x, y *Int) *Int { } // MulOverflow sets z to the product x*y, and returns z and whether overflow occurred -func (z *Int) MulOverflow(x, y *Int) (*Int, bool) { +func (z *Uint) MulOverflow(x, y *Uint) (*Uint, bool) { p := umul(x, y) copy(z.arr[:], p[:4]) return z, (p[4] | p[5] | p[6] | p[7]) != 0 @@ -227,7 +260,7 @@ func umulHop(z, x, y uint64) (hi, lo uint64) { } // umul computes full 256 x 256 -> 512 multiplication. -func umul(x, y *Int) [8]uint64 { +func umul(x, y *Uint) [8]uint64 { var ( res [8]uint64 carry, carry4, carry5, carry6 uint64 @@ -257,9 +290,10 @@ func umul(x, y *Int) [8]uint64 { return res } +// commented out for possible overflow // Div sets z to the quotient x/y for returns z. // If y == 0, z is set to 0 -func (z *Int) Div(x, y *Int) *Int { +func (z *Uint) Div(x, y *Uint) *Uint { if y.IsZero() || y.Gt(x) { return z.Clear() } @@ -274,7 +308,7 @@ func (z *Int) Div(x, y *Int) *Int { // At this point, we know // x/y ; x > y > 0 - var quot Int + var quot Uint udivrem(quot.arr[:], x.arr[:], y) return z.Set(") } @@ -283,7 +317,7 @@ func (z *Int) Div(x, y *Int) *Int { // The quotient is stored in provided quot - len(u)-len(d)+1 words. // It loosely follows the Knuth's division algorithm (sometimes referenced as "schoolbook" division) using 64-bit words. // See Knuth, Volume 2, section 4.3.1, Algorithm D. -func udivrem(quot, u []uint64, d *Int) (rem Int) { +func udivrem(quot, u []uint64, d *Uint) (rem Uint) { var dLen int for i := len(d.arr) - 1; i >= 0; i-- { if d.arr[i] != 0 { @@ -294,7 +328,7 @@ func udivrem(quot, u []uint64, d *Int) (rem Int) { shift := uint(LeadingZeros64(d.arr[dLen-1])) - var dnStorage Int + var dnStorage Uint dn := dnStorage.arr[:dLen] for i := dLen - 1; i > 0; i-- { dn[i] = (d.arr[i] << shift) | (d.arr[i-1] >> (64 - shift)) @@ -391,7 +425,7 @@ func udivremKnuth(quot, u, d []uint64) { // isBitSet returns true if bit n-th is set, where n = 0 is LSB. // The n must be <= 255. -func (z *Int) isBitSet(n uint) bool { +func (z *Uint) isBitSet(n uint) bool { return (z.arr[n/64] & (1 << (n % 64))) != 0 } @@ -451,7 +485,7 @@ func udivrem2by1(uh, ul, d, reciprocal uint64) (quot, rem uint64) { } // Lsh sets z = x << n and returns z. -func (z *Int) Lsh(x *Int, n uint) *Int { +func (z *Uint) Lsh(x *Uint, n uint) *Uint { // n % 64 == 0 if n&0x3f == 0 { switch n { @@ -510,7 +544,7 @@ sh192: } // Rsh sets z = x >> n and returns z. -func (z *Int) Rsh(x *Int, n uint) *Int { +func (z *Uint) Rsh(x *Uint, n uint) *Uint { // n % 64 == 0 if n&0x3f == 0 { switch n { @@ -571,7 +605,7 @@ sh192: // SRsh (Signed/Arithmetic right shift) // considers z to be a signed integer, during right-shift // and sets z = x >> n and returns z. -func (z *Int) SRsh(x *Int, n uint) *Int { +func (z *Uint) SRsh(x *Uint, n uint) *Uint { // If the MSB is 0, SRsh is same as Rsh. if !x.isBitSet(255) { return z.Rsh(x, n) @@ -629,45 +663,45 @@ sh192: return z } -func (z *Int) lsh64(x *Int) *Int { +func (z *Uint) lsh64(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[2], x.arr[1], x.arr[0], 0 return z } -func (z *Int) lsh128(x *Int) *Int { +func (z *Uint) lsh128(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[1], x.arr[0], 0, 0 return z } -func (z *Int) lsh192(x *Int) *Int { +func (z *Uint) lsh192(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = x.arr[0], 0, 0, 0 return z } -func (z *Int) rsh64(x *Int) *Int { +func (z *Uint) rsh64(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, x.arr[3], x.arr[2], x.arr[1] return z } -func (z *Int) rsh128(x *Int) *Int { +func (z *Uint) rsh128(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, x.arr[3], x.arr[2] return z } -func (z *Int) rsh192(x *Int) *Int { +func (z *Uint) rsh192(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x.arr[3] return z } -func (z *Int) srsh64(x *Int) *Int { +func (z *Uint) srsh64(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, x.arr[3], x.arr[2], x.arr[1] return z } -func (z *Int) srsh128(x *Int) *Int { +func (z *Uint) srsh128(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, x.arr[3], x.arr[2] return z } -func (z *Int) srsh192(x *Int) *Int { +func (z *Uint) srsh192(x *Uint) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = MaxUint64, MaxUint64, MaxUint64, x.arr[3] return z } // Or sets z = x | y and returns z. -func (z *Int) Or(x, y *Int) *Int { +func (z *Uint) Or(x, y *Uint) *Uint { z.arr[0] = x.arr[0] | y.arr[0] z.arr[1] = x.arr[1] | y.arr[1] z.arr[2] = x.arr[2] | y.arr[2] @@ -676,7 +710,7 @@ func (z *Int) Or(x, y *Int) *Int { } // And sets z = x & y and returns z. -func (z *Int) And(x, y *Int) *Int { +func (z *Uint) And(x, y *Uint) *Uint { z.arr[0] = x.arr[0] & y.arr[0] z.arr[1] = x.arr[1] & y.arr[1] z.arr[2] = x.arr[2] & y.arr[2] @@ -685,7 +719,7 @@ func (z *Int) And(x, y *Int) *Int { } // Xor sets z = x ^ y and returns z. -func (z *Int) Xor(x, y *Int) *Int { +func (z *Uint) Xor(x, y *Uint) *Uint { z.arr[0] = x.arr[0] ^ y.arr[0] z.arr[1] = x.arr[1] ^ y.arr[1] z.arr[2] = x.arr[2] ^ y.arr[2] @@ -695,19 +729,19 @@ func (z *Int) Xor(x, y *Int) *Int { // MarshalJSON implements json.Marshaler. // MarshalJSON marshals using the 'decimal string' representation. This is _not_ compatible -// with big.Int: big.Int marshals into JSON 'native' numeric format. +// with big.Uint: big.Uint marshals into JSON 'native' numeric format. // // The JSON native format is, on some platforms, (e.g. javascript), limited to 53-bit large // integer space. Thus, U256 uses string-format, which is not compatible with -// big.int (big.Int refuses to unmarshal a string representation). -func (z *Int) MarshalJSON() ([]byte, error) { +// big.int (big.Uint refuses to unmarshal a string representation). +func (z *Uint) MarshalJSON() ([]byte, error) { return []byte(`"` + z.Dec() + `"`), nil } // UnmarshalJSON implements json.Unmarshaler. UnmarshalJSON accepts either // - Quoted string: either hexadecimal OR decimal // - Not quoted string: only decimal -func (z *Int) UnmarshalJSON(input []byte) error { +func (z *Uint) UnmarshalJSON(input []byte) error { if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' { // if not quoted, it must be decimal return z.fromDecimal(string(input)) @@ -717,8 +751,8 @@ func (z *Int) UnmarshalJSON(input []byte) error { // MarshalText implements encoding.TextMarshaler -// MarshalText marshals using the decimal representation (compatible with big.Int) -func (z *Int) MarshalText() ([]byte, error) { +// MarshalText marshals using the decimal representation (compatible with big.Uint) +func (z *Uint) MarshalText() ([]byte, error) { return []byte(z.Dec()), nil } @@ -726,7 +760,7 @@ func (z *Int) MarshalText() ([]byte, error) { // UnmarshalText implements encoding.TextUnmarshaler. This method // can unmarshal either hexadecimal or decimal. // - For hexadecimal, the input _must_ be prefixed with 0x or 0X -func (z *Int) UnmarshalText(input []byte) error { +func (z *Uint) UnmarshalText(input []byte) error { if len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X') { return z.fromHex(string(input)) } @@ -740,7 +774,7 @@ const ( ) // fromHex is the internal implementation of parsing a hex-string. -func (z *Int) fromHex(hex string) error { +func (z *Uint) fromHex(hex string) error { if err := checkNumberS(hex); err != nil { return err } @@ -767,10 +801,10 @@ func (z *Int) fromHex(hex string) error { return nil } -// FromDecimal is a convenience-constructor to create an Int from a +// FromDecimal is a convenience-constructor to create an Uint from a // decimal (base 10) string. Numbers larger than 256 bits are not accepted. -func FromDecimal(decimal string) (*Int, error) { - var z Int +func FromDecimal(decimal string) (*Uint, error) { + var z Uint if err := z.SetFromDecimal(decimal); err != nil { return nil, err } @@ -780,12 +814,12 @@ func FromDecimal(decimal string) (*Int, error) { const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935" // SetFromDecimal sets z from the given string, interpreted as a decimal number. -// OBS! This method is _not_ strictly identical to the (*big.Int).SetString(..., 10) method. +// OBS! This method is _not_ strictly identical to the (*big.Uint).SetString(..., 10) method. // Notable differences: // - This method does not accept underscore input, e.g. "100_000", // - This method does not accept negative zero as valid, e.g "-0", // - (this method does not accept any negative input as valid)) -func (z *Int) SetFromDecimal(s string) (err error) { +func (z *Uint) SetFromDecimal(s string) (err error) { // Remove max one leading + if len(s) > 0 && s[0] == '+' { s = s[1:] @@ -843,18 +877,18 @@ func checkNumberS(input string) error { } // multipliers holds the values that are needed for fromDecimal -var multipliers = [5]*Int{ +var multipliers = [5]*Uint{ nil, // represents first round, no multiplication needed - &Int{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 - &Int{[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 - &Int{[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 - &Int{[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 + &Uint{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 + &Uint{[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 + &Uint{[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 + &Uint{[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 } // fromDecimal is a helper function to only ever be called via SetFromDecimal // this function takes a string and chunks it up, calling ParseUint on it up to 5 times // these chunks are then multiplied by the proper power of 10, then added together. -func (z *Int) fromDecimal(bs string) error { +func (z *Uint) fromDecimal(bs string) error { // first clear the input z.Clear() // the maximum value of uint64 is 18446744073709551615, which is 20 characters @@ -887,7 +921,7 @@ func (z *Int) fromDecimal(bs string) error { if i == 0 { z.SetUint64(num) } else { - base := NewInt(num) + base := NewUint(num) z.Add(z, base.Mul(base, mult)) } // Chop off another 19 characters @@ -907,12 +941,7 @@ func lower(c byte) byte { return c | ('x' - 'X') } -const intSize = 32 << (^uint(0) >> 63) - -// IntSize is the size in bits of an int or uint value. -const IntSize = intSize - -// ParseUint is like ParseInt but for unsigned numbers. +// ParseUint is like ParseUint but for unsigned numbers. // // A sign prefix is not permitted. func parseUint(s string, base int, bitSize int) (uint64, error) { @@ -954,7 +983,7 @@ func parseUint(s string, base int, bitSize int) (uint64, error) { } if bitSize == 0 { - bitSize = IntSize + bitSize = UintSize } else if bitSize < 0 || bitSize > 64 { return 0, errors.New("invalid bit size") } @@ -1065,7 +1094,7 @@ func underscoreOK(s string) bool { } // Dec returns the decimal representation of z. -func (z *Int) Dec() string { +func (z *Uint) Dec() string { if z.IsZero() { return "0" } @@ -1091,14 +1120,14 @@ func (z *Int) Dec() string { // We init it with zeroes, because when strconv appends the ascii representations, // it will omit leading zeroes. out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") - divisor = NewInt(10000000000000000000) // 20 digits - y = new(Int).Set(z) // copy to avoid modifying z + divisor = NewUint(10000000000000000000) // 20 digits + y = new(Uint).Set(z) // copy to avoid modifying z pos = len(out) // position to write to buf = make([]byte, 0, 19) // buffer to write uint64:s to ) for { // Obtain Q and R for divisor - var quot Int + var quot Uint rem := udivrem(quot.arr[:], y.arr[:], divisor) y.Set(") // Set Q for next loop // Convert the R to ascii representation diff --git a/u256/u256_test.gno b/u256/u256_test.gno index 50201e5a..1e31f688 100644 --- a/u256/u256_test.gno +++ b/u256/u256_test.gno @@ -5,7 +5,7 @@ import ( ) func TestArithmetic(t *testing.T) { - x := NewInt(1) - y := NewInt(2) - z := NewInt(0).Add(x, y) + x := NewUint(1) + y := NewUint(2) + NewUint(0).Add(x, y) } \ No newline at end of file From 8fc9b5a6bfa854ba1e4b30ca8b84dbcd86155c3c Mon Sep 17 00:00:00 2001 From: mconcat Date: Wed, 14 Feb 2024 17:57:41 +0900 Subject: [PATCH 12/23] modify all bigint occurance in pool --- pool/_RPC_dry.gno | 48 ++--- pool/liquidity_math.gno | 8 +- pool/pool.gno | 286 +++++++++++++----------------- pool/pool_manager.gno | 14 +- pool/position.gno | 28 +-- pool/sqrt_price_math.gno | 177 ++++++++---------- pool/swap_math.gno | 38 ++-- pool/tick.gno | 16 +- pool/tick_bitmap.gno | 57 +++--- pool/type.gno | 2 +- pool/utils.gno | 8 +- position/_RPC_api.gno | 4 +- position/_RPC_dry.gno | 6 +- position/liquidity_management.gno | 4 +- position/position.gno | 40 ++--- position/sqrt_price_math.gno | 6 +- router/_RPC_api.gno | 4 +- staker/staker.gno | 4 +- u256/i256.gno | 88 +++++++++ u256/u256.gno | 61 ++++++- 20 files changed, 488 insertions(+), 411 deletions(-) diff --git a/pool/_RPC_dry.gno b/pool/_RPC_dry.gno index 074ebefb..c09075e1 100644 --- a/pool/_RPC_dry.gno +++ b/pool/_RPC_dry.gno @@ -4,7 +4,6 @@ import ( "std" "gno.land/p/demo/common" - "gno.land/p/demo/ufmt" "gno.land/p/demo/u256" ) @@ -59,27 +58,27 @@ func DrySwap( if zeroForOne { state = SwapState{ amountSpecifiedRemaining: amountSpecified.Int(), - amountCalculated: 0, + amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: pool.feeGrowthGlobal0X128, - protocolFee: 0, + protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } else { state = SwapState{ amountSpecifiedRemaining: amountSpecified.Int(), - amountCalculated: 0, + amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: pool.feeGrowthGlobal1X128, - protocolFee: 0, + protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit - for state.amountSpecifiedRemaining.NotZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { + for !state.amountSpecifiedRemaining.IsZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { var step StepComputations step.sqrtPriceStartX96 = state.sqrtPriceX96 @@ -98,8 +97,8 @@ func DrySwap( } // get the price for the next tick - step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) - var sqrtRatioTargetX96 bigint + step.sqrtPriceNextX96 = TickMathGetSqrtRatioAtTick(step.tickNext) + var sqrtRatioTargetX96 *u256.Uint isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 @@ -118,31 +117,32 @@ func DrySwap( ) if exactInput { - state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount) - state.amountCalculated -= step.amountOut + state.amountSpecifiedRemaining.Sub(state.amountSpecifiedRemaining, new(u256.Uint).Add(step.amountIn, step.feeAmount).Int()) + state.amountCalculated.Sub(state.amountCalculated, step.amountOut.Int()) } else { - state.amountSpecifiedRemaining += step.amountOut - state.amountCalculated += (step.amountIn + step.feeAmount) + state.amountSpecifiedRemaining.Add(state.amountSpecifiedRemaining, step.amountOut.Int()) + state.amountCalculated.Add(state.amountCalculated, new(u256.Uint).Add(step.amountIn, step.feeAmount).Int()) } // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee if cache.feeProtocol > 0 { - delta := step.feeAmount / bigint(uint64(cache.feeProtocol)) - requireUnsigned(delta, ufmt.Sprintf("[POOL] _RPC_dry.gno__DrySwap() || expected x(%d) > 0", delta)) + delta := new(u256.Uint).Div(step.feeAmount, u256.NewUint(uint64(cache.feeProtocol))) step.feeAmount -= delta state.protocolFee += delta } // update global fee tracker - if state.liquidity > 0 { - state.feeGrowthGlobalX128 += (step.feeAmount * consts.Q128 / state.liquidity) + if state.liquidity.Gt(u256.Zero()) { + update := new(u256.Uint).Mul(step.feeAmount, u256.FromBigint(consts.Q128)) + update.Div(update, state.liquidity) + state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128, update) } // shift tick if we reached the next price if state.sqrtPriceX96 == step.sqrtPriceNextX96 { // if the tick is initialized, run the tick transition if step.initialized { - var fee0, fee1 bigint + var fee0, fee1 *u256.Uint // check for the placeholder value, which we replace with the actual value the first time the swap crosses an initialized tick if zeroForOne { @@ -178,32 +178,32 @@ func DrySwap( } // END LOOP - var amount0, amount1 bigint + var amount0, amount1 *u256.Int if zeroForOne == exactInput { - amount0 = amountSpecified - state.amountSpecifiedRemaining + amount0 = new(u256.Int).Sub(amountSpecified.Int(), state.amountSpecifiedRemaining) amount1 = state.amountCalculated } else { amount0 = state.amountCalculated - amount1 = amountSpecified - state.amountSpecifiedRemaining + amount1 = new(u256.Int).Sub(amountSpecified.Int(), state.amountSpecifiedRemaining) } if zeroForOne { - if !(pool.balances.token1 >= absBigint(amount1)) { + if !(pool.balances.token1.Gte(amount1.Abs())) { // NOT ENOUGH BALANCE for output token1 return 0, 0, false } } else { - if !(pool.balances.token0 >= absBigint(amount0)) { + if !(pool.balances.token0.Gte(amount0.Abs())) { // NOT ENOUGH BALANCE for output token0 return 0, 0, false } } // just not enough balance - if amount0 == 0 && amount1 == 0 { + if amount0.IsZero() && amount1.IsZero() { return 0, 0, false } - return amount0, amount1, true + return amount0.Bigint(), amount1.Bigint(), true } diff --git a/pool/liquidity_math.gno b/pool/liquidity_math.gno index 4b5cc461..d04e014d 100644 --- a/pool/liquidity_math.gno +++ b/pool/liquidity_math.gno @@ -7,14 +7,12 @@ import ( ) func liquidityMathAddDelta(x *u256.Uint, y *u256.Int) *u256.Uint { - if y < 0 { - z := x - (-y) - requireUnsigned(z, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= 0__#1", z)) + if y.IsNeg() { + z := new(u256.Uint).Sub(x, y.Abs()) require(z < x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) < x(%d)", z, x)) return z } else { - z := x + y - requireUnsigned(z, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= 0__#2", z)) + z := new(u256.Uint).Add(x, y.Uint()) require(z >= x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= x(%d)", z, x)) return z } diff --git a/pool/pool.gno b/pool/pool.gno index faa63f21..d3758fa4 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -21,12 +21,12 @@ func Mint( ) (bigint, bigint /* *u256.Int, *u256.Int */) { require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Mint() || PrevRealmPath(%s) == \"gno.land/r/demo/position\"", PrevRealmPath())) - requirePositive(liquidityAmount, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected liquidityAmount(%d) > 0", liquidityAmount)) + require(liquidityAmount_ > 0, ufmt.Sprintf("[POOL] pool.gno__Mint() || liquidityAmount(%d) > 0", liquidityAmount_)) - liquidityAmount := u256.FromBigint(liquidityAmount_) + liquidityAmount := u256.IntFromBigint(liquidityAmount_) pool := GetPool(pToken0Path, pToken1Path, pFee) - _, amount0Int, amount1Int := pool.modifyPosition( + _, amount0, amount1 := pool.modifyPosition( ModifyPositionParams{ recipient, // owner tickLower, // tickLower @@ -35,14 +35,19 @@ func Mint( }, ) - amount0 := bigint(amount0Int) - requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected amount0(%d) >= 0", amount0)) - amount1 := bigint(amount1Int) - requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected amount1(%d) >= 0", amount1)) + if amount0.IsNeg() { + panic("[POOL] pool.gno__Mint() || amount0.IsNeg()") + } + + if amount1.IsNeg() { + panic("[POOL] pool.gno__Mint() || amount1.IsNeg()") + } - var balance0Before, balance1Before u256.Int - if amount0 > 0 { - balance0Before := bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())) + balance0Before := u256.Zero().Int() + balance1Before := u256.Zero().Int() + + if amount0.Signum() > 0 { + balance0Before := u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int() from := std.GetOrigCaller() // token should be transferred from actual user(std.GetOrigCaller), not from the realm(PrevRealm) to := GetOrigPkgAddr() @@ -57,7 +62,7 @@ func Mint( ) require( - balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())), + new(u256.Int).Add(balance0Before, amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), @@ -65,8 +70,8 @@ func Mint( ) } - if amount1 > 0 { - balance1Before = bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())) + if amount1.Signum() > 0 { + balance1Before = u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int() from := std.GetOrigCaller() // token should be transferred from actual user(std.GetOrigCaller), not from the realm(PrevRealm) to := GetOrigPkgAddr() @@ -81,7 +86,7 @@ func Mint( ) require( - balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())), + new(u256.Int).Add(balance1Before, amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), @@ -89,10 +94,10 @@ func Mint( ) } - pool.balances.token0 += amount0 - pool.balances.token1 += amount1 + pool.balances.token0.AddInt(pool.balances.token0, amount0) + pool.balances.token1.AddInt(pool.balances.token1, amount1) - return amount0, amount1 + return amount0.Bigint(), amount1.Bigint() } // only position contract can call this function @@ -106,7 +111,7 @@ func Burn( ) (bigint, bigint) { requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Burn() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, std.PrevRealm().PkgPath())) - requireUnsigned(amount, ufmt.Sprintf("[POOL] pool.gno__Burn() || amount(%d) >= 0", amount)) + burnAmount := u256.FromBigint(amount).Int().Neg() pool := GetPool(token0Path, token1Path, fee) @@ -115,24 +120,20 @@ func Burn( std.PrevRealm().Addr(), // msg.sender tickLower, tickUpper, - -amount, + burnAmount, }, ) - amount0, amount1 := -amount0Int, -amount1Int - requireUnsigned(amount0, ufmt.Sprintf("pool.gno__Burn() || amount0(%d) >= 0", amount0)) - requireUnsigned(amount1, ufmt.Sprintf("pool.gno__Burn() || amount1(%d) >= 0", amount1)) + amount0 := amount0Int.Neg().Uint() + amount1 := amount1Int.Neg().Uint() - if amount0 > 0 || amount1 > 0 { - position.tokensOwed0 += amount0 - position.tokensOwed1 += amount1 - } + position.tokensOwed0.Add(position.tokensOwed0, amount0) + position.tokensOwed1.Add(position.tokensOwed1, amount1) - positionKey := positionGetKey(std.PrevRealm().Addr(), tickLower, tickUpper) - pool.positions[positionKey] = position + key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) + pool.positions[key] = position - // actual token transfer happens in Collect() - return amount0, amount1 + return amount0.Bigint(), amount1.Bigint() } // only position contract can call this function @@ -143,13 +144,13 @@ func Collect( recipient std.Address, tickLower int32, tickUpper int32, - amount0Requested bigint, - amount1Requested bigint, + amount0Requested_ bigint, + amount1Requested_ bigint, ) (bigint, bigint) { requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Collect() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, std.PrevRealm().PkgPath())) - requireUnsigned(amount0Requested, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount0Requested(%d) >= 0", amount0Requested)) - requireUnsigned(amount1Requested, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1Requested(%d) >= 0", amount1Requested)) + amount0Requested := u256.FromBigint(amount0Requested_) + amount1Requested := u256.FromBigint(amount1Requested_) pool := GetPool(token0Path, token1Path, fee) @@ -157,31 +158,25 @@ func Collect( position, exist := pool.positions[positionKey] requireExist(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", positionKey)) - // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 - amount0 := min(amount0Requested, position.tokensOwed0) - amount0 = min(amount0, pool.balances.token0) - requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount0(%d) >= 0", amount0)) + amount0 := amount0Requested.Min(position.tokensOwed0) + amount1 := amount1Requested.Min(position.tokensOwed1) - // Update state first then transfer - position.tokensOwed0 -= amount0 - pool.balances.token0 -= amount0 + require(pool.balances.token0.Gte(amount0), ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%d) >= amount0(%d)", pool.balances.token0, amount0)) transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) - requireUnsigned(pool.balances.token0, ufmt.Sprintf("[POOL] pool.gno__Burn() || pool.balances.token0(%d) >= 0", pool.balances.token0)) - // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 - amount1 := min(amount1Requested, position.tokensOwed1) - amount1 = min(amount1, pool.balances.token1) - requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__Collect() || amount1(%d) >= 0", amount1)) - - // Update state first then transfer - position.tokensOwed1 -= amount1 - pool.balances.token1 -= amount1 + require(pool.balances.token1.Gte(amount1), ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%d) >= amount1(%d)", pool.balances.token1, amount1)) transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) - requireUnsigned(pool.balances.token1, ufmt.Sprintf("[POOL] pool.gno__Burn() || pool.balances.token1(%d) >= 0", pool.balances.token1)) - pool.positions[positionKey] = position + // adjust position + position.tokensOwed0.Sub(position.tokensOwed0, amount0) + position.tokensOwed1.Sub(position.tokensOwed1, amount1) + pool.positions[key] = position - return amount0, amount1 + // adjust pool + pool.balances.token0.Sub(pool.balances.token0, amount0) + pool.balances.token1.Sub(pool.balances.token0, amount1) + + return amount0.Bigint(), amount1.Bigint() } func Swap( @@ -190,34 +185,36 @@ func Swap( fee uint16, recipient std.Address, zeroForOne bool, - amountSpecified bigint, - sqrtPriceLimitX96 bigint, + amountSpecified_ bigint, + sqrtPriceLimitX96_ bigint, payer std.Address, // router ) (bigint, bigint) { requirePrevRealmPath(consts.ROUTER_PATH, ufmt.Sprintf("[POOL] pool.gno__Swap() || expected PrevRealmPath(%s), got %s", consts.ROUTER_PATH, std.PrevRealm().PkgPath())) - // early panic - require(amountSpecified != 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || amountSpecified(%d) != 0", amountSpecified)) + require(amountSpecified_ != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") + + amountSpecified := u256.IntFromBigint(amountSpecified_) + sqrtPriceLimitX96 := u256.FromBigint(sqrtPriceLimitX96_) pool := GetPool(token0Path, token1Path, fee) slot0Start := pool.slot0 require(slot0Start.unlocked, ufmt.Sprintf("[POOL] pool.gno__Swap() || slot0 must be unlocked")) var feeProtocol uint8 - var feeGrowthGlobalX128 bigint + var feeGrowthGlobalX128 *u256.Uint if zeroForOne { require( - sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > consts.MIN_SQRT_RATIO, - ufmt.Sprintf("[POOL] pool.gno__Swap() || sqrtPriceLimitX96(%d) < slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) > consts.MIN_SQRT_RATIO(%d)", + sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Gt(u256.FromBigint(consts.MIN_SQRT_RATIO)), + ufmt.Sprintf("[POOL] pool.gno__Swap() || SPL-zeroForOne(T)__sqrtPriceLimitX96(%d) < slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) > MIN_SQRT_RATIO(%d)", sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, consts.MIN_SQRT_RATIO), ) feeProtocol = slot0Start.feeProtocol % 16 feeGrowthGlobalX128 = pool.feeGrowthGlobal0X128 } else { require( - sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < consts.MAX_SQRT_RATIO, - ufmt.Sprintf("[POOL] pool.gno__Swap() || sqrtPriceLimitX96(%d) > slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) < consts.MAX_SQRT_RATIO(%d)", + sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Lt(u256.FromBigint(consts.MAX_SQRT_RATIO)), + ufmt.Sprintf("[POOL] pool.gno__Swap() || SPL-zeroForOne(F)__sqrtPriceLimitX96(%d) > slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) < MAX_SQRT_RATIO(%d)", sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, consts.MAX_SQRT_RATIO), ) feeProtocol = slot0Start.feeProtocol / 16 @@ -239,32 +236,32 @@ func Swap( } } - exactInput := amountSpecified > 0 + exactInput := amountSpecified.Gt(u256.Zero().Int()) var state SwapState if zeroForOne { state = SwapState{ amountSpecifiedRemaining: amountSpecified, - amountCalculated: 0, + amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: pool.feeGrowthGlobal0X128, - protocolFee: 0, + protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } else { state = SwapState{ amountSpecifiedRemaining: amountSpecified, - amountCalculated: 0, + amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, feeGrowthGlobalX128: pool.feeGrowthGlobal1X128, - protocolFee: 0, + protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } - for state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96 { + for !state.amountSpecifiedRemaining.IsZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { var step StepComputations step.sqrtPriceStartX96 = state.sqrtPriceX96 @@ -285,7 +282,7 @@ func Swap( isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 - var sqrtRatioTargetX96 bigint + var sqrtRatioTargetX96 *u256.Uint if (zeroForOne && isLower) || (!zeroForOne && isHigher) { sqrtRatioTargetX96 = sqrtPriceLimitX96 } else { @@ -301,28 +298,29 @@ func Swap( ) if exactInput { - state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount) - state.amountCalculated -= step.amountOut + state.amountSpecifiedRemaining.Sub(state.amountSpecifiedRemaining, new(u256.Uint).Add(step.amountIn, step.feeAmount).Int()) + state.amountCalculated.Sub(state.amountCalculated, step.amountOut.Int()) } else { - state.amountSpecifiedRemaining += step.amountOut - state.amountCalculated += (step.amountIn + step.feeAmount) + state.amountSpecifiedRemaining.Add(state.amountSpecifiedRemaining, step.amountOut.Int()) + state.amountCalculated.Add(state.amountCalculated, new(u256.Uint).Add(step.amountIn, step.feeAmount).Int()) } if cache.feeProtocol > 0 { - delta := step.feeAmount / bigint(uint64(cache.feeProtocol)) - requireUnsigned(delta, ufmt.Sprintf("[POOL] pool.gno__Swap() || delta(%d) >= 0", delta)) - step.feeAmount -= delta - state.protocolFee += delta + delta := new(u256.Uint).Div(step.feeAmount, u256.NewUint(uint64(cache.feeProtocol))) + step.feeAmount.Sub(step.feeAmount, delta) + state.protocolFee.Add(state.protocolFee, delta) } - if state.liquidity > 0 { + if state.liquidity.Gt(u256.Zero()) { // save fee - state.feeGrowthGlobalX128 += (step.feeAmount * consts.Q128 / state.liquidity) + update := new(u256.Uint).Mul(step.feeAmount, u256.FromBigint(consts.Q128)) + update.Div(update, state.liquidity) + state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128, update) } if state.sqrtPriceX96 == step.sqrtPriceNextX96 { if step.initialized { - var fee0, fee1 bigint + var fee0, fee1 *u256.Uint if zeroForOne { fee0 = state.feeGrowthGlobalX128 @@ -367,103 +365,82 @@ func Swap( if zeroForOne { pool.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128 - if state.protocolFee > 0 { + if state.protocolFee.Gt(u256.Zero()) { pool.protocolFees.token0 += state.protocolFee } } else { pool.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128 - if state.protocolFee > 0 { + if state.protocolFee.Gt(u256.Zero()) { pool.protocolFees.token1 += state.protocolFee } } - var amount0, amount1 bigint + var amount0, amount1 *u256.Int if zeroForOne == exactInput { - amount0 = amountSpecified - state.amountSpecifiedRemaining + amount0 = new(u256.Int).Sub(amountSpecified, state.amountSpecifiedRemaining) amount1 = state.amountCalculated } else { amount0 = state.amountCalculated - amount1 = amountSpecified - state.amountSpecifiedRemaining + amount1 = new(u256.Int).Sub(amountSpecified, state.amountSpecifiedRemaining) } if zeroForOne { // payer > pool - balance0Before := bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())) - ok := transferFromByRegisterCall(pool.token0Path, payer, consts.POOL_ADDR, uint64(amount0)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount0))(%d) failed", - pool.token0Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount0), - ), - ) + balance0Before := balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()) + ok := transferFromByRegisterCall(pool.token0Path, payer, consts.ADDR_POOL, uint64(amount0)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path, payer, ADDR_POOL, uint64(amount0)) failed") + } require( - balance0Before+amount0 <= bigint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())), + new(u256.Int).Add(u256.NewUint(balance0Before).Int(), amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) - pool.balances.token0 += amount0 - require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) >= 0", pool.balances.token0)) - - if amount1 < 0 { // pool > recipient - require(pool.balances.token1 > -amount1, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > -amount1(%d)", pool.balances.token1, -amount1)) + pool.balances.token0.AddInt(pool.balances.token0, amount0) + if amount1.IsNeg() { // pool > recipient + require(pool.balances.token1.Gt(amount1.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > (-1 * amount1)(%d)", pool.balances.token1, (-amount1))) ok := transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(-amount1))(%d) failed", - pool.token1Path, recipient.String(), uint64(-amount1), - ), - ) - pool.balances.token1 += amount1 + if !ok { + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) failed") + } + pool.balances.token1.AddInt(pool.balances.token1, amount1) } - require(pool.balances.token1 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() #1 || pool.balances.token1(%d) >= 0", pool.balances.token1)) } else { // payer > pool - balance1Before := bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())) - ok := transferFromByRegisterCall(pool.token1Path, payer, consts.POOL_ADDR, uint64(amount1)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount1))(%d) failed", - pool.token1Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount1), - ), - ) + balance1Before := balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()) + ok := transferFromByRegisterCall(pool.token1Path, payer, consts.ADDR_POOL, uint64(amount1)) + if !ok { + panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path, payer, ADDR_POOL, uint64(amount1)) failed") + } require( - balance1Before+amount1 <= bigint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())), + new(u256.Int).Add(u256.NewUint(balance1Before).Int(), amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) - pool.balances.token1 += amount1 - require(pool.balances.token1 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() #2 || pool.balances.token1(%d) >= 0", pool.balances.token1)) + pool.balances.token1.AddInt(pool.balances.token1, amount1) - if amount0 < 0 { // pool > recipient - require(pool.balances.token0 > -amount0, ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) > -amount0(%d)", pool.balances.token0, -amount0)) + if amount0.IsNeg() { // pool > recipient + require(pool.balances.token0.Gt(amount0.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) > (-1 * amount0)(%d)", pool.balances.token0, (-amount0))) ok := transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(-amount0))(%d) failed", - pool.token0Path, recipient.String(), uint64(-amount0), - ), - ) - pool.balances.token0 += amount0 + if !ok { + panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) failed") + } + pool.balances.token0.AddInt(pool.balances.token0, amount0) } - require(pool.balances.token0 >= 0, ufmt.Sprintf("[POOL] pool.gno__Swap() #2 || pool.balances.token0(%d) >= 0", pool.balances.token0)) } pool.slot0.unlocked = true - return amount0, amount1 + return amount0.Bigint(), amount1.Bigint() } // ADMIN @@ -503,11 +480,8 @@ func CollectProtocol( pool := GetPool(token0Path, token1Path, fee) - amount0 := min(amount0Requested, pool.protocolFees.token0) - requireUnsigned(amount0, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount0(%d) >= 0", amount0)) - - amount1 := min(amount1Requested, pool.protocolFees.token1) - requireUnsigned(amount1, ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || amount1(%d) >= 0", amount1)) + amount0 := amount0Requested.Min(pool.protocolFees.token0) + amount1 := amount1Requested.Min(pool.protocolFees.token1) // without procotol fee amount0, amount1 = pool.saveProtocolFees(amount0, amount1) @@ -522,15 +496,11 @@ func CollectProtocol( ) ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(amount1))(%d) failed", - pool.token1Path, recipient.String(), uint64(amount1), - ), - ) + if !ok { + panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) failed") + } - return amount0, amount1 + return amount0.Bigint(), amount1.Bigint() } // XXXXX: i made some mistake on int/uint while refactoring this part, please double check it before merge @@ -543,10 +513,10 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 pool.slot0.tick, ) - amount0 := u256.Zero() - amount1 := u256.Zero() + amount0 := u256.Zero().Int() + amount1 := u256.Zero().Int() - if params.liquidityDelta != 0 { + if !params.liquidityDelta.IsZero() { if pool.slot0.tick < params.tickLower { amount0 = sqrtPriceMathGetAmount0Delta( TickMathGetSqrtRatioAtTick(params.tickLower), @@ -589,11 +559,11 @@ func (pool *Pool) updatePosition( liquidityDelta *u256.Int, tick int32, ) PositionInfo { - var _feeGrowthGlobal0X128 bigint = pool.feeGrowthGlobal0X128 - var _feeGrowthGlobal1X128 bigint = pool.feeGrowthGlobal1X128 + var _feeGrowthGlobal0X128 *u256.Uint = pool.feeGrowthGlobal0X128 + var _feeGrowthGlobal1X128 *u256.Uint = pool.feeGrowthGlobal1X128 var flippedLower, flippedUpper bool - if liquidityDelta != 0 { + if !liquidityDelta.IsZero() { flippedLower = pool.tickUpdate( tickLower, tick, @@ -642,7 +612,7 @@ func (pool *Pool) updatePosition( feeGrowthInside1X128, ) - if liquidityDelta < 0 { + if liquidityDelta.IsNeg() { if flippedLower { pool.tickClear(tickLower) } @@ -654,17 +624,17 @@ func (pool *Pool) updatePosition( return position } -func (pool *Pool) saveProtocolFees(amount0, amount1 bigint) (bigint, bigint) { - if amount0 > 0 && amount0 == pool.protocolFees.token0 { +func (pool *Pool) saveProtocolFees(amount0, amount1 *u256.Uint) (*u256.Uint, *u256.Uint) { + if !amount0.IsZero() && amount0.Eq(pool.protocolFees.token0) { amount0-- } - if amount1 > 0 && amount1 == pool.protocolFees.token1 { + if !amount1.IsZero() && amount1.Eq(pool.protocolFees.token1) { amount1-- } - pool.protocolFees.token0 -= amount0 - pool.protocolFees.token1 -= amount1 + pool.protocolFees.token0.Sub(pool.protocolFees.token0, amount0) + pool.protocolFees.token1.Sub(pool.protocolFees.token1, amount1) // return rest fee return amount0, amount1 diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index b5a8200f..8d1d9562 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -138,13 +138,13 @@ func newPool( } balances := Balances{ - token0: 0, - token1: 0, + token0: u256.Zero(), + token1: u256.Zero(), } protocolFees := ProtocolFees{ - token0: 0, - token1: 0, + token0: u256.Zero(), + token1: u256.Zero(), } return &Pool{ @@ -155,10 +155,10 @@ func newPool( tickSpacing: tickSpacing, maxLiquidityPerTick: maxLiquidityPerTick, slot0: slot0, - feeGrowthGlobal0X128: 0, - feeGrowthGlobal1X128: 0, + feeGrowthGlobal0X128: u256.Zero(), + feeGrowthGlobal1X128: u256.Zero(), protocolFees: protocolFees, - liquidity: 0, + liquidity: u256.Zero(), ticks: Ticks{}, tickBitmaps: TickBitmaps{}, positions: Positions{}, diff --git a/pool/position.gno b/pool/position.gno index fb1d0ce4..3835b587 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -40,28 +40,30 @@ func positionUpdate( feeGrowthInside1X128 *u256.Uint, ) PositionInfo { var liquidityNext *u256.Int - if liquidityDelta == 0 { - liquidityNext = self.liquidity + if liquidityDelta.IsZero() { + liquidityNext = self.liquidity.Int() } else { - liquidityNext = liquidityMathAddDelta(self.liquidity, liquidityDelta) + liquidityNext = liquidityMathAddDelta(self.liquidity, liquidityDelta).Int() } - tokensOwed0 := (feeGrowthInside0X128 - self.feeGrowthInside0LastX128) * self.liquidity / consts.Q128 - tokensOwed1 := (feeGrowthInside1X128 - self.feeGrowthInside1LastX128) * self.liquidity / consts.Q128 - requireUnsigned(tokensOwed0, ufmt.Sprintf("[POOL] position.gno__positionUpdate() || tokensOwed0(%d) >= 0", tokensOwed0)) - requireUnsigned(tokensOwed1, ufmt.Sprintf("[POOL] position.gno__positionUpdate() || tokensOwed1(%d) >= 0", tokensOwed1)) + tokensOwed0 := new(u256.Uint).Sub(feeGrowthInside0X128, self.feeGrowthInside0LastX128) + tokensOwed0 = tokensOwed0.Mul(tokensOwed0, self.liquidity) + tokensOwed0 = tokensOwed0.Div(tokensOwed0, u256.FromBigint(consts.Q128)) - if liquidityDelta != 0 { - self.liquidity = liquidityNext + tokensOwed1 := new(u256.Uint).Sub(feeGrowthInside1X128, self.feeGrowthInside1LastX128) + tokensOwed1 = tokensOwed1.Mul(tokensOwed1, self.liquidity) + tokensOwed1 = tokensOwed1.Div(tokensOwed1, u256.FromBigint(consts.Q128)) + + if !liquidityDelta.IsZero() { + self.liquidity = liquidityNext.Uint() } self.feeGrowthInside0LastX128 = feeGrowthInside0X128 self.feeGrowthInside1LastX128 = feeGrowthInside1X128 - if tokensOwed0 > 0 || tokensOwed1 > 0 { - self.tokensOwed0 += tokensOwed0 - self.tokensOwed1 += tokensOwed1 - } + self.tokensOwed0.Add(self.tokensOwed0, tokensOwed0) + self.tokensOwed1.Add(self.tokensOwed1, tokensOwed1) + return self } diff --git a/pool/sqrt_price_math.gno b/pool/sqrt_price_math.gno index 148a3084..f005d485 100644 --- a/pool/sqrt_price_math.gno +++ b/pool/sqrt_price_math.gno @@ -10,37 +10,38 @@ func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp( liquidity *u256.Uint, amount *u256.Uint, add bool, -) bigint { +) *u256.Uint { if amount.IsZero() { return sqrtPX96 } - numerator1 := liquidity << 96 - product := amount * sqrtPX96 + numerator1 := new(u256.Uint).Lsh(liquidity, 96) - if add { - denominator := numerator1 + product + product := new(u256.Uint).Mul(amount, sqrtPX96) - require( - denominator != 0, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || expected denominator != 0, got: %d", - denominator, - )) + var denominator *u256.Uint - return numerator1 * sqrtPX96 / denominator + if add { + if new(u256.Uint).Div(product, amount).Eq(sqrtPX96) { + denominator = new(u256.Uint).Add(numerator1, product) + if denominator.Gte(numerator1) { + res := new(u256.Uint).Mul(numerator1, sqrtPX96) + res.Div(res, denominator) + return res + } + } + + denominator1 := new(u256.Uint).Div(numerator1, sqrtPX96) + denominator1.Add(denominator1, amount) + return new(u256.Uint).Div(numerator1, denominator1) } + require(new(u256.Uint).Div(product, amount).Eq(sqrtPX96) && numerator1.Gt(product), "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || condition must return true") - denominator := numerator1 - product + denominator = new(u256.Uint).Sub(numerator1, product) - requireUnsigned( - denominator, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp() || expected denominator >= 0, got: %d", - denominator, - )) - - return numerator1 / ((numerator1 / sqrtPX96) + amount) + res := new(u256.Uint).Mul(numerator1, sqrtPX96) + res.Div(res, denominator) + return res } func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown( @@ -48,42 +49,29 @@ func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown( liquidity *u256.Uint, amount *u256.Uint, add bool, -) bigint { - var quotient bigint - if amount <= consts.MAX_UINT160 { - quotient = (amount << 96) / liquidity - } else { - quotient = amount * (consts.Q96 / liquidity) - } - - // quotient mutst be positive when amount and liquidity are positive - +) *u256.Uint { + var quotient *u256.Uint if add { - result := sqrtPX96 + quotient - - require( - result >= sqrtPX96, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected result >= sqrtPX96, got: %d", - result, - ), - ) - - return result + if amount.Gte(u256.FromBigint(consts.MAX_UINT160)) { + scaledAmount := new(u256.Uint).Lsh(amount, 96) + quotient = scaledAmount.Div(scaledAmount, liquidity) + } else { + scaledAmount := new(u256.Uint).Mul(amount, u256.FromBigint(consts.Q96)) + quotient = scaledAmount.Div(scaledAmount, liquidity) + } + return new(u256.Uint).Add(sqrtPX96, quotient) + } else { + if amount.Gte(u256.FromBigint(consts.MAX_UINT160)) { + scaledAmount := new(u256.Uint).Lsh(amount, 96) + quotient = scaledAmount.Div(scaledAmount, liquidity) + } else { + scaledAmount := new(u256.Uint).Mul(amount, u256.FromBigint(consts.Q96)) + quotient = scaledAmount.Div(scaledAmount, liquidity) + } + + require(sqrtPX96.Gt(quotient), ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || sqrtPX96(%d) must be greater than quotient(%d)", sqrtPX96, quotient)) + return new(u256.Uint).Sub(sqrtPX96, quotient) } - - result := sqrtPX96 - quotient - - require( - result >= 0, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected sqrtPX96 >= quotient, got: %d, %d", - sqrtPX96, - quotient, - ), - ) - - return result } func sqrtPriceMathGetNextSqrtPriceFromInput( @@ -91,101 +79,80 @@ func sqrtPriceMathGetNextSqrtPriceFromInput( liquidity *u256.Uint, amountIn *u256.Uint, zeroForOne bool, -) (sqrtQ bigint) { +) (sqrtQ *u256.Uint) { if zeroForOne { - amount0Result := sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) - requireUnsigned( - amount0Result, - ufmt.Sprintf( - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromInput() #1 || expected unsigned result, got: %d", - amount0Result, - ), - ) - - return amount0Result + return sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true) } - amount1Result := sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true) - requireUnsigned( - amount1Result, - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromInput() || condition must return `v` >= 0__#2", - ) - - return amount1Result + return sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true) } func sqrtPriceMathGetNextSqrtPriceFromOutput( sqrtPX96 *u256.Uint, liquidity *u256.Uint, - amountOut *u256.Int, + amountOut *u256.Uint, zeroForOne bool, -) (sqrtQ bigint) { +) (sqrtQ *u256.Uint) { if zeroForOne { - amount1Result := sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) - requireUnsigned( - amount1Result, - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromOutput() || condition must return `v` >= 0__#1", - ) - - return amount1Result + return sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false) } - amount0Result := sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false) - requireUnsigned( - amount0Result, - "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromOutput() || condition must return `v` >= 0__#2", - ) - - return amount0Result + return sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false) } func sqrtPriceMathGetAmount0DeltaHelper( sqrtRatioAX96 *u256.Uint, sqrtRatioBX96 *u256.Uint, liquidity *u256.Uint, -) bigint { - if sqrtRatioAX96 > sqrtRatioBX96 { +) *u256.Uint { + if sqrtRatioAX96.Gt(sqrtRatioBX96) { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 } - numerator1 := absBigint(liquidity) << 96 - numerator2 := sqrtRatioBX96 - sqrtRatioAX96 + numerator1 := new(u256.Uint).Lsh(liquidity, 96) + numerator2 := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) - return ((numerator1 * numerator2) / sqrtRatioBX96) / sqrtRatioAX96 + res := new(u256.Uint).Mul(numerator1, numerator2) + res.Div(res, sqrtRatioBX96) + res.Div(res, sqrtRatioAX96) + return res } func sqrtPriceMathGetAmount1DeltaHelper( sqrtRatioAX96 *u256.Uint, sqrtRatioBX96 *u256.Uint, liquidity *u256.Uint, -) bigint { - if sqrtRatioAX96 > sqrtRatioBX96 { +) *u256.Uint { + if sqrtRatioAX96.Gt(sqrtRatioBX96) { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 } - return liquidity * (sqrtRatioBX96 - sqrtRatioAX96) / consts.Q96 + res := new(u256.Uint).Sub(sqrtRatioBX96, sqrtRatioAX96) + res.Mul(res, liquidity) + res.Div(res, u256.FromBigint(consts.Q96)) + return res } func sqrtPriceMathGetAmount0Delta( sqrtRatioAX96 *u256.Uint, sqrtRatioBX96 *u256.Uint, liquidity *u256.Int, -) u256.Int { - if liquidity.IsNegative() { - return -sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, absBigint(-liquidity)) +) *u256.Int { + if liquidity.IsNeg() { + return sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs()).Int().Neg() } - return sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity) + return sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Uint()).Int() } func sqrtPriceMathGetAmount1Delta( sqrtRatioAX96 *u256.Uint, sqrtRatioBX96 *u256.Uint, liquidity *u256.Int, -) bigint { - if liquidity.IsNegative() { - return -sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, absBigint(-liquidity)) +) *u256.Int { + if liquidity.IsNeg() { + return sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Abs()).Int().Neg() } - return sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity) + return sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioAX96, sqrtRatioBX96, liquidity.Uint()).Int() } diff --git a/pool/swap_math.gno b/pool/swap_math.gno index 2ab5c326..52347134 100644 --- a/pool/swap_math.gno +++ b/pool/swap_math.gno @@ -1,8 +1,6 @@ package pool import ( - "gno.land/p/demo/ufmt" - "gno.land/p/demo/u256" ) @@ -17,18 +15,17 @@ func swapMathComputeSwapStep( // POSTIVIE == EXACT_IN => Estimated AmountOut // NEGATIVE == EXACT_OUT => Estimated AmountIn - exactIn := amountRemaining.Gte(u256.Zero()) + exactIn := !amountRemaining.IsNeg() if exactIn { - amountRemainingLessFee := amountRemaining * bigint(uint64(1000000-feePips)) / 1000000 - requireUnsigned(amountRemainingLessFee, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || amountRemainingLessFee(%d) >= 0", amountRemainingLessFee)) + amountRemainingLessFee := new(u256.Uint).Mul(amountRemaining.Uint(), u256.NewUint(uint64(1000000-feePips))) + amountRemainingLessFee.Div(amountRemainingLessFee, u256.NewUint(1000000)) if isToken1Expensive { amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity) } else { amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity) } - requireUnsigned(amountIn, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || amountIn(%d) >= 0", amountIn)) if amountRemainingLessFee >= amountIn { sqrtRatioNextX96 = sqrtRatioTargetX96 @@ -40,7 +37,6 @@ func swapMathComputeSwapStep( isToken1Expensive, ) } - requireUnsigned(sqrtRatioNextX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioNextX96(%d) >= 0", sqrtRatioNextX96)) } else { if isToken1Expensive { amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity) @@ -48,19 +44,16 @@ func swapMathComputeSwapStep( amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity) } - requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || amountOut(%d) >= 0", amountOut)) - - if absBigint(amountRemaining) >= amountOut { + if amountRemaining.Abs().Gte(amountOut) { sqrtRatioNextX96 = sqrtRatioTargetX96 } else { sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromOutput( sqrtRatioCurrentX96, liquidity, - absBigint(-amountRemaining), + amountRemaining.Abs(), isToken1Expensive, ) } - requireUnsigned(sqrtRatioNextX96, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() || sqrtRatioNextX96(%d) >= 0", sqrtRatioNextX96)) } max := sqrtRatioTargetX96 == sqrtRatioNextX96 @@ -71,42 +64,41 @@ func swapMathComputeSwapStep( } else { amountIn = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity) } - requireUnsigned(amountIn, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() #1 || amountIn(%d) >= 0", amountIn)) if max && !exactIn { amountOut = amountOut } else { amountOut = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity) } - requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() #1 || amountOut(%d) >= 0", amountOut)) } else { if max && exactIn { amountIn = amountIn } else { amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity) } - requireUnsigned(amountIn, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() #2 || amountIn(%d) >= 0", amountIn)) if max && !exactIn { amountOut = amountOut } else { amountOut = sqrtPriceMathGetAmount0DeltaHelper(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity) } - requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() #2 || amountOut(%d) >= 0", amountOut)) } - if !exactIn && amountOut > absBigint(-amountRemaining) { - amountOut = absBigint(-amountRemaining) - requireUnsigned(amountOut, ufmt.Sprintf("[POOL] swap_math.gno__swapMathComputeSwapStep() #3 || amountOut(%d) >= 0", amountOut)) + if !exactIn && amountOut.Gt(amountRemaining.Abs()) { + amountOut = amountRemaining.Abs() } - if exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96 { - feeAmount = amountRemaining - amountIn + if exactIn && !sqrtRatioNextX96.Eq(sqrtRatioTargetX96) { + ///XXXXXXXXXXXXXXXXXXXXXXXX + // I was not able to understand this line, commented out for now + // resolve this before merge!!! + // feeAmount = new(u256.Uint).Sub(amountRemaining, amountIn) } else { - feeAmount = amountIn * bigint(uint64(feePips)) / bigint(uint64(1000000-feePips)) + feeAmount = new(u256.Uint).Mul(amountIn, u256.NewUint(uint64(feePips))) + feeAmount.Div(feeAmount, u256.NewUint(uint64(1000000-feePips))) } - if !exactIn && amountOut != 0 { + if !exactIn && !amountOut.IsZero() { amountOut++ } return sqrtRatioNextX96, amountIn, amountOut, feeAmount diff --git a/pool/tick.gno b/pool/tick.gno index 524cff58..6f32e309 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -6,11 +6,11 @@ import ( ) func tickTickSpacingToMaxLiquidityPerTick(tickSpacing int32) *u256.Uint { - minTick := (MIN_TICK / tickSpacing) * tickSpacing - maxTick := (MAX_TICK / tickSpacing) * tickSpacing - numTicks := bigint((maxTick-minTick)/tickSpacing) + 1 + minTick := (consts.MIN_TICK / tickSpacing) * tickSpacing + maxTick := (consts.MAX_TICK / tickSpacing) * tickSpacing + numTicks := u256.NewUint(uint64(((maxTick - minTick) / tickSpacing) + 1)) - return new(u256.Uint).Div(MAX_UINT128, numTicks) + return new(u256.Uint).Div(u256.FromBigint(consts.MAX_UINT128), numTicks) } func (pool *Pool) tickGetFeeGrowthInside( @@ -64,9 +64,9 @@ func (pool *Pool) tickUpdate( liquidityGrossAfter := liquidityMathAddDelta(liquidityGrossBefore, liquidityDelta) require(liquidityGrossAfter.Lte(maxLiquidity), ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%d) <= maxLiquidity(%d)", liquidityGrossAfter, maxLiquidity)) - flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0) + flipped = (liquidityGrossAfter.IsZero()) != (liquidityGrossBefore.IsZero()) - if liquidityGrossBefore == 0 { + if liquidityGrossBefore.IsZero() { if tick <= tickCurrent { info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 @@ -78,9 +78,9 @@ func (pool *Pool) tickUpdate( info.liquidityGross = liquidityGrossAfter if upper { - info.liquidityNet -= liquidityDelta + info.liquidityNet.Sub(info.liquidityNet, liquidityDelta) } else { - info.liquidityNet += liquidityDelta + info.liquidityNet.Add(info.liquidityNet, liquidityDelta) } pool.ticks[tick] = info diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index d5fb3411..628c113c 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -19,14 +19,17 @@ func (pool *Pool) tickBitmapFlipTick( ) { require(tick%tickSpacing == 0, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || tick(%d) MOD tickSpacing(%d) != 0 (got:%d)", tick, tickSpacing, tick%tickSpacing)) wordPos, bitPos := tickBitmapPosition(tick / tickSpacing) - mask := - bigint(1) << - uint64(bitPos) // 2 ** bitPos - mask := bigint(int64(1)) << uint64(bitPos) // 2 ** bitPos + mask := u256.NewUint(1) + mask.Lsh(mask, uint(bitPos)) // 2 ** bitPos - requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) >= 0", mask)) + //XXXXXXXXXXXXXXXX + // mask.Lsh is exptected to check overflow with the signed integer type. + // Lets add an overflow proteced shift method for Uint so we dont have to make it a signed integer. + // not implemented, should be done in a separate pr - pool.tickBitmaps[wordPos] ^= mask + //requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) > 0", mask)) + + pool.tickBitmaps[wordPos].Xor(pool.tickBitmaps[wordPos], mask) } func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( @@ -42,16 +45,18 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( if lte { wordPos, bitPos := tickBitmapPosition(compress) - mask := (bigint(1) << uint64(bitPos)) - bigint(1) + (bigint(1) << uint64(bitPos)) - requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord #1 || mask(%d) >= 0", mask)) + mask := new(u256.Uint).Lsh(u256.One(), uint(bitPos)) + mask.Sub(mask, u256.One()) + mask.Add(mask, (new(u256.Uint).Lsh(u256.One(), uint(bitPos)))) + // XXXXXXXX overflow check Lsh! + // requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord__mask(%d) >= 0__#1", mask)) - masked := pool.tickBitmaps[wordPos] & mask - requireUnsigned(masked, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord #1 || masked(%d) >= 0", masked)) + masked := new(u256.Uint).And(pool.tickBitmaps[wordPos], mask) - initialized := masked != 0 + initialized := !masked.IsZero() if initialized { - next := (compress - int32(bitPos-bitMathMostSignificantBit(u256.FromBigint(mask)))) * tickSpacing + next := (compress - int32(bitPos-bitMathMostSignificantBit(mask))) * tickSpacing return next, initialized } @@ -61,17 +66,21 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( wordPos, bitPos := tickBitmapPosition(compress + 1) - _mask := bigint(1) << uint64(bitPos) - _mask -= bigint(1) + _mask := new(u256.Uint).Lsh(u256.One(), uint(bitPos)) + _mask.Sub(_mask, u256.One()) mask := bigintBitwiseNotForUint256BitmapIndexing(_mask) - requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord #2 || mask(%d) >= 0", mask)) - masked := pool.tickBitmaps[wordPos] & mask - requireUnsigned(masked, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord #2 || masked(%d) >= 0", masked)) + // XXXXXXXX overflow check Lsh! + // requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord__mask(%d) >= 0__#2", mask)) + + masked := new(u256.Uint).And(pool.tickBitmaps[wordPos], mask) - initialized := masked != 0 + // XXXXXXXX overflow check And! + //requireUnsigned(masked, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord__masked(%d) >= 0__#2", masked)) + + initialized := !masked.IsZero() if initialized { - next := (compress + 1 + int32(bitMathLeastSignificantBit(u256.FromBigint(masked))-bitPos)) * tickSpacing + next := (compress + 1 + int32(bitMathLeastSignificantBit(masked)-bitPos)) * tickSpacing return next, initialized } else { next := (compress + 1 + int32(int64(consts.MAX_UINT8-bigint(uint64(bitPos))))) * tickSpacing @@ -79,14 +88,12 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( } } -func bigintBitwiseNotForUint256BitmapIndexing(x bigint) bigint { - // REF: https://stackoverflow.com/a/77071037 - +func bigintBitwiseNotForUint256BitmapIndexing(x *u256.Uint) *u256.Uint { // Create a mask with all bits set to 1 - mask := consts.MAX_UINT256 - mask -= bigint(1) + mask := u256.FromBigint(consts.MAX_UINT256) + mask.Sub(mask, u256.One()) // XOR with mask to perform bitwise NOT - result := x ^ mask + result := new(u256.Uint).Xor(x, mask) return result } diff --git a/pool/type.gno b/pool/type.gno index 842f44cd..cb3195dd 100644 --- a/pool/type.gno +++ b/pool/type.gno @@ -81,7 +81,7 @@ type TickInfo struct { } type Ticks map[int32]TickInfo // tick => TickInfo -type TickBitmaps map[int16]bigint // tick(wordPos) => bitmap(tickWord ^ mask) +type TickBitmaps map[int16]*u256.Uint // tick(wordPos) => bitmap(tickWord ^ mask) type Positions map[string]PositionInfo // positionKey => PositionInfo // type Pool describes a single Pool/s state diff --git a/pool/utils.gno b/pool/utils.gno index f35adb97..a6f867d7 100644 --- a/pool/utils.gno +++ b/pool/utils.gno @@ -10,23 +10,25 @@ import ( "gno.land/p/demo/u256" ) - -func checkTicks(tickLower, tickUpper u256.Int) { +/* +func checkTicks(tickLower, tickUpper *u256.Int) { require(tickLower.Lt(tickUpper), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) < tickUpper(%d)", tickLower, tickUpper)) require(tickLower.Gte(MIN_TICK), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickLower(%d) >= MIN_TICK(%d)", tickLower, MIN_TICK)) require(tickUpper.Lte(MAX_TICK), ufmt.Sprintf("[POOL] pool.gno__checkTicks() || tickUpper(%d) <= MAX_TICK(%d)", tickUpper, MAX_TICK)) } - +*/ func a2u(addr std.Address) users.AddressOrName { return users.AddressOrName(addr) } +/* func min(num1, num2 bigint) bigint { if num1 < num2 { return num1 } return num2 } +*/ func removeDuplicateString(strSlice []string) []string { // map to store unique keys diff --git a/position/_RPC_api.gno b/position/_RPC_api.gno index 86bbd0ef..f2448fc3 100644 --- a/position/_RPC_api.gno +++ b/position/_RPC_api.gno @@ -80,8 +80,8 @@ func rpcMakePosition(lpTokenId uint64) RpcPosition { token0Balance, token1Balance := common.GetAmountsForLiquidity( currentX96, - lowerX96, - upperX96, + lowerX96.Bigint(), + upperX96.Bigint(), position.liquidity, ) diff --git a/position/_RPC_dry.gno b/position/_RPC_dry.gno index fc62054c..9ae0b191 100644 --- a/position/_RPC_dry.gno +++ b/position/_RPC_dry.gno @@ -11,9 +11,9 @@ func DryMint( amount0Desired bigint, amount1Desired bigint, ) (amount0, amount1 bigint) { - sqrtRatioX96 := common.TickMathGetSqrtRatioAtTick(tickCurrent) - sqrtLowerX96 := common.TickMathGetSqrtRatioAtTick(tickLower) - sqrtUpperX96 := common.TickMathGetSqrtRatioAtTick(tickUpper) + sqrtRatioX96 := p.TickMathGetSqrtRatioAtTick(tickCurrent).Bigint() + sqrtLowerX96 := p.TickMathGetSqrtRatioAtTick(tickLower).Bigint() + sqrtUpperX96 := p.TickMathGetSqrtRatioAtTick(tickUpper).Bigint() liquidity := common.GetLiquidityForAmounts( sqrtRatioX96, diff --git a/position/liquidity_management.gno b/position/liquidity_management.gno index 697c1776..98249075 100644 --- a/position/liquidity_management.gno +++ b/position/liquidity_management.gno @@ -12,8 +12,8 @@ func addLiquidity(params AddLiquidityParams) (bigint, bigint, bigint) { pool := pl.GetPoolFromPoolPath(params.poolKey) sqrtPriceX96 := pool.PoolGetSlot0SqrtPriceX96() - sqrtRatioAX96 := common.TickMathGetSqrtRatioAtTick(params.tickLower) - sqrtRatioBX96 := common.TickMathGetSqrtRatioAtTick(params.tickUpper) + sqrtRatioAX96 := p.TickMathGetSqrtRatioAtTick(params.tickLower).Bigint() + sqrtRatioBX96 := p.TickMathGetSqrtRatioAtTick(params.tickUpper).Bigint() liquidity := common.GetLiquidityForAmounts( sqrtPriceX96, diff --git a/position/position.gno b/position/position.gno index d87e08c3..ada81e45 100644 --- a/position/position.gno +++ b/position/position.gno @@ -83,8 +83,8 @@ func mint(params MintParams) (uint64, bigint, bigint, bigint) { tickLower: params.tickLower, tickUpper: params.tickUpper, liquidity: liquidity, - feeGrowthInside0LastX128: feeGrowthInside0LastX128, - feeGrowthInside1LastX128: feeGrowthInside1LastX128, + feeGrowthInside0LastX128: feeGrowthInside0LastX128.Bigint(), + feeGrowthInside1LastX128: feeGrowthInside1LastX128.Bigint(), tokensOwed0: 0, tokensOwed1: 0, } @@ -208,18 +208,13 @@ func decreaseLiquidity(params DecreaseLiquidityParams) (uint64, bigint, bigint, positionKey := positionKeyCompute(GetOrigPkgAddr(), position.tickLower, position.tickUpper) feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) - position.tokensOwed0 += burnedAmount0 + ((feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128) - position.tokensOwed1 += burnedAmount1 + ((feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128) + tokensOwed0 += (feeGrowthInside0LastX128.Bigint() - position.feeGrowthInside0LastX128) * position.liquidity / Q128 + tokensOwed1 += (feeGrowthInside1LastX128.Bigint() - position.feeGrowthInside1LastX128) * position.liquidity / Q128 - position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128 - position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128 - - position.liquidity = positionLiquidity - params.liquidity - - positions[params.tokenId] = position + position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128.Bigint() + position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128.Bigint() - // GIVE BACK TO USER - amount0, amount1 := pl.Collect( + amount0, amount1 := p.Collect( pToken0, pToken1, pFee, @@ -267,17 +262,20 @@ func CollectFee(tokenId uint64) (uint64, bigint, bigint, string) { // tokenId, t pool := pl.GetPoolFromPoolPath(position.poolKey) feeGrowthInside0LastX128, feeGrowthInside1LastX128 := pool.PoolGetPositionFeeGrowthInside0LastX128(positionKey), pool.PoolGetPositionFeeGrowthInside1LastX128(positionKey) - tokensOwed0 += (feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128) * position.liquidity / consts.Q128 - tokensOwed1 += (feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128) * position.liquidity / consts.Q128 + position.tokensOwed0 += burnAmount0 + ((feeGrowthInside0LastX128.Bigint() - position.feeGrowthInside0LastX128) * position.liquidity / Q128) + position.tokensOwed1 += burnAmount1 + ((feeGrowthInside1LastX128.Bigint() - position.feeGrowthInside1LastX128) * position.liquidity / Q128) - position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128 - position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128 + position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128.Bigint() + position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128.Bigint() - amount0, amount1 := pl.Collect( - token0, - token1, - fee, - std.GetOrigCaller(), + position.liquidity = 0 + positions[tokenId] = position + + collectAmount0, collectAmount1 := p.Collect( + pToken0, + pToken1, + pFee, + GetOrigCaller(), position.tickLower, position.tickUpper, consts.MAX_UINT64, // r3v4_xxx: current grc20 handles amount by `uint64` diff --git a/position/sqrt_price_math.gno b/position/sqrt_price_math.gno index 4125d797..e24ceb19 100644 --- a/position/sqrt_price_math.gno +++ b/position/sqrt_price_math.gno @@ -119,9 +119,9 @@ func sqrtPriceMathGetNextSqrtPriceFromOutput( } func sqrtPriceMathGetAmount0DeltaHelper( - sqrtRatioAX96 *u256.Uint, - sqrtRatioBX96 *u256.Uint, - liquidity *u256.Uint, + sqrtRatioAX96 bigint, + sqrtRatioBX96 bigint, + liquidity bigint, ) bigint { if sqrtRatioAX96 > sqrtRatioBX96 { sqrtRatioAX96, sqrtRatioBX96 = sqrtRatioBX96, sqrtRatioAX96 diff --git a/router/_RPC_api.gno b/router/_RPC_api.gno index 0eec3b76..ddac16f8 100644 --- a/router/_RPC_api.gno +++ b/router/_RPC_api.gno @@ -119,8 +119,8 @@ func calculateTokenRatio(currentToken string, routes []PoolWithMeta, proceed int oppositeTick := -poolTick oppositeSqrtPriceX96 := common.TickMathGetSqrtRatioAtTick(oppositeTick) - priceX96 *= oppositeSqrtPriceX96 * oppositeSqrtPriceX96 - currentToken = token1Path + currentPriceX96 *= oppositeSqrtPriceX96.Bigint() * oppositeSqrtPriceX96.Bigint() + currentToken = poolToken0 } else { // wrong condition // panic("[ROUTER] _RPC_api.gno__calculateTokenRatio() || wrong condition") diff --git a/staker/staker.gno b/staker/staker.gno index e775bb8b..cf82e5cd 100644 --- a/staker/staker.gno +++ b/staker/staker.gno @@ -261,8 +261,8 @@ func getTokenPairBalanceFromPosition(tokenId uint64) (bigint, bigint) { pool := pl.GetPoolFromPoolPath(poolKey) currentX96 := pool.PoolGetSlot0SqrtPriceX96() - lowerX96 := common.TickMathGetSqrtRatioAtTick(pn.PositionGetPositionTickLower(tokenId)) - upperX96 := common.TickMathGetSqrtRatioAtTick(pn.PositionGetPositionTickUpper(tokenId)) + lowerX96 := pl.TickMathGetSqrtRatioAtTick(pos.PositionGetPositionTickLower(tokenId)).Bigint() + upperX96 := pl.TickMathGetSqrtRatioAtTick(pos.PositionGetPositionTickUpper(tokenId)).Bigint() token0Balance, token1Balance := common.GetAmountsForLiquidity( currentX96, diff --git a/u256/i256.gno b/u256/i256.gno index 5aba58f3..02cbf59d 100644 --- a/u256/i256.gno +++ b/u256/i256.gno @@ -22,10 +22,67 @@ func NewInt(v int64) *Int { } } +func IntFromBigint(v bigint) *Int { + if v > MaxUint256/2-1 { + panic("overflow") + } + if v < -MaxUint256/2 { + panic("underflow") + } + + if v >= 0 { + return &Int{v: *FromBigint(v)} + } + + panic("not implemented") +} + +func (x *Int) IsNeg() bool { + return x.Signum() < 0 +} + +func (x *Int) Add(y *Int, z *Int) *Int { + x.v.Add(&y.v, &z.v) + + ys := y.Signum() + zs := z.Signum() + + if ys > 0 && zs > 0 && x.Signum() < 0 { + panic("overflow") + } + + if ys < 0 && zs < 0 && x.Signum() > 0 { + panic("underflow") + } + + return x +} + +func (x *Int) Sub(y *Int, z *Int) *Int { + x.v.Sub(&y.v, &z.v) + + ys := y.Signum() + zs := z.Signum() + + if ys > 0 && zs < 0 && x.Signum() < 0 { + panic("overflow") + } + + if ys < 0 && zs > 0 && x.Signum() > 0 { + panic("underflow") + } + + return x +} + func (x *Int) Eq(y *Int) bool { return x.v.Eq(&y.v) } +func (x *Int) IsZero() bool { + return x.v.IsZero() +} + func (x *Int) Signum() int { if x.v.arr[3] == 0 && x.v.arr[2] == 0 && x.v.arr[1] == 0 && x.v.arr[0] == 0 { return 0 @@ -52,6 +109,11 @@ func (x *Int) Gt(y *Int) bool { return y.v.Gt(&x.v) } +func (x *Int) Lte(y *Int) bool { + return !x.Gt(y) +} + + func (x *Int) Gte(y *Int) bool { xs := x.Signum() ys := y.Signum() @@ -75,4 +137,30 @@ func (x *Int) Int64() int64 { } // TODO: check if this is correct return -int64(^x.v.arr[0] + 1) +} + +func (x *Int) Abs() *Uint { + if x.Signum() > 0 { + return &x.v + } + x1 := &Int{v: x.v} // so that we don't modify x + return &x1.Neg().v +} + +func (x *Int) Neg() *Int { + // twos complement + x.v.Not(&x.v) + x.v.Add(&x.v, &Uint{arr: [4]uint64{1, 0, 0, 0}}) + return x +} + +func (x *Int) Bigint() bigint { + panic("not implemented") +} + +func (x *Int) Uint() *Uint { + if x.Signum() < 0 { + panic("negative") + } + return &x.v } \ No newline at end of file diff --git a/u256/u256.gno b/u256/u256.gno index ccc7cadb..06af1b99 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -19,6 +19,13 @@ func One() *Uint { return NewUint(1) } +func (x *Uint) Min(y *Uint) *Uint { + if x.Lt(y) { + return x + } + return y +} + // Uint is represented as an array of 4 uint64, in little-endian order, // so that Uint[3] is the most significant, and Uint[0] is the least significant type Uint struct { @@ -40,6 +47,10 @@ func FromBigint(x bigint) *Uint { panic("overflow") } + if x < 0 { + panic("underflow") + } + var z Uint z.arr[0] = uint64(x) z.arr[1] = uint64(x >> 64) @@ -49,6 +60,10 @@ func FromBigint(x bigint) *Uint { return &z } +func (x *Uint) Bigint() bigint { + return (bigint(x.arr[3]) << 192) + (bigint(x.arr[2]) << 128) + (bigint(x.arr[1]) << 64) + bigint(x.arr[0]) +} + // NewUint returns a new initialized Uint. func NewUint(val uint64) *Uint { z := &Uint{arr: [4]uint64{val, 0, 0, 0}} @@ -107,6 +122,10 @@ func (z *Uint) Gte(x *Uint) bool { return !z.Lt(x) } +func (z *Uint) Lte(x *Uint) bool { + return !x.Gt(z) +} + // Lt returns true if z < x func (z *Uint) Lt(x *Uint) bool { // z < x <=> z - x < 0 i.e. when subtraction overflows. @@ -154,6 +173,13 @@ func (z *Uint) SetOne() *Uint { return z } +func (z *Uint) AddInt(x *Uint, y *Int) *Uint { + if y.IsNeg() { + return z.Sub(x, y.Abs()) + } + return z.Add(x, y.Uint()) +} + // Add sets z to the sum x+y func (z *Uint) Add(x, y *Uint) *Uint { var carry uint64 @@ -163,11 +189,9 @@ func (z *Uint) Add(x, y *Uint) *Uint { z.arr[3], _ = Add64(x.arr[3], y.arr[3], carry) // Different from the original implementation! // We panic on overflow - /* if carry != 0 { panic("overflow") } - */ return z } @@ -200,11 +224,9 @@ func (z *Uint) Sub(x, y *Uint) *Uint { z.arr[3], _ = Sub64(x.arr[3], y.arr[3], carry) // Different from the original implementation! // We panic on underflow - /* if carry != 0 { panic("underflow") } - */ return z } @@ -1143,3 +1165,34 @@ func (z *Uint) Dec() string { // skip leading zeroes by only using the 'used size' of buf return string(out[pos-len(buf):]) } + +/// Mod sets z to the modulus x%y for y != 0 and returns z. +// If y == 0, z is set to 0 (OBS: differs from the big.Uint) +func (z *Uint) Mod(x, y *Uint) *Uint { + if x.IsZero() || y.IsZero() { + return z.Clear() + } + switch x.Cmp(y) { + case -1: + // x < y + copy(z.arr[:], x.arr[:]) + return z + case 0: + // x == y + return z.Clear() // They are equal + } + + // At this point: + // x != 0 + // y != 0 + // x > y + + // Shortcut trivial case + if x.IsUint64() { + return z.SetUint64(x.Uint64() % y.Uint64()) + } + + var quot Uint + *z = udivrem(quot.arr[:], x.arr[:], y) + return z +} \ No newline at end of file From 904f6f54551ce8c0db584c784f69b4cd09ad9b5b Mon Sep 17 00:00:00 2001 From: mconcat Date: Mon, 19 Feb 2024 20:50:41 +0900 Subject: [PATCH 13/23] fix errors --- u256/u256.gno | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/u256/u256.gno b/u256/u256.gno index 06af1b99..adfee32c 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -52,16 +52,20 @@ func FromBigint(x bigint) *Uint { } var z Uint - z.arr[0] = uint64(x) - z.arr[1] = uint64(x >> 64) - z.arr[2] = uint64(x >> 128) - z.arr[3] = uint64(x >> 192) + z.arr[0] = uint64(x % (1 << 64)) + z.arr[1] = uint64((x >> 64) % (1 << 64)) + z.arr[2] = uint64((x >> 128) % (1 << 64)) + z.arr[3] = uint64((x >> 192) % (1 << 64)) return &z } func (x *Uint) Bigint() bigint { - return (bigint(x.arr[3]) << 192) + (bigint(x.arr[2]) << 128) + (bigint(x.arr[1]) << 64) + bigint(x.arr[0]) + return ( + bigint(x.arr[0]) + + bigint(x.arr[1]) * (1 << 64) + + bigint(x.arr[2]) * (1 << 128) + + bigint(x.arr[3]) * (1 << 192)) } // NewUint returns a new initialized Uint. @@ -79,6 +83,14 @@ func (z *Uint) Uint64() uint64 { return z.arr[0] } +func (z *Uint) Int32() int32 { + x := z.arr[0] + if x > 0x7fffffff { + panic("overflow") + } + return int32(x) +} + // SetUint64 sets z to the value x func (z *Uint) SetUint64(x uint64) *Uint { z.arr[3], z.arr[2], z.arr[1], z.arr[0] = 0, 0, 0, x From 32dc616996307beaec37abd775394081dfeaa396 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 14:40:29 +0900 Subject: [PATCH 14/23] feat: Clone(), fix: Lte() --- u256/u256.gno | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/u256/u256.gno b/u256/u256.gno index adfee32c..5830d6b8 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -61,11 +61,10 @@ func FromBigint(x bigint) *Uint { } func (x *Uint) Bigint() bigint { - return ( - bigint(x.arr[0]) + - bigint(x.arr[1]) * (1 << 64) + - bigint(x.arr[2]) * (1 << 128) + - bigint(x.arr[3]) * (1 << 192)) + return (bigint(x.arr[0]) + + bigint(x.arr[1])*(1<<64) + + bigint(x.arr[2])*(1<<128) + + bigint(x.arr[3])*(1<<192)) } // NewUint returns a new initialized Uint. @@ -73,11 +72,7 @@ func NewUint(val uint64) *Uint { z := &Uint{arr: [4]uint64{val, 0, 0, 0}} return z } -/* -func ToString(x *Uint) string { - return "tostring not implemented" -} -*/ + // Uint64 returns the lower 64-bits of z func (z *Uint) Uint64() uint64 { return z.arr[0] @@ -135,7 +130,7 @@ func (z *Uint) Gte(x *Uint) bool { } func (z *Uint) Lte(x *Uint) bool { - return !x.Gt(z) + return x.Gt(z) } // Lt returns true if z < x @@ -783,14 +778,12 @@ func (z *Uint) UnmarshalJSON(input []byte) error { return z.UnmarshalText(input[1 : len(input)-1]) } - // MarshalText implements encoding.TextMarshaler // MarshalText marshals using the decimal representation (compatible with big.Uint) func (z *Uint) MarshalText() ([]byte, error) { return []byte(z.Dec()), nil } - // UnmarshalText implements encoding.TextUnmarshaler. This method // can unmarshal either hexadecimal or decimal. // - For hexadecimal, the input _must_ be prefixed with 0x or 0X @@ -912,8 +905,8 @@ func checkNumberS(input string) error { // multipliers holds the values that are needed for fromDecimal var multipliers = [5]*Uint{ - nil, // represents first round, no multiplication needed - &Uint{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 + nil, // represents first round, no multiplication needed + &Uint{[4]uint64{10000000000000000000, 0, 0, 0}}, // 10 ^ 19 &Uint{[4]uint64{687399551400673280, 5421010862427522170, 0, 0}}, // 10 ^ 38 &Uint{[4]uint64{5332261958806667264, 17004971331911604867, 2938735877055718769, 0}}, // 10 ^ 57 &Uint{[4]uint64{0, 8607968719199866880, 532749306367912313, 1593091911132452277}}, // 10 ^ 76 @@ -1128,7 +1121,7 @@ func underscoreOK(s string) bool { } // Dec returns the decimal representation of z. -func (z *Uint) Dec() string { +func (z *Uint) Dec() string { // toString() if z.IsZero() { return "0" } @@ -1156,8 +1149,8 @@ func (z *Uint) Dec() string { out = []byte("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") divisor = NewUint(10000000000000000000) // 20 digits y = new(Uint).Set(z) // copy to avoid modifying z - pos = len(out) // position to write to - buf = make([]byte, 0, 19) // buffer to write uint64:s to + pos = len(out) // position to write to + buf = make([]byte, 0, 19) // buffer to write uint64:s to ) for { // Obtain Q and R for divisor @@ -1178,7 +1171,7 @@ func (z *Uint) Dec() string { return string(out[pos-len(buf):]) } -/// Mod sets z to the modulus x%y for y != 0 and returns z. +// Mod sets z to the modulus x%y for y != 0 and returns z. // If y == 0, z is set to 0 (OBS: differs from the big.Uint) func (z *Uint) Mod(x, y *Uint) *Uint { if x.IsZero() || y.IsZero() { @@ -1207,4 +1200,15 @@ func (z *Uint) Mod(x, y *Uint) *Uint { var quot Uint *z = udivrem(quot.arr[:], x.arr[:], y) return z -} \ No newline at end of file +} + +// Clone creates a new Int identical to z +func (z *Uint) Clone() *Uint { + var x Uint + x.arr[0] = z.arr[0] + x.arr[1] = z.arr[1] + x.arr[2] = z.arr[2] + x.arr[3] = z.arr[3] + + return &x +} From d34355bbf6397eb36e2800639cfbe7592e6b915e Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 14:55:25 +0900 Subject: [PATCH 15/23] revert: Lte() --- u256/u256.gno | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/u256/u256.gno b/u256/u256.gno index 5830d6b8..f0f49f18 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -130,7 +130,7 @@ func (z *Uint) Gte(x *Uint) bool { } func (z *Uint) Lte(x *Uint) bool { - return x.Gt(z) + return !x.Gt(z) } // Lt returns true if z < x From 23c2ce556874465da2cc2502d16638863f3f4021 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 16:41:07 +0900 Subject: [PATCH 16/23] feat, change: various - feat: Int.Or(), Int.Dec(), Int.Lsh(), Int.Rsh(), Int.Mul() - change: Int to Uint() do not panic --- u256/i256.gno | 67 +++++++++++++++++++++++++++++++++++++++------- u256/u256.gno | 12 ++++----- u256/u256_test.gno | 17 ++++++++---- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/u256/i256.gno b/u256/i256.gno index 02cbf59d..7b5b7fb9 100644 --- a/u256/i256.gno +++ b/u256/i256.gno @@ -34,7 +34,7 @@ func IntFromBigint(v bigint) *Int { return &Int{v: *FromBigint(v)} } - panic("not implemented") + panic("IntFromBigint not implemented") } func (x *Int) IsNeg() bool { @@ -43,18 +43,18 @@ func (x *Int) IsNeg() bool { func (x *Int) Add(y *Int, z *Int) *Int { x.v.Add(&y.v, &z.v) - + ys := y.Signum() zs := z.Signum() if ys > 0 && zs > 0 && x.Signum() < 0 { panic("overflow") } - + if ys < 0 && zs < 0 && x.Signum() > 0 { panic("underflow") } - + return x } @@ -65,13 +65,48 @@ func (x *Int) Sub(y *Int, z *Int) *Int { zs := z.Signum() if ys > 0 && zs < 0 && x.Signum() < 0 { - panic("overflow") + panic("Sub overflow") } if ys < 0 && zs > 0 && x.Signum() > 0 { - panic("underflow") + panic("Sub underflow") + } + + return x +} + +func (x *Int) Mul(y *Int, z *Int) *Int { + x.v.Mul(&y.v, &z.v) + + ys := y.Signum() + zs := z.Signum() + + if ys > 0 && zs > 0 && x.Signum() < 0 { + panic("Mul overflow #1") + } + + if ys < 0 && zs < 0 && x.Signum() < 0 { + panic("Mul overflow #2") + } + + if ys > 0 && zs < 0 && x.Signum() > 0 { + panic("Mul underflow #1") } + if ys < 0 && zs > 0 && x.Signum() > 0 { + panic("Mul underflow #2") + } + + return x +} + +func (x *Int) Lsh(y *Int, n uint) *Int { + x.v.Lsh(&y.v, n) + return x +} + +func (x *Int) Rsh(y *Int, n uint) *Int { + x.v.Rsh(&y.v, n) return x } @@ -113,7 +148,6 @@ func (x *Int) Lte(y *Int) bool { return !x.Gt(y) } - func (x *Int) Gte(y *Int) bool { xs := x.Signum() ys := y.Signum() @@ -154,13 +188,26 @@ func (x *Int) Neg() *Int { return x } +func (x *Int) Dec() string { + if x.Signum() < 0 { + return "-" + x.Abs().Dec() + } + return x.Abs().Dec() +} + func (x *Int) Bigint() bigint { - panic("not implemented") + panic("Bigint not implemented") } func (x *Int) Uint() *Uint { if x.Signum() < 0 { - panic("negative") + // panic("Uint negative") + return &x.Neg().v // r3v4_xxx: safe ?? } return &x.v -} \ No newline at end of file +} + +func (z *Int) Or(x, y *Int) *Int { + z.v.Or(&x.v, &y.v) + return z +} diff --git a/u256/u256.gno b/u256/u256.gno index f0f49f18..2ce09fe6 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -35,7 +35,7 @@ type Uint struct { func (x *Uint) Int() *Int { // panic if x > MaxInt64 if x.arr[3] > 0x7fffffffffffffff { - panic("overflow") + panic("Int overflow") } return &Int{v: *x} @@ -44,11 +44,11 @@ func (x *Uint) Int() *Int { // TODO: to be removed func FromBigint(x bigint) *Uint { if x > MaxUint256 { - panic("overflow") + panic("FromBigint overflow") } if x < 0 { - panic("underflow") + panic("FromBigint underflow") } var z Uint @@ -81,7 +81,7 @@ func (z *Uint) Uint64() uint64 { func (z *Uint) Int32() int32 { x := z.arr[0] if x > 0x7fffffff { - panic("overflow") + panic("Int32 overflow") } return int32(x) } @@ -197,7 +197,7 @@ func (z *Uint) Add(x, y *Uint) *Uint { // Different from the original implementation! // We panic on overflow if carry != 0 { - panic("overflow") + panic("Add overflow") } return z } @@ -232,7 +232,7 @@ func (z *Uint) Sub(x, y *Uint) *Uint { // Different from the original implementation! // We panic on underflow if carry != 0 { - panic("underflow") + panic("Sub underflow") } return z } diff --git a/u256/u256_test.gno b/u256/u256_test.gno index 1e31f688..8590221d 100644 --- a/u256/u256_test.gno +++ b/u256/u256_test.gno @@ -4,8 +4,15 @@ import ( "testing" ) -func TestArithmetic(t *testing.T) { - x := NewUint(1) - y := NewUint(2) - NewUint(0).Add(x, y) -} \ No newline at end of file +func TestFuncs(t *testing.T) { + x := NewUint(0) + y := NewUint(1) + + // println(x.Dec()) + + var m256 bigint = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + + z := FromBigint(m256) + + println(z.Dec()) +} From 30fd674330eef951cd4630a36af6f31d68f2f77c Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 20:33:44 +0900 Subject: [PATCH 17/23] feat: Int.Bigint(), Int.NilToZero(), Uint.unsafeSub(), Uint.IsNil(), Uint.NilToZero() --- u256/i256.gno | 44 ++++++++++++++++++++++++++++++-------------- u256/u256.gno | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/u256/i256.gno b/u256/i256.gno index 7b5b7fb9..5b560c56 100644 --- a/u256/i256.gno +++ b/u256/i256.gno @@ -24,17 +24,25 @@ func NewInt(v int64) *Int { func IntFromBigint(v bigint) *Int { if v > MaxUint256/2-1 { - panic("overflow") + panic("I256 IntFromBigint overflow") } if v < -MaxUint256/2 { - panic("underflow") + panic("I256 IntFromBigint underflow") } if v >= 0 { return &Int{v: *FromBigint(v)} } - panic("IntFromBigint not implemented") + panic("I256 IntFromBigint not implemented") +} + +func (x *Int) Bigint() bigint { + if x.Signum() < 0 { + panic("I256 Bigint negative") + } + return x.v.Bigint() + } func (x *Int) IsNeg() bool { @@ -48,28 +56,28 @@ func (x *Int) Add(y *Int, z *Int) *Int { zs := z.Signum() if ys > 0 && zs > 0 && x.Signum() < 0 { - panic("overflow") + panic("I256 Add overflow") } if ys < 0 && zs < 0 && x.Signum() > 0 { - panic("underflow") + panic("I256 Add underflow") } return x } func (x *Int) Sub(y *Int, z *Int) *Int { - x.v.Sub(&y.v, &z.v) + x.v.unsafeSub(&y.v, &z.v) ys := y.Signum() zs := z.Signum() if ys > 0 && zs < 0 && x.Signum() < 0 { - panic("Sub overflow") + panic("I256 Sub overflow") } if ys < 0 && zs > 0 && x.Signum() > 0 { - panic("Sub underflow") + panic("I256 Sub underflow") } return x @@ -82,19 +90,19 @@ func (x *Int) Mul(y *Int, z *Int) *Int { zs := z.Signum() if ys > 0 && zs > 0 && x.Signum() < 0 { - panic("Mul overflow #1") + panic("I256 Mul overflow #1") } if ys < 0 && zs < 0 && x.Signum() < 0 { - panic("Mul overflow #2") + panic("I256 Mul overflow #2") } if ys > 0 && zs < 0 && x.Signum() > 0 { - panic("Mul underflow #1") + panic("I256 Mul underflow #1") } if ys < 0 && zs > 0 && x.Signum() > 0 { - panic("Mul underflow #2") + panic("I256 Mul underflow #2") } return x @@ -196,12 +204,12 @@ func (x *Int) Dec() string { } func (x *Int) Bigint() bigint { - panic("Bigint not implemented") + panic("I256 Bigint not implemented") } func (x *Int) Uint() *Uint { if x.Signum() < 0 { - // panic("Uint negative") + // panic("I256 Uint negative") return &x.Neg().v // r3v4_xxx: safe ?? } return &x.v @@ -211,3 +219,11 @@ func (z *Int) Or(x, y *Int) *Int { z.v.Or(&x.v, &y.v) return z } + +func (z *Int) NilToZero() *Int { + if z == nil { + z = NewInt(0) + } + + return z +} diff --git a/u256/u256.gno b/u256/u256.gno index 2ce09fe6..91c407ca 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -35,7 +35,7 @@ type Uint struct { func (x *Uint) Int() *Int { // panic if x > MaxInt64 if x.arr[3] > 0x7fffffffffffffff { - panic("Int overflow") + panic("U256 Int overflow") } return &Int{v: *x} @@ -44,11 +44,11 @@ func (x *Uint) Int() *Int { // TODO: to be removed func FromBigint(x bigint) *Uint { if x > MaxUint256 { - panic("FromBigint overflow") + panic("U256 FromBigint overflow") } if x < 0 { - panic("FromBigint underflow") + panic("U256 FromBigint underflow") } var z Uint @@ -81,7 +81,7 @@ func (z *Uint) Uint64() uint64 { func (z *Uint) Int32() int32 { x := z.arr[0] if x > 0x7fffffff { - panic("Int32 overflow") + panic("U256 Int32 overflow") } return int32(x) } @@ -171,6 +171,7 @@ func (z *Uint) Cmp(x *Uint) (r int) { // Set sets z to x and returns z. func (z *Uint) Set(x *Uint) *Uint { *z = *x + return z } @@ -197,7 +198,7 @@ func (z *Uint) Add(x, y *Uint) *Uint { // Different from the original implementation! // We panic on overflow if carry != 0 { - panic("Add overflow") + panic("U256 Add overflow") } return z } @@ -232,11 +233,22 @@ func (z *Uint) Sub(x, y *Uint) *Uint { // Different from the original implementation! // We panic on underflow if carry != 0 { - panic("Sub underflow") + panic("U256 Sub underflow") } return z } +// Sub sets z to the difference x-y +func (z *Uint) unsafeSub(x, y *Uint) *Uint { + var carry uint64 + z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) + z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) + z.arr[2], carry = Sub64(x.arr[2], y.arr[2], carry) + z.arr[3], _ = Sub64(x.arr[3], y.arr[3], carry) + + return z +} + // commented out for possible overflow // Mul sets z to the product x*y func (z *Uint) Mul(x, y *Uint) *Uint { @@ -1128,6 +1140,7 @@ func (z *Uint) Dec() string { // toString() if z.IsUint64() { return FormatUint(z.Uint64(), 10) } + // The max uint64 value being 18446744073709551615, the largest // power-of-ten below that is 10000000000000000000. // When we do a DivMod using that number, the remainder that we @@ -1212,3 +1225,15 @@ func (z *Uint) Clone() *Uint { return &x } + +func (z *Uint) IsNil() bool { + return z == nil +} + +func (z *Uint) NilToZero() *Uint { + if z == nil { + z = NewUint(0) + } + + return z +} From 74ef1e04ee84a11d2ccafa133952b1f2fa076c21 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 20:44:23 +0900 Subject: [PATCH 18/23] feat: rpc to return string --- pool/_RPC_api.gno | 88 +++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/pool/_RPC_api.gno b/pool/_RPC_api.gno index 24e369c3..0a428f87 100644 --- a/pool/_RPC_api.gno +++ b/pool/_RPC_api.gno @@ -9,7 +9,6 @@ import ( "time" "gno.land/p/demo/ufmt" - "gno.land/p/demo/u256" ) type RpcPool struct { @@ -18,8 +17,8 @@ type RpcPool struct { Token0Path string `json:"token0Path"` Token1Path string `json:"token1Path"` - BalancesToken0 *u256.Uint `json:"balanceToken0"` - BalancesToken1 *u256.Uint `json:"balanceToken1"` + BalancesToken0 string `json:"balanceToken0"` + BalancesToken1 string `json:"balanceToken1"` // fee is the fee tier of the pool Fee uint16 `json:"fee"` @@ -28,40 +27,41 @@ type RpcPool struct { TickSpacing int32 `json:"tickSpacing"` // maxLiquidityPerTick is the maximum amount of liquidity that can be added per tick - MaxLiquidityPerTick *u256.Uint `json:"maxLiquidityPerTick"` + MaxLiquidityPerTick string `json:"maxLiquidityPerTick"` // slot0 is the current tick and price of the pool - Slot0SqrtPriceX96 *u256.Uint `json:"sqrtPriceX96"` + Slot0SqrtPriceX96 string `json:"sqrtPriceX96"` Slot0Tick int32 `json:"tick"` Slot0FeeProtocol uint8 `json:"feeProtocol"` Slot0Unlocked bool `json:"unlocked"` - FeeGrowthGlobal0X128 *u256.Uint `json:"feeGrowthGlobal0X128"` - FeeGrowthGlobal1X128 *u256.Uint `json:"feeGrowthGlobal1X128"` + FeeGrowthGlobal0X128 string `json:"feeGrowthGlobal0X128"` + FeeGrowthGlobal1X128 string `json:"feeGrowthGlobal1X128"` - ProtocolFeesToken0 *u256.Uint `json:"protocolFeeToken0"` - ProtocolFeesToken1 *u256.Uint `json:"protocolFeeToken1"` + ProtocolFeesToken0 string `json:"protocolFeeToken0"` + ProtocolFeesToken1 string `json:"protocolFeeToken1"` // liquidity is the total amount of liquidity in the pool - Liquidity *u256.Uint `json:"liquidity"` + Liquidity string `json:"liquidity"` // ticks is a mapping from tick index to tick Ticks RpcTicks `json:"ticks"` // tickBitmaps is a mapping from tick index to tick bitmap - TickBitmaps TickBitmaps `json:"tickBitmaps"` + TickBitmaps RpcTickBitmaps `json:"tickBitmaps"` Positions []RpcPosition `json:"positions"` } -type RpcTicks map[int32]RpcTickInfo // tick => RpcTickInfo +type RpcTicks map[int32]RpcTickInfo // tick => RpcTickInfo +type RpcTickBitmaps map[int16]string // tick(wordPos) => bitmap(tickWord ^ mask) type RpcTickInfo struct { - LiquidityGross *u256.Uint `json:"liquidityGross"` - LiquidityNet *u256.Int `json:"liquidityNet"` + LiquidityGross string `json:"liquidityGross"` + LiquidityNet string `json:"liquidityNet"` - FeeGrowthOutside0X128 *u256.Uint `json:"feeGrowthOutside0X128"` - FeeGrowthOutside1X128 *u256.Uint `json:"feeGrowthOutside1X128"` + FeeGrowthOutside0X128 string `json:"feeGrowthOutside0X128"` + FeeGrowthOutside1X128 string `json:"feeGrowthOutside1X128"` //TickCumulativeOutside bigint `json:"tickCumulativeOutside"` @@ -74,13 +74,13 @@ type RpcTickInfo struct { type RpcPosition struct { Owner string `json:"owner"` - TickLower bigint `json:"tickLower"` - TickUpper bigint `json:"tickUpper"` + TickLower string `json:"tickLower"` + TickUpper string `json:"tickUpper"` - Liquidity *u256.Uint `json:"liquidity"` + Liquidity string `json:"liquidity"` - Token0Owed *u256.Uint `json:"token0Owed"` - Token1Owed *u256.Uint `json:"token1Owed"` + Token0Owed string `json:"token0Owed"` + Token1Owed string `json:"token1Owed"` } type ResponseQueryBase struct { @@ -125,46 +125,52 @@ func rpcMakePool(poolPath string) RpcPool { rpcPool.Token0Path = pool.token0Path rpcPool.Token1Path = pool.token1Path - rpcPool.BalancesToken0 = pool.balances.token0 - rpcPool.BalancesToken1 = pool.balances.token1 + rpcPool.BalancesToken0 = pool.balances.token0.Dec() + rpcPool.BalancesToken1 = pool.balances.token1.Dec() rpcPool.Fee = pool.fee rpcPool.TickSpacing = pool.tickSpacing - rpcPool.MaxLiquidityPerTick = pool.maxLiquidityPerTick + rpcPool.MaxLiquidityPerTick = pool.maxLiquidityPerTick.Dec() - rpcPool.Slot0SqrtPriceX96 = pool.slot0.sqrtPriceX96 + rpcPool.Slot0SqrtPriceX96 = pool.slot0.sqrtPriceX96.Dec() rpcPool.Slot0Tick = pool.slot0.tick rpcPool.Slot0FeeProtocol = pool.slot0.feeProtocol rpcPool.Slot0Unlocked = pool.slot0.unlocked - rpcPool.FeeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128 - rpcPool.FeeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128 + rpcPool.FeeGrowthGlobal0X128 = pool.feeGrowthGlobal0X128.Dec() + rpcPool.FeeGrowthGlobal1X128 = pool.feeGrowthGlobal1X128.Dec() - rpcPool.ProtocolFeesToken0 = pool.protocolFees.token0 - rpcPool.ProtocolFeesToken1 = pool.protocolFees.token1 + rpcPool.ProtocolFeesToken0 = pool.protocolFees.token0.Dec() + rpcPool.ProtocolFeesToken1 = pool.protocolFees.token1.Dec() - rpcPool.Liquidity = pool.liquidity + rpcPool.Liquidity = pool.liquidity.Dec() rpcPool.Ticks = RpcTicks{} + for tick, tickInfo := range pool.ticks { rpcPool.Ticks[tick] = RpcTickInfo{ - LiquidityGross: tickInfo.liquidityGross, - LiquidityNet: tickInfo.liquidityNet, - FeeGrowthOutside0X128: tickInfo.feeGrowthOutside0X128, - FeeGrowthOutside1X128: tickInfo.feeGrowthOutside1X128, + LiquidityGross: tickInfo.liquidityGross.NilToZero().Dec(), + LiquidityNet: tickInfo.liquidityNet.NilToZero().Dec(), + FeeGrowthOutside0X128: tickInfo.feeGrowthOutside0X128.NilToZero().Dec(), + FeeGrowthOutside1X128: tickInfo.feeGrowthOutside1X128.NilToZero().Dec(), //TickCumulativeOutside: tickInfo.tickCumulativeOutside, //SecondsPerLiquidityOutsideX: tickInfo.secondsPerLiquidityOutsideX128, //SecondsOutside: tickInfo.secondsOutside, - Initialized: tickInfo.initialized, + Initialized: tickInfo.initialized, } } - rpcPool.TickBitmaps = pool.tickBitmaps + tickBitmaps := pool.tickBitmaps + rpcPool.TickBitmaps = RpcTickBitmaps{} + for tick, tickBitmap := range tickBitmaps { + rpcPool.TickBitmaps[tick] = tickBitmap.NilToZero().Dec() + } Positions := pool.positions rpcPositions := []RpcPosition{} + for posKey, posInfo := range Positions { owner, tickLower, tickUpper := posKeyDivide(posKey) @@ -172,9 +178,9 @@ func rpcMakePool(poolPath string) RpcPool { Owner: owner, TickLower: tickLower, TickUpper: tickUpper, - Liquidity: posInfo.liquidity, - Token0Owed: posInfo.tokensOwed0, - Token1Owed: posInfo.tokensOwed1, + Liquidity: posInfo.liquidity.Dec(), + Token0Owed: posInfo.tokensOwed0.Dec(), + Token1Owed: posInfo.tokensOwed1.Dec(), }) } rpcPool.Positions = rpcPositions @@ -182,7 +188,7 @@ func rpcMakePool(poolPath string) RpcPool { return rpcPool } -func posKeyDivide(posKey string) (string, bigint, bigint) { +func posKeyDivide(posKey string) (string, string, string) { // base64 decode kDec, _ := b64.StdEncoding.DecodeString(posKey) posKey = string(kDec) @@ -194,5 +200,5 @@ func posKeyDivide(posKey string) (string, bigint, bigint) { owner, tickLower, tickUpper := res[0], res[1], res[2] - return owner, bigint(tickLower), bigint(tickUpper) + return owner, tickLower, tickUpper } From a5d664bf3241961f7a345273b710ae305b300cf3 Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 20:44:32 +0900 Subject: [PATCH 19/23] chore: test case --- ...est.gnoa => _TEST_pool_single_lp_test.gno} | 167 +++++++++--------- pool/_TEST_rpc_test.gnoa | 59 ++----- 2 files changed, 106 insertions(+), 120 deletions(-) rename pool/{_TEST_pool_single_lp_test.gnoa => _TEST_pool_single_lp_test.gno} (62%) diff --git a/pool/_TEST_pool_single_lp_test.gnoa b/pool/_TEST_pool_single_lp_test.gno similarity index 62% rename from pool/_TEST_pool_single_lp_test.gnoa rename to pool/_TEST_pool_single_lp_test.gno index 27560c78..2bc8d1eb 100644 --- a/pool/_TEST_pool_single_lp_test.gnoa +++ b/pool/_TEST_pool_single_lp_test.gno @@ -1,17 +1,13 @@ package pool import ( + "encoding/gjson" "std" "testing" - "time" - - "encoding/gjson" - - "gno.land/r/demo/bar" - "gno.land/r/demo/foo" - "gno.land/r/demo/gns" "gno.land/r/demo/consts" + + _ "gno.land/r/demo/grc20_wrapper" ) var ( @@ -25,48 +21,46 @@ var ( // 1. Init & Create Pool using Factory Contract by Gnoswap Admin func TestInitCreatePool(t *testing.T) { - std.TestSetPrevAddr(gsa) + std.TestSetOrigCaller(test1) InitManual() - std.TestSetPrevAddr(test1) - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - CreatePool(barPath, fooPath, fee500, 130621891405341611593710811006) // x2.7 + std.TestSetOrigCaller(test1) + CreatePool(barPath, fooPath, fee500, 130621891405341611593710811006) + + // sqrtPrice + // 130621891405341611593710811006 // tick = 10000 + shouldPanic(t, func() { CreatePool(barPath, fooPath, 500, 130621891405341611593710811006) }) } // 2. Mint by test1 func TestMint(t *testing.T) { - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - foo.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) - amount0, amount1 := Mint( + + Mint( fooPath, barPath, fee500, - consts.POSITION_ADDR, + posAddr, test_tickLower, test_tickUpper, test_liquidityExpect, ) - shouldEQ(t, amount0, bigint(29)) - shouldEQ(t, amount1, bigint(80)) pool := GetPool(barPath, fooPath, fee500) test_liquidity := pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m81, m82 := Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - m101, m102 := Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + m81, m82 := Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m101, m102 := Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) shouldNEQ(t, m81, bigint(0)) shouldNEQ(t, m82, bigint(0)) @@ -77,27 +71,27 @@ func TestMint(t *testing.T) { shouldEQ(t, test_liquidity, test_liquidityExpect*10) // tickLower > currentTick == don't add to current liquidity - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower2, test_tickUpper2, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) // tickUpper < current tick == don't add to current liquidity - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) // tickUpper < tickLower == don't add to current liquidity - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, -test_tickUpper, -test_tickLower, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) test_liquidity = pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*10) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) test_liquidity = pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, test_liquidityExpect*20) @@ -105,7 +99,7 @@ func TestMint(t *testing.T) { // 3. Burn by test1 func TestBurn(t *testing.T) { - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) b11, b12 := Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) @@ -132,11 +126,15 @@ func TestBurn(t *testing.T) { Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*9) test_liquidity = pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) + + // can't burn when liq is 0 + // Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) + shouldPanic(t, func() { Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) }) } // 4. Collect func TestCollect(t *testing.T) { - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) // withdraw all token before test `Collect` @@ -147,18 +145,18 @@ func TestCollect(t *testing.T) { test_liquidity := pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c11, c12 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c21, c22 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c11, c21) shouldEQ(t, c12, c22) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c31, c32 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 100, 100) shouldEQ(t, c31, bigint(100)) @@ -169,7 +167,7 @@ func TestCollect(t *testing.T) { shouldEQ(t, c42, c22-bigint(100)) // Mint > No Burn => nothing to collect - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) // Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c51, c52 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c51, bigint(0)) @@ -188,17 +186,17 @@ func TestSwap(t *testing.T) { test_liquidity := pool.PoolGetLiquidity() shouldEQ(t, test_liquidity, bigint(0)) - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) - Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*20000) + Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*20000) // Swap several times - std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetPrevRealm("gno.land/r/demo/router") std.TestSetOrigCaller(test1) test_price := bigint(consts.MIN_PRICE) { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -217,8 +215,8 @@ func TestSwap(t *testing.T) { shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -230,16 +228,16 @@ func TestSwap(t *testing.T) { } { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, true, 5000, test_price, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -251,16 +249,16 @@ func TestSwap(t *testing.T) { } { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, true, 1000, test_price, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -273,16 +271,16 @@ func TestSwap(t *testing.T) { // Swap token1 -> token0 { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, false, 16000, consts.MAX_PRICE, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -297,38 +295,39 @@ func TestSwap(t *testing.T) { // 6. SetFeeProtocol by Gnoswap Admin func TestSetFeeProtocol(t *testing.T) { // non admin call - std.TestSetOrigCaller(gsa) + std.TestSetOrigCaller(test1) shouldPanic(t, func() { SetFeeProtocol(2, 2) }) // admin call - std.TestSetOrigCaller(gsa) + std.TestSetOrigCaller(test1) SetFeeProtocol(6, 8) for _, pool := range pools { - shouldEQ(t, pool.PoolGetSlot0().feeProtocol, 134) + shouldEQ(t, pool.PoolGetSlot0().feeProtocol, bigint(134)) } } // 7. CollectProtocol by Gnoswap Admin func TestCollectProtocol(t *testing.T) { - std.TestSetOrigCaller(gsa) + std.TestSetOrigCaller(test1) + SetFeeProtocol(6, 8) pool := GetPool(barPath, fooPath, fee500) test_slot0 := pool.PoolGetSlot0() - shouldEQ(t, test_slot0.feeProtocol, 134) + shouldEQ(t, test_slot0.feeProtocol, bigint(134)) // Make ProtocolFee via Swap by test1 ( Mint by test1 ) std.TestSetOrigCaller(test1) { - std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetPrevRealm("gno.land/r/demo/router") std.TestSetOrigCaller(test1) Swap(barPath, fooPath, fee500, test1, true, 200000, consts.MIN_PRICE, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 test1OldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) test1OldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) - std.TestSetPrevAddr(gsa) + std.TestSetPrevAddr(test1) amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, 100000, 100000) test1NewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) @@ -340,14 +339,14 @@ func TestCollectProtocol(t *testing.T) { { - std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetPrevRealm("gno.land/r/demo/router") std.TestSetOrigCaller(test1) - Swap(barPath, fooPath, fee500, test1, false, 200000, consts.MAX_SQRT_RATIO-1, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 + Swap(barPath, fooPath, fee500, test1, false, 200000, MAX_SQRT_RATIO-1, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 test1OldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) test1OldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) - std.TestSetPrevAddr(gsa) + std.TestSetPrevAddr(test1) amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, 100000, 100000) test1NewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) @@ -360,8 +359,18 @@ func TestApiGetPools(t *testing.T) { gpls := ApiGetPools() jsonStr := gjson.Parse(gpls) - shouldEQ(t, jsonStr.Get("stat.height").Int(), std.GetHeight()) - shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), time.Now().Unix()) + shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) + shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) shouldEQ(t, len(jsonStr.Get("response").Array()), 1) } + +func TestApiGetPool(t *testing.T) { + gpl := ApiGetPool("gno.land/r/demo/bar:gno.land/r/demo/foo:500") + jsonStr := gjson.Parse(gpl) + + shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) + shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) + + shouldEQ(t, len(jsonStr.Get("response.positions").Array()), 4) +} diff --git a/pool/_TEST_rpc_test.gnoa b/pool/_TEST_rpc_test.gnoa index 9f22ce44..18fbdaed 100644 --- a/pool/_TEST_rpc_test.gnoa +++ b/pool/_TEST_rpc_test.gnoa @@ -6,94 +6,68 @@ import ( "encoding/gjson" - "gno.land/r/demo/consts" - - "gno.land/r/demo/bar" - "gno.land/r/demo/baz" - "gno.land/r/demo/foo" - - "gno.land/r/demo/gns" + _ "gno.land/r/demo/grc20_wrapper" ) var ( test_tickLower = int32(9000) test_tickUpper = int32(11000) test_liquidityExpect = bigint(100_000_000) + + test_tickLower2 = int32(50000) + test_tickUpper2 = int32(100000) ) // 1. Init Pool func TestInit(t *testing.T) { - std.TestSetPrevAddr(gsa) + std.TestSetOrigCaller(test1) InitManual() - std.TestSkipHeights(1) } // 2. Create Foo:Bar Pool func TestCreateFooBarPool(t *testing.T) { - std.TestSetPrevAddr(test1) - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - std.TestSkipHeights(1) - + std.TestSetOrigCaller(test1) CreatePool(fooPath, barPath, fee500, 130621891405341611593710811006) shouldEQ(t, len(pools), 1) - std.TestSkipHeights(1) } // 3. Create Bar:Baz Pool func TestCreateBarBazPool(t *testing.T) { - std.TestSetPrevAddr(test1) - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - std.TestSkipHeights(1) - + std.TestSetOrigCaller(test1) CreatePool(barPath, bazPath, fee500, 130621891405341611593710811006) shouldEQ(t, len(pools), 2) - std.TestSkipHeights(1) } // 4. Mint Foo:Bar Liquidity by test1 func TestMintFooBarLiquidity(t *testing.T) { - std.TestSetPrevAddr(test1) - foo.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - bar.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - std.TestSkipHeights(2) - - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) - amount0, amount1 := Mint( + + Mint( fooPath, barPath, fee500, - consts.POSITION_ADDR, + posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect, ) - shouldEQ(t, amount0, bigint(8040315)) - shouldEQ(t, amount1, bigint(2958014)) - std.TestSkipHeights(1) } // 5. Mint Bar:Baz Liquidity by test1 func TestMintBarBazLiquidity(t *testing.T) { - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - baz.Approve(a2u(consts.POOL_ADDR), uint64(consts.MAX_UINT64)) - std.TestSkipHeights(2) - - std.TestSetPrevRealm(consts.POSITION_PATH) + std.TestSetPrevRealm("gno.land/r/demo/position") std.TestSetOrigCaller(test1) - amount0, amount1 := Mint( + + Mint( barPath, bazPath, fee500, - consts.POSITION_ADDR, + posAddr, test_tickLower, test_tickUpper, test_liquidityExpect, ) - shouldEQ(t, amount0, bigint(2958014)) - shouldEQ(t, amount1, bigint(8040315)) - std.TestSkipHeights(1) } // API TESTS @@ -101,4 +75,7 @@ func TestApiGetPools(t *testing.T) { jsonStr := ApiGetPools() jsonArr := gjson.Parse(jsonStr).Get("response").Array() shouldEQ(t, len(jsonArr), 2) + + // debug + println(jsonStr) } From 4d228437e27226e94926f88d307ad6eaa1c8f17b Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 21 Feb 2024 20:46:07 +0900 Subject: [PATCH 20/23] fix: u256 related --- pool/gno.mod | 7 +- pool/liquidity_math.gno | 4 +- pool/pool.gno | 127 ++++++++++------------- pool/pool_manager.gno | 65 ++++++------ pool/position.gno | 21 ++-- pool/tick.gno | 33 ++++-- pool/tick_bitmap.gno | 6 +- pool/tick_math.gno | 223 ++++++++++++++++++++++++++++++++++++++++ pool/type.gno | 10 +- 9 files changed, 365 insertions(+), 131 deletions(-) create mode 100644 pool/tick_math.gno diff --git a/pool/gno.mod b/pool/gno.mod index a45f3e80..4ccbab82 100644 --- a/pool/gno.mod +++ b/pool/gno.mod @@ -1,18 +1,15 @@ module gno.land/r/demo/pool require ( - gno.land/p/demo/common v0.0.0-latest + gno.land/p/demo/u256 v0.0.0-latest gno.land/p/demo/ufmt v0.0.0-latest gno.land/r/demo/bar v0.0.0-latest gno.land/r/demo/baz v0.0.0-latest gno.land/r/demo/consts v0.0.0-latest gno.land/r/demo/foo v0.0.0-latest - gno.land/r/demo/fred v0.0.0-latest gno.land/r/demo/gns v0.0.0-latest gno.land/r/demo/gov v0.0.0-latest - gno.land/r/demo/obl v0.0.0-latest - gno.land/r/demo/qux v0.0.0-latest - gno.land/r/demo/thud v0.0.0-latest + gno.land/r/demo/grc20_wrapper v0.0.0-latest gno.land/r/demo/users v0.0.0-latest gno.land/r/demo/wugnot v0.0.0-latest ) diff --git a/pool/liquidity_math.gno b/pool/liquidity_math.gno index d04e014d..4a8ce5a4 100644 --- a/pool/liquidity_math.gno +++ b/pool/liquidity_math.gno @@ -9,11 +9,11 @@ import ( func liquidityMathAddDelta(x *u256.Uint, y *u256.Int) *u256.Uint { if y.IsNeg() { z := new(u256.Uint).Sub(x, y.Abs()) - require(z < x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) < x(%d)", z, x)) + require(z.Lt(x), ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) < x(%d)", z, x)) return z } else { z := new(u256.Uint).Add(x, y.Uint()) - require(z >= x, ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= x(%d)", z, x)) + require(z.Gte(x), ufmt.Sprintf("[POOL] liquidity_math.gno__liquidityMathAddDelta() || z(%d) >= x(%d)", z, x)) return z } } diff --git a/pool/pool.gno b/pool/pool.gno index d3758fa4..1c543939 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -3,17 +3,19 @@ package pool import ( "std" - "gno.land/p/demo/common" "gno.land/p/demo/ufmt" + "gno.land/r/demo/consts" + g "gno.land/r/demo/gov" + "gno.land/p/demo/u256" ) // only position contract can call this function func Mint( - token0Path string, - token1Path string, - fee uint16, + pToken0Path string, + pToken1Path string, + pFee uint16, recipient std.Address, tickLower int32, tickUpper int32, @@ -49,23 +51,19 @@ func Mint( if amount0.Signum() > 0 { balance0Before := u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int() - from := std.GetOrigCaller() // token should be transferred from actual user(std.GetOrigCaller), not from the realm(PrevRealm) + from := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) to := GetOrigPkgAddr() - ok := transferFromByRegisterCall(pool.token0Path, from, to, uint64(amount0)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token0Path(%s), from(%s), to(%s), uint64(amount0))(%d) failed", - pool.token0Path, from.String(), to.String(), uint64(amount0), - ), - ) + ok := transferFromByRegisterCall(pool.token0Path, from, to, uint64(amount0.Int64())) + if !ok { + panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token0Path, from, to, uint64(amount0)) failed") + } require( new(u256.Int).Add(balance0Before, amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", - balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())(%d)", + balance0Before, amount0, balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) } @@ -73,23 +71,19 @@ func Mint( if amount1.Signum() > 0 { balance1Before = u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int() - from := std.GetOrigCaller() // token should be transferred from actual user(std.GetOrigCaller), not from the realm(PrevRealm) + from := GetOrigCaller() // token should be transferred from actual user(GetOrigCaller), not from the realm(PrevRealm) to := GetOrigPkgAddr() - ok := transferFromByRegisterCall(pool.token1Path, from, to, uint64(amount1)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token1Path(%s), from(%s), to(%s), uint64(amount1))(%d) failed", - pool.token1Path, from.String(), to.String(), uint64(amount1), - ), - ) + ok := transferFromByRegisterCall(pool.token1Path, from, to, uint64(amount1.Int64())) + if !ok { + panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token1Path, from, to, uint64(amount1)) failed") + } require( new(u256.Int).Add(balance1Before, amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", - balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())(%d)", + balance1Before, amount1, balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) } @@ -102,22 +96,22 @@ func Mint( // only position contract can call this function func Burn( - token0Path string, - token1Path string, - fee uint16, + pToken0Path string, + pToken1Path string, + pFee uint16, tickLower int32, tickUpper int32, amount bigint, ) (bigint, bigint) { - requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Burn() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, std.PrevRealm().PkgPath())) + require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Burn() || caller(%s) must be position contract", PrevRealmPath())) burnAmount := u256.FromBigint(amount).Int().Neg() - pool := GetPool(token0Path, token1Path, fee) + pool := GetPool(pToken0Path, pToken1Path, pFee) position, amount0Int, amount1Int := pool.modifyPosition( ModifyPositionParams{ - std.PrevRealm().Addr(), // msg.sender + PrevRealmAddr(), // msg.sender tickLower, tickUpper, burnAmount, @@ -138,25 +132,25 @@ func Burn( // only position contract can call this function func Collect( - token0Path string, - token1Path string, - fee uint16, + pToken0Path string, + pToken1Path string, + pFee uint16, recipient std.Address, tickLower int32, tickUpper int32, amount0Requested_ bigint, amount1Requested_ bigint, ) (bigint, bigint) { - requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Collect() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, std.PrevRealm().PkgPath())) + require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Collect() || caller(%s) must be position contract(gno.land/r/demo/position)", PrevRealmPath())) amount0Requested := u256.FromBigint(amount0Requested_) amount1Requested := u256.FromBigint(amount1Requested_) - pool := GetPool(token0Path, token1Path, fee) + pool := GetPool(pToken0Path, pToken1Path, pFee) - positionKey := positionGetKey(std.PrevRealm().Addr(), tickLower, tickUpper) - position, exist := pool.positions[positionKey] - requireExist(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", positionKey)) + key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) + position, exist := pool.positions[key] + require(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", key)) amount0 := amount0Requested.Min(position.tokensOwed0) amount1 := amount1Requested.Min(position.tokensOwed1) @@ -180,25 +174,25 @@ func Collect( } func Swap( - token0Path string, - token1Path string, - fee uint16, + pToken0Path string, + pToken1Path string, + pFee uint16, recipient std.Address, zeroForOne bool, amountSpecified_ bigint, sqrtPriceLimitX96_ bigint, payer std.Address, // router ) (bigint, bigint) { - requirePrevRealmPath(consts.ROUTER_PATH, ufmt.Sprintf("[POOL] pool.gno__Swap() || expected PrevRealmPath(%s), got %s", consts.ROUTER_PATH, std.PrevRealm().PkgPath())) + require(PrevRealmPath() == "gno.land/r/demo/router", ufmt.Sprintf("[POOL] pool.gno__Swap() || caller(%s) must be router contract(gno.land/r/demo/router)", PrevRealmPath())) require(amountSpecified_ != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") amountSpecified := u256.IntFromBigint(amountSpecified_) sqrtPriceLimitX96 := u256.FromBigint(sqrtPriceLimitX96_) - pool := GetPool(token0Path, token1Path, fee) + pool := GetPool(pToken0Path, pToken1Path, pFee) slot0Start := pool.slot0 - require(slot0Start.unlocked, ufmt.Sprintf("[POOL] pool.gno__Swap() || slot0 must be unlocked")) + require(slot0Start.unlocked, "[POOL] pool.gno__Swap() || slot0 must be unlocked") var feeProtocol uint8 var feeGrowthGlobalX128 *u256.Uint @@ -278,7 +272,7 @@ func Swap( step.tickNext = consts.MAX_TICK } - step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) + step.sqrtPriceNextX96 = TickMathGetSqrtRatioAtTick(step.tickNext) isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 @@ -349,7 +343,7 @@ func Swap( state.tick = step.tickNext } } else if state.sqrtPriceX96 != step.sqrtPriceStartX96 { - state.tick = common.TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) + state.tick = TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) } } @@ -395,8 +389,8 @@ func Swap( require( new(u256.Int).Add(u256.NewUint(balance0Before).Int(), amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", - balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())(%d)", + balance0Before, amount0, balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) pool.balances.token0.AddInt(pool.balances.token0, amount0) @@ -422,8 +416,8 @@ func Swap( require( new(u256.Int).Add(u256.NewUint(balance1Before).Int(), amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", - balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())(%d)", + balance1Before, amount1, balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) pool.balances.token1.AddInt(pool.balances.token1, amount1) @@ -448,14 +442,11 @@ func SetFeeProtocol( feeProtocol0 uint8, feeProtocol1 uint8, ) { - require(isAdmin(std.PrevRealm().Addr()), ufmt.Sprintf("[POOL] pool.gno__SetFeeProtocol() || caller(%s) must be admin", std.PrevRealm().Addr())) + require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__SetFeeProtocol() || caller(%s) must be admin", PrevRealmAddr())) require( (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)), - ufmt.Sprintf( - "[POOL] pool.gno__SetFeeProtocol() || expected (feeProtocol0(%d) == 0 || (feeProtocol0(%d) >= 4 && feeProtocol0(%d) <= 10)) && (feeProtocol1(%d) == 0 || (feeProtocol1(%d) >= 4 && feeProtocol1(%d) <= 10))", - feeProtocol0, feeProtocol0, feeProtocol0, feeProtocol1, feeProtocol1, feeProtocol1, - ), + "Invalid fee protocol", ) // iterate all pool @@ -464,21 +455,21 @@ func SetFeeProtocol( } // update governace value - gv.SetGovParameter("protocoL_fees", feeProtocol0+(feeProtocol1<<4)) + g.SetGovParameter("protocoL_fees", feeProtocol0+(feeProtocol1<<4)) } // ADMIN func CollectProtocol( - token0Path string, - token1Path string, - fee uint16, + pToken0Path string, + pToken1Path string, + pFee uint16, recipient std.Address, amount0Requested *u256.Uint, amount1Requested *u256.Uint, ) (bigint, bigint) { require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr())) - pool := GetPool(token0Path, token1Path, fee) + pool := GetPool(pToken0Path, pToken1Path, pFee) amount0 := amount0Requested.Min(pool.protocolFees.token0) amount1 := amount1Requested.Min(pool.protocolFees.token1) @@ -487,13 +478,9 @@ func CollectProtocol( amount0, amount1 = pool.saveProtocolFees(amount0, amount1) ok := transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) - require( - ok, - ufmt.Sprintf( - "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(amount0))(%d) failed", - pool.token0Path, recipient.String(), uint64(amount0), - ), - ) + if !ok { + panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) failed") + } ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) if !ok { @@ -563,6 +550,7 @@ func (pool *Pool) updatePosition( var _feeGrowthGlobal1X128 *u256.Uint = pool.feeGrowthGlobal1X128 var flippedLower, flippedUpper bool + if !liquidityDelta.IsZero() { flippedLower = pool.tickUpdate( tickLower, @@ -592,7 +580,6 @@ func (pool *Pool) updatePosition( pool.tickBitmapFlipTick(tickUpper, pool.tickSpacing) } } - // NO LIQ, ONLY BURN 0 feeGrowthInside0X128, feeGrowthInside1X128 := pool.tickGetFeeGrowthInside( diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index 8d1d9562..7100d18f 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -4,7 +4,6 @@ import ( "std" "strconv" - "gno.land/p/demo/common" "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" @@ -15,31 +14,28 @@ var ( admins []std.Address initialized bool = false - feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[feeAmount]tick_spacing - pools map[string]*Pool = make(map[string]*Pool) // map[poolPath]*Pool + feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[fee_amount]tick_spacing + pools map[string]*Pool = make(map[string]*Pool) // map[pool_key]*Pool ) func InitManual() { - // MUST BE ORIGIN CALLER - std.AssertOriginCall() - require(!initialized, ufmt.Sprintf("[POOl] pool_manager.gno__InitManual() || expected initialized(%t) == false", initialized)) - require(std.GetOrigCaller() == consts.GNOSWAP_ADMIN, ufmt.Sprintf("[POOl] pool_manager.gno__InitManual() || expected std.GetOrigCaller(%s) == consts.GNOSWAP_ADMIN(%s)", std.GetOrigCaller().String(), consts.GNOSWAP_ADMIN.String())) + require(!initialized, "[POOl] pool_manager.gno__InitManual() || contract must not be initialized") feeAmountTickSpacing[100] = 2 // 0.01% feeAmountTickSpacing[500] = 10 // 0.05% feeAmountTickSpacing[3000] = 60 // 0.3% feeAmountTickSpacing[10000] = 200 // 1% - admins = append(admins, std.PrevRealm().Addr()) + admins = append(admins, PrevRealmAddr()) initialized = true } func CreatePool( - token0Path string, - token1Path string, + tokenAPath string, + tokenBPath string, fee uint16, sqrtPriceX96_ bigint, ) *Pool { - require(initialized, ufmt.Sprintf("[POOl] pool_manager.gno__gno__CreatePool() || expected initialized(%t) == true", initialized)) - require(token0Path != token1Path, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || expected token0Path(%s) != token1Path(%s)", token0Path, token1Path)) + require(initialized, "[POOl] pool_manager.gno__CreatePool() || contract must be initialized") + require(tokenAPath != tokenBPath, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__tokenAPath(%s) != tokenBPath(%s)", tokenAPath, tokenBPath)) sqrtPriceX96 := u256.FromBigint(sqrtPriceX96_) @@ -51,41 +47,44 @@ func CreatePool( // check tickSpacing for fee tickSpacing := feeAmountTickSpacing[fee] - requirePositive(bigint(tickSpacing), ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || expected tickSpacing(%d) > 0", tickSpacing)) + require(tickSpacing > 0, ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || tickSpacing(%d) > 0", tickSpacing)) - // calculate poolPath - poolPath := GetPoolPath(token0Path, token1Path, fee) + // calculate poolKey + poolKey := GetPoolKey(tokenAPath, tokenBPath, fee) // check whether the pool already exist - pool, exist := pools[poolPath] - requireExist(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || expected poolPath(%s) not to exist", poolPath)) + pool, exist := pools[poolKey] + require(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || pool(%s) already exist", poolKey)) if !exist { - gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(consts.GNOSWAP_ADMIN), consts.POOL_CREATION_FEE) + // 500 GNS as creation fee + // recipent is same address that receives protocol fee + // r3v4_xxx: change address when publish + gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(std.Address("g1vaekzh6lta047h6lta047h6lta047h6lutmjdk")), 500) - pool = newPool(token0Path, token1Path, fee, tickSpacing, sqrtPriceX96) - pools[poolPath] = pool + pool = newPool(tokenAPath, tokenBPath, fee, tickSpacing, sqrtPriceX96) + pools[poolKey] = pool } return pool } -func GetPool(token0Path, token1Path string, fee uint16) *Pool { - poolPath := GetPoolPath(token0Path, token1Path, fee) - pool, exist := pools[poolPath] - requireExist(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || expected poolPath(%s) to exist", poolPath)) +func GetPool(token0, token1 string, fee uint16) *Pool { + poolKey := GetPoolKey(token0, token1, fee) + pool, exist := pools[poolKey] + require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || pool(%s) not found", poolKey)) return pool } -func GetPoolFromPoolPath(poolPath string) *Pool { - pool, exist := pools[poolPath] - requireExist(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolPath() || expected poolPath(%s) to exist", poolPath)) +func GetPoolFromPoolKey(poolKey string) *Pool { + pool, exist := pools[poolKey] + require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolKey() || pool(%s) not found", poolKey)) return pool } -func GetPoolPath(token0Path, token1Path string, fee uint16) string { +func GetPoolKey(token0Path, token1Path string, fee uint16) string { if token0Path < token1Path { return token0Path + ":" + token1Path + ":" + strconv.Itoa(int(fee)) } else { @@ -94,16 +93,16 @@ func GetPoolPath(token0Path, token1Path string, fee uint16) string { } func AddAdmin(addr std.Address) { - caller := std.PrevRealm().Addr() + caller := PrevRealmAddr() if isAdmin(caller) { admins = append(admins, addr) } else { - panic(ufmt.Sprintf("[POOL] pool_manager.gno__AddAdmin() || caller(%s) is not admin", caller.String())) + panic("[POOL] pool_manager.gno__AddAdmin() || caller is not admin") // r3v4_xx: detailed error message ?? } } func RemoveAdmin(addr std.Address) { - caller := std.PrevRealm().Addr() + caller := PrevRealmAddr() if isAdmin(caller) { if len(admins) == 1 { @@ -116,7 +115,7 @@ func RemoveAdmin(addr std.Address) { } } } else { - panic(ufmt.Sprintf("[POOL] pool_manager.gno__RemoveAdmin() || caller(%s) is not admin", caller.String())) + panic("[POOL] pool_manager.gno__RemoveAdmin() || caller is not admin") // r3v4_xx: detailed error message ?? } } @@ -128,7 +127,7 @@ func newPool( sqrtPriceX96 *u256.Uint, ) *Pool { maxLiquidityPerTick := tickTickSpacingToMaxLiquidityPerTick(tickSpacing) - tick := common.TickMathGetTickAtSqrtRatio(sqrtPriceX96) + tick := TickMathGetTickAtSqrtRatio(sqrtPriceX96) slot0 := Slot0{ sqrtPriceX96: sqrtPriceX96, diff --git a/pool/position.gno b/pool/position.gno index 3835b587..7fb2e544 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -6,6 +6,8 @@ import ( "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" + + "gno.land/r/demo/consts" ) func positionGetKey( @@ -13,9 +15,9 @@ func positionGetKey( tickLower int32, tickUpper int32, ) string { - positionKey := ufmt.Sprintf("%s__%d__%d", owner.String(), tickLower, tickUpper) + key := ufmt.Sprintf("%s__%d__%d", owner.String(), tickLower, tickUpper) - encoded := base64.StdEncoding.EncodeToString([]byte(positionKey)) + encoded := base64.StdEncoding.EncodeToString([]byte(key)) return encoded } @@ -28,7 +30,7 @@ func (pool *Pool) positionUpdateWithKey( position := pool.positions[key] p := positionUpdate(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128) - pool.positions[positionKey] = p + pool.positions[key] = p return p } @@ -39,14 +41,22 @@ func positionUpdate( feeGrowthInside0X128 *u256.Uint, feeGrowthInside1X128 *u256.Uint, ) PositionInfo { - var liquidityNext *u256.Int + liquidityNext := u256.NewInt(0) + + // check nil + self.liquidity = self.liquidity.NilToZero() + self.feeGrowthInside0LastX128 = self.feeGrowthInside0LastX128.NilToZero() + self.feeGrowthInside1LastX128 = self.feeGrowthInside1LastX128.NilToZero() + self.tokensOwed0 = self.tokensOwed0.NilToZero() + self.tokensOwed1 = self.tokensOwed1.NilToZero() + // + if liquidityDelta.IsZero() { liquidityNext = self.liquidity.Int() } else { liquidityNext = liquidityMathAddDelta(self.liquidity, liquidityDelta).Int() } - tokensOwed0 := new(u256.Uint).Sub(feeGrowthInside0X128, self.feeGrowthInside0LastX128) tokensOwed0 = tokensOwed0.Mul(tokensOwed0, self.liquidity) tokensOwed0 = tokensOwed0.Div(tokensOwed0, u256.FromBigint(consts.Q128)) @@ -64,6 +74,5 @@ func positionUpdate( self.tokensOwed0.Add(self.tokensOwed0, tokensOwed0) self.tokensOwed1.Add(self.tokensOwed1, tokensOwed1) - return self } diff --git a/pool/tick.gno b/pool/tick.gno index 6f32e309..21e65eb8 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -3,6 +3,7 @@ package pool import ( "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" + "gno.land/r/demo/consts" ) func tickTickSpacingToMaxLiquidityPerTick(tickSpacing int32) *u256.Uint { @@ -20,25 +21,30 @@ func (pool *Pool) tickGetFeeGrowthInside( feeGrowthGlobal0X128 *u256.Uint, feeGrowthGlobal1X128 *u256.Uint, ) (feeGrowthInside0X128, feeGrowthInside1X128 *u256.Uint) { + // init return values + feeGrowthInside0X128 = u256.Zero() + feeGrowthInside1X128 = u256.Zero() + // + lower := pool.ticks[tickLower] upper := pool.ticks[tickUpper] var feeGrowthBelow0X128, feeGrowthBelow1X128 *u256.Uint if tickCurrent >= tickLower { - feeGrowthBelow0X128 = lower.feeGrowthOutside0X128 - feeGrowthBelow1X128 = lower.feeGrowthOutside1X128 + feeGrowthBelow0X128 = lower.feeGrowthOutside0X128.NilToZero() + feeGrowthBelow1X128 = lower.feeGrowthOutside1X128.NilToZero() } else { - feeGrowthBelow0X128.Sub(feeGrowthGlobal0X128, lower.feeGrowthOutside0X128) - feeGrowthBelow1X128.Sub(feeGrowthGlobal1X128, lower.feeGrowthOutside1X128) + feeGrowthBelow0X128.Sub(feeGrowthGlobal0X128, lower.feeGrowthOutside0X128.NilToZero()) + feeGrowthBelow1X128.Sub(feeGrowthGlobal1X128, lower.feeGrowthOutside1X128.NilToZero()) } var feeGrowthAbove0X128, feeGrowthAbove1X128 *u256.Uint if tickCurrent < tickUpper { - feeGrowthAbove0X128 = upper.feeGrowthOutside0X128 - feeGrowthAbove1X128 = upper.feeGrowthOutside1X128 + feeGrowthAbove0X128 = upper.feeGrowthOutside0X128.NilToZero() + feeGrowthAbove1X128 = upper.feeGrowthOutside1X128.NilToZero() } else { - feeGrowthAbove0X128.Sub(feeGrowthGlobal0X128, upper.feeGrowthOutside0X128) - feeGrowthAbove1X128.Sub(feeGrowthGlobal1X128, upper.feeGrowthOutside1X128) + feeGrowthAbove0X128.Sub(feeGrowthGlobal0X128, upper.feeGrowthOutside0X128.NilToZero()) + feeGrowthAbove1X128.Sub(feeGrowthGlobal1X128, upper.feeGrowthOutside1X128.NilToZero()) } feeGrowthInside0X128.Sub(feeGrowthGlobal0X128, feeGrowthBelow0X128) @@ -61,8 +67,13 @@ func (pool *Pool) tickUpdate( ) (flipped bool) { info := pool.ticks[tick] liquidityGrossBefore := info.liquidityGross + + if liquidityGrossBefore == nil { + liquidityGrossBefore = u256.Zero() + } + liquidityGrossAfter := liquidityMathAddDelta(liquidityGrossBefore, liquidityDelta) - require(liquidityGrossAfter.Lte(maxLiquidity), ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%d) <= maxLiquidity(%d)", liquidityGrossAfter, maxLiquidity)) + require(!liquidityGrossAfter.Lte(maxLiquidity), ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%s) <= maxLiquidity(%s)", liquidityGrossAfter.Dec(), maxLiquidity.Dec())) flipped = (liquidityGrossAfter.IsZero()) != (liquidityGrossBefore.IsZero()) @@ -77,6 +88,10 @@ func (pool *Pool) tickUpdate( info.liquidityGross = liquidityGrossAfter + if info.liquidityNet == nil { + info.liquidityNet = u256.NewInt(0) + } + if upper { info.liquidityNet.Sub(info.liquidityNet, liquidityDelta) } else { diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index 628c113c..0371f245 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -22,13 +22,17 @@ func (pool *Pool) tickBitmapFlipTick( mask := u256.NewUint(1) mask.Lsh(mask, uint(bitPos)) // 2 ** bitPos - //XXXXXXXXXXXXXXXX + // XXXXXXXXXXXXXXXX // mask.Lsh is exptected to check overflow with the signed integer type. // Lets add an overflow proteced shift method for Uint so we dont have to make it a signed integer. // not implemented, should be done in a separate pr //requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) > 0", mask)) + // check init value + if pool.tickBitmaps[wordPos] == nil { + pool.tickBitmaps[wordPos] = u256.Zero() + } pool.tickBitmaps[wordPos].Xor(pool.tickBitmaps[wordPos], mask) } diff --git a/pool/tick_math.gno b/pool/tick_math.gno new file mode 100644 index 00000000..e1cc165c --- /dev/null +++ b/pool/tick_math.gno @@ -0,0 +1,223 @@ +package pool + +import ( + "gno.land/p/demo/u256" + "gno.land/p/demo/ufmt" + + "gno.land/r/demo/consts" +) + +func TickMathGetSqrtRatioAtTick(tick int32) *u256.Uint { + var absTick *u256.Uint + if tick < 0 { + absTick = u256.NewUint(uint64(-tick)) + } else { + absTick = u256.NewUint(uint64(tick)) + } + + require(!absTick.Lte(u256.NewUint(uint64(consts.MAX_TICK))), ufmt.Sprintf("[POOL] tick_math.gno__TickMathGetSqrtRatioAtTick() || Tick out of range [absTick(%s) <= MAX_TICK(%d)]", absTick.Dec(), consts.MAX_TICK)) + + var ratio *u256.Uint + if !new(u256.Uint).And(absTick, u256.NewUint(0x1)).IsZero() { + ratio = u256.FromBigint(0xfffcb933bd6fad37aa2d162d1a594001) + } else { + ratio = u256.FromBigint(0x100000000000000000000000000000000) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x2)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xfff97272373d413259a46990580e213a)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x4)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xfff2e50f5f656932ef12357cf3c7fdcc)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x8)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xffe5caca7e10e4e61c3624eaa0941cd0)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x10)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xffcb9843d60f6159c9db58835c926644)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x20)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xff973b41fa98c081472e6896dfb254c0)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x40)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xff2ea16466c96a3843ec78b326b52861)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x80)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xfe5dee046a99a2a811c461f1969c3053)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x100)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xfcbe86c7900a88aedcffc83b479aa3a4)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x200)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xf987a7253ac413176f2b074cf7815e54)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x400)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xf3392b0822b70005940c7a398e4b70f3)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x800)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xe7159475a2c29b7443b29c7fa6e889d9)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x1000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xd097f3bdfd2022b8845ad8f792aa5825)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x2000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0xa9f746462d870fdf8a65dc1f90e061e5)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x4000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x70d869a156d2a1b890bb3df62baf32f7)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x8000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x31be135f97d08fd981231505542fcfa6)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x10000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x9aa508b5b7a84e1c677de54f3e99bc9)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x20000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x5d6af8dedb81196699c329225ee604)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x40000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x2216e584f5fa1ea926041bedfe98)) + ratio.Rsh(ratio, 128) + } + + if !new(u256.Uint).And(absTick, u256.NewUint(0x80000)).IsZero() { + ratio.Mul(ratio, u256.FromBigint(0x48a170391f7dc42444e8fa2)) + ratio.Rsh(ratio, 128) + } + + if tick > 0 { + ratio.Div(u256.FromBigint(consts.MAX_UINT256), ratio) + } + + shiftedRatio := new(u256.Uint).Rsh(ratio, 32) + + remainder := new(u256.Uint).Mod(ratio, u256.NewUint(1<<32)) + + if new(u256.Uint).Add(shiftedRatio, remainder).IsZero() { + return shiftedRatio + } else { + return shiftedRatio.Add(shiftedRatio, u256.One()) + } +} + +func TickMathGetTickAtSqrtRatio(sqrtPriceX96 *u256.Uint) int32 { + require(sqrtPriceX96.Gte(u256.FromBigint(consts.MIN_SQRT_RATIO)) && sqrtPriceX96.Lt(u256.FromBigint(consts.MAX_SQRT_RATIO)), ufmt.Sprintf("[POOL] tick_math.gno__tickMathGetTickAtSqrtRatio() || Tick out of range [sqrtPriceX96(%s) >= MIN_SQRT_RATIO(%d) && sqrtPriceX96(%s) < MAX_SQRT_RATIO(%d)]", sqrtPriceX96.Dec(), consts.MIN_SQRT_RATIO, sqrtPriceX96.Dec(), consts.MAX_SQRT_RATIO)) + ratio := new(u256.Uint).Lsh(sqrtPriceX96, 32) + + r := ratio.Clone() + msb := uint64(0) + + // array + _tv := [8]*u256.Uint{ + u256.FromBigint(0), + u256.FromBigint(3), + u256.FromBigint(15), + u256.FromBigint(255), + u256.FromBigint(65535), + u256.FromBigint(4294967295), + u256.FromBigint(18446744073709551615), + u256.FromBigint(340282366920938463463374607431768211455), + } + + for i := 7; i >= 1; i-- { + f := gt(r, _tv[i]) << uint(i) + msb = msb | f + r.Rsh(r, uint(f)) + } + { + f := gt(r, u256.One()) + msb = msb | f + } + + if msb >= 128 { + r = new(u256.Uint).Rsh(ratio, uint(msb-127)) + } else { + r = new(u256.Uint).Lsh(ratio, uint(127-msb)) + } + + log_2 := u256.NewInt(int64(msb - 128)) + log_2.Lsh(log_2, 64) // -18446744073709551616 + + for i := 63; i >= 51; i-- { + + r.Mul(r, r) + r.Rsh(r, 127) + f := new(u256.Uint).Rsh(r, 128) + + _temp := new(u256.Uint).Lsh(f, uint(i)) + + log_2.Or(log_2, new(u256.Uint).Lsh(f, uint(i)).Int()) + + r.Rsh(r, uint(f.Uint64())) // XXXXXXXXX possibly overflow + } + + { + r.Mul(r, r) + r.Rsh(r, 127) + f := new(u256.Uint).Rsh(r, 128) + log_2.Or(log_2, new(u256.Uint).Lsh(f, uint(50)).Int()) + } + + log_sqrt10001 := new(u256.Int).Mul(log_2, u256.FromBigint(255738958999603826347141).Int()) + + 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 + + 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).Lte(sqrtPriceX96) { + tick = tickHi + } else { + tick = tickLow + } + + return tick +} + +func gt(x, y *u256.Uint) uint64 { + if x.Gt(y) { + return 1 + } else { + return 0 + } +} diff --git a/pool/type.gno b/pool/type.gno index cb3195dd..14087fac 100644 --- a/pool/type.gno +++ b/pool/type.gno @@ -37,12 +37,12 @@ type SwapCache struct { type SwapState struct { amountSpecifiedRemaining *u256.Int - amountCalculated *u256.Int + amountCalculated *u256.Int sqrtPriceX96 *u256.Uint tick int32 - feeGrowthGlobalX128 *u256.Uint + feeGrowthGlobalX128 *u256.Uint protocolFee *u256.Uint - liquidity *u256.Uint + liquidity *u256.Uint } type StepComputations struct { @@ -58,7 +58,7 @@ type StepComputations struct { type PositionInfo struct { liquidity *u256.Uint - feeGrowthInside0LastX128 *u256.Uint + feeGrowthInside0LastX128 *u256.Uint feeGrowthInside1LastX128 *u256.Uint tokensOwed0 *u256.Uint @@ -81,7 +81,7 @@ type TickInfo struct { } type Ticks map[int32]TickInfo // tick => TickInfo -type TickBitmaps map[int16]*u256.Uint // tick(wordPos) => bitmap(tickWord ^ mask) +type TickBitmaps map[int16]*u256.Uint // tick(wordPos) => bitmap(tickWord ^ mask) type Positions map[string]PositionInfo // positionKey => PositionInfo // type Pool describes a single Pool/s state From 5d16d7ed029a7d688571da79c3ff2ca0297d1e40 Mon Sep 17 00:00:00 2001 From: mconcat Date: Tue, 27 Feb 2024 17:10:53 +0900 Subject: [PATCH 21/23] fix syntax errors --- common/liquidity_amounts.gno | 65 +++++++---- common/tick_math.gno | 168 +++++++++++++------------- pool/bit_math.gno | 131 ++++++++------------- pool/pool.gno | 220 +++++++++++++++++++++-------------- pool/sqrt_price_math.gno | 28 +++++ 5 files changed, 333 insertions(+), 279 deletions(-) diff --git a/common/liquidity_amounts.gno b/common/liquidity_amounts.gno index 863a721e..448b7989 100644 --- a/common/liquidity_amounts.gno +++ b/common/liquidity_amounts.gno @@ -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 } @@ -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 @@ -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 { diff --git a/common/tick_math.gno b/common/tick_math.gno index f2299252..f3e55fc5 100644 --- a/common/tick_math.gno +++ b/common/tick_math.gno @@ -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) @@ -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 @@ -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 @@ -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 @@ -168,20 +176,12 @@ 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) { @@ -189,3 +189,11 @@ func require(condition bool, message string) { panic(message) } } + +func absTick(n int32) uint64 { + if n < 0 { + return uint64(-n) + } + + return uint64(n) +} diff --git a/pool/bit_math.gno b/pool/bit_math.gno index 6db50d8a..54b8cd69 100644 --- a/pool/bit_math.gno +++ b/pool/bit_math.gno @@ -2,105 +2,68 @@ package pool import ( "gno.land/p/demo/ufmt" + "gno.land/r/demo/consts" "gno.land/p/demo/u256" ) -func bitMathMostSignificantBit(x *u256.Uint) uint8 { - require(x.Gt(u256.NewUint(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || x(%s) > 0", x)) - - r := uint8(0) - if x.Gte(u256.FromBigint(0x100000000000000000000000000000000)) { - x.Rsh(x, 128) - r += 128 - } - - if x.Gte(u256.FromBigint(0x10000000000000000)) { - x.Rsh(x, 64) - r += 64 - } - - if x.Gte(u256.NewUint(0x100000000)) { - x.Rsh(x, 32) - r += 32 - } - - if x.Gte(u256.NewUint(0x10000)) { - x.Rsh(x, 16) - r += 16 - } - - if x.Gte(u256.NewUint(0x100)) { - x.Rsh(x, 8) - r += 8 - } - - if x.Gte(u256.NewUint(0x10)) { - x.Rsh(x, 4) - r += 4 - } +type bitShift struct { + bitPattern *u256.Uint // mask or limit + shift uint +} - if x.Gte(u256.NewUint(0x4)) { - x.Rsh(x, 2) - r += 2 +func bitMathMostSignificantBit(x *u256.Uint) uint8 { + require( + !x.IsZero(), + ufmt.Sprintf("[POOL] bit_math.gno__bitMathMostSignificantBit() || expected x(%d) != 0", x), + ) + + shifts := []bitShift{ + {u256.FromBigint(0x100000000000000000000000000000000), 128}, + {u256.FromBigint(0x10000000000000000), 64}, + {u256.FromBigint(0x100000000), 32}, + {u256.FromBigint(0x10000), 16}, + {u256.FromBigint(0x100), 8}, + {u256.FromBigint(0x10), 4}, + {u256.FromBigint(0x4), 2}, + {u256.FromBigint(0x2), 1}, } - if x.Gte(u256.NewUint(0x2)) { - r += 1 + r := uint8(0) + for _, s := range shifts { + if x.Gte(s.bitPattern) { + x.Rsh(x, s.shift) + r += uint8(s.shift) + } } return r } func bitMathLeastSignificantBit(x *u256.Uint) uint8 { - require(x.Gt(u256.NewUint(0)), ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || x(%s) > 0", x)) - - r := uint8(255) - - if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT128)).Gt(u256.NewUint(0)) { - r -= 128 - } else { - x.Rsh(x, 128) - } - - if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT64)).Gt(u256.NewUint(0)) { - r -= 64 - } else { - x.Rsh(x, 64) - } - - if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT32)).Gt(u256.NewUint(0)) { - r -= 32 - } else { - x.Rsh(x, 32) - } - - if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT16)).Gt(u256.NewUint(0)) { - r -= 16 - } else { - x.Rsh(x, 16) - } - - if u256.NewUint(0).And(x, u256.FromBigint(MAX_UINT8)).Gt(u256.NewUint(0)) { - r -= 8 - } else { - x.Rsh(x, 8) - } - - if u256.NewUint(0).And(x, u256.FromBigint(0xf)).Gt(u256.NewUint(0)) { - r -= 4 - } else { - x.Rsh(x, 4) - } - - if u256.NewUint(0).And(x, u256.FromBigint(0x3)).Gt(u256.NewUint(0)) { - r -= 2 - } else { - x.Rsh(x, 2) + require( + !x.IsZero(), + ufmt.Sprintf("[POOL] bit_math.gno__bitMathLeastSignificantBit() || expected x(%d) != 0", x), + ) + + shifts := []bitShift{ + {u256.FromBigint(consts.MAX_UINT128), 128}, + {u256.FromBigint(consts.MAX_UINT64), 64}, + {u256.FromBigint(consts.MAX_UINT32), 32}, + {u256.FromBigint(consts.MAX_UINT16), 16}, + {u256.FromBigint(consts.MAX_UINT8), 8}, + {u256.FromBigint(0xf), 4}, + {u256.FromBigint(0x3), 2}, + {u256.FromBigint(0x1), 1}, } - if u256.NewUint(0).And(x, u256.FromBigint(0x1)).Gt(u256.NewUint(0)) { - r -= 1 + r := uint8(255) + for _, s := range shifts { + if !new(u256.Uint).And(x, s.bitPattern).IsZero() { + r -= uint8(s.shift) + } else { + x.Rsh(x, s.shift) + } } return r diff --git a/pool/pool.gno b/pool/pool.gno index 1c543939..8bee6181 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -3,9 +3,11 @@ package pool import ( "std" + "gno.land/p/demo/common" "gno.land/p/demo/ufmt" "gno.land/r/demo/consts" + g "gno.land/r/demo/gov" "gno.land/p/demo/u256" @@ -13,21 +15,21 @@ import ( // only position contract can call this function func Mint( - pToken0Path string, - pToken1Path string, - pFee uint16, + token0Path string, + token1Path string, + fee uint16, recipient std.Address, tickLower int32, tickUpper int32, liquidityAmount_ bigint, ) (bigint, bigint /* *u256.Int, *u256.Int */) { - require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Mint() || PrevRealmPath(%s) == \"gno.land/r/demo/position\"", PrevRealmPath())) + requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, PrevRealmPath())) - require(liquidityAmount_ > 0, ufmt.Sprintf("[POOL] pool.gno__Mint() || liquidityAmount(%d) > 0", liquidityAmount_)) + requirePositive(liquidityAmount_, ufmt.Sprintf("[POOL] pool.gno__Mint() || expected liquidityAmount(%d) > 0", liquidityAmount_)) liquidityAmount := u256.IntFromBigint(liquidityAmount_) - pool := GetPool(pToken0Path, pToken1Path, pFee) + pool := GetPool(token0Path, token1Path, fee) _, amount0, amount1 := pool.modifyPosition( ModifyPositionParams{ recipient, // owner @@ -38,11 +40,11 @@ func Mint( ) if amount0.IsNeg() { - panic("[POOL] pool.gno__Mint() || amount0.IsNeg()") + panic(ufmt.Sprintf("[POOL] pool.gno__Mint() || expected amount0(%d) >= 0", amount0)) } if amount1.IsNeg() { - panic("[POOL] pool.gno__Mint() || amount1.IsNeg()") + panic(ufmt.Sprintf("[POOL] pool.gno__Mint() || expected amount1(%d) >= 0", amount1)) } balance0Before := u256.Zero().Int() @@ -55,15 +57,19 @@ func Mint( to := GetOrigPkgAddr() ok := transferFromByRegisterCall(pool.token0Path, from, to, uint64(amount0.Int64())) - if !ok { - panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token0Path, from, to, uint64(amount0)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token0Path(%s), from(%s), to(%s), uint64(amount0))(%d) failed", + pool.token0Path, from.String(), to.String(), uint64(amount0.Int64()), + ), + ) require( new(u256.Int).Add(balance0Before, amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())(%d)", - balance0Before, amount0, balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", + balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) } @@ -75,15 +81,19 @@ func Mint( to := GetOrigPkgAddr() ok := transferFromByRegisterCall(pool.token1Path, from, to, uint64(amount1.Int64())) - if !ok { - panic("[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token1Path, from, to, uint64(amount1)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token1Path(%s), from(%s), to(%s), uint64(amount1))(%d) failed", + pool.token1Path, from.String(), to.String(), uint64(amount1), + ), + ) require( new(u256.Int).Add(balance1Before, amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())(%d)", - balance1Before, amount1, balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Mint() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", + balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) } @@ -96,18 +106,18 @@ func Mint( // only position contract can call this function func Burn( - pToken0Path string, - pToken1Path string, - pFee uint16, + token0Path string, + token1Path string, + fee uint16, tickLower int32, tickUpper int32, amount bigint, ) (bigint, bigint) { - require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Burn() || caller(%s) must be position contract", PrevRealmPath())) + requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Burn() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, PrevRealmPath())) burnAmount := u256.FromBigint(amount).Int().Neg() - pool := GetPool(pToken0Path, pToken1Path, pFee) + pool := GetPool(token0Path, token1Path, fee) position, amount0Int, amount1Int := pool.modifyPosition( ModifyPositionParams{ @@ -124,75 +134,78 @@ func Burn( position.tokensOwed0.Add(position.tokensOwed0, amount0) position.tokensOwed1.Add(position.tokensOwed1, amount1) - key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) - pool.positions[key] = position + positionKey := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) + pool.positions[positionKey] = position + // actual token transfer happens in Collect() return amount0.Bigint(), amount1.Bigint() } // only position contract can call this function func Collect( - pToken0Path string, - pToken1Path string, - pFee uint16, + token0Path string, + token1Path string, + fee uint16, recipient std.Address, tickLower int32, tickUpper int32, amount0Requested_ bigint, amount1Requested_ bigint, ) (bigint, bigint) { - require(PrevRealmPath() == "gno.land/r/demo/position", ufmt.Sprintf("[POOL] pool.gno__Collect() || caller(%s) must be position contract(gno.land/r/demo/position)", PrevRealmPath())) + requirePrevRealmPath(consts.POSITION_PATH, ufmt.Sprintf("[POOL] pool.gno__Collect() || expected PrevRealmPath(%s), got %s", consts.POSITION_PATH, PrevRealmPath())) amount0Requested := u256.FromBigint(amount0Requested_) amount1Requested := u256.FromBigint(amount1Requested_) - pool := GetPool(pToken0Path, pToken1Path, pFee) + pool := GetPool(token0Path, token1Path, fee) - key := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) - position, exist := pool.positions[key] - require(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", key)) + positionKey := positionGetKey(PrevRealmAddr(), tickLower, tickUpper) + position, exist := pool.positions[positionKey] + requireExist(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", positionKey)) + // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 amount0 := amount0Requested.Min(position.tokensOwed0) + amount0 = amount0.Min(pool.balances.token0) amount1 := amount1Requested.Min(position.tokensOwed1) - - require(pool.balances.token0.Gte(amount0), ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token0(%d) >= amount0(%d)", pool.balances.token0, amount0)) - transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) - - require(pool.balances.token1.Gte(amount1), ufmt.Sprintf("[POOL] pool.gno__Collect() || pool.balances.token1(%d) >= amount1(%d)", pool.balances.token1, amount1)) - transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) + amount1 = amount1.Min(pool.balances.token1) // adjust position position.tokensOwed0.Sub(position.tokensOwed0, amount0) position.tokensOwed1.Sub(position.tokensOwed1, amount1) - pool.positions[key] = position // adjust pool pool.balances.token0.Sub(pool.balances.token0, amount0) pool.balances.token1.Sub(pool.balances.token0, amount1) + transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) + transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) + + pool.positions[positionKey] = position + return amount0.Bigint(), amount1.Bigint() } func Swap( - pToken0Path string, - pToken1Path string, - pFee uint16, + token0Path string, + token1Path string, + fee uint16, recipient std.Address, zeroForOne bool, amountSpecified_ bigint, sqrtPriceLimitX96_ bigint, payer std.Address, // router ) (bigint, bigint) { - require(PrevRealmPath() == "gno.land/r/demo/router", ufmt.Sprintf("[POOL] pool.gno__Swap() || caller(%s) must be router contract(gno.land/r/demo/router)", PrevRealmPath())) + requirePrevRealmPath(consts.ROUTER_PATH, ufmt.Sprintf("[POOL] pool.gno__Swap() || expected PrevRealmPath(%s), got %s", consts.ROUTER_PATH, PrevRealmPath())) - require(amountSpecified_ != 0, "[POOL] pool.gno__Swap() || amountSpecified can't be zero") + // early panic + require(amountSpecified_ != 0, ufmt.Sprintf("[POOL] pool.gno__Swap() || amountSpecified(%d) != 0", amountSpecified_)) amountSpecified := u256.IntFromBigint(amountSpecified_) sqrtPriceLimitX96 := u256.FromBigint(sqrtPriceLimitX96_) - pool := GetPool(pToken0Path, pToken1Path, pFee) + pool := GetPool(token0Path, token1Path, fee) slot0Start := pool.slot0 - require(slot0Start.unlocked, "[POOL] pool.gno__Swap() || slot0 must be unlocked") + require(slot0Start.unlocked, ufmt.Sprintf("[POOL] pool.gno__Swap() || slot0 must be unlocked")) var feeProtocol uint8 var feeGrowthGlobalX128 *u256.Uint @@ -200,7 +213,7 @@ func Swap( if zeroForOne { require( sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Gt(u256.FromBigint(consts.MIN_SQRT_RATIO)), - ufmt.Sprintf("[POOL] pool.gno__Swap() || SPL-zeroForOne(T)__sqrtPriceLimitX96(%d) < slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) > MIN_SQRT_RATIO(%d)", + ufmt.Sprintf("[POOL] pool.gno__Swap() || sqrtPriceLimitX96(%d) < slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) > consts.MIN_SQRT_RATIO(%d)", sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, consts.MIN_SQRT_RATIO), ) feeProtocol = slot0Start.feeProtocol % 16 @@ -208,7 +221,7 @@ func Swap( } else { require( sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Lt(u256.FromBigint(consts.MAX_SQRT_RATIO)), - ufmt.Sprintf("[POOL] pool.gno__Swap() || SPL-zeroForOne(F)__sqrtPriceLimitX96(%d) > slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) < MAX_SQRT_RATIO(%d)", + ufmt.Sprintf("[POOL] pool.gno__Swap() || sqrtPriceLimitX96(%d) > slot0Start.sqrtPriceX96(%d) && sqrtPriceLimitX96(%d) < consts.MAX_SQRT_RATIO(%d)", sqrtPriceLimitX96, slot0Start.sqrtPriceX96, sqrtPriceLimitX96, consts.MAX_SQRT_RATIO), ) feeProtocol = slot0Start.feeProtocol / 16 @@ -272,7 +285,7 @@ func Swap( step.tickNext = consts.MAX_TICK } - step.sqrtPriceNextX96 = TickMathGetSqrtRatioAtTick(step.tickNext) + step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 @@ -343,7 +356,7 @@ func Swap( state.tick = step.tickNext } } else if state.sqrtPriceX96 != step.sqrtPriceStartX96 { - state.tick = TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) + state.tick = common.TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) } } @@ -381,26 +394,34 @@ func Swap( if zeroForOne { // payer > pool balance0Before := balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()) - ok := transferFromByRegisterCall(pool.token0Path, payer, consts.ADDR_POOL, uint64(amount0)) - if !ok { - panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path, payer, ADDR_POOL, uint64(amount0)) failed") - } + ok := transferFromByRegisterCall(pool.token0Path, payer, consts.POOL_ADDR, uint64(amount0)) + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount0))(%d) failed", + pool.token0Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount0), + ), + ) require( new(u256.Int).Add(u256.NewUint(balance0Before).Int(), amount0).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr())(%d)", - balance0Before, amount0, balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance0Before(%d) + amount0(%d) <= balanceOfByRegisterCall(pool.token0Path(%s), GetOrigPkgAddr()(%s))(%d)", + balance0Before, amount0, pool.token0Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()), ), ) pool.balances.token0.AddInt(pool.balances.token0, amount0) if amount1.IsNeg() { // pool > recipient - require(pool.balances.token1.Gt(amount1.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > (-1 * amount1)(%d)", pool.balances.token1, (-amount1))) + require(pool.balances.token1.Gt(amount1.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > -amount1(%d)", pool.balances.token1, amount1.Neg())) ok := transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) - if !ok { - panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(-amount1))(%d) failed", + pool.token1Path, recipient.String(), uint64(-amount1), + ), + ) pool.balances.token1.AddInt(pool.balances.token1, amount1) } @@ -408,27 +429,35 @@ func Swap( } else { // payer > pool balance1Before := balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()) - ok := transferFromByRegisterCall(pool.token1Path, payer, consts.ADDR_POOL, uint64(amount1)) - if !ok { - panic("[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path, payer, ADDR_POOL, uint64(amount1)) failed") - } + ok := transferFromByRegisterCall(pool.token1Path, payer, consts.POOL_ADDR, uint64(amount1)) + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount1))(%d) failed", + pool.token1Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount1), + ), + ) require( new(u256.Int).Add(u256.NewUint(balance1Before).Int(), amount1).Lte(u256.NewUint(balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())).Int()), ufmt.Sprintf( - "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr())(%d)", - balance1Before, amount1, balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), + "[POOL] pool.gno__Swap() || balance1Before(%d) + amount1(%d) <= balanceOfByRegisterCall(pool.token1Path(%s), GetOrigPkgAddr()(%s))(%d)", + balance1Before, amount1, pool.token1Path, GetOrigPkgAddr().String(), balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()), ), ) pool.balances.token1.AddInt(pool.balances.token1, amount1) if amount0.IsNeg() { // pool > recipient - require(pool.balances.token0.Gt(amount0.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) > (-1 * amount0)(%d)", pool.balances.token0, (-amount0))) + require(pool.balances.token0.Gt(amount0.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) > -amount0(%d)", pool.balances.token0, amount0.Neg())) ok := transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) - if !ok { - panic("[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(-amount0))(%d) failed", + pool.token0Path, recipient.String(), uint64(-amount0), + ), + ) pool.balances.token0.AddInt(pool.balances.token0, amount0) } } @@ -446,7 +475,10 @@ func SetFeeProtocol( require( (feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) && (feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10)), - "Invalid fee protocol", + ufmt.Sprintf( + "[POOL] pool.gno__SetFeeProtocol() || expected (feeProtocol0(%d) == 0 || (feeProtocol0(%d) >= 4 && feeProtocol0(%d) <= 10)) && (feeProtocol1(%d) == 0 || (feeProtocol1(%d) >= 4 && feeProtocol1(%d) <= 10))", + feeProtocol0, feeProtocol0, feeProtocol0, feeProtocol1, feeProtocol1, feeProtocol1, + ), ) // iterate all pool @@ -460,16 +492,16 @@ func SetFeeProtocol( // ADMIN func CollectProtocol( - pToken0Path string, - pToken1Path string, - pFee uint16, + token0Path string, + token1Path string, + fee uint16, recipient std.Address, amount0Requested *u256.Uint, amount1Requested *u256.Uint, ) (bigint, bigint) { require(isAdmin(PrevRealmAddr()), ufmt.Sprintf("[POOL] pool.gno__CollectProtocol() || caller(%s) must be admin", PrevRealmAddr())) - pool := GetPool(pToken0Path, pToken1Path, pFee) + pool := GetPool(token0Path, token1Path, fee) amount0 := amount0Requested.Min(pool.protocolFees.token0) amount1 := amount1Requested.Min(pool.protocolFees.token1) @@ -478,14 +510,22 @@ func CollectProtocol( amount0, amount1 = pool.saveProtocolFees(amount0, amount1) ok := transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) - if !ok { - panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(amount0))(%d) failed", + pool.token0Path, recipient.String(), uint64(amount0), + ), + ) ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) - if !ok { - panic("[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) failed") - } + require( + ok, + ufmt.Sprintf( + "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(amount1))(%d) failed", + pool.token1Path, recipient.String(), uint64(amount1), + ), + ) return amount0.Bigint(), amount1.Bigint() } @@ -506,8 +546,8 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 if !params.liquidityDelta.IsZero() { if pool.slot0.tick < params.tickLower { amount0 = sqrtPriceMathGetAmount0Delta( - TickMathGetSqrtRatioAtTick(params.tickLower), - TickMathGetSqrtRatioAtTick(params.tickUpper), + common.TickMathGetSqrtRatioAtTick(params.tickLower), + common.TickMathGetSqrtRatioAtTick(params.tickUpper), params.liquidityDelta, ) } else if pool.slot0.tick < params.tickUpper { @@ -515,12 +555,12 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 amount0 = sqrtPriceMathGetAmount0Delta( pool.slot0.sqrtPriceX96, - TickMathGetSqrtRatioAtTick(params.tickUpper), + common.TickMathGetSqrtRatioAtTick(params.tickUpper), params.liquidityDelta, ) amount1 = sqrtPriceMathGetAmount1Delta( - TickMathGetSqrtRatioAtTick(params.tickLower), + common.TickMathGetSqrtRatioAtTick(params.tickLower), pool.slot0.sqrtPriceX96, params.liquidityDelta, ) @@ -529,8 +569,8 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 } else { amount1 = sqrtPriceMathGetAmount1Delta( - TickMathGetSqrtRatioAtTick(params.tickLower), - TickMathGetSqrtRatioAtTick(params.tickUpper), + common.TickMathGetSqrtRatioAtTick(params.tickLower), + common.TickMathGetSqrtRatioAtTick(params.tickUpper), params.liquidityDelta, ) } @@ -590,10 +630,10 @@ func (pool *Pool) updatePosition( _feeGrowthGlobal1X128, ) - key := positionGetKey(owner, tickLower, tickUpper) + positionKey := positionGetKey(owner, tickLower, tickUpper) position := pool.positionUpdateWithKey( - key, + positionKey, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128, diff --git a/pool/sqrt_price_math.gno b/pool/sqrt_price_math.gno index f005d485..9ed6a9a9 100644 --- a/pool/sqrt_price_math.gno +++ b/pool/sqrt_price_math.gno @@ -1,8 +1,16 @@ package pool +// XXXXXXXXX +// u256 Add/Sub are overflow protected, but Bitwise operations are not. +// Functions in this file are supposed to be overflow protected. +// swap_math.gno is assuming that these functions are overflow protected. +// XXXXXXXXX + import ( "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" + + "gno.land/r/demo/consts" ) func sqrtPriceMathGetNextSqrtPriceFromAmount0RoundingUp( @@ -72,6 +80,26 @@ func sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown( require(sqrtPX96.Gt(quotient), ufmt.Sprintf("[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || sqrtPX96(%d) must be greater than quotient(%d)", sqrtPX96, quotient)) return new(u256.Uint).Sub(sqrtPX96, quotient) } + + // quotient mutst be positive when amount and liquidity are positive + + if add { + result := new(u256.Uint).Add(sqrtPX96, quotient) + + require( + result.Gte(sqrtPX96), + ufmt.Sprintf( + "[POOL] sqrt_price_math.gno__sqrtPriceMathGetNextSqrtPriceFromAmount1RoundingDown() || expected result >= sqrtPX96, got: %d", + result, + ), + ) + + return result + } + + result := new(u256.Uint).Sub(sqrtPX96, quotient) + + return result } func sqrtPriceMathGetNextSqrtPriceFromInput( From 73500a1c4445bc6cf4eb278b901487d4b460359c Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 28 Feb 2024 16:32:26 +0900 Subject: [PATCH 22/23] fix: bunch of bigint <-> uint256 --- pool/TEST_liquidity_math_test.gnoa | 96 +++++++++--------- pool/_TEST_pool_single_lp_test.gno | 156 +++++++++++++---------------- pool/pool.gno | 100 ++++++++++-------- pool/pool_manager.gno | 68 +++++++------ pool/swap_math.gno | 7 +- pool/tick.gno | 15 ++- pool/tick_bitmap.gno | 8 +- u256/i256.gno | 31 ++++-- u256/u256.gno | 4 +- 9 files changed, 260 insertions(+), 225 deletions(-) diff --git a/pool/TEST_liquidity_math_test.gnoa b/pool/TEST_liquidity_math_test.gnoa index 0fa08051..00c00e29 100644 --- a/pool/TEST_liquidity_math_test.gnoa +++ b/pool/TEST_liquidity_math_test.gnoa @@ -1,53 +1,53 @@ package pool -import ( - "testing" - "gno.land/p/demo/ufmt" -) +// import ( +// "testing" +// "gno.land/p/demo/ufmt" +// ) -func TestLiquidityMathAddDelta(t *testing.T) { - testCases := []struct { - x bigint - y bigint - expected bigint - isErr bool - }{ - {-1000, -500, -1500, true}, - {-1000, 500, -500, true}, - {-200, -100, -300, true}, - {-200, 100, -100, true}, - {-100, -50, -150, true}, - {-100, 50, -50, true}, - {0, -100, -100, true}, - {0, 0, 0, false}, - {10, -5, 5, false}, - {10, 5, 15, false}, - {100, -50, 50, false}, - {100, 50, 150, false}, - {200, -100, 100, false}, - {200, 100, 300, false}, - {1000, -500, 500, false}, - {1000, 500, 1500, false}, - } +// func TestLiquidityMathAddDelta(t *testing.T) { +// testCases := []struct { +// x bigint +// y bigint +// expected bigint +// isErr bool +// }{ +// {-1000, -500, -1500, true}, +// {-1000, 500, -500, true}, +// {-200, -100, -300, true}, +// {-200, 100, -100, true}, +// {-100, -50, -150, true}, +// {-100, 50, -50, true}, +// {0, -100, -100, true}, +// {0, 0, 0, false}, +// {10, -5, 5, false}, +// {10, 5, 15, false}, +// {100, -50, 50, false}, +// {100, 50, 150, false}, +// {200, -100, 100, false}, +// {200, 100, 300, false}, +// {1000, -500, 500, false}, +// {1000, 500, 1500, false}, +// } - for i, tc := range testCases { - func() { - defer func() { - if r := recover(); r != nil { - if !tc.isErr { - t.Errorf("Test case %d failed: x=%d, y=%d, expected=%d, got panic but didn't expect one", i+1, tc.x, tc.y, tc.expected) - } - } else { - if tc.isErr { - t.Errorf("Test case %d failed: x=%d, y=%d, expected panic but didn't get one", i+1, tc.x, tc.y) - } - } - }() +// for i, tc := range testCases { +// func() { +// defer func() { +// if r := recover(); r != nil { +// if !tc.isErr { +// t.Errorf("Test case %d failed: x=%d, y=%d, expected=%d, got panic but didn't expect one", i+1, tc.x, tc.y, tc.expected) +// } +// } else { +// if tc.isErr { +// t.Errorf("Test case %d failed: x=%d, y=%d, expected panic but didn't get one", i+1, tc.x, tc.y) +// } +// } +// }() - result := liquidityMathAddDelta(tc.x, tc.y) - if result != tc.expected && !tc.isErr { - t.Errorf("Test case %d failed: x=%d, y=%d, expected=%d, got=%d", i+1, tc.x, tc.y, tc.expected, result) - } - }() - } -} \ No newline at end of file +// result := liquidityMathAddDelta(tc.x, tc.y) +// if result != tc.expected && !tc.isErr { +// t.Errorf("Test case %d failed: x=%d, y=%d, expected=%d, got=%d", i+1, tc.x, tc.y, tc.expected, result) +// } +// }() +// } +// } diff --git a/pool/_TEST_pool_single_lp_test.gno b/pool/_TEST_pool_single_lp_test.gno index 2bc8d1eb..eeb1689c 100644 --- a/pool/_TEST_pool_single_lp_test.gno +++ b/pool/_TEST_pool_single_lp_test.gno @@ -1,13 +1,14 @@ package pool import ( - "encoding/gjson" "std" "testing" + "encoding/gjson" + "gno.land/r/demo/consts" - _ "gno.land/r/demo/grc20_wrapper" + "gno.land/p/demo/u256" ) var ( @@ -34,14 +35,14 @@ func TestInitCreatePool(t *testing.T) { // 2. Mint by test1 func TestMint(t *testing.T) { - std.TestSetPrevRealm("gno.land/r/demo/position") + std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) Mint( fooPath, barPath, fee500, - posAddr, + consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect, @@ -49,18 +50,18 @@ func TestMint(t *testing.T) { pool := GetPool(barPath, fooPath, fee500) test_liquidity := pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect)), true) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) - m81, m82 := Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - m101, m102 := Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + m81, m82 := Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + m101, m102 := Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) shouldNEQ(t, m81, bigint(0)) shouldNEQ(t, m82, bigint(0)) @@ -68,38 +69,38 @@ func TestMint(t *testing.T) { shouldEQ(t, m82, m102) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*10) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*10)), true) // tickLower > currentTick == don't add to current liquidity - Mint(barPath, fooPath, fee500, posAddr, test_tickLower2, test_tickUpper2, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower2, test_tickUpper2, test_liquidityExpect) // tickUpper < current tick == don't add to current liquidity - Mint(barPath, fooPath, fee500, posAddr, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, -test_tickUpper2, -test_tickLower2, test_liquidityExpect) // tickUpper < tickLower == don't add to current liquidity - Mint(barPath, fooPath, fee500, posAddr, -test_tickUpper, -test_tickLower, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, -test_tickUpper, -test_tickLower, test_liquidityExpect) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*10) - - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*10)), true) + + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*20) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*20)), true) } // 3. Burn by test1 func TestBurn(t *testing.T) { - std.TestSetPrevRealm("gno.land/r/demo/position") + std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) b11, b12 := Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) @@ -109,32 +110,28 @@ func TestBurn(t *testing.T) { pool := GetPool(barPath, fooPath, fee500) test_liquidity := pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*18) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*18)), true) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*8) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*10) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*10)), true) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, 1) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, bigint(9999)) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(9999)), true) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, 999) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, test_liquidityExpect*9) + shouldEQ(t, test_liquidity.Eq(u256.FromBigint(test_liquidityExpect*9)), true) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*9) test_liquidity = pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, bigint(0)) - - // can't burn when liq is 0 - // Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) - shouldPanic(t, func() { Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect) }) + shouldEQ(t, test_liquidity.IsZero(), true) } // 4. Collect func TestCollect(t *testing.T) { - std.TestSetPrevRealm("gno.land/r/demo/position") + std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) // withdraw all token before test `Collect` @@ -143,20 +140,20 @@ func TestCollect(t *testing.T) { // pool should have zero liquidity pool := GetPool(barPath, fooPath, fee500) test_liquidity := pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, bigint(0)) + shouldEQ(t, test_liquidity.IsZero(), true) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c11, c12 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c21, c22 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c11, c21) shouldEQ(t, c12, c22) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c31, c32 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 100, 100) shouldEQ(t, c31, bigint(100)) @@ -167,7 +164,7 @@ func TestCollect(t *testing.T) { shouldEQ(t, c42, c22-bigint(100)) // Mint > No Burn => nothing to collect - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*15) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*15) // Burn(barPath, fooPath, fee500, test_tickLower, test_tickUpper, test_liquidityExpect*15) c51, c52 := Collect(barPath, fooPath, fee500, test1, test_tickLower, test_tickUpper, 50000000, 50000000) shouldEQ(t, c51, bigint(0)) @@ -184,19 +181,19 @@ func TestCollect(t *testing.T) { func TestSwap(t *testing.T) { pool := GetPool(barPath, fooPath, fee500) test_liquidity := pool.PoolGetLiquidity() - shouldEQ(t, test_liquidity, bigint(0)) + shouldEQ(t, test_liquidity.IsZero(), true) - std.TestSetPrevRealm("gno.land/r/demo/position") + std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) - Mint(barPath, fooPath, fee500, posAddr, test_tickLower, test_tickUpper, test_liquidityExpect*20000) + Mint(barPath, fooPath, fee500, consts.POSITION_ADDR, test_tickLower, test_tickUpper, test_liquidityExpect*20000) // Swap several times - std.TestSetPrevRealm("gno.land/r/demo/router") + std.TestSetPrevRealm(consts.ROUTER_PATH) std.TestSetOrigCaller(test1) test_price := bigint(consts.MIN_PRICE) { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -215,8 +212,8 @@ func TestSwap(t *testing.T) { shouldNEQ(t, amount0, bigint(0)) shouldNEQ(t, amount1, bigint(0)) - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -228,16 +225,16 @@ func TestSwap(t *testing.T) { } { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, true, 5000, test_price, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -249,16 +246,16 @@ func TestSwap(t *testing.T) { } { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, true, 1000, test_price, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -271,16 +268,16 @@ func TestSwap(t *testing.T) { // Swap token1 -> token0 { - poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userOldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userOldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) amount0, amount1 := Swap(barPath, fooPath, fee500, test1, false, 16000, consts.MAX_PRICE, std.GetOrigCaller()) // give enough amount to take fees away - poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, poolAddr) - poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, poolAddr) + poolNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, consts.POOL_ADDR) + poolNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, consts.POOL_ADDR) userNewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) userNewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -303,7 +300,7 @@ func TestSetFeeProtocol(t *testing.T) { SetFeeProtocol(6, 8) for _, pool := range pools { - shouldEQ(t, pool.PoolGetSlot0().feeProtocol, bigint(134)) + shouldEQ(t, pool.PoolGetSlot0().feeProtocol, 134) } } @@ -314,13 +311,13 @@ func TestCollectProtocol(t *testing.T) { SetFeeProtocol(6, 8) pool := GetPool(barPath, fooPath, fee500) test_slot0 := pool.PoolGetSlot0() - shouldEQ(t, test_slot0.feeProtocol, bigint(134)) + shouldEQ(t, test_slot0.feeProtocol, 134) // Make ProtocolFee via Swap by test1 ( Mint by test1 ) std.TestSetOrigCaller(test1) { - std.TestSetPrevRealm("gno.land/r/demo/router") + std.TestSetPrevRealm(consts.ROUTER_PATH) std.TestSetOrigCaller(test1) Swap(barPath, fooPath, fee500, test1, true, 200000, consts.MIN_PRICE, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 @@ -328,7 +325,7 @@ func TestCollectProtocol(t *testing.T) { test1OldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) std.TestSetPrevAddr(test1) - amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, 100000, 100000) + amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, u256.NewUint(100000), u256.NewUint(100000)) test1NewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) test1NewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -338,16 +335,15 @@ func TestCollectProtocol(t *testing.T) { } { - - std.TestSetPrevRealm("gno.land/r/demo/router") + std.TestSetPrevRealm(consts.ROUTER_PATH) std.TestSetOrigCaller(test1) - Swap(barPath, fooPath, fee500, test1, false, 200000, MAX_SQRT_RATIO-1, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 + Swap(barPath, fooPath, fee500, test1, false, 200000, consts.MAX_SQRT_RATIO-1, std.GetOrigCaller()) // swap token0 -> token1 => fee only in token0 test1OldToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) test1OldToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) std.TestSetPrevAddr(test1) - amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, 100000, 100000) + amount0, amount1 := CollectProtocol(barPath, fooPath, fee500, test1, u256.NewUint(100000), u256.NewUint(100000)) test1NewToken0Bal := balanceOfByRegisterCall(pool.token0Path, test1) test1NewToken1Bal := balanceOfByRegisterCall(pool.token1Path, test1) @@ -364,13 +360,3 @@ func TestApiGetPools(t *testing.T) { shouldEQ(t, len(jsonStr.Get("response").Array()), 1) } - -func TestApiGetPool(t *testing.T) { - gpl := ApiGetPool("gno.land/r/demo/bar:gno.land/r/demo/foo:500") - jsonStr := gjson.Parse(gpl) - - shouldEQ(t, jsonStr.Get("stat.height").Int(), GetHeight()) - shouldEQ(t, jsonStr.Get("stat.timestamp").Int(), GetTimestamp()) - - shouldEQ(t, len(jsonStr.Get("response.positions").Array()), 4) -} diff --git a/pool/pool.gno b/pool/pool.gno index 8bee6181..00917ee8 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -85,7 +85,7 @@ func Mint( ok, ufmt.Sprintf( "[POOL] pool.gno__Mint() || transferFromByRegisterCall(pool.token1Path(%s), from(%s), to(%s), uint64(amount1))(%d) failed", - pool.token1Path, from.String(), to.String(), uint64(amount1), + pool.token1Path, from.String(), to.String(), uint64(amount1.Int64()), ), ) @@ -169,20 +169,31 @@ func Collect( amount1 := amount1Requested.Min(position.tokensOwed1) amount1 = amount1.Min(pool.balances.token1) + // backup calculated amount + resAmount0 := amount0.Clone() + resAmount1 := amount1.Clone() + // adjust position - position.tokensOwed0.Sub(position.tokensOwed0, amount0) - position.tokensOwed1.Sub(position.tokensOwed1, amount1) + _amount0 := resAmount0.Clone() + _amount1 := resAmount1.Clone() + position.tokensOwed0.Sub(position.tokensOwed0, _amount0) + position.tokensOwed1.Sub(position.tokensOwed1, _amount1) // adjust pool - pool.balances.token0.Sub(pool.balances.token0, amount0) - pool.balances.token1.Sub(pool.balances.token0, amount1) + _amount0 = resAmount0.Clone() + _amount1 = resAmount1.Clone() + pool.balances.token0.Sub(pool.balances.token0, _amount0) + pool.balances.token1.Sub(pool.balances.token1, _amount1) - transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) - transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) + // tranfer + _amount0 = resAmount0.Clone() + _amount1 = resAmount1.Clone() + transferByRegisterCall(pool.token0Path, recipient, _amount0.Uint64()) + transferByRegisterCall(pool.token1Path, recipient, _amount1.Uint64()) pool.positions[positionKey] = position - return amount0.Bigint(), amount1.Bigint() + return resAmount0.Bigint(), resAmount1.Bigint() } func Swap( @@ -207,6 +218,8 @@ func Swap( slot0Start := pool.slot0 require(slot0Start.unlocked, ufmt.Sprintf("[POOL] pool.gno__Swap() || slot0 must be unlocked")) + // println("SQRT:", slot0Start.sqrtPriceX96.Dec()) + var feeProtocol uint8 var feeGrowthGlobalX128 *u256.Uint @@ -268,6 +281,7 @@ func Swap( } } + origAmountSpecified := amountSpecified.Clone() for !state.amountSpecifiedRemaining.IsZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { var step StepComputations @@ -286,8 +300,8 @@ func Swap( } step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) - isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 - isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 + isLower := step.sqrtPriceNextX96.Lt(sqrtPriceLimitX96) + isHigher := step.sqrtPriceNextX96.Gt(sqrtPriceLimitX96) var sqrtRatioTargetX96 *u256.Uint if (zeroForOne && isLower) || (!zeroForOne && isHigher) { @@ -359,8 +373,8 @@ func Swap( state.tick = common.TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) } } - // END LOOP + pool.slot0.sqrtPriceX96 = state.sqrtPriceX96 if state.tick != slot0Start.tick { pool.slot0.tick = state.tick @@ -373,33 +387,37 @@ func Swap( if zeroForOne { pool.feeGrowthGlobal0X128 = state.feeGrowthGlobalX128 if state.protocolFee.Gt(u256.Zero()) { - pool.protocolFees.token0 += state.protocolFee + pool.protocolFees.token0.Add(pool.protocolFees.token0, state.protocolFee) } } else { pool.feeGrowthGlobal1X128 = state.feeGrowthGlobalX128 if state.protocolFee.Gt(u256.Zero()) { - pool.protocolFees.token1 += state.protocolFee + pool.protocolFees.token1.Add(pool.protocolFees.token1, state.protocolFee) } } var amount0, amount1 *u256.Int if zeroForOne == exactInput { - amount0 = new(u256.Int).Sub(amountSpecified, state.amountSpecifiedRemaining) + amount0 = new(u256.Int).Sub(origAmountSpecified, state.amountSpecifiedRemaining) amount1 = state.amountCalculated } else { amount0 = state.amountCalculated - amount1 = new(u256.Int).Sub(amountSpecified, state.amountSpecifiedRemaining) + amount1 = new(u256.Int).Sub(origAmountSpecified, state.amountSpecifiedRemaining) } + // backUP + resAmount0 := amount0.Clone() + resAmount1 := amount1.Clone() + if zeroForOne { // payer > pool balance0Before := balanceOfByRegisterCall(pool.token0Path, GetOrigPkgAddr()) - ok := transferFromByRegisterCall(pool.token0Path, payer, consts.POOL_ADDR, uint64(amount0)) + ok := transferFromByRegisterCall(pool.token0Path, payer, consts.POOL_ADDR, uint64(amount0.Int64())) require( ok, ufmt.Sprintf( "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token0Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount0))(%d) failed", - pool.token0Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount0), + pool.token0Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount0.Int64()), ), ) @@ -414,27 +432,25 @@ func Swap( if amount1.IsNeg() { // pool > recipient require(pool.balances.token1.Gt(amount1.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token1(%d) > -amount1(%d)", pool.balances.token1, amount1.Neg())) - ok := transferByRegisterCall(pool.token1Path, recipient, uint64(-amount1)) + ok := transferByRegisterCall(pool.token1Path, recipient, amount1.Abs().Uint64()) require( ok, ufmt.Sprintf( "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(-amount1))(%d) failed", - pool.token1Path, recipient.String(), uint64(-amount1), + pool.token1Path, recipient.String(), amount1.Abs().Uint64(), ), ) pool.balances.token1.AddInt(pool.balances.token1, amount1) - } - } else { // payer > pool balance1Before := balanceOfByRegisterCall(pool.token1Path, GetOrigPkgAddr()) - ok := transferFromByRegisterCall(pool.token1Path, payer, consts.POOL_ADDR, uint64(amount1)) + ok := transferFromByRegisterCall(pool.token1Path, payer, consts.POOL_ADDR, uint64(amount1.Int64())) require( ok, ufmt.Sprintf( "[POOL] pool.gno__Swap() || transferFromByRegisterCall(pool.token1Path(%s), payer(%s), consts.POOL_ADDR(%s), uint64(amount1))(%d) failed", - pool.token1Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount1), + pool.token1Path, payer.String(), consts.POOL_ADDR.String(), uint64(amount1.Int64()), ), ) @@ -450,12 +466,12 @@ func Swap( if amount0.IsNeg() { // pool > recipient require(pool.balances.token0.Gt(amount0.Abs()), ufmt.Sprintf("[POOL] pool.gno__Swap() || pool.balances.token0(%d) > -amount0(%d)", pool.balances.token0, amount0.Neg())) - ok := transferByRegisterCall(pool.token0Path, recipient, uint64(-amount0)) + ok := transferByRegisterCall(pool.token0Path, recipient, amount0.Abs().Uint64()) require( ok, ufmt.Sprintf( "[POOL] pool.gno__Swap() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(-amount0))(%d) failed", - pool.token0Path, recipient.String(), uint64(-amount0), + pool.token0Path, recipient.String(), amount0.Abs().Uint64(), ), ) pool.balances.token0.AddInt(pool.balances.token0, amount0) @@ -463,7 +479,8 @@ func Swap( } pool.slot0.unlocked = true - return amount0.Bigint(), amount1.Bigint() + + return resAmount0.Bigint(), resAmount1.Bigint() } // ADMIN @@ -506,24 +523,24 @@ func CollectProtocol( amount0 := amount0Requested.Min(pool.protocolFees.token0) amount1 := amount1Requested.Min(pool.protocolFees.token1) - // without procotol fee - amount0, amount1 = pool.saveProtocolFees(amount0, amount1) + // after + amount0, amount1 = pool.saveProtocolFees(amount0.Clone(), amount1.Clone()) - ok := transferByRegisterCall(pool.token0Path, recipient, uint64(amount0)) + ok := transferByRegisterCall(pool.token0Path, recipient, uint64(amount0.Uint64())) require( ok, ufmt.Sprintf( "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token0Path(%s), recipient(%s), uint64(amount0))(%d) failed", - pool.token0Path, recipient.String(), uint64(amount0), + pool.token0Path, recipient.String(), uint64(amount0.Uint64()), ), ) - ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1)) + ok = transferByRegisterCall(pool.token1Path, recipient, uint64(amount1.Uint64())) require( ok, ufmt.Sprintf( "[POOL] pool.gno__CollectProtocol() || transferByRegisterCall(pool.token1Path(%s), recipient(%s), uint64(amount1))(%d) failed", - pool.token1Path, recipient.String(), uint64(amount1), + pool.token1Path, recipient.String(), uint64(amount1.Uint64()), ), ) @@ -548,7 +565,7 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 amount0 = sqrtPriceMathGetAmount0Delta( common.TickMathGetSqrtRatioAtTick(params.tickLower), common.TickMathGetSqrtRatioAtTick(params.tickUpper), - params.liquidityDelta, + params.liquidityDelta.Clone(), ) } else if pool.slot0.tick < params.tickUpper { liquidityBefore := pool.liquidity @@ -556,22 +573,22 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 amount0 = sqrtPriceMathGetAmount0Delta( pool.slot0.sqrtPriceX96, common.TickMathGetSqrtRatioAtTick(params.tickUpper), - params.liquidityDelta, + params.liquidityDelta.Clone(), ) amount1 = sqrtPriceMathGetAmount1Delta( common.TickMathGetSqrtRatioAtTick(params.tickLower), pool.slot0.sqrtPriceX96, - params.liquidityDelta, + params.liquidityDelta.Clone(), ) - pool.liquidity = liquidityMathAddDelta(liquidityBefore, params.liquidityDelta) + pool.liquidity = liquidityMathAddDelta(liquidityBefore, params.liquidityDelta.Clone()) } else { amount1 = sqrtPriceMathGetAmount1Delta( common.TickMathGetSqrtRatioAtTick(params.tickLower), common.TickMathGetSqrtRatioAtTick(params.tickUpper), - params.liquidityDelta, + params.liquidityDelta.Clone(), ) } } @@ -583,9 +600,11 @@ func (pool *Pool) updatePosition( owner std.Address, tickLower int32, tickUpper int32, - liquidityDelta *u256.Int, + _liquidityDelta *u256.Int, tick int32, ) PositionInfo { + liquidityDelta := _liquidityDelta.Clone() + var _feeGrowthGlobal0X128 *u256.Uint = pool.feeGrowthGlobal0X128 var _feeGrowthGlobal1X128 *u256.Uint = pool.feeGrowthGlobal1X128 @@ -648,16 +667,17 @@ func (pool *Pool) updatePosition( pool.tickClear(tickUpper) } } + return position } func (pool *Pool) saveProtocolFees(amount0, amount1 *u256.Uint) (*u256.Uint, *u256.Uint) { if !amount0.IsZero() && amount0.Eq(pool.protocolFees.token0) { - amount0-- + amount0.Sub(amount0, u256.One()) } if !amount1.IsZero() && amount1.Eq(pool.protocolFees.token1) { - amount1-- + amount1.Sub(amount1, u256.One()) } pool.protocolFees.token0.Sub(pool.protocolFees.token0, amount0) diff --git a/pool/pool_manager.gno b/pool/pool_manager.gno index 7100d18f..49524e59 100644 --- a/pool/pool_manager.gno +++ b/pool/pool_manager.gno @@ -4,9 +4,12 @@ import ( "std" "strconv" + "gno.land/p/demo/common" "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" + "gno.land/r/demo/consts" + gns "gno.land/r/demo/gns" ) @@ -14,12 +17,12 @@ var ( admins []std.Address initialized bool = false - feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[fee_amount]tick_spacing - pools map[string]*Pool = make(map[string]*Pool) // map[pool_key]*Pool + feeAmountTickSpacing map[uint16]int32 = make(map[uint16]int32) // map[feeAmount]tick_spacing + pools map[string]*Pool = make(map[string]*Pool) // map[poolPath]*Pool ) func InitManual() { - require(!initialized, "[POOl] pool_manager.gno__InitManual() || contract must not be initialized") + require(!initialized, ufmt.Sprintf("[POOl] pool_manager.gno__InitManual() || expected initialized(%t) == false", initialized)) feeAmountTickSpacing[100] = 2 // 0.01% feeAmountTickSpacing[500] = 10 // 0.05% feeAmountTickSpacing[3000] = 60 // 0.3% @@ -29,62 +32,61 @@ func InitManual() { } func CreatePool( - tokenAPath string, - tokenBPath string, + token0Path string, + token1Path string, fee uint16, sqrtPriceX96_ bigint, ) *Pool { - require(initialized, "[POOl] pool_manager.gno__CreatePool() || contract must be initialized") - require(tokenAPath != tokenBPath, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || token pair cannot be the same__tokenAPath(%s) != tokenBPath(%s)", tokenAPath, tokenBPath)) + require(initialized, ufmt.Sprintf("[POOl] pool_manager.gno__gno__CreatePool() || expected initialized(%t) == true", initialized)) + require(token0Path != token1Path, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || expected token0Path(%s) != token1Path(%s)", token0Path, token1Path)) sqrtPriceX96 := u256.FromBigint(sqrtPriceX96_) - if tokenBPath < tokenAPath { - tokenAPath, tokenBPath = tokenBPath, tokenAPath - tick := -(TickMathGetTickAtSqrtRatio(sqrtPriceX96)) - sqrtPriceX96 = TickMathGetSqrtRatioAtTick(tick) + if token1Path < token0Path { + token0Path, token1Path = token1Path, token0Path + tick := -(common.TickMathGetTickAtSqrtRatio(sqrtPriceX96)) + sqrtPriceX96 = common.TickMathGetSqrtRatioAtTick(tick) } // check tickSpacing for fee tickSpacing := feeAmountTickSpacing[fee] - require(tickSpacing > 0, ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || tickSpacing(%d) > 0", tickSpacing)) + requirePositive(bigint(tickSpacing), ufmt.Sprintf("[POOL] pool_manager.gno__CreatePool() || expected tickSpacing(%d) > 0", tickSpacing)) - // calculate poolKey - poolKey := GetPoolKey(tokenAPath, tokenBPath, fee) + // calculate poolPath + poolPath := GetPoolPath(token0Path, token1Path, fee) // check whether the pool already exist - pool, exist := pools[poolKey] - require(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || pool(%s) already exist", poolKey)) + pool, exist := pools[poolPath] + requireExist(!exist, ufmt.Sprintf("[POOl] pool_manager.gno__CreatePool() || expected poolPath(%s) not to exist", poolPath)) if !exist { - // 500 GNS as creation fee - // recipent is same address that receives protocol fee - // r3v4_xxx: change address when publish - gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(std.Address("g1vaekzh6lta047h6lta047h6lta047h6lutmjdk")), 500) + // recipient is same address that receives protocol fee + // r3v4_xxx: change admin address when publish + gns.TransferFrom(a2u(std.GetOrigCaller()), a2u(consts.GNOSWAP_ADMIN), consts.POOL_CREATION_FEE) - pool = newPool(tokenAPath, tokenBPath, fee, tickSpacing, sqrtPriceX96) - pools[poolKey] = pool + pool = newPool(token0Path, token1Path, fee, tickSpacing, sqrtPriceX96) + pools[poolPath] = pool } return pool } -func GetPool(token0, token1 string, fee uint16) *Pool { - poolKey := GetPoolKey(token0, token1, fee) - pool, exist := pools[poolKey] - require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || pool(%s) not found", poolKey)) +func GetPool(token0Path, token1Path string, fee uint16) *Pool { + poolPath := GetPoolPath(token0Path, token1Path, fee) + pool, exist := pools[poolPath] + requireExist(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPool() || expected poolPath(%s) to exist", poolPath)) return pool } -func GetPoolFromPoolKey(poolKey string) *Pool { - pool, exist := pools[poolKey] - require(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolKey() || pool(%s) not found", poolKey)) +func GetPoolFromPoolPath(poolPath string) *Pool { + pool, exist := pools[poolPath] + requireExist(exist, ufmt.Sprintf("[POOL] pool_manager.gno__GetPoolFromPoolPath() || expected poolPath(%s) to exist", poolPath)) return pool } -func GetPoolKey(token0Path, token1Path string, fee uint16) string { +func GetPoolPath(token0Path, token1Path string, fee uint16) string { if token0Path < token1Path { return token0Path + ":" + token1Path + ":" + strconv.Itoa(int(fee)) } else { @@ -97,7 +99,7 @@ func AddAdmin(addr std.Address) { if isAdmin(caller) { admins = append(admins, addr) } else { - panic("[POOL] pool_manager.gno__AddAdmin() || caller is not admin") // r3v4_xx: detailed error message ?? + panic(ufmt.Sprintf("[POOL] pool_manager.gno__AddAdmin() || caller(%s) is not admin", caller.String())) } } @@ -115,7 +117,7 @@ func RemoveAdmin(addr std.Address) { } } } else { - panic("[POOL] pool_manager.gno__RemoveAdmin() || caller is not admin") // r3v4_xx: detailed error message ?? + panic(ufmt.Sprintf("[POOL] pool_manager.gno__RemoveAdmin() || caller(%s) is not admin", caller.String())) } } @@ -127,7 +129,7 @@ func newPool( sqrtPriceX96 *u256.Uint, ) *Pool { maxLiquidityPerTick := tickTickSpacingToMaxLiquidityPerTick(tickSpacing) - tick := TickMathGetTickAtSqrtRatio(sqrtPriceX96) + tick := common.TickMathGetTickAtSqrtRatio(sqrtPriceX96) slot0 := Slot0{ sqrtPriceX96: sqrtPriceX96, diff --git a/pool/swap_math.gno b/pool/swap_math.gno index 52347134..d418af00 100644 --- a/pool/swap_math.gno +++ b/pool/swap_math.gno @@ -27,7 +27,7 @@ func swapMathComputeSwapStep( amountIn = sqrtPriceMathGetAmount1DeltaHelper(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity) } - if amountRemainingLessFee >= amountIn { + if amountRemainingLessFee.Gte(amountIn) { sqrtRatioNextX96 = sqrtRatioTargetX96 } else { sqrtRatioNextX96 = sqrtPriceMathGetNextSqrtPriceFromInput( @@ -89,10 +89,7 @@ func swapMathComputeSwapStep( } if exactIn && !sqrtRatioNextX96.Eq(sqrtRatioTargetX96) { - ///XXXXXXXXXXXXXXXXXXXXXXXX - // I was not able to understand this line, commented out for now - // resolve this before merge!!! - // feeAmount = new(u256.Uint).Sub(amountRemaining, amountIn) + feeAmount = new(u256.Uint).Sub(amountRemaining.Uint(), amountIn) } else { feeAmount = new(u256.Uint).Mul(amountIn, u256.NewUint(uint64(feePips))) feeAmount.Div(feeAmount, u256.NewUint(uint64(1000000-feePips))) diff --git a/pool/tick.gno b/pool/tick.gno index 21e65eb8..94c6fc2d 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -29,7 +29,8 @@ func (pool *Pool) tickGetFeeGrowthInside( lower := pool.ticks[tickLower] upper := pool.ticks[tickUpper] - var feeGrowthBelow0X128, feeGrowthBelow1X128 *u256.Uint + feeGrowthBelow0X128 := u256.Zero() + feeGrowthBelow1X128 := u256.Zero() if tickCurrent >= tickLower { feeGrowthBelow0X128 = lower.feeGrowthOutside0X128.NilToZero() feeGrowthBelow1X128 = lower.feeGrowthOutside1X128.NilToZero() @@ -38,7 +39,9 @@ func (pool *Pool) tickGetFeeGrowthInside( feeGrowthBelow1X128.Sub(feeGrowthGlobal1X128, lower.feeGrowthOutside1X128.NilToZero()) } - var feeGrowthAbove0X128, feeGrowthAbove1X128 *u256.Uint + feeGrowthAbove0X128 := u256.Zero() + feeGrowthAbove1X128 := u256.Zero() + if tickCurrent < tickUpper { feeGrowthAbove0X128 = upper.feeGrowthOutside0X128.NilToZero() feeGrowthAbove1X128 = upper.feeGrowthOutside1X128.NilToZero() @@ -113,8 +116,12 @@ func (pool *Pool) tickCross( feeGrowthGlobal1X128 *u256.Uint, ) *u256.Int { info := pool.ticks[tick] - info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128 - info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128 + + info.feeGrowthOutside0X128 = info.feeGrowthOutside0X128.NilToZero() + info.feeGrowthOutside1X128 = info.feeGrowthOutside1X128.NilToZero() + + info.feeGrowthOutside0X128 = new(u256.Uint).Sub(feeGrowthGlobal0X128, info.feeGrowthOutside0X128) + info.feeGrowthOutside1X128 = new(u256.Uint).Sub(feeGrowthGlobal1X128, info.feeGrowthOutside1X128) pool.ticks[tick] = info diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index 0371f245..3b28f78b 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -50,8 +50,9 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( wordPos, bitPos := tickBitmapPosition(compress) mask := new(u256.Uint).Lsh(u256.One(), uint(bitPos)) - mask.Sub(mask, u256.One()) + mask.UnsafeSub(mask, u256.One()) mask.Add(mask, (new(u256.Uint).Lsh(u256.One(), uint(bitPos)))) + // XXXXXXXX overflow check Lsh! // requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord__mask(%d) >= 0__#1", mask)) @@ -60,7 +61,8 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( initialized := !masked.IsZero() if initialized { - next := (compress - int32(bitPos-bitMathMostSignificantBit(mask))) * tickSpacing + next := (compress - int32(bitPos-bitMathMostSignificantBit(masked))) * tickSpacing + return next, initialized } @@ -71,7 +73,7 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( wordPos, bitPos := tickBitmapPosition(compress + 1) _mask := new(u256.Uint).Lsh(u256.One(), uint(bitPos)) - _mask.Sub(_mask, u256.One()) + _mask.UnsafeSub(_mask, u256.One()) mask := bigintBitwiseNotForUint256BitmapIndexing(_mask) // XXXXXXXX overflow check Lsh! diff --git a/u256/i256.gno b/u256/i256.gno index 5b560c56..09380c1a 100644 --- a/u256/i256.gno +++ b/u256/i256.gno @@ -39,7 +39,7 @@ func IntFromBigint(v bigint) *Int { func (x *Int) Bigint() bigint { if x.Signum() < 0 { - panic("I256 Bigint negative") + return -x.Neg().v.Bigint() } return x.v.Bigint() @@ -67,7 +67,7 @@ func (x *Int) Add(y *Int, z *Int) *Int { } func (x *Int) Sub(y *Int, z *Int) *Int { - x.v.unsafeSub(&y.v, &z.v) + x.v.UnsafeSub(&y.v, &z.v) ys := y.Signum() zs := z.Signum() @@ -190,6 +190,10 @@ func (x *Int) Abs() *Uint { } func (x *Int) Neg() *Int { + if x.Signum() == 0 { + return x + } + // twos complement x.v.Not(&x.v) x.v.Add(&x.v, &Uint{arr: [4]uint64{1, 0, 0, 0}}) @@ -203,10 +207,6 @@ func (x *Int) Dec() string { return x.Abs().Dec() } -func (x *Int) Bigint() bigint { - panic("I256 Bigint not implemented") -} - func (x *Int) Uint() *Uint { if x.Signum() < 0 { // panic("I256 Uint negative") @@ -227,3 +227,22 @@ func (z *Int) NilToZero() *Int { return z } + +// Clone creates a new Int identical to z +func (z *Int) Clone() *Int { + var x Int + + x.Sub(z, NewInt(0)) + return &x +} + +// // Clone creates a new Int identical to z +// func (z *Uint) Clone() *Uint { +// var x Uint +// x.arr[0] = z.arr[0] +// x.arr[1] = z.arr[1] +// x.arr[2] = z.arr[2] +// x.arr[3] = z.arr[3] + +// return &x +// } diff --git a/u256/u256.gno b/u256/u256.gno index 91c407ca..71a67258 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -230,8 +230,10 @@ func (z *Uint) Sub(x, y *Uint) *Uint { z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) z.arr[2], carry = Sub64(x.arr[2], y.arr[2], carry) z.arr[3], _ = Sub64(x.arr[3], y.arr[3], carry) + // Different from the original implementation! // We panic on underflow + // r3v4 -> mconcat : why do we panic? if carry != 0 { panic("U256 Sub underflow") } @@ -239,7 +241,7 @@ func (z *Uint) Sub(x, y *Uint) *Uint { } // Sub sets z to the difference x-y -func (z *Uint) unsafeSub(x, y *Uint) *Uint { +func (z *Uint) UnsafeSub(x, y *Uint) *Uint { var carry uint64 z.arr[0], carry = Sub64(x.arr[0], y.arr[0], 0) z.arr[1], carry = Sub64(x.arr[1], y.arr[1], carry) From 4e7e4ca3742803e5133337c3af3d09ec4fe1d8ba Mon Sep 17 00:00:00 2001 From: n3wbie Date: Wed, 28 Feb 2024 20:34:19 +0900 Subject: [PATCH 23/23] wip: need to handle FromBigint, IntFromBIgint --- pool/_RPC_dry.gno | 51 ++++--- ...a => _TEST_pool_dryswap_and_swap_test.gno} | 67 ++++----- ...noa => _TEST_pool_multi_token_test.gno_OK} | 138 +++++++----------- ...gnoa => _TEST_pool_native_swap_test.gn_OK} | 0 ...t.gno => _TEST_pool_single_lp_test.gno_OK} | 0 ...ST_rpc_test.gnoa => _TEST_rpc_test.gno_OK} | 0 pool/pool.gno | 57 +++++++- pool/position.gno | 12 +- pool/tick.gno | 38 ++++- pool/tick_bitmap.gno | 11 +- u256/i256.gno | 5 + u256/u256.gno | 6 +- 12 files changed, 225 insertions(+), 160 deletions(-) rename pool/{_TEST_pool_dryswap_and_swap_test.gnoa => _TEST_pool_dryswap_and_swap_test.gno} (69%) rename pool/{_TEST_pool_multi_token_test.gnoa => _TEST_pool_multi_token_test.gno_OK} (69%) rename pool/{_TEST_pool_native_swap_test.gnoa => _TEST_pool_native_swap_test.gn_OK} (100%) rename pool/{_TEST_pool_single_lp_test.gno => _TEST_pool_single_lp_test.gno_OK} (100%) rename pool/{_TEST_rpc_test.gnoa => _TEST_rpc_test.gno_OK} (100%) diff --git a/pool/_RPC_dry.gno b/pool/_RPC_dry.gno index c09075e1..7f6cbc66 100644 --- a/pool/_RPC_dry.gno +++ b/pool/_RPC_dry.gno @@ -5,6 +5,8 @@ import ( "gno.land/p/demo/common" "gno.land/p/demo/u256" + + "gno.land/r/demo/consts" ) func DrySwap( @@ -24,15 +26,15 @@ func DrySwap( pool := GetPool(token0Path, token1Path, pFee) slot0Start := pool.slot0 - amountSpecified := u256.FromBigint(amountSpecified_) - sqrtPriceLimitX96 := u256.FromBigint(sqrtPriceLimitX96_) + amountSpecified := u256.IntFromBigint(amountSpecified_) + sqrtPriceLimitX96 := u256.FromDecimal(string(sqrtPriceLimitX96_)) if zeroForOne { - if !(sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Gt(u256.FromBigint(MIN_SQRT_RATIO))) { + if !(sqrtPriceLimitX96.Lt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Gt(u256.FromBigint(consts.MIN_SQRT_RATIO))) { return 0, 0, false } } else { - if !(sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Lt(u256.FromBigint(MAX_SQRT_RATIO))) { + if !(sqrtPriceLimitX96.Gt(slot0Start.sqrtPriceX96) && sqrtPriceLimitX96.Lt(u256.FromBigint(consts.MAX_SQRT_RATIO))) { return 0, 0, false } } @@ -52,33 +54,35 @@ func DrySwap( } } - exactInput := amountSpecified.Gt(u256.Zero()) + exactInput := amountSpecified.Gt(u256.Zero().Int()) var state SwapState if zeroForOne { state = SwapState{ - amountSpecifiedRemaining: amountSpecified.Int(), + amountSpecifiedRemaining: amountSpecified, amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: pool.feeGrowthGlobal0X128, + feeGrowthGlobalX128: pool.feeGrowthGlobal0X128.Clone(), protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } else { state = SwapState{ - amountSpecifiedRemaining: amountSpecified.Int(), + amountSpecifiedRemaining: amountSpecified, amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: pool.feeGrowthGlobal1X128, + feeGrowthGlobalX128: pool.feeGrowthGlobal1X128.Clone(), protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit + origAmountSpecified := amountSpecified.Clone() for !state.amountSpecifiedRemaining.IsZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { + var step StepComputations step.sqrtPriceStartX96 = state.sqrtPriceX96 @@ -97,11 +101,11 @@ func DrySwap( } // get the price for the next tick - step.sqrtPriceNextX96 = TickMathGetSqrtRatioAtTick(step.tickNext) - var sqrtRatioTargetX96 *u256.Uint + step.sqrtPriceNextX96 = common.TickMathGetSqrtRatioAtTick(step.tickNext) + isLower := step.sqrtPriceNextX96.Lt(sqrtPriceLimitX96) + isHigher := step.sqrtPriceNextX96.Gt(sqrtPriceLimitX96) - isLower := step.sqrtPriceNextX96 < sqrtPriceLimitX96 - isHigher := step.sqrtPriceNextX96 > sqrtPriceLimitX96 + var sqrtRatioTargetX96 *u256.Uint if (zeroForOne && isLower) || (!zeroForOne && isHigher) { sqrtRatioTargetX96 = sqrtPriceLimitX96 } else { @@ -127,15 +131,16 @@ func DrySwap( // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee if cache.feeProtocol > 0 { delta := new(u256.Uint).Div(step.feeAmount, u256.NewUint(uint64(cache.feeProtocol))) - step.feeAmount -= delta - state.protocolFee += delta + step.feeAmount.Sub(step.feeAmount, delta) + state.protocolFee.Add(state.protocolFee, delta) } // update global fee tracker if state.liquidity.Gt(u256.Zero()) { - update := new(u256.Uint).Mul(step.feeAmount, u256.FromBigint(consts.Q128)) + // save fee + update := new(u256.Uint).Mul(step.feeAmount, u256.FromDecimal(string(consts.Q128))) update.Div(update, state.liquidity) - state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128, update) + state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128.Clone(), update) } // shift tick if we reached the next price @@ -160,7 +165,7 @@ func DrySwap( // if we're moving leftward, we interpret liquidityNet as the opposite sign if zeroForOne { - liquidityNet = -liquidityNet + liquidityNet = liquidityNet.Neg() } state.liquidity = liquidityMathAddDelta(state.liquidity, liquidityNet) @@ -180,13 +185,17 @@ func DrySwap( var amount0, amount1 *u256.Int if zeroForOne == exactInput { - amount0 = new(u256.Int).Sub(amountSpecified.Int(), state.amountSpecifiedRemaining) + amount0 = new(u256.Int).Sub(origAmountSpecified, state.amountSpecifiedRemaining) amount1 = state.amountCalculated } else { amount0 = state.amountCalculated - amount1 = new(u256.Int).Sub(amountSpecified.Int(), state.amountSpecifiedRemaining) + amount1 = new(u256.Int).Sub(origAmountSpecified, state.amountSpecifiedRemaining) } + // backUP + resAmount0 := amount0.Clone() + resAmount1 := amount1.Clone() + if zeroForOne { if !(pool.balances.token1.Gte(amount1.Abs())) { // NOT ENOUGH BALANCE for output token1 @@ -205,5 +214,5 @@ func DrySwap( return 0, 0, false } - return amount0.Bigint(), amount1.Bigint(), true + return resAmount0.Bigint(), resAmount1.Bigint(), true } diff --git a/pool/_TEST_pool_dryswap_and_swap_test.gnoa b/pool/_TEST_pool_dryswap_and_swap_test.gno similarity index 69% rename from pool/_TEST_pool_dryswap_and_swap_test.gnoa rename to pool/_TEST_pool_dryswap_and_swap_test.gno index 61eaebfb..628c6e88 100644 --- a/pool/_TEST_pool_dryswap_and_swap_test.gnoa +++ b/pool/_TEST_pool_dryswap_and_swap_test.gno @@ -49,6 +49,7 @@ func TestDrySwap_ZeroForOneTrue_AmountSpecified_Positive_16000(t *testing.T) { std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) Mint(fooPath, barPath, fee500, consts.POSITION_ADDR, -tickUpper, -tickLower, 10) + DrySwap(fooPath, barPath, fee500, "_", true, 16000, consts.MIN_PRICE) _, _, ok = DrySwap(fooPath, barPath, fee500, "_", true, 16000, consts.MIN_PRICE) shouldEQ(t, ok, false) @@ -93,36 +94,36 @@ func TestDrySwap_ZeroForOneTrue_AmountSpecified_Negative_16000(t *testing.T) { shouldEQ(t, poolOut, bigint(-16000)) } -func TestDrySwap_ZeroForOneFalse_AmountSpecified_Positive_16000(t *testing.T) { - // zeroForOne false - // amountSpecified 16000 - - poolOut, poolIn, _ := DrySwap( - fooPath, // fooPath - barPath, // barPath - fee500, // fee500 - "_", // recipient - false, // zeroForOne - 16000, // amountSpecified - consts.MAX_PRICE, // sqrtPriceLimitX96 - ) - - shouldEQ(t, poolOut, bigint(-43468)) - shouldEQ(t, poolIn, bigint(16000)) -} - -func TestDrySwap_ZeroForOneFalse_AmountSpecified_Negative_16000(t *testing.T) { - // zeroForOne false - // amountSpecified -16000 - poolOut, poolIn, _ := DrySwap( - fooPath, // fooPath - barPath, // barPath - fee500, // fee500 - "_", // recipient - false, // zeroForOne - -16000, // amountSpecified - consts.MAX_PRICE, // sqrtPriceLimitX96 - ) - shouldEQ(t, poolOut, bigint(-16000)) - shouldEQ(t, poolIn, bigint(5888)) -} +// func TestDrySwap_ZeroForOneFalse_AmountSpecified_Positive_16000(t *testing.T) { +// // zeroForOne false +// // amountSpecified 16000 + +// poolOut, poolIn, _ := DrySwap( +// fooPath, // fooPath +// barPath, // barPath +// fee500, // fee500 +// "_", // recipient +// false, // zeroForOne +// 16000, // amountSpecified +// consts.MAX_PRICE, // sqrtPriceLimitX96 +// ) + +// shouldEQ(t, poolOut, bigint(-43468)) +// shouldEQ(t, poolIn, bigint(16000)) +// } + +// func TestDrySwap_ZeroForOneFalse_AmountSpecified_Negative_16000(t *testing.T) { +// // zeroForOne false +// // amountSpecified -16000 +// poolOut, poolIn, _ := DrySwap( +// fooPath, // fooPath +// barPath, // barPath +// fee500, // fee500 +// "_", // recipient +// false, // zeroForOne +// -16000, // amountSpecified +// consts.MAX_PRICE, // sqrtPriceLimitX96 +// ) +// shouldEQ(t, poolOut, bigint(-16000)) +// shouldEQ(t, poolIn, bigint(5888)) +// } diff --git a/pool/_TEST_pool_multi_token_test.gnoa b/pool/_TEST_pool_multi_token_test.gno_OK similarity index 69% rename from pool/_TEST_pool_multi_token_test.gnoa rename to pool/_TEST_pool_multi_token_test.gno_OK index dd0d030d..4c9f63f7 100644 --- a/pool/_TEST_pool_multi_token_test.gnoa +++ b/pool/_TEST_pool_multi_token_test.gno_OK @@ -5,53 +5,43 @@ import ( "testing" "gno.land/r/demo/consts" - - "gno.land/r/demo/bar" - "gno.land/r/demo/baz" - "gno.land/r/demo/foo" - - "gno.land/r/demo/gns" ) var ( test_tickLower = int32(9000) test_tickUpper = int32(11000) test_liquidityExpect = bigint(100_000_000) + + test_tickLower2 = int32(50000) + test_tickUpper2 = int32(100000) ) // 1. Init Pool func TestInit(t *testing.T) { - std.TestSetPrevAddr(gsa) + std.TestSetOrigCaller(test1) InitManual() } // 2. Create Foo:Bar Pool func TestCreateFooBarPool(t *testing.T) { - std.TestSetPrevAddr(test1) - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - + std.TestSetOrigCaller(test1) CreatePool(fooPath, barPath, fee500, 130621891405341611593710811006) shouldEQ(t, len(pools), 1) } // 3. Create Bar:Baz Pool func TestCreateBarBazPool(t *testing.T) { - std.TestSetPrevAddr(test1) - gns.Approve(a2u(consts.POOL_ADDR), consts.POOL_CREATION_FEE) - + std.TestSetOrigCaller(test1) CreatePool(barPath, bazPath, fee500, 130621891405341611593710811006) shouldEQ(t, len(pools), 2) } // 4. Mint Foo:Bar Liquidity by test1 func TestMintFooBarLiquidity(t *testing.T) { - std.TestSetPrevAddr(test1) - foo.Approve(a2u(consts.POOL_ADDR), 2958014) - bar.Approve(a2u(consts.POOL_ADDR), 8040315) - std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) - amount0, amount1 := Mint( + + Mint( fooPath, barPath, fee500, @@ -60,19 +50,14 @@ func TestMintFooBarLiquidity(t *testing.T) { -test_tickLower, test_liquidityExpect, ) - shouldEQ(t, amount0, bigint(8040315)) - shouldEQ(t, amount1, bigint(2958014)) } // 5. Mint Bar:Baz Liquidity by test1 func TestMintBarBazLiquidity(t *testing.T) { - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), 2958014) - baz.Approve(a2u(consts.POOL_ADDR), 8040315) - std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) - amount0, amount1 := Mint( + + Mint( barPath, bazPath, fee500, @@ -81,26 +66,21 @@ func TestMintBarBazLiquidity(t *testing.T) { test_tickUpper, test_liquidityExpect, ) - shouldEQ(t, amount0, bigint(2958014)) - shouldEQ(t, amount1, bigint(8040315)) } // 6. Swap Foo:Bar Foo > Bar by test1 func TestSwapFooBarFooToBar(t *testing.T) { - oldTest1Bar := balanceOfByRegisterCall(barPath, test1) - oldTest1Foo := balanceOfByRegisterCall(fooPath, test1) - - oldPoolBar := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) - oldPoolFoo := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) - - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), 16000) - std.TestSetPrevRealm(consts.ROUTER_PATH) std.TestSetOrigCaller(test1) + + oldtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + oldPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) + oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) + poolIn, poolOut := Swap( - barPath, fooPath, + barPath, fee500, test1, true, @@ -108,34 +88,33 @@ func TestSwapFooBarFooToBar(t *testing.T) { consts.MIN_PRICE, std.GetOrigCaller(), ) + pool := GetPool(fooPath, barPath, fee500) + shouldEQ(t, poolIn, bigint(16000)) shouldEQ(t, poolOut, bigint(-5882)) - newTest1Bar := balanceOfByRegisterCall(barPath, test1) - newTest1Foo := balanceOfByRegisterCall(fooPath, test1) - - newPoolBar := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) - newPoolFoo := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) + newtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) + newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) - shouldEQ(t, oldTest1Bar-newTest1Bar, 16000) - shouldEQ(t, newTest1Foo-oldTest1Foo, 5882) + shouldEQ(t, oldtest1Token0Balance-newtest1Token0Balance, 16000) + shouldEQ(t, newtest1Token1Balance-oldtest1Token1Balance, 5882) + shouldEQ(t, newPoolToken0Balance-oldPoolToken0Balance, 16000) + shouldEQ(t, oldPoolToken1Balance-newPoolToken1Balance, 5882) - shouldEQ(t, newPoolBar-oldPoolBar, 16000) - shouldEQ(t, oldPoolFoo-newPoolFoo, 5882) } // 7. Swap Bar:Baz Bar > Baz by test1 func TestSwapBarBazBarToBaz(t *testing.T) { - oldTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + std.TestSetPrevRealm(consts.ROUTER_PATH) + std.TestSetOrigCaller(test1) + + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) oldtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) oldPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) - std.TestSetPrevAddr(test1) - bar.Approve(a2u(consts.POOL_ADDR), 16000) - - std.TestSetPrevRealm(consts.ROUTER_PATH) - std.TestSetOrigCaller(test1) poolIn, poolOut := Swap( barPath, bazPath, @@ -149,12 +128,12 @@ func TestSwapBarBazBarToBaz(t *testing.T) { shouldEQ(t, poolIn, bigint(16000)) shouldEQ(t, poolOut, bigint(-43457)) - newTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) newtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) newPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) - shouldEQ(t, oldTest1Token0Balance-newTest1Token0Balance, 16000) + shouldEQ(t, oldtest1Token0Balance-newtest1Token0Balance, 16000) shouldEQ(t, newtest1BazBalance-oldtest1BazBalance, 43457) shouldEQ(t, newPoolToken0Balance-oldPoolToken0Balance, 16000) shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, 43457) @@ -162,11 +141,12 @@ func TestSwapBarBazBarToBaz(t *testing.T) { // 8. Collect Foo:Bar Fees by test1 func TestCollectFooBarFees(t *testing.T) { + std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) - oldTest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) - oldTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + oldtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) oldPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) @@ -187,13 +167,13 @@ func TestCollectFooBarFees(t *testing.T) { shouldNEQ(t, c0, bigint(0)) // swap was foo > bar, so only foo has fees shouldEQ(t, c1, bigint(0)) // swap was foo > bar, so bar has no fees - newTest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) - newTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) newPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) - shouldEQ(t, newTest1Token1Balance-oldTest1Token1Balance, uint64(c1)) - shouldEQ(t, newTest1Token0Balance-oldTest1Token0Balance, uint64(c0)) + shouldEQ(t, newtest1Token1Balance-oldtest1Token1Balance, uint64(c1)) + shouldEQ(t, newtest1Token0Balance-oldtest1Token0Balance, uint64(c0)) shouldEQ(t, oldPoolToken1Balance-newPoolToken1Balance, uint64(c1)) shouldEQ(t, oldPoolToken0Balance-newPoolToken0Balance, uint64(c0)) } @@ -203,7 +183,7 @@ func TestCollectBarBazFees(t *testing.T) { std.TestSetPrevRealm(consts.POSITION_PATH) std.TestSetOrigCaller(test1) - oldTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) oldtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) oldPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) @@ -225,12 +205,12 @@ func TestCollectBarBazFees(t *testing.T) { shouldNEQ(t, c0, bigint(0)) // swap was foo > bar, so only foo has fees shouldEQ(t, c1, bigint(0)) // swap was foo > bar, so bar has no fees - newTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) newtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) newPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) - shouldEQ(t, newTest1Token0Balance-oldTest1Token0Balance, uint64(c0)) + shouldEQ(t, newtest1Token0Balance-oldtest1Token0Balance, uint64(c0)) shouldEQ(t, newtest1BazBalance-oldtest1BazBalance, uint64(c1)) shouldEQ(t, oldPoolToken0Balance-newPoolToken0Balance, uint64(c0)) shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, uint64(c1)) @@ -252,13 +232,8 @@ func TestBurnFooBarLiquidity(t *testing.T) { -test_tickLower, test_liquidityExpect, ) - - shouldNEQ(t, b0, bigint(0)) - shouldNEQ(t, b1, bigint(0)) - - poolNewLiquidity := pool.PoolGetLiquidity() - - shouldEQ(t, poolOldLiquidity-poolNewLiquidity, test_liquidityExpect) + shouldEQ(t, b0, bigint(8056307)) + shouldEQ(t, b1, bigint(2952131)) } // 11. Burn Bar:Baz Liquidity by test1 @@ -277,13 +252,8 @@ func TestBurnBarBazLiquidity(t *testing.T) { test_tickUpper, test_liquidityExpect, ) - shouldNEQ(t, b0, bigint(0)) shouldNEQ(t, b1, bigint(0)) - - poolNewLiquidity := pool.PoolGetLiquidity() - - shouldEQ(t, poolOldLiquidity-poolNewLiquidity, test_liquidityExpect) } // 12. Collect Foo:Bar burned Liquidity by test1 @@ -291,8 +261,8 @@ func TestCollectFooBarLiquidity(t *testing.T) { std.TestSetOrigCaller(test1) std.TestSetPrevRealm(consts.POSITION_PATH) - oldTest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) - oldTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + oldtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) oldPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) @@ -310,13 +280,13 @@ func TestCollectFooBarLiquidity(t *testing.T) { shouldNEQ(t, c0, bigint(0)) shouldNEQ(t, c1, bigint(0)) - newTest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) - newTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newtest1Token1Balance := balanceOfByRegisterCall(fooPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) newPoolToken1Balance := balanceOfByRegisterCall(fooPath, consts.POOL_ADDR) newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) - shouldEQ(t, newTest1Token1Balance-oldTest1Token1Balance, uint64(c0)) - shouldEQ(t, newTest1Token0Balance-oldTest1Token0Balance, uint64(c1)) + shouldEQ(t, newtest1Token1Balance-oldtest1Token1Balance, uint64(c0)) + shouldEQ(t, newtest1Token0Balance-oldtest1Token0Balance, uint64(c1)) shouldEQ(t, oldPoolToken1Balance-newPoolToken1Balance, uint64(c0)) shouldEQ(t, oldPoolToken0Balance-newPoolToken0Balance, uint64(c1)) } @@ -326,7 +296,7 @@ func TestCollectBarBazLiquidity(t *testing.T) { std.TestSetOrigCaller(test1) std.TestSetPrevRealm(consts.POSITION_PATH) - oldTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + oldtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) oldtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) oldPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) oldPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) @@ -345,12 +315,12 @@ func TestCollectBarBazLiquidity(t *testing.T) { shouldNEQ(t, c0, bigint(0)) shouldNEQ(t, c1, bigint(0)) - newTest1Token0Balance := balanceOfByRegisterCall(barPath, test1) + newtest1Token0Balance := balanceOfByRegisterCall(barPath, test1) newtest1BazBalance := balanceOfByRegisterCall(bazPath, test1) newPoolToken0Balance := balanceOfByRegisterCall(barPath, consts.POOL_ADDR) newPoolBazBalance := balanceOfByRegisterCall(bazPath, consts.POOL_ADDR) - shouldEQ(t, newTest1Token0Balance-oldTest1Token0Balance, uint64(c0)) + shouldEQ(t, newtest1Token0Balance-oldtest1Token0Balance, uint64(c0)) shouldEQ(t, newtest1BazBalance-oldtest1BazBalance, uint64(c1)) shouldEQ(t, oldPoolToken0Balance-newPoolToken0Balance, uint64(c0)) shouldEQ(t, oldPoolBazBalance-newPoolBazBalance, uint64(c1)) diff --git a/pool/_TEST_pool_native_swap_test.gnoa b/pool/_TEST_pool_native_swap_test.gn_OK similarity index 100% rename from pool/_TEST_pool_native_swap_test.gnoa rename to pool/_TEST_pool_native_swap_test.gn_OK diff --git a/pool/_TEST_pool_single_lp_test.gno b/pool/_TEST_pool_single_lp_test.gno_OK similarity index 100% rename from pool/_TEST_pool_single_lp_test.gno rename to pool/_TEST_pool_single_lp_test.gno_OK diff --git a/pool/_TEST_rpc_test.gnoa b/pool/_TEST_rpc_test.gno_OK similarity index 100% rename from pool/_TEST_rpc_test.gnoa rename to pool/_TEST_rpc_test.gno_OK diff --git a/pool/pool.gno b/pool/pool.gno index 00917ee8..00d43a85 100644 --- a/pool/pool.gno +++ b/pool/pool.gno @@ -164,6 +164,11 @@ func Collect( requireExist(exist, ufmt.Sprintf("[POOL] pool.gno__Collect() || position(%s) does not exist", positionKey)) // Smallest of three: amount0Requested, position.tokensOwed0, pool.balances.token0 + // println("amount0Requested:", amount0Requested.Dec()) + // println("position.tokensOwed0:", position.tokensOwed0.Dec()) + // println("pool.balances.token0:", pool.balances.token0.Dec()) + // println() + amount0 := amount0Requested.Min(position.tokensOwed0) amount0 = amount0.Min(pool.balances.token0) amount1 := amount1Requested.Min(position.tokensOwed1) @@ -265,7 +270,7 @@ func Swap( amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: pool.feeGrowthGlobal0X128, + feeGrowthGlobalX128: pool.feeGrowthGlobal0X128.Clone(), protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } @@ -275,12 +280,13 @@ func Swap( amountCalculated: u256.Zero().Int(), sqrtPriceX96: slot0Start.sqrtPriceX96, tick: slot0Start.tick, - feeGrowthGlobalX128: pool.feeGrowthGlobal1X128, + feeGrowthGlobalX128: pool.feeGrowthGlobal1X128.Clone(), protocolFee: u256.Zero(), liquidity: cache.liquidityStart, } } + // continue swapping as long as we haven't used the entire input/output and haven't reached the price limit origAmountSpecified := amountSpecified.Clone() for !state.amountSpecifiedRemaining.IsZero() && !state.sqrtPriceX96.Eq(sqrtPriceLimitX96) { var step StepComputations @@ -293,6 +299,7 @@ func Swap( zeroForOne, ) + // ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds if step.tickNext < consts.MIN_TICK { step.tickNext = consts.MIN_TICK } else if step.tickNext > consts.MAX_TICK { @@ -303,6 +310,7 @@ func Swap( isLower := step.sqrtPriceNextX96.Lt(sqrtPriceLimitX96) isHigher := step.sqrtPriceNextX96.Gt(sqrtPriceLimitX96) + // get the price for the next tick var sqrtRatioTargetX96 *u256.Uint if (zeroForOne && isLower) || (!zeroForOne && isHigher) { sqrtRatioTargetX96 = sqrtPriceLimitX96 @@ -326,23 +334,28 @@ func Swap( state.amountCalculated.Add(state.amountCalculated, new(u256.Uint).Add(step.amountIn, step.feeAmount).Int()) } + // if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee if cache.feeProtocol > 0 { delta := new(u256.Uint).Div(step.feeAmount, u256.NewUint(uint64(cache.feeProtocol))) step.feeAmount.Sub(step.feeAmount, delta) state.protocolFee.Add(state.protocolFee, delta) } + // update global fee tracker if state.liquidity.Gt(u256.Zero()) { // save fee - update := new(u256.Uint).Mul(step.feeAmount, u256.FromBigint(consts.Q128)) + update := new(u256.Uint).Mul(step.feeAmount, u256.FromDecimal(string(consts.Q128))) update.Div(update, state.liquidity) - state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128, update) + state.feeGrowthGlobalX128.Add(state.feeGrowthGlobalX128.Clone(), update) } + // shift tick if we reached the next price if state.sqrtPriceX96 == step.sqrtPriceNextX96 { + // if the tick is initialized, run the tick transition if step.initialized { var fee0, fee1 *u256.Uint + // check for the placeholder value, which we replace with the actual value the first time the swap crosses an initialized tick if zeroForOne { fee0 = state.feeGrowthGlobalX128 fee1 = pool.feeGrowthGlobal1X128 @@ -357,8 +370,9 @@ func Swap( fee1, ) + // if we're moving leftward, we interpret liquidityNet as the opposite sign if zeroForOne { - liquidityNet = -liquidityNet + liquidityNet = liquidityNet.Neg() } state.liquidity = liquidityMathAddDelta(state.liquidity, liquidityNet) @@ -370,10 +384,12 @@ func Swap( state.tick = step.tickNext } } else if state.sqrtPriceX96 != step.sqrtPriceStartX96 { + // recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved state.tick = common.TickMathGetTickAtSqrtRatio(state.sqrtPriceX96) } } // END LOOP + println("END") pool.slot0.sqrtPriceX96 = state.sqrtPriceX96 if state.tick != slot0Start.tick { @@ -556,6 +572,9 @@ func (pool *Pool) modifyPosition(params ModifyPositionParams) (PositionInfo, *u2 params.liquidityDelta, pool.slot0.tick, ) + // println("MODI_position.tokensOwed0", position.tokensOwed0.Dec()) + // println("MODI_position.tokensOwed1", position.tokensOwed1.Dec()) + // println() amount0 := u256.Zero().Int() amount1 := u256.Zero().Int() @@ -603,6 +622,8 @@ func (pool *Pool) updatePosition( _liquidityDelta *u256.Int, tick int32, ) PositionInfo { + // println(">> liquidityDelta", _liquidityDelta.Dec()) + // println(">> tick:", tick) liquidityDelta := _liquidityDelta.Clone() var _feeGrowthGlobal0X128 *u256.Uint = pool.feeGrowthGlobal0X128 @@ -611,6 +632,12 @@ func (pool *Pool) updatePosition( var flippedLower, flippedUpper bool if !liquidityDelta.IsZero() { + // println("tickLower", tickLower) + // println("tick", tick) + // println("liquidityDelta", liquidityDelta.Dec()) + // println("_feeGrowthGlobal0X128", _feeGrowthGlobal0X128.Dec()) + // println("_feeGrowthGlobal1X128", _feeGrowthGlobal1X128.Dec()) + // println("pool.maxLiquidityPerTick", pool.maxLiquidityPerTick.Dec()) flippedLower = pool.tickUpdate( tickLower, tick, @@ -621,6 +648,12 @@ func (pool *Pool) updatePosition( pool.maxLiquidityPerTick, ) + // println("tickUpper", tickUpper) + // println("tick", tick) + // println("liquidityDelta", liquidityDelta.Dec()) + // println("_feeGrowthGlobal0X128", _feeGrowthGlobal0X128.Dec()) + // println("_feeGrowthGlobal1X128", _feeGrowthGlobal1X128.Dec()) + // println("pool.maxLiquidityPerTick", pool.maxLiquidityPerTick.Dec()) flippedUpper = pool.tickUpdate( tickUpper, tick, @@ -632,15 +665,24 @@ func (pool *Pool) updatePosition( ) if flippedLower { + // println("3") + // println("tickLower:", tickLower) + // println("pool.tickSpacing:", pool.tickSpacing) pool.tickBitmapFlipTick(tickLower, pool.tickSpacing) } if flippedUpper { + // println("4") pool.tickBitmapFlipTick(tickUpper, pool.tickSpacing) } } // NO LIQ, ONLY BURN 0 + // println("tickLower", tickLower) + // println("tickUpper", tickUpper) + // println("tick", tick) + // println("_feeGrowthGlobal0X128", _feeGrowthGlobal0X128.Dec()) + // println("_feeGrowthGlobal1X128", _feeGrowthGlobal1X128.Dec()) feeGrowthInside0X128, feeGrowthInside1X128 := pool.tickGetFeeGrowthInside( tickLower, tickUpper, @@ -651,12 +693,17 @@ func (pool *Pool) updatePosition( positionKey := positionGetKey(owner, tickLower, tickUpper) + // println("positionKey", positionKey) + // println("liquidityDelta", liquidityDelta.Dec()) + // println("feeGrowthInside0X128", feeGrowthInside0X128.Dec()) // XXX + // println("feeGrowthInside1X128", feeGrowthInside1X128.Dec()) position := pool.positionUpdateWithKey( positionKey, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128, ) + // println(">> position.tokensOwed0", position.tokensOwed0.Dec()) if liquidityDelta.IsNeg() { if flippedLower { diff --git a/pool/position.gno b/pool/position.gno index 7fb2e544..8856181a 100644 --- a/pool/position.gno +++ b/pool/position.gno @@ -6,7 +6,6 @@ import ( "gno.land/p/demo/u256" "gno.land/p/demo/ufmt" - "gno.land/r/demo/consts" ) @@ -15,22 +14,23 @@ func positionGetKey( tickLower int32, tickUpper int32, ) string { - key := ufmt.Sprintf("%s__%d__%d", owner.String(), tickLower, tickUpper) + positionKey := ufmt.Sprintf("%s__%d__%d", owner.String(), tickLower, tickUpper) - encoded := base64.StdEncoding.EncodeToString([]byte(key)) + encoded := base64.StdEncoding.EncodeToString([]byte(positionKey)) return encoded } func (pool *Pool) positionUpdateWithKey( - key string, + positionKey string, liquidityDelta *u256.Int, feeGrowthInside0X128 *u256.Uint, feeGrowthInside1X128 *u256.Uint, ) PositionInfo { - position := pool.positions[key] + + position := pool.positions[positionKey] p := positionUpdate(position, liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128) - pool.positions[key] = p + pool.positions[positionKey] = p return p } diff --git a/pool/tick.gno b/pool/tick.gno index 94c6fc2d..947d1736 100644 --- a/pool/tick.gno +++ b/pool/tick.gno @@ -24,13 +24,25 @@ func (pool *Pool) tickGetFeeGrowthInside( // init return values feeGrowthInside0X128 = u256.Zero() feeGrowthInside1X128 = u256.Zero() - // + + // println("tickLower", tickLower) + // println("tickUpper", tickUpper) + // println("tickCurrent", tickCurrent) + // println("feeGrowthGlobal0X128", feeGrowthGlobal0X128.Dec()) + // println("feeGrowthGlobal1X128", feeGrowthGlobal1X128.Dec()) + // println() + + // END INIT lower := pool.ticks[tickLower] upper := pool.ticks[tickUpper] feeGrowthBelow0X128 := u256.Zero() feeGrowthBelow1X128 := u256.Zero() + + // println("CHK", tickLower) + // println("0x", lower.feeGrowthOutside0X128.Dec()) + // println("1x", lower.feeGrowthOutside1X128.Dec()) if tickCurrent >= tickLower { feeGrowthBelow0X128 = lower.feeGrowthOutside0X128.NilToZero() feeGrowthBelow1X128 = lower.feeGrowthOutside1X128.NilToZero() @@ -39,6 +51,9 @@ func (pool *Pool) tickGetFeeGrowthInside( feeGrowthBelow1X128.Sub(feeGrowthGlobal1X128, lower.feeGrowthOutside1X128.NilToZero()) } + // println("feeGrowthBelow0X128", feeGrowthBelow0X128.Dec()) + // println("feeGrowthBelow1X128", feeGrowthBelow1X128.Dec()) + feeGrowthAbove0X128 := u256.Zero() feeGrowthAbove1X128 := u256.Zero() @@ -69,14 +84,21 @@ func (pool *Pool) tickUpdate( maxLiquidity *u256.Uint, ) (flipped bool) { info := pool.ticks[tick] + info.feeGrowthOutside0X128 = info.feeGrowthOutside0X128.NilToZero() + info.feeGrowthOutside1X128 = info.feeGrowthOutside1X128.NilToZero() + info.liquidityNet = info.liquidityNet.NilToZero() + liquidityGrossBefore := info.liquidityGross if liquidityGrossBefore == nil { liquidityGrossBefore = u256.Zero() } + // println("liquidityGrossBefore", liquidityGrossBefore.Dec()) + liquidityGrossAfter := liquidityMathAddDelta(liquidityGrossBefore, liquidityDelta) require(!liquidityGrossAfter.Lte(maxLiquidity), ufmt.Sprintf("[POOL] tick.gno__tickUpdate() || liquidityGrossAfter(%s) <= maxLiquidity(%s)", liquidityGrossAfter.Dec(), maxLiquidity.Dec())) + // println("liquidityGrossAfter", liquidityGrossAfter.Dec()) flipped = (liquidityGrossAfter.IsZero()) != (liquidityGrossBefore.IsZero()) @@ -89,17 +111,23 @@ func (pool *Pool) tickUpdate( info.initialized = true } - info.liquidityGross = liquidityGrossAfter + // println("info.feeGrowthOutside0X128", info.feeGrowthOutside0X128.Dec()) + // println("info.feeGrowthOutside1X128", info.feeGrowthOutside1X128.Dec()) - if info.liquidityNet == nil { - info.liquidityNet = u256.NewInt(0) - } + info.liquidityGross = liquidityGrossAfter if upper { + // println("11") + // println("info.liquidityNet", info.liquidityNet.Dec()) + // println("liquidityDelta", liquidityDelta.Dec()) info.liquidityNet.Sub(info.liquidityNet, liquidityDelta) } else { + // println("22") + // println("info.liquidityNet", info.liquidityNet.Dec()) + // println("liquidityDelta", liquidityDelta.Dec()) info.liquidityNet.Add(info.liquidityNet, liquidityDelta) } + // println("info.liquidityNet", info.liquidityNet.Dec()) pool.ticks[tick] = info diff --git a/pool/tick_bitmap.gno b/pool/tick_bitmap.gno index 3b28f78b..2282b042 100644 --- a/pool/tick_bitmap.gno +++ b/pool/tick_bitmap.gno @@ -21,6 +21,9 @@ func (pool *Pool) tickBitmapFlipTick( wordPos, bitPos := tickBitmapPosition(tick / tickSpacing) mask := u256.NewUint(1) mask.Lsh(mask, uint(bitPos)) // 2 ** bitPos + // println("wordPos", wordPos) + // println("bitPos", bitPos) + // println("mask", mask.Dec()) // XXXXXXXXXXXXXXXX // mask.Lsh is exptected to check overflow with the signed integer type. @@ -30,10 +33,11 @@ func (pool *Pool) tickBitmapFlipTick( //requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapFlipTick() || mask(%d) > 0", mask)) // check init value - if pool.tickBitmaps[wordPos] == nil { - pool.tickBitmaps[wordPos] = u256.Zero() - } + pool.tickBitmaps[wordPos] = pool.tickBitmaps[wordPos].NilToZero() + + // println("pool.tickBitmaps[wordPos]:", pool.tickBitmaps[wordPos].Dec()) pool.tickBitmaps[wordPos].Xor(pool.tickBitmaps[wordPos], mask) + // println("pool.tickBitmaps[wordPos]:", pool.tickBitmaps[wordPos].Dec()) } func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( @@ -56,6 +60,7 @@ func (pool *Pool) tickBitmapNextInitializedTickWithInOneWord( // XXXXXXXX overflow check Lsh! // requireUnsigned(mask, ufmt.Sprintf("[POOL] tick_bitmap.gno__tickBitmapNextInitializedTickWithInOneWord__mask(%d) >= 0__#1", mask)) + pool.tickBitmaps[wordPos] = pool.tickBitmaps[wordPos].NilToZero() masked := new(u256.Uint).And(pool.tickBitmaps[wordPos], mask) initialized := !masked.IsZero() diff --git a/u256/i256.gno b/u256/i256.gno index 09380c1a..3db2f8cf 100644 --- a/u256/i256.gno +++ b/u256/i256.gno @@ -32,6 +32,11 @@ func IntFromBigint(v bigint) *Int { if v >= 0 { return &Int{v: *FromBigint(v)} + } else { + var tmp Int + tmp.v = *FromBigint(-v) + tmp.Neg() + return &tmp } panic("I256 IntFromBigint not implemented") diff --git a/u256/u256.gno b/u256/u256.gno index 71a67258..21cc9714 100644 --- a/u256/u256.gno +++ b/u256/u256.gno @@ -844,12 +844,12 @@ func (z *Uint) fromHex(hex string) error { // FromDecimal is a convenience-constructor to create an Uint from a // decimal (base 10) string. Numbers larger than 256 bits are not accepted. -func FromDecimal(decimal string) (*Uint, error) { +func FromDecimal(decimal string) *Uint { var z Uint if err := z.SetFromDecimal(decimal); err != nil { - return nil, err + panic(err.Error()) } - return &z, nil + return &z } const twoPow256Sub1 = "115792089237316195423570985008687907853269984665640564039457584007913129639935"