Function to calculate x[i] given other balances x[0..N_COINS-1] and invariant D.
Returns: y (uint256).
Input
Type
Description
_ANN
uint256
ANN = A * N**N
_gamma
_gamma
AMM.gamma() value
x
uint256[N_COINS]
Balances multiplied by prices and precisions of all coins
_D
uint256
Invariant
i
uint256
Index of coin to calculate y
Source code
N_COINS:constant(uint256)=3A_MULTIPLIER:constant(uint256)=10000MIN_GAMMA:constant(uint256)=10**10MAX_GAMMA:constant(uint256)=5*10**16MIN_A:constant(uint256)=N_COINS**N_COINS*A_MULTIPLIER/100MAX_A:constant(uint256)=N_COINS**N_COINS*A_MULTIPLIER*1000@external@viewdefget_y(_ANN:uint256,_gamma:uint256,x:uint256[N_COINS],_D:uint256,i:uint256)->uint256[2]:""" @notice Calculate x[i] given other balances x[0..N_COINS-1] and invariant D. @dev ANN = A * N**N. @param _ANN AMM.A() value. @param _gamma AMM.gamma() value. @param x Balances multiplied by prices and precisions of all coins. @param _D Invariant. @param i Index of coin to calculate y. """# Safety checksassert_ANN>MIN_A-1and_ANN<MAX_A+1# dev: unsafe values Aassert_gamma>MIN_GAMMA-1and_gamma<MAX_GAMMA+1# dev: unsafe values gammaassert_D>10**17-1and_D<10**15*10**18+1# dev: unsafe values Dfrac:uint256=0forkinrange(3):ifk!=i:frac=x[k]*10**18/_Dassertfrac>10**16-1andfrac<10**20+1,"Unsafe values x[i]"# if above conditions are met, x[k] > 0j:uint256=0k:uint256=0ifi==0:j=1k=2elifi==1:j=0k=2elifi==2:j=0k=1ANN:int256=convert(_ANN,int256)gamma:int256=convert(_gamma,int256)D:int256=convert(_D,int256)x_j:int256=convert(x[j],int256)x_k:int256=convert(x[k],int256)gamma2:int256=unsafe_mul(gamma,gamma)a:int256=10**36/27# 10**36/9 + 2*10**18*gamma/27 - D**2/x_j*gamma**2*ANN/27**2/convert(A_MULTIPLIER, int256)/x_kb:int256=(unsafe_add(10**36/9,unsafe_div(unsafe_mul(2*10**18,gamma),27))-unsafe_div(unsafe_div(unsafe_div(unsafe_mul(unsafe_div(unsafe_mul(D,D),x_j),gamma2)*ANN,27**2),convert(A_MULTIPLIER,int256)),x_k,))# <------- The first two expressions can be unsafe, and unsafely added.# 10**36/9 + gamma*(gamma + 4*10**18)/27 + gamma**2*(x_j+x_k-D)/D*ANN/27/convert(A_MULTIPLIER, int256)c:int256=(unsafe_add(10**36/9,unsafe_div(unsafe_mul(gamma,unsafe_add(gamma,4*10**18)),27))+unsafe_div(unsafe_div(unsafe_mul(unsafe_div(gamma2*unsafe_sub(unsafe_add(x_j,x_k),D),D),ANN),27),convert(A_MULTIPLIER,int256),))# <--------- Same as above with the first two expressions. In the third# expression, x_j + x_k will not overflow since we know their range from# previous assert statements.# (10**18 + gamma)**2/27d:int256=unsafe_div(unsafe_add(10**18,gamma)**2,27)# abs(3*a*c/b - b)d0:int256=abs(unsafe_mul(3,a)*c/b-b)# <------------ a is smol.divider:int256=0ifd0>10**48:divider=10**30elifd0>10**44:divider=10**26elifd0>10**40:divider=10**22elifd0>10**36:divider=10**18elifd0>10**32:divider=10**14elifd0>10**28:divider=10**10elifd0>10**24:divider=10**6elifd0>10**20:divider=10**2else:divider=1additional_prec:int256=0ifabs(a)>abs(b):additional_prec=abs(unsafe_div(a,b))a=unsafe_div(unsafe_mul(a,additional_prec),divider)b=unsafe_div(b*additional_prec,divider)c=unsafe_div(c*additional_prec,divider)d=unsafe_div(d*additional_prec,divider)else:additional_prec=abs(unsafe_div(b,a))a=unsafe_div(a/additional_prec,divider)b=unsafe_div(unsafe_div(b,additional_prec),divider)c=unsafe_div(unsafe_div(c,additional_prec),divider)d=unsafe_div(unsafe_div(d,additional_prec),divider)# 3*a*c/b - b_3ac:int256=unsafe_mul(3,a)*cdelta0:int256=unsafe_div(_3ac,b)-b# 9*a*c/b - 2*b - 27*a**2/b*d/bdelta1:int256=(unsafe_div(3*_3ac,b)-unsafe_mul(2,b)-unsafe_div(unsafe_div(27*a**2,b)*d,b))# delta1**2 + 4*delta0**2/b*delta0sqrt_arg:int256=(delta1**2+unsafe_div(4*delta0**2,b)*delta0)sqrt_val:int256=0ifsqrt_arg>0:sqrt_val=convert(isqrt(convert(sqrt_arg,uint256)),int256)else:return[self._newton_y(_ANN,_gamma,x,_D,i),0]b_cbrt:int256=0ifb>=0:b_cbrt=convert(self._cbrt(convert(b,uint256)),int256)else:b_cbrt=-convert(self._cbrt(convert(-b,uint256)),int256)second_cbrt:int256=0ifdelta1>0:# convert(self._cbrt(convert((delta1 + sqrt_val), uint256)/2), int256)second_cbrt=convert(self._cbrt(unsafe_div(convert(delta1+sqrt_val,uint256),2)),int256)else:second_cbrt=-convert(self._cbrt(unsafe_div(convert(-(delta1-sqrt_val),uint256),2)),int256)# b_cbrt*b_cbrt/10**18*second_cbrt/10**18C1:int256=unsafe_div(unsafe_div(b_cbrt*b_cbrt,10**18)*second_cbrt,10**18)# (b + b*delta0/C1 - C1)/3root_K0:int256=unsafe_div(b+b*delta0/C1-C1,3)# D*D/27/x_k*D/x_j*root_K0/aroot:int256=unsafe_div(unsafe_div(unsafe_div(unsafe_div(D*D,27),x_k)*D,x_j)*root_K0,a)out:uint256[2]=[convert(root,uint256),convert(unsafe_div(10**18*root_K0,a),uint256)]frac=unsafe_div(out[0]*10**18,_D)assertfrac>=10**16-1andfrac<10**20+1,"Unsafe value for y"# due to precision issues, get_y can be off by 2 wei or so wrt _newton_yreturnout
Function to calculate the invariant with Newtons method using good initial guesses.
Returns: D invariant (uint256).
Input
Type
Description
ANN
uint256
ANN = A * N**N
gamma
uint256
AMM.gamma() value
x_unsorted
uint256[N_COINS]
Unsorted array of coin balances
K0_prev
uint256
apriori for newton's method derived from get_y_int. Defaults to zero (no apriori)
Source code
@external@viewdefnewton_D(ANN:uint256,gamma:uint256,x_unsorted:uint256[N_COINS],K0_prev:uint256=0,)->uint256:""" @notice Finding the invariant via newtons method using good initial guesses. @dev ANN is higher by the factor A_MULTIPLIER @dev ANN is already A * N**N @param ANN the A * N**N value @param gamma the gamma value @param x_unsorted the array of coin balances (not sorted) @param K0_prev apriori for newton's method derived from get_y_int. Defaults to zero (no apriori) """x:uint256[N_COINS]=self._sort(x_unsorted)assertx[0]<max_value(uint256)/10**18*N_COINS**N_COINS# dev: out of limitsassertx[0]>0# dev: empty pool# Safe to do unsafe add since we checked largest x's bounds previouslyS:uint256=unsafe_add(unsafe_add(x[0],x[1]),x[2])D:uint256=0ifK0_prev==0:# Geometric mean of 3 numbers cannot be larger than the largest number# so the following is safe to do:D=unsafe_mul(N_COINS,self._geometric_mean(x))else:ifS>10**36:D=self._cbrt(unsafe_div(unsafe_div(x[0]*x[1],10**36)*x[2],K0_prev)*27*10**12)elifS>10**24:D=self._cbrt(unsafe_div(unsafe_div(x[0]*x[1],10**24)*x[2],K0_prev)*27*10**6)else:D=self._cbrt(unsafe_div(unsafe_div(x[0]*x[1],10**18)*x[2],K0_prev)*27)# D not zero here if K0_prev > 0, and we checked if x[0] is gt 0.# initialise variables:K0:uint256=0_g1k0:uint256=0mul1:uint256=0mul2:uint256=0neg_fprime:uint256=0D_plus:uint256=0D_minus:uint256=0D_prev:uint256=0diff:uint256=0frac:uint256=0foriinrange(255):D_prev=D# K0 = 10**18 * x[0] * N_COINS / D * x[1] * N_COINS / D * x[2] * N_COINS / DK0=unsafe_div(unsafe_mul(unsafe_mul(unsafe_div(unsafe_mul(unsafe_mul(unsafe_div(unsafe_mul(unsafe_mul(10**18,x[0]),N_COINS),D,),x[1],),N_COINS,),D,),x[2],),N_COINS,),D,)# <-------- We can convert the entire expression using unsafe math.# since x_i is not too far from D, so overflow is not expected. Also# D > 0, since we proved that already. unsafe_div is safe. K0 > 0# since we can safely assume that D < 10**18 * x[0]. K0 is also# in the range of 10**18 (it's a property)._g1k0=unsafe_add(gamma,10**18)# <--------- safe to do unsafe_add.if_g1k0>K0:# The following operations can safely be unsafe._g1k0=unsafe_add(unsafe_sub(_g1k0,K0),1)else:_g1k0=unsafe_add(unsafe_sub(K0,_g1k0),1)# D / (A * N**N) * _g1k0**2 / gamma**2# mul1 = 10**18 * D / gamma * _g1k0 / gamma * _g1k0 * A_MULTIPLIER / ANNmul1=unsafe_div(unsafe_mul(unsafe_mul(unsafe_div(unsafe_mul(unsafe_div(unsafe_mul(10**18,D),gamma),_g1k0),gamma,),_g1k0,),A_MULTIPLIER,),ANN,)# <------ Since D > 0, gamma is small, _g1k0 is small, the rest are# non-zero and small constants, and D has a cap in this method,# we can safely convert everything to unsafe maths.# 2*N*K0 / _g1k0# mul2 = (2 * 10**18) * N_COINS * K0 / _g1k0mul2=unsafe_div(unsafe_mul(2*10**18*N_COINS,K0),_g1k0)# <--------------- K0 is approximately around D, which has a cap of# 10**15 * 10**18 + 1, since we get that in get_y which is called# with newton_D. _g1k0 > 0, so the entire expression can be unsafe.# neg_fprime: uint256 = (S + S * mul2 / 10**18) + mul1 * N_COINS / K0 - mul2 * D / 10**18neg_fprime=unsafe_sub(unsafe_add(unsafe_add(S,unsafe_div(unsafe_mul(S,mul2),10**18)),unsafe_div(unsafe_mul(mul1,N_COINS),K0),),unsafe_div(unsafe_mul(mul2,D),10**18),)# <--- mul1 is a big number but not huge: safe to unsafely multiply# with N_coins. neg_fprime > 0 if this expression executes.# mul2 is in the range of 10**18, since K0 is in that range, S * mul2# is safe. The first three sums can be done using unsafe math safely# and since the final expression will be small since mul2 is small, we# can safely do the entire expression unsafely.# D -= f / fprime# D * (neg_fprime + S) / neg_fprimeD_plus=unsafe_div(D*unsafe_add(neg_fprime,S),neg_fprime)# D*D / neg_fprimeD_minus=unsafe_div(D*D,neg_fprime)# Since we know K0 > 0, and neg_fprime > 0, several unsafe operations# are possible in the following. Also, (10**18 - K0) is safe to mul.# So the only expressions we keep safe are (D_minus + ...) and (D * ...)if10**18>K0:# D_minus += D * (mul1 / neg_fprime) / 10**18 * (10**18 - K0) / K0D_minus+=unsafe_div(unsafe_mul(unsafe_div(D*unsafe_div(mul1,neg_fprime),10**18),unsafe_sub(10**18,K0),),K0,)else:# D_minus -= D * (mul1 / neg_fprime) / 10**18 * (K0 - 10**18) / K0D_minus-=unsafe_div(unsafe_mul(unsafe_div(D*unsafe_div(mul1,neg_fprime),10**18),unsafe_sub(K0,10**18),),K0,)ifD_plus>D_minus:D=unsafe_sub(D_plus,D_minus)# <--------- Safe since we check.else:D=unsafe_div(unsafe_sub(D_minus,D_plus),2)ifD>D_prev:diff=unsafe_sub(D,D_prev)else:diff=unsafe_sub(D_prev,D)# Could reduce precision for gas efficiency here:ifunsafe_mul(diff,10**14)<max(10**16,D):# Test that we are safe with the next get_yfor_xinx:frac=unsafe_div(unsafe_mul(_x,10**18),D)assertfrac>=10**16-1andfrac<10**20+1,"Unsafe values x[i]"returnDraise"Did not converge"
@external@viewdefget_p(_xp:uint256[N_COINS],_D:uint256,_A_gamma:uint256[N_COINS-1])->uint256[N_COINS-1]:""" @notice Calculates dx/dy. @dev Output needs to be multiplied with price_scale to get the actual value. @param _xp Balances of the pool. @param _D Current value of D. @param _A_gamma Amplification coefficient and gamma. """assert_D>10**17-1and_D<10**15*10**18+1# dev: unsafe D values# K0 = P * N**N / D**N.# K0 is dimensionless and has 10**36 precision:K0:uint256=unsafe_div(unsafe_div(unsafe_div(27*_xp[0]*_xp[1],_D)*_xp[2],_D)*10**36,_D)# GK0 is in 10**36 precision and is dimensionless.# GK0 = (# 2 * _K0 * _K0 / 10**36 * _K0 / 10**36# + (gamma + 10**18)**2# - (_K0 * _K0 / 10**36 * (2 * gamma + 3 * 10**18) / 10**18)# )# GK0 is always positive. So the following should never revert:GK0:uint256=(unsafe_div(unsafe_div(2*K0*K0,10**36)*K0,10**36)+pow_mod256(unsafe_add(_A_gamma[1],10**18),2)-unsafe_div(unsafe_div(pow_mod256(K0,2),10**36)*unsafe_add(unsafe_mul(2,_A_gamma[1]),3*10**18),10**18))# NNAG2 = N**N * A * gamma**2NNAG2:uint256=unsafe_div(unsafe_mul(_A_gamma[0],pow_mod256(_A_gamma[1],2)),A_MULTIPLIER)# denominator = (GK0 + NNAG2 * x / D * _K0 / 10**36)denominator:uint256=(GK0+unsafe_div(unsafe_div(NNAG2*_xp[0],_D)*K0,10**36))# p_xy = x * (GK0 + NNAG2 * y / D * K0 / 10**36) / y * 10**18 / denominator# p_xz = x * (GK0 + NNAG2 * z / D * K0 / 10**36) / z * 10**18 / denominator# p is in 10**18 precision.return[unsafe_div(_xp[0]*(GK0+unsafe_div(unsafe_div(NNAG2*_xp[1],_D)*K0,10**36))/_xp[1]*10**18,denominator),unsafe_div(_xp[0]*(GK0+unsafe_div(unsafe_div(NNAG2*_xp[2],_D)*K0,10**36))/_xp[2]*10**18,denominator),]
@external@viewdefcbrt(x:uint256)->uint256:""" @notice Calculate the cubic root of a number in 1e18 precision @dev Consumes around 1500 gas units @param x The number to calculate the cubic root of """returnself._cbrt(x)@internal@puredef_cbrt(x:uint256)->uint256:xx:uint256=0ifx>=115792089237316195423570985008687907853269*10**18:xx=xelifx>=115792089237316195423570985008687907853269:xx=unsafe_mul(x,10**18)else:xx=unsafe_mul(x,10**36)log2x:int256=convert(self._snekmate_log_2(xx,False),int256)# When we divide log2x by 3, the remainder is (log2x % 3).# So if we just multiply 2**(log2x/3) and discard the remainder to calculate our# guess, the newton method will need more iterations to converge to a solution,# since it is missing that precision. It's a few more calculations now to do less# calculations later:# pow = log2(x) // 3# remainder = log2(x) % 3# initial_guess = 2 ** pow * cbrt(2) ** remainder# substituting -> 2 = 1.26 ≈ 1260 / 1000, we get:## initial_guess = 2 ** pow * 1260 ** remainder // 1000 ** remainderremainder:uint256=convert(log2x,uint256)%3a:uint256=unsafe_div(unsafe_mul(pow_mod256(2,unsafe_div(convert(log2x,uint256),3)),# <- powpow_mod256(1260,remainder),),pow_mod256(1000,remainder),)# Because we chose good initial values for cube roots, 7 newton raphson iterations# are just about sufficient. 6 iterations would result in non-convergences, and 8# would be one too many iterations. Without initial values, the iteration count# can go up to 20 or greater. The iterations are unrolled. This reduces gas costs# but takes up more bytecode:a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)ifx>=115792089237316195423570985008687907853269*10**18:a=unsafe_mul(a,10**12)elifx>=115792089237316195423570985008687907853269:a=unsafe_mul(a,10**6)returna@internal@puredef_snekmate_log_2(x:uint256,roundup:bool)->uint256:""" @notice An `internal` helper function that returns the log in base 2 of `x`, following the selected rounding direction. @dev This implementation is derived from Snekmate, which is authored by pcaversaccio (Snekmate), distributed under the AGPL-3.0 license. https://github.com/pcaversaccio/snekmate @dev Note that it returns 0 if given 0. The implementation is inspired by OpenZeppelin's implementation here: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/Math.sol. @param x The 32-byte variable. @param roundup The Boolean variable that specifies whether to round up or not. The default `False` is round down. @return uint256 The 32-byte calculation result. """value:uint256=xresult:uint256=empty(uint256)# The following lines cannot overflow because we have the well-known# decay behaviour of `log_2(max_value(uint256)) < max_value(uint256)`.ifx>>128!=empty(uint256):value=x>>128result=128ifvalue>>64!=empty(uint256):value=value>>64result=unsafe_add(result,64)ifvalue>>32!=empty(uint256):value=value>>32result=unsafe_add(result,32)ifvalue>>16!=empty(uint256):value=value>>16result=unsafe_add(result,16)ifvalue>>8!=empty(uint256):value=value>>8result=unsafe_add(result,8)ifvalue>>4!=empty(uint256):value=value>>4result=unsafe_add(result,4)ifvalue>>2!=empty(uint256):value=value>>2result=unsafe_add(result,2)ifvalue>>1!=empty(uint256):result=unsafe_add(result,1)if(roundupand(1<<result)<x):result=unsafe_add(result,1)returnresult
Function to calculate the geometric mean of a list of numbers in 1e18 precision.
Returns: gemoetric mean (uint256).
Input
Type
Description
_x
uint256
list of three numbers
Source code
@external@viewdefgeometric_mean(_x:uint256[3])->uint256:""" @notice Calculate the geometric mean of a list of numbers in 1e18 precision. @param _x list of 3 numbers to sort """returnself._geometric_mean(_x)@internal@viewdef_geometric_mean(_x:uint256[3])->uint256:# calculates a geometric mean for three numbers.prod:uint256=unsafe_div(unsafe_div(_x[0]*_x[1],10**18)*_x[2],10**18)ifprod==0:return0returnself._cbrt(prod)@internal@puredef_cbrt(x:uint256)->uint256:xx:uint256=0ifx>=115792089237316195423570985008687907853269*10**18:xx=xelifx>=115792089237316195423570985008687907853269:xx=unsafe_mul(x,10**18)else:xx=unsafe_mul(x,10**36)log2x:int256=convert(self._snekmate_log_2(xx,False),int256)# When we divide log2x by 3, the remainder is (log2x % 3).# So if we just multiply 2**(log2x/3) and discard the remainder to calculate our# guess, the newton method will need more iterations to converge to a solution,# since it is missing that precision. It's a few more calculations now to do less# calculations later:# pow = log2(x) // 3# remainder = log2(x) % 3# initial_guess = 2 ** pow * cbrt(2) ** remainder# substituting -> 2 = 1.26 ≈ 1260 / 1000, we get:## initial_guess = 2 ** pow * 1260 ** remainder // 1000 ** remainderremainder:uint256=convert(log2x,uint256)%3a:uint256=unsafe_div(unsafe_mul(pow_mod256(2,unsafe_div(convert(log2x,uint256),3)),# <- powpow_mod256(1260,remainder),),pow_mod256(1000,remainder),)# Because we chose good initial values for cube roots, 7 newton raphson iterations# are just about sufficient. 6 iterations would result in non-convergences, and 8# would be one too many iterations. Without initial values, the iteration count# can go up to 20 or greater. The iterations are unrolled. This reduces gas costs# but takes up more bytecode:a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)a=unsafe_div(unsafe_add(unsafe_mul(2,a),unsafe_div(xx,unsafe_mul(a,a))),3)ifx>=115792089237316195423570985008687907853269*10**18:a=unsafe_mul(a,10**12)elifx>=115792089237316195423570985008687907853269:a=unsafe_mul(a,10**6)returna
Function to calculate the reduction coefficient for x and fee_gamma. This method is used for calculating fees.
Returns: reduction coefficient (uint256).
Input
Type
Description
x
uint256[N_COINS]
Values of x
fee_gamma
uint256
Fee gamma
Source code
@external@viewdefreduction_coefficient(x:uint256[N_COINS],fee_gamma:uint256)->uint256:""" @notice Calculates the reduction coefficient for the given x and fee_gamma @dev This method is used for calculating fees. @param x The x values @param fee_gamma The fee gamma value """returnself._reduction_coefficient(x,fee_gamma)@internal@puredef_reduction_coefficient(x:uint256[N_COINS],fee_gamma:uint256)->uint256:# fee_gamma / (fee_gamma + (1 - K))# where# K = prod(x) / (sum(x) / N)**N# (all normalized to 1e18)S:uint256=x[0]+x[1]+x[2]# Could be good to pre-sort x, but it is used only for dynamic feeK:uint256=10**18*N_COINS*x[0]/SK=unsafe_div(K*N_COINS*x[1],S)# <- unsafe div is safu.K=unsafe_div(K*N_COINS*x[2],S)iffee_gamma>0:K=fee_gamma*10**18/(fee_gamma+10**18-K)returnK
@external@viewdefwad_exp(_power:int256)->uint256:""" @notice Calculates the e**x with 1e18 precision @param _power The number to calculate the exponential of """returnself._snekmate_wad_exp(_power)@internal@puredef_snekmate_wad_exp(x:int256)->uint256:""" @dev Calculates the natural exponential function of a signed integer with a precision of 1e18. @notice Note that this function consumes about 810 gas units. The implementation is inspired by Remco Bloemen's implementation under the MIT license here: https://xn--2-umb.com/22/exp-ln. @dev This implementation is derived from Snekmate, which is authored by pcaversaccio (Snekmate), distributed under the AGPL-3.0 license. https://github.com/pcaversaccio/snekmate @param x The 32-byte variable. @return int256 The 32-byte calculation result. """value:int256=x# If the result is `< 0.5`, we return zero. This happens when we have the following:# "x <= floor(log(0.5e18) * 1e18) ~ -42e18".if(x<=-42139678854452767551):returnempty(uint256)# When the result is "> (2 ** 255 - 1) / 1e18" we cannot represent it as a signed integer.# This happens when "x >= floor(log((2 ** 255 - 1) / 1e18) * 1e18) ~ 135".assertx<135305999368893231589,"wad_exp overflow"# `x` is now in the range "(-42, 136) * 1e18". Convert to "(-42, 136) * 2 ** 96" for higher# intermediate precision and a binary base. This base conversion is a multiplication with# "1e18 / 2 ** 96 = 5 ** 18 / 2 ** 78".value=unsafe_div(x<<78,5**18)# Reduce the range of `x` to "(-½ ln 2, ½ ln 2) * 2 ** 96" by factoring out powers of two# so that "exp(x) = exp(x') * 2 ** k", where `k` is a signer integer. Solving this gives# "k = round(x / log(2))" and "x' = x - k * log(2)". Thus, `k` is in the range "[-61, 195]".k:int256=unsafe_add(unsafe_div(value<<96,54916777467707473351141471128),2**95)>>96value=unsafe_sub(value,unsafe_mul(k,54916777467707473351141471128))# Evaluate using a "(6, 7)"-term rational approximation. Since `p` is monic,# we will multiply by a scaling factor later.y:int256=unsafe_add(unsafe_mul(unsafe_add(value,1346386616545796478920950773328),value)>>96,57155421227552351082224309758442)p:int256=unsafe_add(unsafe_mul(unsafe_add(unsafe_mul(unsafe_sub(unsafe_add(y,value),94201549194550492254356042504812),y)>>96,\
28719021644029726153956944680412240),value),4385272521454847904659076985693276<<96)# We leave `p` in the "2 ** 192" base so that we do not have to scale it up# again for the division.q:int256=unsafe_add(unsafe_mul(unsafe_sub(value,2855989394907223263936484059900),value)>>96,50020603652535783019961831881945)q=unsafe_sub(unsafe_mul(q,value)>>96,533845033583426703283633433725380)q=unsafe_add(unsafe_mul(q,value)>>96,3604857256930695427073651918091429)q=unsafe_sub(unsafe_mul(q,value)>>96,14423608567350463180887372962807573)q=unsafe_add(unsafe_mul(q,value)>>96,26449188498355588339934803723976023)# The polynomial `q` has no zeros in the range because all its roots are complex.# No scaling is required, as `p` is already "2 ** 96" too large. Also,# `r` is in the range "(0.09, 0.25) * 2**96" after the division.r:int256=unsafe_div(p,q)# To finalise the calculation, we have to multiply `r` by:# - the scale factor "s = ~6.031367120",# - the factor "2 ** k" from the range reduction, and# - the factor "1e18 / 2 ** 96" for the base conversion.# We do this all at once, with an intermediate result in "2**213" base,# so that the final right shift always gives a positive value.# Note that to circumvent Vyper's safecast feature for the potentially# negative parameter value `r`, we first convert `r` to `bytes32` and# subsequently to `uint256`. Remember that the EVM default behaviour is# to use two's complement representation to handle signed integers.returnunsafe_mul(convert(convert(r,bytes32),uint256),3822833074963236453042738258902158003155416615667)>>convert(unsafe_sub(195,k),uint256)