Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}