utils.gno
7.68 Kb ยท 249 lines
1package v1
2
3import (
4 "math"
5 "strconv"
6 "strings"
7
8 i256 "gno.land/p/gnoswap/int256"
9 u256 "gno.land/p/gnoswap/uint256"
10 "gno.land/p/nt/ufmt"
11 pl "gno.land/r/gnoswap/pool"
12)
13
14const (
15 MAX_UINT64 string = "18446744073709551615"
16 MAX_INT64 string = "9223372036854775807"
17 MAX_INT128 string = "170141183460469231731687303715884105727"
18 MAX_UINT128 string = "340282366920938463463374607431768211455"
19 MAX_INT256 string = "57896044618658097711785492504343953926634992332820282019728792003956564819967"
20
21 INT64_MIN int64 = -9223372036854775808
22 INT64_MAX int64 = 9223372036854775807
23
24 Q96_RESOLUTION uint = 96
25 Q128_RESOLUTION uint = 128
26
27 Q64 string = "18446744073709551616" // 2 ** 64
28 Q96 string = "79228162514264337593543950336" // 2 ** 96
29 Q128 string = "340282366920938463463374607431768211456" // 2 ** 128
30)
31
32var (
33 maxInt128FromDecimal = i256.MustFromDecimal(MAX_INT128)
34 maxUint128FromDecimal = u256.MustFromDecimal(MAX_UINT128)
35 q128FromDecimal = u256.MustFromDecimal(Q128)
36)
37
38var uint128Mask = func() *u256.Uint {
39 m := u256.Zero().Lsh(u256.One(), Q128_RESOLUTION)
40 return m.Sub(m, u256.One())
41}()
42
43// safeConvertToUint64 safely converts a *u256.Uint value to a uint64, ensuring no overflow.
44// This function attempts to convert the given *u256.Uint value to a uint64.
45// If the value exceeds the maximum allowable range for uint64 (2^64 - 1), it panics.
46func safeConvertToUint64(value *u256.Uint) uint64 {
47 if value == nil {
48 panic(newErrorWithDetail(errInvalidInput, "value is nil"))
49 }
50 res, overflow := value.Uint64WithOverflow()
51 if overflow {
52 panic(ufmt.Sprintf(
53 "%v: amount(%s) overflows uint64 range (max %s)",
54 errOutOfRange,
55 value.ToString(),
56 MAX_UINT64,
57 ))
58 }
59 return res
60}
61
62// safeConvertToInt64 safely converts a *u256.Uint value to an int64, ensuring no overflow.
63// This function attempts to convert the given *u256.Uint value to an int64.
64// If the value exceeds the maximum allowable range for int64 (2^63 - 1), it panics.
65func safeConvertToInt64(value *u256.Uint) int64 {
66 if value == nil {
67 panic(newErrorWithDetail(errInvalidInput, "value is nil"))
68 }
69 res, overflow := value.Uint64WithOverflow()
70 if overflow || res > uint64(INT64_MAX) {
71 panic(ufmt.Sprintf(
72 "%v: amount(%s) overflows int64 range (max %s)",
73 errOutOfRange,
74 value.ToString(),
75 MAX_INT64,
76 ))
77 }
78 return int64(res)
79}
80
81// safeConvertToInt128 safely converts a *u256.Uint value to an *i256.Int, ensuring it does not exceed the int128 range.
82// This function converts an unsigned 256-bit integer to a signed 256-bit integer.
83// If the value exceeds the maximum allowable int128 range (2^127 - 1), it panics.
84func safeConvertToInt128(value *u256.Uint) *i256.Int {
85 liquidityDelta := i256.FromUint256(value)
86 if liquidityDelta.Gt(maxInt128FromDecimal) {
87 panic(ufmt.Sprintf(
88 "%v: amount(%s) overflows int128 range",
89 errOverflow, value.ToString()))
90 }
91 return liquidityDelta
92}
93
94// toUint128 ensures a *u256.Uint value fits within the uint128 range.
95//
96// This function validates that the given `value` is properly initialized and checks whether
97// it exceeds the maximum value of uint128. If the value exceeds the uint128 range,
98// it applies a masking operation to truncate the value to fit within the uint128 limit.
99//
100// Parameters:
101// - value: *u256.Uint, the value to be checked and possibly truncated.
102//
103// Returns:
104// - *u256.Uint: A value guaranteed to fit within the uint128 range.
105//
106// Notes:
107// - The function first checks if the value is not nil to avoid potential runtime errors.
108// - The mask ensures that only the lower 128 bits of the value are retained.
109// - If the input value is already within the uint128 range, it is returned unchanged.
110// - If masking is required, a new instance is returned without modifying the input.
111// - MAX_UINT128 is a constant representing `2^128 - 1`.
112func toUint128(value *u256.Uint) *u256.Uint {
113 if value == nil {
114 panic(newErrorWithDetail(errInvalidInput, "value is nil"))
115 }
116
117 if value.Gt(maxUint128FromDecimal) {
118 return u256.Zero().And(value, uint128Mask)
119 }
120 return value
121}
122
123// u256Min returns the smaller of two *u256.Uint values.
124//
125// This function compares two unsigned 256-bit integers and returns the smaller of the two.
126// If `num1` is less than `num2`, it returns `num1`; otherwise, it returns `num2`.
127//
128// Parameters:
129// - num1 (*u256.Uint): The first unsigned 256-bit integer.
130// - num2 (*u256.Uint): The second unsigned 256-bit integer.
131//
132// Returns:
133// - *u256.Uint: The smaller of `num1` and `num2`.
134//
135// Notes:
136// - This function uses the `Lt` (less than) method of `*u256.Uint` to perform the comparison.
137// - The function assumes both input values are non-nil. If nil inputs are possible in the usage context,
138// additional validation may be needed.
139//
140// Example:
141// smaller := u256Min(u256.MustFromDecimal("10"), u256.MustFromDecimal("20")) // Returns 10
142// smaller := u256Min(u256.MustFromDecimal("30"), u256.MustFromDecimal("20")) // Returns 20
143func u256Min(num1, num2 *u256.Uint) *u256.Uint {
144 if num1.Lt(num2) {
145 return num1
146 }
147 return num2
148}
149
150// checkOverFlowInt128 checks if the value overflows the int128 range.
151func checkOverFlowInt128(value *i256.Int) {
152 if value.Gt(maxInt128FromDecimal) {
153 panic(ufmt.Sprintf(
154 "%v: amount(%s) overflows int128 range",
155 errOverflow, value.ToString()))
156 }
157}
158
159// checkTickSpacing checks if the tick is divisible by the tickSpacing.
160func checkTickSpacing(tick, tickSpacing int32) {
161 if tick%tickSpacing != 0 {
162 panic(newErrorWithDetail(
163 errInvalidTickAndTickSpacing,
164 ufmt.Sprintf("tick(%d) MOD tickSpacing(%d) != 0(%d)", tick, tickSpacing, tick%tickSpacing),
165 ))
166 }
167}
168
169// formatUint converts various unsigned integer types to string representation.
170func formatUint(v any) string {
171 switch v := v.(type) {
172 case uint8:
173 return strconv.FormatUint(uint64(v), 10)
174 case uint16:
175 return strconv.FormatUint(uint64(v), 10)
176 case uint32:
177 return strconv.FormatUint(uint64(v), 10)
178 case uint64:
179 return strconv.FormatUint(v, 10)
180 default:
181 panic(ufmt.Sprintf("invalid type: %T", v))
182 }
183}
184
185// formatInt converts various signed integer types to string representation.
186func formatInt(v any) string {
187 switch v := v.(type) {
188 case int32:
189 return strconv.FormatInt(int64(v), 10)
190 case int64:
191 return strconv.FormatInt(v, 10)
192 case int:
193 return strconv.Itoa(v)
194 default:
195 panic(ufmt.Sprintf("invalid type: %T", v))
196 }
197}
198
199// formatBool converts a boolean value to string representation.
200func formatBool(v bool) string {
201 return strconv.FormatBool(v)
202}
203
204// ticksToString converts tick indices within a range to their string representation,
205// including detailed tick information from the pool's tick storage.
206func ticksToString(pool *pl.Pool, startTick, endTick int32) string {
207 minTick, maxTick := startTick, endTick
208
209 if minTick > maxTick {
210 minTick, maxTick = maxTick, minTick
211 }
212
213 tickInfoStrings := make([]string, 0)
214
215 pool.IterateTicks(minTick, maxTick, func(tick int32, tickInfo pl.TickInfo) bool {
216 tickEventInfo := NewTickEventInfo(tick, tickInfo)
217 tickInfoStrings = append(tickInfoStrings, tickEventInfo.ToString())
218
219 return false
220 })
221
222 if len(tickInfoStrings) == 0 {
223 return "[]"
224 }
225
226 return "[" + strings.Join(tickInfoStrings, ",") + "]"
227}
228
229// safeAddInt64 performs safe addition of int64 values, panicking on overflow or underflow
230func safeAddInt64(a, b int64) int64 {
231 if a > 0 && b > math.MaxInt64-a {
232 panic("int64 addition overflow")
233 }
234 if a < 0 && b < math.MinInt64-a {
235 panic("int64 addition underflow")
236 }
237 return a + b
238}
239
240// safeSubInt64 performs safe subtraction of int64 values, panicking on overflow or underflow
241func safeSubInt64(a, b int64) int64 {
242 if b > 0 && a < math.MinInt64+b {
243 panic("int64 subtraction underflow")
244 }
245 if b < 0 && a > math.MaxInt64+b {
246 panic("int64 subtraction overflow")
247 }
248 return a - b
249}