Function to calculate the logarithm base 2 of x, following the selected rounding direction.
Returns: 32-byte calculation result (uint256).
Input
Type
Description
x
uint256
32-byte variable
roundup
bool
Whether to round up or not. Default is False to round down
Source code
@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 find the D invariant using the Newton method.
Returns: D invariant (uint256).
Input
Type
Description
_ANN
uint256
Amplification coefficient adjusted by N; _ANN = A * N^N.
gamma
uint256
Gamma value.
x_unsorted
uint256[N_COINS]
Unsorted array of the pool balances.
K0_prev
uint256
A priori for Newton's method derived from get_y_int. Defaults to zero if no a priori is provided.
Source code
@external@viewdefnewton_D(ANN:uint256,gamma:uint256,x_unsorted:uint256[N_COINS],K0_prev:uint256=0)->uint256:""" Finding the invariant using Newton method. ANN is higher by the factor A_MULTIPLIER ANN is already A * N**N """# Safety checksassertANN>MIN_A-1andANN<MAX_A+1# dev: unsafe values Aassertgamma>MIN_GAMMA-1andgamma<MAX_GAMMA+1# dev: unsafe values gamma# Initial value of invariant D is that for constant-product invariantx:uint256[N_COINS]=x_unsortedifx[0]<x[1]:x=[x_unsorted[1],x_unsorted[0]]assertx[0]>10**9-1andx[0]<10**15*10**18+1# dev: unsafe values x[0]assertunsafe_div(x[1]*10**18,x[0])>10**14-1# dev: unsafe values x[i] (input)S:uint256=unsafe_add(x[0],x[1])# can unsafe add here because we checked x[0] boundsD:uint256=0ifK0_prev==0:D=N_COINS*isqrt(unsafe_mul(x[0],x[1]))else:# D = isqrt(x[0] * x[1] * 4 / K0_prev * 10**18)D=isqrt(unsafe_mul(unsafe_div(unsafe_mul(unsafe_mul(4,x[0]),x[1]),K0_prev),10**18))ifS<D:D=S__g1k0:uint256=gamma+10**18diff:uint256=0foriinrange(255):D_prev:uint256=DassertD>0# Unsafe division by D and D_prev is now safe# K0: uint256 = 10**18# for _x in x:# K0 = K0 * _x * N_COINS / D# collapsed for 2 coinsK0:uint256=unsafe_div(unsafe_div((10**18*N_COINS**2)*x[0],D)*x[1],D)_g1k0:uint256=__g1k0if_g1k0>K0:_g1k0=unsafe_add(unsafe_sub(_g1k0,K0),1)# > 0else:_g1k0=unsafe_add(unsafe_sub(K0,_g1k0),1)# > 0# D / (A * N**N) * _g1k0**2 / gamma**2mul1:uint256=unsafe_div(unsafe_div(unsafe_div(10**18*D,gamma)*_g1k0,gamma)*_g1k0*A_MULTIPLIER,ANN)# 2*N*K0 / _g1k0mul2:uint256=unsafe_div(((2*10**18)*N_COINS)*K0,_g1k0)# calculate neg_fprime. here K0 > 0 is being validated (safediv).neg_fprime:uint256=(S+unsafe_div(S*mul2,10**18))+mul1*N_COINS/K0-unsafe_div(mul2*D,10**18)# D -= f / fprime; neg_fprime safediv being validatedD_plus:uint256=D*(neg_fprime+S)/neg_fprimeD_minus:uint256=unsafe_div(D*D,neg_fprime)if10**18>K0:D_minus+=unsafe_div(unsafe_div(D*unsafe_div(mul1,neg_fprime),10**18)*unsafe_sub(10**18,K0),K0)else:D_minus-=unsafe_div(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)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)ifdiff*10**14<max(10**16,D):# Could reduce precision for gas efficiency herefor_xinx:frac:uint256=_x*10**18/Dassert(frac>=10**16-1)and(frac<10**20+1)# dev: unsafe values x[i]returnDraise"Did not converge"
Function to calculate dx/dy. The output needs to be multiplied with price_scale to get the actual value. The function is externally called form a twocrypto-ng pools when prices are tweaked via tweak_price.
Returns: dx/dy (uint256).
Input
Type
Description
_xp
uint256[N_COINS]
Balances of the pool.
_D
uint256
Current value of D.
_A_gamma
uint256[N_COINS]
Amplification coefficient and gamma.
Source code
@external@viewdefget_p(_xp:uint256[N_COINS],_D:uint256,_A_gamma:uint256[N_COINS])->uint256:""" @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(4*_xp[0]*_xp[1],_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 is in 10**18 precision.returnunsafe_div(_xp[0]*(GK0+unsafe_div(unsafe_div(NNAG2*_xp[1],_D)*K0,10**36))/_xp[1]*10**18,denominator)
Function to calculate the natural exponential function of a signed integer with a precision of 1e18.
Returns: 32-byte calculation result (int256).
Input
Type
Description
x
int256
32-byte variable
Source code
@external@puredefwad_exp(x:int256)->int256:""" @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. @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<=-42_139_678_854_452_767_551):returnempty(int256)# 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<135_305_999_368_893_231_589,"Math: 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,54_916_777_467_707_473_351_141_471_128),2**95)>>96value=unsafe_sub(value,unsafe_mul(k,54_916_777_467_707_473_351_141_471_128))# 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,1_346_386_616_545_796_478_920_950_773_328),value)>>96,57_155_421_227_552_351_082_224_309_758_442)p:int256=unsafe_add(unsafe_mul(unsafe_add(unsafe_mul(unsafe_sub(unsafe_add(y,value),94_201_549_194_550_492_254_356_042_504_812),y)>>96,\
28_719_021_644_029_726_153_956_944_680_412_240),value),4_385_272_521_454_847_904_659_076_985_693_276<<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,2_855_989_394_907_223_263_936_484_059_900),value)>>96,50_020_603_652_535_783_019_961_831_881_945)q=unsafe_sub(unsafe_mul(q,value)>>96,533_845_033_583_426_703_283_633_433_725_380)q=unsafe_add(unsafe_mul(q,value)>>96,3_604_857_256_930_695_427_073_651_918_091_429)q=unsafe_sub(unsafe_mul(q,value)>>96,14_423_608_567_350_463_180_887_372_962_807_573)q=unsafe_add(unsafe_mul(q,value)>>96,26_449_188_498_355_588_339_934_803_723_976_023)# 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.returnconvert(unsafe_mul(convert(convert(r,bytes32),uint256),3_822_833_074_963_236_453_042_738_258_902_158_003_155_416_615_667)>>\
convert(unsafe_sub(195,k),uint256),int256)