Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

safe_math.gno

4.03 Kb · 164 lines
  1package gnsmath
  2
  3import (
  4	"math"
  5
  6	ufmt "gno.land/p/nt/ufmt/v0"
  7
  8	i256 "gno.land/p/gnoswap/int256"
  9	u256 "gno.land/p/gnoswap/uint256"
 10)
 11
 12// Range bounds used by the safe conversion helpers.
 13const (
 14	maxInt64Decimal  = "9223372036854775807"                     // 2^63 - 1
 15	maxInt128Decimal = "170141183460469231731687303715884105727" // 2^127 - 1
 16)
 17
 18// maxInt128 is the largest value representable in a signed 128-bit integer.
 19// It is used by SafeConvertToInt128 to bound liquidity deltas.
 20func MaxInt128() *i256.Int {
 21	return i256.MustFromDecimal(maxInt128Decimal)
 22}
 23
 24// SafeAddInt64 returns a + b, panicking on int64 overflow or underflow.
 25func SafeAddInt64(a, b int64) int64 {
 26	if a > 0 && b > math.MaxInt64-a {
 27		panic("int64 addition overflow")
 28	}
 29	if a < 0 && b < math.MinInt64-a {
 30		panic("int64 addition underflow")
 31	}
 32	return a + b
 33}
 34
 35// SafeSubInt64 returns a - b, panicking on int64 overflow or underflow.
 36func SafeSubInt64(a, b int64) int64 {
 37	if b > 0 && a < math.MinInt64+b {
 38		panic("int64 subtraction underflow")
 39	}
 40	if b < 0 && a > math.MaxInt64+b {
 41		panic("int64 subtraction overflow")
 42	}
 43	return a - b
 44}
 45
 46// SafeMulInt64 returns a * b, panicking on int64 overflow or underflow.
 47func SafeMulInt64(a, b int64) int64 {
 48	if a == 0 || b == 0 {
 49		return 0
 50	}
 51
 52	if a > 0 && b > 0 {
 53		if a > math.MaxInt64/b {
 54			panic("int64 multiplication overflow")
 55		}
 56	} else if a < 0 && b < 0 {
 57		if a < math.MaxInt64/b {
 58			panic("int64 multiplication overflow")
 59		}
 60	} else if a > 0 && b < 0 {
 61		if b < math.MinInt64/a {
 62			panic("int64 multiplication underflow")
 63		}
 64	} else { // a < 0 && b > 0
 65		if a < math.MinInt64/b {
 66			panic("int64 multiplication underflow")
 67		}
 68	}
 69
 70	return a * b
 71}
 72
 73// SafeMulDivInt64 returns (a * b) / c, computing the intermediate product in
 74// 256-bit signed arithmetic so a*b need not fit in int64. The final quotient
 75// must fit in int64, otherwise it panics. It panics on overflow.
 76func SafeMulDivInt64(a, b, c int64) int64 {
 77	if a == 0 || b == 0 {
 78		return 0
 79	}
 80
 81	result, overflow := i256.Zero().MulOverflow(i256.NewInt(a), i256.NewInt(b))
 82	if overflow {
 83		panic(errSafeMathOverflow)
 84	}
 85
 86	result = i256.Zero().Div(result, i256.NewInt(c))
 87	if !result.IsInt64() {
 88		panic(errSafeMathOverflow)
 89	}
 90
 91	return result.Int64()
 92}
 93
 94// SafeAbsInt64 returns the absolute value of a, panicking when a is math.MinInt64
 95// since its absolute value is not representable in int64.
 96func SafeAbsInt64(a int64) int64 {
 97	if a == math.MinInt64 {
 98		panic(errSafeMathOverflow)
 99	}
100	if a < 0 {
101		return -a
102	}
103	return a
104}
105
106// SafeAddUint64 returns a + b, panicking on uint64 overflow.
107func SafeAddUint64(a, b uint64) uint64 {
108	if a > math.MaxUint64-b {
109		panic("uint64 addition overflow")
110	}
111	return a + b
112}
113
114// SafeSubUint64 returns a - b, panicking on uint64 underflow.
115func SafeSubUint64(a, b uint64) uint64 {
116	if a < b {
117		panic("uint64 subtraction underflow")
118	}
119	return a - b
120}
121
122// SafeUint64ToInt64 converts a uint64 to int64, panicking when the value
123// exceeds the int64 range (2^63 - 1).
124func SafeUint64ToInt64(value uint64) int64 {
125	if value > uint64(math.MaxInt64) {
126		panic(ufmt.Sprintf(
127			"amount(%d) overflows int64 range (max: %s)",
128			value, maxInt64Decimal,
129		))
130	}
131	return int64(value)
132}
133
134// SafeConvertToInt64 converts a *u256.Uint to int64, panicking when the value
135// is nil or exceeds the int64 range (2^63 - 1).
136func SafeConvertToInt64(value *u256.Uint) int64 {
137	if value == nil {
138		panic("SafeConvertToInt64: value is nil")
139	}
140	res, overflow := value.Uint64WithOverflow()
141	if overflow || res > uint64(math.MaxInt64) {
142		panic(ufmt.Sprintf(
143			"amount(%s) overflows int64 range (max: %s)",
144			value.ToString(), maxInt64Decimal,
145		))
146	}
147	return int64(res)
148}
149
150// SafeConvertToInt128 converts a *u256.Uint to an *i256.Int, panicking when the
151// value exceeds the signed int128 range (2^127 - 1).
152func SafeConvertToInt128(value *u256.Uint) *i256.Int {
153	if value == nil {
154		panic("SafeConvertToInt128: value is nil")
155	}
156	converted := i256.FromUint256(value)
157	if converted.Gt(MaxInt128()) {
158		panic(ufmt.Sprintf(
159			"amount(%s) overflows int128 range",
160			value.ToString(),
161		))
162	}
163	return converted
164}