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

fullmath.gno

3.32 Kb · 98 lines
 1// REF: https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
 2
 3// fullmath implements Uniswap V3's FullMath library.
 4//
 5// This library provides advanced fixed-point math operations that are essential
 6// for Uniswap V3's tick math and liquidity calculations. It enables precise
 7// calculations of (a * b / denominator) with full 512-bit intermediate precision.
 8//
 9// NOTE: Unlike other arithmetic functions in the uint256 package that return errors,
10// functions in this file panic on invalid inputs to maintain behavioral compatibility
11// with the original Solidity implementation which uses require() statements.
12//
13// This design choice is intentional because:
14// 1. These functions are typically used in hot paths where error handling would add overhead
15// 2. Invalid inputs (like zero denominator) represent programming errors, not runtime conditions
16// 3. Staying close to the Solidity implementation makes protocol porting more reliable
17//
18// If you need error-returning versions, wrap these functions with appropriate error handling.
19package uint256
20
21// MulDiv calculates (a * b) / denominator with full 512-bit intermediate precision.
22// Panics if denominator is zero or if the result overflows 256 bits.
23func MulDiv(a, b, denominator *Uint) *Uint {
24	if denominator.IsZero() {
25		panic("denominator must be greater than 0")
26	}
27
28	// 512-bit product (8 limbs of 64 bits)
29	p := umul(a, b)
30
31	if (p[4] | p[5] | p[6] | p[7]) == 0 {
32		var lo Uint
33		lo[0], lo[1], lo[2], lo[3] = p[0], p[1], p[2], p[3]
34		return new(Uint).Div(&lo, denominator)
35	}
36
37	// optional early overflow check:
38	// If hi >= denominator then floor((hi*2^256 + lo) / denominator) >= 2^256, which is overflow.
39	{
40		var hi Uint
41		hi[0], hi[1], hi[2], hi[3] = p[4], p[5], p[6], p[7]
42		if denominator.Lte(&hi) {
43			panic("overflow: denominator(" + denominator.ToString() + ") must be greater than hi(" + hi.ToString() + ")")
44		}
45	}
46
47	// perform 512 / 256 division
48	// udivrem stores quotient into `quot` (len(u) - len(d) + 1 words)
49	// we pass 8 words to be safe.
50	var quot [8]uint64
51	udivrem(quot[:], p[:], denominator) // ignore remainder
52
53	if (quot[4] | quot[5] | quot[6] | quot[7]) != 0 {
54		panic("uint256: MulDiv overflow (high quotient words non-zero)")
55	}
56
57	// return lower 256 bits of quotient
58	var z Uint
59	copy(z[:], quot[:4])
60	return &z
61}
62
63// MulDivRoundingUp calculates ceil((a * b) / denominator) with full 512-bit intermediate precision.
64// Panics if denominator is zero or if the result overflows 256 bits.
65func MulDivRoundingUp(a, b, denominator *Uint) *Uint {
66	result := MulDiv(a, b, denominator)
67
68	// Check if there's a remainder
69	mulModResult := new(Uint).MulMod(a, b, denominator)
70
71	// If there's no remainder, return the result as-is
72	if mulModResult.IsZero() {
73		return result
74	}
75
76	// Add 1 to round up, but check for overflow
77	if result.Eq(MaxUint256()) {
78		panic("overflow: result(" + result.ToString() + ") + 1 would exceed MAX_UINT256")
79	}
80
81	return result.Add(result, &Uint{1, 0, 0, 0})
82}
83
84// DivRoundingUp calculates ceil(x / y) and returns the result.
85// Panics if y is zero.
86func DivRoundingUp(x, y *Uint) *Uint {
87	div := new(Uint).Div(x, y)
88	mod := new(Uint).Mod(x, y)
89	return new(Uint).Add(div, gt(mod, &Uint{0, 0, 0, 0}))
90}
91
92// gt returns One() if x > y, otherwise returns Zero().
93func gt(x, y *Uint) *Uint {
94	if x.Gt(y) {
95		return &Uint{1, 0, 0, 0}
96	}
97	return &Uint{0, 0, 0, 0}
98}