Search Apps Documentation Source Content File Folder Download Copy Actions Download

fullmath.gno

3.44 Kb · 104 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
 21var MAX_UINT256_FROM_DECIMAL *Uint
 22
 23func init() {
 24	MAX_UINT256_FROM_DECIMAL = MustFromDecimal(MAX_UINT256)
 25}
 26
 27// MulDiv calculates (a * b) / denominator with full 512-bit intermediate precision.
 28// Panics if denominator is zero or if the result overflows 256 bits.
 29func MulDiv(a, b, denominator *Uint) *Uint {
 30	if denominator.IsZero() {
 31		panic("denominator must be greater than 0")
 32	}
 33
 34	// 512-bit product (8 limbs of 64 bits)
 35	p := umul(a, b)
 36
 37	if (p[4] | p[5] | p[6] | p[7]) == 0 {
 38		var lo Uint
 39		lo[0], lo[1], lo[2], lo[3] = p[0], p[1], p[2], p[3]
 40		return new(Uint).Div(&lo, denominator)
 41	}
 42
 43	// optional early overflow check:
 44	// If hi >= denominator then floor((hi*2^256 + lo) / denominator) >= 2^256, which is overflow.
 45	{
 46		var hi Uint
 47		hi[0], hi[1], hi[2], hi[3] = p[4], p[5], p[6], p[7]
 48		if denominator.Lte(&hi) {
 49			panic("overflow: denominator(" + denominator.ToString() + ") must be greater than hi(" + hi.ToString() + ")")
 50		}
 51	}
 52
 53	// perform 512 / 256 division
 54	// udivrem stores quotient into `quot` (len(u) - len(d) + 1 words)
 55	// we pass 8 words to be safe.
 56	var quot [8]uint64
 57	udivrem(quot[:], p[:], denominator) // ignore remainder
 58
 59	if (quot[4] | quot[5] | quot[6] | quot[7]) != 0 {
 60		panic("uint256: MulDiv overflow (high quotient words non-zero)")
 61	}
 62
 63	// return lower 256 bits of quotient
 64	var z Uint
 65	copy(z[:], quot[:4])
 66	return &z
 67}
 68
 69// MulDivRoundingUp calculates ceil((a * b) / denominator) with full 512-bit intermediate precision.
 70// Panics if denominator is zero or if the result overflows 256 bits.
 71func MulDivRoundingUp(a, b, denominator *Uint) *Uint {
 72	result := MulDiv(a, b, denominator)
 73
 74	// Check if there's a remainder
 75	mulModResult := new(Uint).MulMod(a, b, denominator)
 76
 77	// If there's no remainder, return the result as-is
 78	if mulModResult.IsZero() {
 79		return result
 80	}
 81
 82	// Add 1 to round up, but check for overflow
 83	if result.Eq(MAX_UINT256_FROM_DECIMAL) {
 84		panic("overflow: result(" + result.ToString() + ") + 1 would exceed MAX_UINT256")
 85	}
 86
 87	return result.Add(result, &Uint{1, 0, 0, 0})
 88}
 89
 90// DivRoundingUp calculates ceil(x / y) and returns the result.
 91// Panics if y is zero.
 92func DivRoundingUp(x, y *Uint) *Uint {
 93	div := new(Uint).Div(x, y)
 94	mod := new(Uint).Mod(x, y)
 95	return new(Uint).Add(div, gt(mod, &Uint{0, 0, 0, 0}))
 96}
 97
 98// gt returns One() if x > y, otherwise returns Zero().
 99func gt(x, y *Uint) *Uint {
100	if x.Gt(y) {
101		return &Uint{1, 0, 0, 0}
102	}
103	return &Uint{0, 0, 0, 0}
104}