Search Apps Documentation Source Content File Folder Download Copy Actions Download

utils.gno

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