diff --git a/fastlane_bot/helpers/poolandtokens.py b/fastlane_bot/helpers/poolandtokens.py index ce57058e1..c6ac1f61d 100644 --- a/fastlane_bot/helpers/poolandtokens.py +++ b/fastlane_bot/helpers/poolandtokens.py @@ -493,6 +493,7 @@ def decimal_converter(idx): return lst FEE_LOOKUP = { + 0.000001: Univ3Calculator.FEE1, 0.000008: Univ3Calculator.FEE8, 0.00001: Univ3Calculator.FEE10, 0.00004: Univ3Calculator.FEE40, diff --git a/fastlane_bot/helpers/univ3calc.py b/fastlane_bot/helpers/univ3calc.py index c0750d185..f3a5e6920 100644 --- a/fastlane_bot/helpers/univ3calc.py +++ b/fastlane_bot/helpers/univ3calc.py @@ -24,6 +24,7 @@ class Univ3Calculator(): __VERSION__ = __VERSION__ __DATE__ = __DATE__ + FEE1 = 1 FEE8 = 8 FEE10 = 10 FEE40 = 40 @@ -39,6 +40,7 @@ class Univ3Calculator(): FEE10000 = 10000 TICKSZ = { + FEE1: 1, FEE8: 1, FEE10: 1, FEE40: 8, @@ -136,6 +138,7 @@ def __post_init__(self, tkn0decv=None, tkn1decv=None, addrdec=None): super().__setattr__('liquidity', int(self.liquidity)) super().__setattr__('fee_const', int(self.fee_const)) assert self.fee_const in { + self.FEE1, self.FEE8, self.FEE10, self.FEE40, @@ -149,7 +152,7 @@ def __post_init__(self, tkn0decv=None, tkn1decv=None, addrdec=None): self.FEE2500, self.FEE3000, self.FEE10000, - }, "fee not one of the FEEXXX constants {self.fee_const}" + }, f"fee not one of the FEEXXX constants {self.fee_const}" assert not self.tkn0dec is None, "tkn0dec is None" assert not self.tkn1dec is None, "tkn1dec is None" diff --git a/fastlane_bot/tools/univ3calc_DELETE.py b/fastlane_bot/tools/univ3calc_DELETE.py deleted file mode 100644 index b5d6a5aac..000000000 --- a/fastlane_bot/tools/univ3calc_DELETE.py +++ /dev/null @@ -1,253 +0,0 @@ -""" -translating Uniswap v3 contract values - -(c) Copyright Bprotocol foundation 2023. -Licensed under MIT - -NOTE: this class is not part of the API of the Carbon protocol, and you must expect breaking -changes even in minor version updates. Use at your own risk. -""" -__VERSION__ = "1.1" -__DATE__ = "19/Apr/2023" - -from math import sqrt -from dataclasses import dataclass, InitVar, asdict - - -@dataclass(frozen=True) -class Univ3Calculator: - __VERSION__ = __VERSION__ - __DATE__ = __DATE__ - - FEE100 = 100 - FEE500 = 500 - FEE3000 = 3000 - FEE10000 = 10000 - - TICKSZ = {FEE100: 1, FEE500: 10, FEE3000: 60, FEE10000: 200} - - Q96 = 2**96 - Q192 = 2**192 - - tkn0: str - tkn1: str - sp96: int # sqrt_price_q96 - tick: int - liquidity: int - fee_const: int - - tkn0decv: InitVar[int] = None - tkn1decv: InitVar[int] = None - - @classmethod - def from_dict(cls, d, fee_const): - return cls( - tkn0=d["token0"], - tkn1=d["token1"], - sp96=d["sqrt_price_q96"], - tick=d["tick"], - liquidity=d["liquidity"], - fee_const=fee_const, - ) - - ADDRDEC = dict( - USDC=("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", 6), - WETH=("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 18), - ) - ADDRR = {v[0]: k for k, v in ADDRDEC.items()} - - def __post_init__(self, tkn0decv=None, tkn1decv=None): - try: - super().__setattr__("tkn0", self.ADDRR[self.tkn0]) - super().__setattr__("tkn1", self.ADDRR[self.tkn1]) - except Exception as e: - pass - - if tkn0decv is None: - super().__setattr__("_tkn0dec", self.ADDRDEC[self.tkn0][1]) - else: - super().__setattr__("_tkn0dec", tkn0decv) - if tkn1decv is None: - super().__setattr__("_tkn1dec", self.ADDRDEC[self.tkn1][1]) - else: - super().__setattr__("_tkn1dec", tkn1decv) - super().__setattr__("sp96", int(self.sp96)) - super().__setattr__("tick", int(self.tick)) - super().__setattr__("liquidity", int(self.liquidity)) - super().__setattr__("fee_const", int(self.fee_const)) - assert self.fee_const in { - self.FEE100, - self.FEE500, - self.FEE3000, - self.FEE10000, - }, "fee not one of the FEEXXX constants {self.fee_const}" - assert not self.tkn0dec is None, "tkn0dec is None" - assert not self.tkn1dec is None, "tkn1dec is None" - - @property - def pair(self): - """the directed pair tknb/tknq = tkn0/tkn1""" - return f"{self.tkn0}/{self.tkn1}" - - @property - def fee(self): - """fee in basis points""" - return self.fee_const / 1000000 - - @property - def ticksize(self): - """tick size""" - return self.TICKSZ[self.fee_const] - - @property - def tickab(self): - """returns the tick values of Pa and Pb""" - ticka = (self.tick // self.ticksize) * self.ticksize - return ticka, ticka + self.ticksize - - @property - def tkn0dec(self): - """token 0 decimals""" - return self._tkn0dec - - @property - def tkn1dec(self): - """token 1 decimals""" - return self._tkn1dec - - @property - def papb_raw(self): - """raw Pa and Pb values (1.0001**tickab)""" - ta, tb = self.tickab - return (1.0001**ta, 1.0001**tb) - - @property - def papb_tkn1_per_tkn0(self): - """Pa and Pb values in units of token 1 per token 0""" - par, pbr = self.papb_raw - return (par * self.decf, pbr * self.decf) - - papb = papb_tkn1_per_tkn0 - - @property - def papb_tkn0_per_tkn1(self): - """Pa and Pb values in units of token 0 per token 1""" - pa, pb = self.papb_tkn1_per_tkn0 - return (1 / pa, 1 / pb) - - @property - def dec_factor_wei0_per_wei1(self): - """token wei of token 0 per token wei of token 1 at price=1""" - return 10 ** (self.tkn0dec - self.tkn1dec) - - decf = dec_factor_wei0_per_wei1 - - @classmethod - def _price_f(cls, sp96): - """price tkn1 per tkn0 in wei units""" - return sp96**2 / cls.Q192 - - @property - def price_tkn1_per_tkn0(self): - """price of token 1 per token 0 in token units""" - return self._price_f(self.sp96) * self.decf - - p = price_tkn1_per_tkn0 - - @property - def price_tkn0_per_tkn1(self): - """price of token 0 per token 1 in token units""" - return 1 / self.price_tkn1_per_tkn0 - - @property - def price_convention(self): - return f"{self.tkn0}/{self.tkn1} [{self.tkn1} per {self.tkn0}]" - - @classmethod - def sp96_from_price(cls, price): - """calculate sp96 = sqrt(p) * Q96""" - return sqrt(price) * cls.Q96 - - @property - def L(self): - """the Uniswap L value, in token units; L**2=k, and k=xy where x,y are virtual token amounts""" - return self.liquidity / 10 ** (0.5 * (self.tkn0dec + self.tkn1dec)) - - @property - def Lsquared(self): - """convenience methods for self.L**2 in token units""" - return self.L**2 - - L2 = Lsquared - - @property - def k(self): - """x*y=k, where x,y are virtual token amounts; k=L**2""" - return self.L**2 - - @property - def kbar(self): - """kbar = sqrt(k) = L; kbar = sqrt(xy)""" - return self.L - - def tkn0reserve(self, *, incltoken=False): - """returns the token 0 reserve in token units""" - _, pb = self.papb - sqrtPb = sqrt(pb) - sqrtP = sqrt(self.p) - tkn0reserve = self.liquidity * (sqrtPb - sqrtP) / (sqrtP * sqrtPb) - tkn0reserve *= self.decf - if not incltoken: - return tkn0reserve - return (tkn0reserve, self.tkn0) - - def tkn1reserve(self, *, incltoken=False): - """returns the token 1 reserve in token units""" - pa, _ = self.papb - sqrtPa = sqrt(pa) - sqrtP = sqrt(self.p) - tkn1reserve = self.liquidity * (sqrtP - sqrtPa) - tkn1reserve *= self.decf - if not incltoken: - return tkn1reserve - return (tkn1reserve, self.tkn1) - - def tvl(self, *, astkn0=False, incltoken=False): - """returns the total value locked in the pool""" - tkn0reserve, tkn1reserve = self.tkn0reserve(), self.tkn1reserve() - tvl_tkn0 = tkn0reserve + tkn1reserve * self.price_tkn0_per_tkn1 - tvl = tvl_tkn0 if astkn0 else tvl_tkn0 * self.price_tkn1_per_tkn0 - if not incltoken: - return tvl - return (tvl, self.tkn0 if astkn0 else self.tkn1) - - def cpc_params(self, **kwargs): - """ - returns a kwarg dict suitable for CPC.from_univ3 - - :kwargs: additional kwargs to return - """ - pa, pb = self.papb - result = dict( - Pmarg=self.p, - uniL=self.L, - uniPa=pa, - uniPb=pb, - pair=self.pair, - fee=self.fee, - **kwargs, - ) - return result - - def info(self): - pa, pb = self.papb - p = self.p - s = f"Uniswap v3 Range {self.tkn0}/{self.tkn1} (fee={self.fee*100:.02f}%)\n" - s += f" Pa = {pa:12,.3f} P={p:12,.3f} Pb = {pb:12,.3f} {self.tkn1} per {self.tkn0}\n" - s += f"1/Pa = {1/pa:12,.3f} 1/P={1/p:12,.3f} 1/Pb = {1/pb:12,.3f} {self.tkn0} per {self.tkn1}\n---\n" - - s += f" full P = {p}, full 1/P = {1/p}\n" - return s - - -U3 = Univ3Calculator